class StructX
StructX
is an extension of Ruby standard Struct. The diffences are that 1) the constructor handles hash table as key-value pairs, 2) you can specify members as statements, and 3) you can set default values of member. StructX's API is compatible with Struct.
@example Constructor with hash table
StructX.new(:x, :y, :z).new(x: 1, y: 2, z: 3) #=> #<struct x=1, y=10, z=100>
@example Member sentences
class A < StructX member :x member :y member :z end A.new(1, 2, 3) #=> #<struct A x=1, y=2, z=3>
@example Default values
class B < StructX member :x member :y, default: 10 member :z, default: 100 end B.new(1) # => #<struct B x=1, y=10, z=100>
Constants
- VERSION
version of
StructX
Public Class Methods
Return default values.
@return [Hash{Symbol=>Object}]
default values
# File lib/structx.rb, line 90 def default_values @__member__.inject({}) do |tbl, (key, val)| tbl.tap {|x| x[key] = val[:default] if val.has_key?(:default)} end end
# File lib/structx.rb, line 96 def immutable(b=true) @immutable = b end
# File lib/structx.rb, line 100 def immutable? @immutable end
Add member into structure.
@param name [Symbol]
member name
@param data [Hash]
member options
# File lib/structx.rb, line 72 def member(name, data={}) (@__member__ ||= {})[name] = Hash.new.merge(data) # clone the data # define member's value reader define_method(name) do @value[name] end # define member's value writer define_method("%s=" % name) do |val| @value[name] = val end end
Same as Struct#members.
# File lib/structx.rb, line 62 def members (@__member__ ||= {}).keys end
Create a instance or sublcass. If this class has members, create an instance. The case handles hash table as key-value pairs. If this class has no members, create a subclass.
@param args [Array or Hash]
Same as Struct if args is Array. Consider args as key-value pairs if it is Hash.
# File lib/structx.rb, line 41 def new(*args) # create an instance return orig_new(*args) if @__member__ and @__member__.keys.size > 0 # create subclass Class.new(StructX).tap do |subclass| # class name if args.first.kind_of?(String) const_set(args.first, subclass) args = args.drop(1) end # set members args.each {|m| subclass.member(*m)} # this is according to MRI, why yield? yield if block_given? end end
See Struct.new.
# File lib/structx.rb, line 121 def initialize(*values) if values.first.kind_of?(Hash) and values.size == 1 @value = __build__(values.first) else raise ArgumentError.new("struct size differs #{values} #{members} ") if values.size > members.size @value = __build__(members.zip(values)) end end
Private Class Methods
# File lib/structx.rb, line 106 def inherited(subclass) @__member__.each {|key, data| subclass.member(key, data)} if @__member__ subclass.instance_variable_set(:@immutable, true) if @immutable end
Public Instance Methods
Same as Struct#[].
# File lib/structx.rb, line 131 def [](idx) case idx when Integer size > idx && -size <= idx ? values[idx] : (raise IndexError.new(idx)) when Symbol, String members.include?(idx.to_sym) ? @value[idx.to_sym] : (raise NameError.new(idx.to_s)) end end
Same as Struct#[]=.
# File lib/structx.rb, line 144 def []=(idx, val) case idx when Integer if size > idx && -size <= idx if not(immutable?) @value[members[idx]] = val else self.class.new(@value.merge(members[idx] => val)) end else raise IndexError.new(idx) end when Symbol, String if members.include?(idx.to_sym) if not(immutable?) @value[idx.to_sym] = val else self.class.new(@value.merge(idx.to_sym => val)) end else raise NameError.new(idx.to_s) end end end
Same as Struct#eql?.
# File lib/structx.rb, line 191 def eql?(other) self.class == other.class and @value == other.to_h end
Same as Struct#inspect.
# File lib/structx.rb, line 180 def inspect name = self.class.inspect[0] == "#" ? "" : " " + self.class.inspect values = (@value || []).map do |key, val| k = (key.to_s[0] == "@" ? ":" : "") + key.to_s v = (self == val ? "#<struct %s:...>" % val : val.inspect) "%s=%s" % [k, v] end "#<struct%s %s>" % [name, values.join(", ")] end
Same as []=
, but you can set values by hash.
# File lib/structx.rb, line 170 def set(pairs={}) if not(immutable?) pairs.each {|idx, val| self[idx] = val} return self else pairs.inject(self) {|obj, (idx, val)| obj.send("[]=", idx, val)} end end
Same as Struct#to_h. This method is available in Ruby 1.9 too.
# File lib/structx.rb, line 197 def to_h @value end
Private Instance Methods
# File lib/structx.rb, line 203 def __build__(data) tbl = data.inject({}) {|tbl, (m, val)| tbl.tap {|x| x[m] = val unless val.nil?}} default_values = self.class.default_values.inject({}) do |_tbl, (key, val)| _tbl.tap {|x| x[key] = __build_default_value__(val, data)} end default_values.merge(tbl) end
# File lib/structx.rb, line 211 def __build_default_value__(val, data) return val unless val.kind_of?(Proc) if val.arity == 1 val.call(self) elsif val.arity > 1 val.call(self, data) elsif val.arity < 0 val.call(self, data) else val.call end end