ruby-changes:62952
From: Marc-Andre <ko1@a...>
Date: Tue, 15 Sep 2020 01:47:36 +0900 (JST)
Subject: [ruby-changes:62952] 12a2e32d43 (master): [ruby/ostruct] Add access to public instance methods in case they are overriden
https://git.ruby-lang.org/ruby.git/commit/?id=12a2e32d43 From 12a2e32d43256e37d36903c5fa5fabe556337d84 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune <github@m...> Date: Tue, 8 Sep 2020 17:13:29 -0400 Subject: [ruby/ostruct] Add access to public instance methods in case they are overriden diff --git a/lib/ostruct.rb b/lib/ostruct.rb index c6f3be0..d674274 100644 --- a/lib/ostruct.rb +++ b/lib/ostruct.rb @@ -62,8 +62,7 @@ https://github.com/ruby/ruby/blob/trunk/lib/ostruct.rb#L62 # first_pet # => #<OpenStruct name="Rowdy"> # first_pet == second_pet # => true # -# -# == Implementation +# == Caveats # # An OpenStruct utilizes Ruby's method lookup structure to find and define the # necessary methods for properties. This is accomplished through the methods @@ -72,10 +71,41 @@ https://github.com/ruby/ruby/blob/trunk/lib/ostruct.rb#L71 # This should be a consideration if there is a concern about the performance of # the objects that are created, as there is much more overhead in the setting # of these properties compared to using a Hash or a Struct. +# Creating an open struct from a small Hash and accessing a few of the +# entries can be 200 times slower than accessing the hash directly. +# +# This may also be the source of incompatibilities between Ruby versions: +# +# o = OpenStruct.new +# o.then # => nil in Ruby < 2.6, enumerator for Ruby >= 2.6 +# +# Builtin methods may be overwritten this way, which may be a source of bugs +# or security issues: +# +# o = OpenStruct.new +# o.methods # => [:to_h, :marshal_load, :marshal_dump, :each_pair, ... +# 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 `!`: +# +# 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 `!`. +# +# For all these reasons, consider not using OpenStruct at all. # class OpenStruct VERSION = "0.2.0" + instance_methods.each do |method| + new_name = "#{method}!" + alias_method new_name, method + end + # # Creates a new OpenStruct object. By default, the resulting OpenStruct # object will have no attributes. diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb index 3c934cc..d07fef3 100644 --- a/test/ostruct/test_ostruct.rb +++ b/test/ostruct/test_ostruct.rb @@ -241,4 +241,9 @@ class TC_OpenStruct < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ostruct/test_ostruct.rb#L241 assert_equal(:foo, os.method) assert_equal(:bar, os.class) end + + def test_access_original_methods + os = OpenStruct.new(method: :foo) + assert_equal(os.object_id, os.method!(:object_id).call) + end end -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/