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
Examples of 'Things' That Are Not Objects in Ruby
Ruby Amazon S3 Access Denied When Listing Buckets
Rspec Testing Has_Many :Through and After_Save
Custom Rails Configuration Section
Carrierwave How to Get the File Extension
How to Enable Compression in Ruby on Rails
Ruby Net::Smtp - Send Email with Bcc: Recipients
Is It a Bad Idea to Reload Routes Dynamically in Rails
How to Print Information About a Net:Httprequest for Debug Purposes
Rails 12 Hour Am/Pm Range for a Day
Discriminate First and Last Element in Each
How to Connect to Postgresql Database After Upgrading to Yosemite 10.10
Ruby: How to Calculate a Path Relative to Another One