Bug #15409

OpenStruct error when attribute is called 'method'

Added by elioncho (ElĂ­as Orozco) about 1 month ago. Updated about 1 month ago.

Target version:
ruby -v:
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin16]


The following error is shown when you try to access an OpenStruct with a property called method:

`method': wrong number of arguments (given 0, expected 1) (ArgumentError)

To replicate:

require 'ostruct'
o ={ method: 'get' })

The expected behavior should be to return 'get'


Updated by oleynikov (Alexander Oleynikov) about 1 month ago

According to the docs, OpenStruct uses method_missing, so it does not redefine existing methods of Object or BasicObject.

o =
  method:  'method',
  class:   'class',
  display: 'display',
  send:    'send',
  __id__:  '__id__',
  frozen?: 'frozen?'

o.method  #=> ArgumentError (wrong number of arguments (given 0, expected 1))
o.class   #=> OpenStruct
o.display #=> #<OpenStruct method="method", class="class", display="display"...
o.send    #=> ArgumentError (no method name given)
o.__id__  #=> 7539827944720
o.frozen? #=> false

Updated by mame (Yusuke Endoh) about 1 month ago

  • Assignee set to marcandre (Marc-Andre Lafortune)
  • Status changed from Open to Assigned

Yes, the current behavior is intentional. OpenStruct prohibits redefinition of the superclass methods.

However, the current spec that prohibits overwrite is fragile against newly introduced methods to Object class. For example, Object#then is planned to be introduced in Ruby 2.6. It breaks OpenStruct({ :then => 42 }) which worked well in Ruby 2.5.

I considered this issue with some committers, and found two possible solutions:

  1. Just warn if a specified key name conflicts with any method of Object class. This does not solve the issue itself, but a user can notice the breakage.
  2. Allow overwrite. This solves the issue. But if a user gives untrusted input as a key of OpenStruct, an attacker might be able to overwrite some basic methods (for example, Object#dup, Object#object_id, etc.), which might lead to a vulnerability of the application. (It would be very rare, I guess, though.)

marcandre (Marc-Andre Lafortune), a maintainer of OpenStruct, what do you think?

Updated by marcandre (Marc-Andre Lafortune) about 1 month ago

I don't have a good solution.

OpenStruct is kind of an anti-pattern. This is even more apparent when adding methods to Object/Kernel.

I note that Struct allows overriding builtin methods:, keyword_init: true).new(method: :foo).method # => :foo

I'd be tempted to allow overriding of methods in OpenStruct for setters (e.g. method=), but not very confident about it.

Even if we allow overriding like this, this could still yield to potentially unexpected results.

o =
o.then # => nil in Ruby 2.5, Enumerator in Ruby 2.6
o.then = :foo  # (assuming we allow overriding from setters)
o.then # => :foo

If the object is inited with the data like :foo), then both versions would at least behave the same.

Updated by shevegen (Robert A. Heiler) about 1 month ago

I think it should be overridable and a warning could be issued for those methods that would lead to breakage. I don't really use Struct and OpenStruct much at all these days though - I tend to just define what I need via simple classes.

Updated by mame (Yusuke Endoh) about 1 month ago

OpenStruct is kind of an anti-pattern.

Completely agreed. I'd like to prohibit the library itself, honestly.

I note that Struct allows overriding builtin methods:

Interesting. I believe no one passes untrusted keys to Struct. But I heard that OpenStruct is used for JSON.

o =
o.then # => nil in Ruby 2.5, Enumerator in Ruby 2.6

Ah... It's just a design flaw.

Also available in: Atom PDF