Raise Exception When Accessing Attributes That Doesn't Exist in Openstruct

Raise exception when accessing attributes that doesn't exist in OpenStruct

I went with this solution which does exactly what I need:

class Request < Hash
class RequestError < StandardError; end
class MissingAttributeError < RequestError; end

def initialize(hash)
hash.each do |key, value|
self[key] = value
end
end

def [](key)
unless self.include?(key)
raise MissingAttributeError.new("Attribute '#{key}' not found in request")
end

super
end
end

OpenStruct.new stores attribute but doesn't retrieve it

Something somewhere is pulling in Psych for YAML stuff. Psych patches Kernel to add a psych_y method which is aliased to y. So, everything has a y method defined:

> o = OpenStruct.new
> o.method(:y)
=> #<Method: OpenStruct(Kernel)#psych_y>

AFAIK, OpenStruct uses method_missing and an internal Hash to produce accessor and mutator methods; but, there's already a y from that "friendly" patch to Kernel so OpenStruct's magic doesn't get to handle the y method because Psych's magic is in the way. The mutator, y=, is fine though so you can safely o.y = 11 and see your 11 inside o.

You could remove the y like this:

> o = OpenStruct.new
> o.class_eval('undef_method :y')
> o.y = 11
> o.y
=> 11

You could probably remove the method from Kernel and hope that nothing depends on that silly y alias:

> Kernel.send(:undef_method, :y)
> o = OpenStruct.new
> o.y = 11
> o.y
=> 11

Or you could just remove it from OpenStruct:

> OpenStruct.send(:undef_method, :y)
> o = OpenStruct.new
> o.y = 11
> o.y
=> 11

This sort of thing is why a lot of people don't like monkey patching, especially monkey patching something as fundamental as Kernel.

How can I return nil if a key does not exist in OpenStruct?

OpenStruct is not recursive. In this case TXT.ftp returns a Hash, not an OpenStruct, so #lastname is not defined.

If you want, there is a library called recursive-open-struct. Use it like this:

require 'recursive-open-struct'

TXT = RecursiveOpenStruct.new(TXT_HASH)
pp TXT.ftp.lastname #=> nil

When should I use an OpenStruct instead of a Hash?

I think this mostly comes down to a performance decision. From the Ruby Documentation:

An OpenStruct utilizes Ruby’s method lookup structure to and find and define the necessary methods for properties. This is accomplished through the method method_missing and define_method.

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.

Additionally, something like a Hash has additional functionality with all of the methods it provides (has_key?, include?, etc.). The OpenStruct is a very simple object from that standpoint, but if you don't have any concerns from a performance standpoint and just want an easy object to work with, OpenStruct is a good choice.

Using marshal_load with OpenStruct

The marshal_load method exists to provide support for Marshal.load.

event = OpenStruct.new({ 'time' => Time.now, 'title' => 'Birthday Party' })
binary = Marshal.dump(event)
loaded = Marshal.load(binary) # the OpenStruct

The easiest way to programmatically load a hash into a struct is using send:

event = OpenStruct.new
hash.each do |key, value|
event.send("#{key}=", value)
end


Related Topics



Leave a reply



Submit