aetherknight / recursive-open-struct

OpenStruct subclass that returns nested hash attributes as RecursiveOpenStructs
Other
276 stars 54 forks source link

Can't have numbers as methods #36

Closed ryanjones closed 7 years ago

ryanjones commented 9 years ago

I don't think this is a problem with the gem itself, but if a hash starts with a number you can't access the RecursiveOpenStruct's below it. http://stackoverflow.com/questions/10542354/what-are-the-restrictions-for-method-names-in-ruby

It would be cool to have an option to put a string at the front (or something that allows access below). I just flipped it back to a hash and was fine.

#Broken:

{"0"=>
  {"id"=>"",
   "coverage_id"=>"555",
   "first_name"=>"Ryan ",
   "last_name"=>"Jones",
   "birth_date(2i)"=>"11",
   "birth_date(3i)"=>"10",
   "birth_date(1i)"=>"1940"}
  }
}

#Some type of fix?

{"a0"=>
  {"id"=>"",
   "coverage_id"=>"555",
   "first_name"=>"Ryan ",
   "last_name"=>"Jones",
   "birth_date(2i)"=>"11",
   "birth_date(3i)"=>"10",
   "birth_date(1i)"=>"1940"}
  }
}```
ryanjones commented 9 years ago

Just to add, same issue w/ birth_date(2i)

aetherknight commented 8 years ago

This isn't something that OpenStruct supports, and it isn't an edge case introduced by ROS. You can use send or public_send though to call methods that contain characters with special syntactic meaning to Ruby. Eg:

r = RecursiveOpenStruct.new({
  "0"=>{
    "id"=>"",
    "coverage_id"=>"555",
    "first_name"=>"Ryan ",
    "last_name"=>"Jones",
    "birth_date(2i)"=>"11",
    "birth_date(3i)"=>"10",
    "birth_date(1i)"=>"1940"
  }
})
# => #<RecursiveOpenStruct 0={:id=>"", :coverage_id=>"555", :first_name=>"Ryan ", :last_name=>"Jones", :"birth_date(2i)"=>"11", :"birth_date(3i)"=>"10", :"birth_date(1i)"=>"1940"}>

r.public_send('0')
# => #<RecursiveOpenStruct id="", coverage_id="555", first_name="Ryan ", last_name="Jones", birth_date(2i)="11", birth_date(3i)="10", birth_date(1i)="1940">

r.public_send('0').public_send('birth_date(1i)')
# => "1940"

That said, I would be willing to consider a PR that optionally adds a prefix that would apply to all keys (although this could be done by preprocessing the hash before passing it to ROS). However, it would not solve your problem with birth_date(2i) --- that would require public_send or mangling the key through some sort of pre-processing of all keys.

A prefix would also make it easier/safer to access methods from Object/BasicObject, which might lead to less strange behavior if you try to use RecursiveOpenStruct on untrusted input (I can't think of a clear code-execution scenario, but untrusted input could certainly break code or introduce business logic bypasses if methods like class are trusted):

r = RecursiveOpenStruct.new({ 'class' => :foo, 'bar' => :baz }, prefix_keys_with: 'prefix_')
r.class # => RecursiveOpenStruct
r.prefix_class # => :foo
aetherknight commented 7 years ago

Closing this issue b/c there hasn't been any activity in almost 2 years, and I have no plans to implement this feature myself.

Anyone wanting something like this with ROS: If you want/need to use characters that are not permitted in method names, or if you want to dynamically lookup values with send/public_send, you should use subscript notation instead. You should also think hard about whether ROS is the right tool for what you are trying to do. (IE, it might make sense as a simple mock/double with deeply nested objects and it is nice for configuration files, but it is not entirely equivalent to a JavaScript-style Object aside from trivial use-cases).