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

[](*args)

Same as Struct[].

Alias for: new
default_values() click to toggle source

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
immutable(b=true) click to toggle source
# File lib/structx.rb, line 96
def immutable(b=true)
  @immutable = b
end
immutable?() click to toggle source
# File lib/structx.rb, line 100
def immutable?
  @immutable
end
member(name, data={}) click to toggle source

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
members() click to toggle source

Same as Struct#members.

# File lib/structx.rb, line 62
def members
  (@__member__ ||= {}).keys
end
new(*args) { || ... } click to toggle source

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
Also aliased as: orig_new, []
new(*values) click to toggle source

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
orig_new(*args)
Alias for: new

Private Class Methods

inherited(subclass) click to toggle source
# 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

"=="(other)
Alias for: eql?
[](idx) click to toggle source

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
[]=(idx, val) click to toggle source

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
eql?(other) click to toggle source

Same as Struct#eql?.

# File lib/structx.rb, line 191
def eql?(other)
  self.class == other.class and @value == other.to_h
end
Also aliased as: "=="
inspect() click to toggle source

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
set(pairs={}) click to toggle source

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
to_h() click to toggle source

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

__build__(data) click to toggle source
# 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
__build_default_value__(val, data) click to toggle source
# 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