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

Instance Method Summary collapse

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

#tableObject (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

#freezeObject 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