Module: AltStruct::Behavior
- Included in:
- AltStruct
- Defined in:
- lib/astruct/behavior.rb
Overview
An AltStruct is a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby’s meta-programming to define methods on the class itself.
Examples:
require 'astruct'
class Profile < AltStruct
end
person = Profile.new name: "John Smith"
person.age = 70
puts person.name # => "John Smith"
puts person.age # => 70
puts person.dump # => { :name => "John Smith", :age => 70 }
An AltStruct employs a Hash internally to store the methods and values and can even be initialized with one:
australia = AltStruct.new(
country: "Australia",
population: 20_000_000
)
puts australia.inspect
# => <AltStruct country="Australia", population=20000000>
Hash keys with spaces or characters that would normally not be able to use for method calls (e.g. ()[]*) will not be immediately available on the AltStruct object as a method for retrieval or assignment, but can be still be reached through the ‘Object#send` method.
measurements = AltStruct.new "length (in inches)" => 24
measurements.send "length (in inches)" # => 24
data_point = AltStruct.new :queued? => true
data_point.queued? # => true
data_point.send "queued?=", false
data_point.queued? # => false
Removing the presence of a method requires the execution the delete_field or delete (like a hash) method as setting the property value to nil
will not remove the method.
first_pet = AltStruct.new :name => 'Rowdy', :owner => 'John Smith'
first_pet.owner = nil
second_pet = AltStruct.new :name => 'Rowdy'
first_pet == second_pet # -> false
first_pet.delete_field(:owner)
first_pet == second_pet # -> true
Implementation:
An AltStruct 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_singleton_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.
Constant Summary collapse
- THREAD_KEY =
:nodoc:
:__as_ids__
- NESTED_INSPECT =
"...".freeze
- INSPECT_DELIMITER =
", ".freeze
- WRAP_PATTERN =
/__/.freeze
- UNSETABLE_PATTERN =
/\@|\[|\]|\=\=|\~|\>|\<|\!\=/.freeze
- SUFFIX_PATTERN =
/(\?|\!)$/.freeze
Instance Attribute Summary collapse
-
#table ⇒ Object
readonly
We want to give easy access to the table.
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#__delete__(key) ⇒ Object
(also: #delete_field, #delete)
The ‘delete()` method removes a key/value pair on the @table and on the singleton class.
-
#__dump__(*keys) ⇒ Object
(also: #marshal_dump, #dump, #to_hash)
The ‘dump()` takes the table and out puts in it’s natural hash format.
- #__inspect__ ⇒ Object (also: #inspect, #to_sym)
-
#__load__(pairs) ⇒ Object
(also: #marshal_load, #load, #merge)
This is the ‘load()` method, which works like initialize in that it will create new fields for each pair passed.
-
#__load__!(pairs) ⇒ Object
(also: #marshal_load!, #load!, #merge!)
This is the ‘load!()` method, which works like Hash#merge! See: `AltStruct#load()`.
- #freeze ⇒ Object (also: #__freeze__)
-
#initialize(pairs = {}) ⇒ Object
Create a new field for each of the key/value pairs passed.
-
#method_missing(method, *arguments) ⇒ Object
The ‘method_missing()` method catches all non-tabled method calls.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *arguments) ⇒ Object
The ‘method_missing()` method catches all non-tabled method calls. The AltStruct object will return two specific errors depending on the call.
168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/astruct/behavior.rb', line 168 def method_missing(method, *arguments) name = method.to_s if name.split("").last == "=" && arguments.size == 1 __define_field__(name.chomp!("="), arguments.first) else if name.split.last != "=" super else arguments.size > 1 raise(ArgumentError,"wrong number of arguments (#{arguments.size} for 1)") end end end |
Instance Attribute Details
#table ⇒ Object (readonly)
We want to give easy access to the table
80 81 82 |
# File 'lib/astruct/behavior.rb', line 80 def table @table end |
Instance Method Details
#==(other) ⇒ Object
181 182 183 184 185 186 187 |
# File 'lib/astruct/behavior.rb', line 181 def ==(other) if other.respond_to?(:table) table == other.table else false end end |
#__delete__(key) ⇒ Object Also known as: delete_field, delete
The ‘delete()` method removes a key/value pair on the @table and on the singleton class. It also mimics the Hash#delete method.
157 158 159 160 161 |
# File 'lib/astruct/behavior.rb', line 157 def __delete__(key) __singleton_class__.send(:remove_method, key) __singleton_class__.send(:remove_method, "#{key}=") @table.delete(key.to_sym) end |
#__dump__(*keys) ⇒ Object Also known as: marshal_dump, dump, to_hash
The ‘dump()` takes the table and out puts in it’s natural hash format. In addition you can pass along a specific set of keys to dump.
142 143 144 |
# File 'lib/astruct/behavior.rb', line 142 def __dump__(*keys) if keys.empty? then @table else __dump_specific__(keys) end end |
#__inspect__ ⇒ Object Also known as: inspect, to_sym
149 150 151 |
# File 'lib/astruct/behavior.rb', line 149 def __inspect__ "#<#{__class__}#{__dump_inspect__}>" end |
#__load__(pairs) ⇒ Object Also known as: marshal_load, load, merge
This is the ‘load()` method, which works like initialize in that it will create new fields for each pair passed. It mimics the behavior of a Hash#merge.
123 124 125 |
# File 'lib/astruct/behavior.rb', line 123 def __load__(pairs) __iterate_set_over__(pairs) unless pairs.empty? end |
#__load__!(pairs) ⇒ Object Also known as: marshal_load!, load!, merge!
This is the ‘load!()` method, which works like Hash#merge! See: `AltStruct#load()`
132 133 134 |
# File 'lib/astruct/behavior.rb', line 132 def __load__!(pairs) __iterate_set_over__(pairs, true) end |
#freeze ⇒ Object Also known as: __freeze__
189 190 191 192 |
# File 'lib/astruct/behavior.rb', line 189 def freeze super @table.freeze end |
#initialize(pairs = {}) ⇒ Object
Create a new field for each of the key/value pairs passed. By default the resulting OpenStruct object will have no attributes. If no pairs are passed avoid any work.
require "astruct"
hash = { "country" => "Australia", :population => 20_000_000 }
data = AltStruct.new hash
p data # => <AltStruct country="Australia" population=20000000>
If you happen to be inheriting then you can define your own ‘@table` ivar before the `super()` call. AltStruct will respect your `@table`.
115 116 117 118 |
# File 'lib/astruct/behavior.rb', line 115 def initialize(pairs = {}) @table ||= {} __iterate_set_over__(pairs) unless pairs.empty? end |