Feature #1400

Please add a method to enumerate fields in OpenStruct

Added by Tomas Pospisek almost 3 years ago. Updated 7 months ago.

Status:Assigned Start date:04/23/2009
Priority:Normal Due date:
Assignee:Yukihiro Matsumoto % Done:

0%

Category:lib
Target version:2.0.0

Description

There are two ways to find out what fields an OpenStruct instance has. One is through inspect,
however that returns a String that needs to be parsed.

The second is by white box engineering, looking at OpenStructs source code and seeing that in
fact, it has a hash and getting the keys of that hash...

The second way is faster, more robust, but will break once OpenStruct will be re-engineered...

So I suggest to add an explicit method to return a list of fields in an OpenStruct instance:

--- ostruct.rb.old	2009-04-23 15:26:45.000000000 +0200
+++ ostruct.rb	2009-04-23 15:32:41.000000000 +0200
@@ -110,6 +110,15 @@
     @table.delete name.to_sym
   end

+  #
+  # Returns an Array containing the fields of an OpenStruct instance
+  #
+  # p record.fields # -> [:age, :pension, :name]
+  #
+  def fields
+    @table.keys
+  end
+
   InspectKey = :__inspect_key__ # :nodoc:

   #

History

Updated by Tomas Pospisek almost 3 years ago

A usage example and context for the feature request here: http://www.sourcepole.ch/2009/4/23/what-fields-does-this-openstruct-instance-have

Updated by Marc-Andre Lafortune over 2 years ago

  • Category set to lib
I believe this could be useful.

#members might be a better name, because it is similar in role to Struct.members

The problem is that any new method is a potential compatibility break. #__members__ ?

Updated by Dan Rathbun almost 2 years ago

This can be done right now. In several ways.
# obj is an class OpenStruct with some number of members.
ary = obj.methods(false).sort -> Array of attr getters and setters
keys=[]
ary.each {|i| keys.push(i) unless i.include?('=') }
# keys is now an Array of fieldnames

Also using existing method .marshal_dump which is really an attribute getter for @table (having a name different than the attribute.)
Just noticed .table as a attribute getter alias of .marshal_dump, BUT .table is set protected, while .marshal_dump is not?
Why is THAT?

# obj is an class OpenStruct with some number of members.
obj.marshal_dump.keys -> Array of keys
obj.marshal_dump.values -> Array of values
obj.marshal_dump.each {|k,v| ... }

The danger in accessing the Hash directly is someone's going to modify it without removing the attr getter and setter methods defined in the class instance. Which by the way is a bug in the current OpenStruct. It's delete_field method does just that, leaving the accessor methods without a key/value pair in @table.

There is also an opposite bug. IF an attempt is made, to create a field who's name is already used as a method (without the '='); new_ostruct_member does NOT create the accessor methods, BUT does NOT return a indicator of success, so method_missing just goes ahead and adds the field to @table.
Ex:
obj.inspect='Sherlock Holmes'
The unless block (line 72) in new_ostruct_member needs an else clause thats raises a NameError Exception "#{name} is already in use as a method of #{self}."

Additionally, .marshal_load is flawed. It wipes out @table instead of appending; and puts everything in @table before checking it. The arg x should be typechecked as an OpenStruct or Hash; it should be put first in a temp reference. Looks like another argument may be needed for overwrite/ignore of matching keys.

Updated by Kazuhiro NISHIYAMA almost 2 years ago

  • Category set to lib
  • Target version set to 2.0.0

Updated by Yui NARUSE 7 months ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto

Also available in: Atom PDF