Project

General

Profile

Bug #12136

OpenStruct.new(format: :bar).send :format # => too few arguments

Added by niko (Niko Dittmann) over 1 year ago. Updated over 1 year ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
[ruby-core:74094]

Description

#send(:format) to an OpenStruct with a field named :format raises an ArgumentError in Ruby 2.3.0:

OpenStruct.new(format: :bar).send :format
ArgumentError: too few arguments

It works as expected in ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux] and with any other method name I tried:

OpenStruct.new(f: :bar).send :f
=> :bar

String or Symbol in the OpenStruct definition and as argument of #send make no difference.


Related issues

Related to Ruby trunk - Bug #12251: DelegateClass(OpenStruct) behavior in 2.3.0 different from 2.2Open

History

#1 [ruby-core:74097] Updated by niko (Niko Dittmann) over 1 year ago

It's this commit: https://github.com/ruby/ruby/blob/7fa21558051e5412dcb790f528e392476edd4389/lib/ostruct.rb

By defining the getters and setters lazily the Kernel, Object and BasicObject instance methods shine through and #method_missing doesn't kick in. Therefor the #send semantics is broken for methods colliding with methods defined in parent classes.

#2 [ruby-core:74123] Updated by marcandre (Marc-Andre Lafortune) over 1 year ago

Indeed, latest optimization of OpenStruct now allows conflicts with Object private methods.

I didn't realize it, but conflicts with public methods are already ignored (i.e. OpenStruct.new(hash: 'code').hash does not return 'code)

Note that OpenStruct.new(format: :bar).public_send :format does return :bar.

Possibilities:

a) Keep behavior the same and rubyists can alleviate these by using public_send instead of send

b) Modify new to check for conflict between keys and Object private instance methods and define actual methods in these cases.

c) Revert optimization. Optionally create OpenStruct.lazy for the optimized version.

I'm in favor for the later, but maybe I'm missing alternatives?

BTW, I thought at first that we could undefine private instance methods of OpenStruct, except for the usual callbacks and modify respond_to_missing? + method_missing so that calls to these private methods still work.

Sadly, there's no way to know from method_missing if that method is called privately or publicly, so this would effectively make all private methods become public which is not acceptable.

#3 [ruby-core:74134] Updated by Eregon (Benoit Daloze) over 1 year ago

Marc-Andre Lafortune wrote:

Sadly, there's no way to know from method_missing if that method is called privately or publicly, so this would effectively make all private methods become public which is not acceptable.

There is a way now, mentioned in #12113. I'm not sure whether it is good idea to use it, though.

#4 Updated by marcandre (Marc-Andre Lafortune) over 1 year ago

  • Related to Bug #12251: DelegateClass(OpenStruct) behavior in 2.3.0 different from 2.2 added

#5 [ruby-core:74833] Updated by dblock (Daniel Doubrovkine) over 1 year ago

a) Keep behavior the same and rubyists can alleviate these by using public_send instead of send

It doesn't seem that swapping send by public_send has any effect, at least not in the example in #12251.

Is there a workaround for existing code that would make things work the way it worked in Ruby 2.2.x?

#6 Updated by marcandre (Marc-Andre Lafortune) over 1 year ago

  • Has duplicate Bug #12349: Can't load OpenStruct with Syck with Ruby 2.3.x added

#7 Updated by marcandre (Marc-Andre Lafortune) over 1 year ago

  • Has duplicate deleted (Bug #12349: Can't load OpenStruct with Syck with Ruby 2.3.x)

Also available in: Atom PDF