summaryrefslogtreecommitdiff
path: root/lib/ostruct.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ostruct.rb')
-rw-r--r--lib/ostruct.rb67
1 files changed, 49 insertions, 18 deletions
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index 1203d10e47..c762baa5a5 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -93,21 +93,29 @@
# o.methods = [:foo, :bar]
# o.methods # => [:foo, :bar]
#
-# To help remedy clashes, OpenStruct uses only protected/private methods ending with `!`
-# and defines aliases for builtin public methods by adding a `!`:
+# To help remedy clashes, OpenStruct uses only protected/private methods ending with <code>!</code>
+# and defines aliases for builtin public methods by adding a <code>!</code>:
#
# o = OpenStruct.new(make: 'Bentley', class: :luxury)
# o.class # => :luxury
# o.class! # => OpenStruct
#
-# It is recommended (but not enforced) to not use fields ending in `!`;
+# It is recommended (but not enforced) to not use fields ending in <code>!</code>;
# Note that a subclass' methods may not be overwritten, nor can OpenStruct's own methods
-# ending with `!`.
+# ending with <code>!</code>.
#
# For all these reasons, consider not using OpenStruct at all.
#
class OpenStruct
- VERSION = "0.4.0"
+ VERSION = "0.6.0"
+
+ HAS_PERFORMANCE_WARNINGS = begin
+ Warning[:performance]
+ true
+ rescue NoMethodError, ArgumentError
+ false
+ end
+ private_constant :HAS_PERFORMANCE_WARNINGS
#
# Creates a new OpenStruct object. By default, the resulting OpenStruct
@@ -124,6 +132,10 @@ class OpenStruct
# data # => #<OpenStruct country="Australia", capital="Canberra">
#
def initialize(hash=nil)
+ if HAS_PERFORMANCE_WARNINGS && Warning[:performance]
+ warn "OpenStruct use is discouraged for performance reasons", uplevel: 1, category: :performance
+ end
+
if hash
update_to_values!(hash)
else
@@ -197,7 +209,7 @@ class OpenStruct
# data.each_pair.to_a # => [[:country, "Australia"], [:capital, "Canberra"]]
#
def each_pair
- return to_enum(__method__) { @table.size } unless block_given!
+ return to_enum(__method__) { @table.size } unless defined?(yield)
@table.each_pair{|p| yield p}
self
end
@@ -221,8 +233,17 @@ class OpenStruct
#
def new_ostruct_member!(name) # :nodoc:
unless @table.key?(name) || is_method_protected!(name)
- define_singleton_method!(name) { @table[name] }
- define_singleton_method!("#{name}=") {|x| @table[name] = x}
+ if defined?(::Ractor)
+ getter_proc = nil.instance_eval{ Proc.new { @table[name] } }
+ setter_proc = nil.instance_eval{ Proc.new {|x| @table[name] = x} }
+ ::Ractor.make_shareable(getter_proc)
+ ::Ractor.make_shareable(setter_proc)
+ else
+ getter_proc = Proc.new { @table[name] }
+ setter_proc = Proc.new {|x| @table[name] = x}
+ end
+ define_singleton_method!(name, &getter_proc)
+ define_singleton_method!("#{name}=", &setter_proc)
end
end
private :new_ostruct_member!
@@ -237,7 +258,7 @@ class OpenStruct
if owner.class == ::Class
owner < ::OpenStruct
else
- self.class.ancestors.any? do |mod|
+ self.class!.ancestors.any? do |mod|
return false if mod == ::OpenStruct
mod == owner
end
@@ -273,7 +294,7 @@ class OpenStruct
# :call-seq:
# ostruct[name] -> object
#
- # Returns the value of an attribute, or `nil` if there is no such attribute.
+ # Returns the value of an attribute, or +nil+ if there is no such attribute.
#
# require "ostruct"
# person = OpenStruct.new("name" => "John Smith", "age" => 70)
@@ -308,7 +329,7 @@ class OpenStruct
# Finds and returns the object in nested objects
# that is specified by +name+ and +identifiers+.
# The nested objects may be instances of various classes.
- # See {Dig Methods}[rdoc-ref:doc/dig_methods.rdoc].
+ # See {Dig Methods}[rdoc-ref:dig_methods.rdoc].
#
# Examples:
# require "ostruct"
@@ -347,15 +368,15 @@ class OpenStruct
#
# person.delete_field('number') { 8675_309 } # => 8675309
#
- def delete_field(name)
+ def delete_field(name, &block)
sym = name.to_sym
begin
singleton_class.remove_method(sym, "#{sym}=")
rescue NameError
end
@table.delete(sym) do
- return yield if block_given!
- raise! NameError.new("no field `#{sym}' in #{self}", sym)
+ return yield if block
+ raise! NameError.new("no field '#{sym}' in #{self}", sym)
end
end
@@ -446,13 +467,23 @@ class OpenStruct
update_to_values!(h)
end
- # Make all public methods (builtin or our own) accessible with `!`:
- instance_methods.each do |method|
+ # Make all public methods (builtin or our own) accessible with <code>!</code>:
+ give_access = instance_methods
+ # See https://github.com/ruby/ostruct/issues/30
+ give_access -= %i[instance_exec instance_eval eval] if RUBY_ENGINE == 'jruby'
+ give_access.each do |method|
+ next if method.match(/\W$/)
+
new_name = "#{method}!"
alias_method new_name, method
end
# Other builtin private methods we use:
alias_method :raise!, :raise
- alias_method :block_given!, :block_given?
- private :raise!, :block_given!
+ private :raise!
+
+ # See https://github.com/ruby/ostruct/issues/40
+ if RUBY_ENGINE != 'jruby'
+ alias_method :block_given!, :block_given?
+ private :block_given!
+ end
end