class DataStruct

Constants

VERSION

Public Class Methods

from_array(array) click to toggle source

A more ideomatic way of calling +new(*array)+

# File lib/datastruct.rb, line 45
def self.from_array(array)
  self.new(*array)
end
from_hash(hash) click to toggle source

A more ideomatic way of calling +new(**hash)+

# File lib/datastruct.rb, line 52
def self.from_hash(hash)
  hash = symbol_keys(hash)

  self.new(**hash)
end
new(*args, &block) click to toggle source

Makes sure +@data+ is set before initialize is called

The +@data+ instance variable is set to a hash with each key in PROPERTIES set to nil before the constructor is called. This avoids a host of annoying issues when users override the constructor.

# File lib/datastruct.rb, line 23
def self.new(*args, &block)
  instance = allocate()

  data_container = {}

  if instance.class.const_defined? :PROPERTIES
    properties = instance.class::PROPERTIES
  else
    properties = []
  end

  properties.each { |key| data_container[key] = nil }
  instance.instance_variable_set(:@data, data_container)

  instance.send(:initialize, *args, &block)

  return instance
end
new(*args, **kwargs) click to toggle source
# File lib/datastruct.rb, line 58
def initialize(*args, **kwargs)
  self.update(*args)
  self.update(**kwargs)
end

Private Class Methods

symbol_keys(hash) click to toggle source
# File lib/datastruct.rb, line 283
def self.symbol_keys(hash)
  Hash[hash.each_pair.map { |x| [x[0].to_sym, x[1]] }]
end

Public Instance Methods

==(other) click to toggle source

Delegates to +Hash#==+

# File lib/datastruct.rb, line 66
def ==(other)
  if not other.instance_of? self.class
    false
  else
    @data == other.instance_variable_get(:@data)
  end
end
[](key) click to toggle source

Does the same as #get, but returns nil instead of raising KeyError

@param [String, Symbol] key @return [Object]

# File lib/datastruct.rb, line 113
def [](key)
  get(key)
rescue KeyError
  nil
end
[]=(property, value)
Alias for: set
dup() click to toggle source

Returns a shallow copy

# File lib/datastruct.rb, line 87
def dup
  self.class.from_hash(@data.dup)
end
each(*args, &block) click to toggle source

Delegates to +Hash#each+

@see ruby-doc.org/core-2.0.0/Hash.html#method-i-each Hash#each @overload each

# File lib/datastruct.rb, line 80
def each(*args, &block)
  @data.each(*args, &block)
end
get(property) click to toggle source

Returns a property using its getter method

@param [String, Symbol] property @raise [KeyError] on invalid property name

# File lib/datastruct.rb, line 97
def get(property)
  property = property.to_sym

  if not valid_property? property
    fail KeyError, "Property not defined: #{property}"
  end

  self.send(property)
end
inspect() click to toggle source

Produces a text representation of the object

# File lib/datastruct.rb, line 122
def inspect
  text = "#<#{self.class.to_s}"

  text << self.class::PROPERTIES.reduce("") { |a, key|
    a << " #{key}=#{self.get(key).inspect}"
  }

  text << ">"

  return text
end
merge(other) click to toggle source

Delegates to +Hash#merge+

@return [Hash]

# File lib/datastruct.rb, line 277
def merge(other)
  @data.merge(other)
end
respond_to?(method_name) click to toggle source
Calls superclass method
# File lib/datastruct.rb, line 134
def respond_to?(method_name)
  if valid_property?(method_name) or valid_property?(getter(method_name))
    true
  else
    super
  end
end
set(property, value) click to toggle source

Sets the value of a property using its setter method

@param [String, Symbol] property @param [Object] value @raise [KeyError] on invalid property name

# File lib/datastruct.rb, line 149
def set(property, value)
  property = property.to_sym

  if not valid_property? property
    fail KeyError, "Property not defined: #{property}"
  end

  self.send(setter(property), value)
end
Also aliased as: []=
to_a()
Alias for: to_array
to_array() click to toggle source

Returns the properties of the object as an array

@return [Array]

# File lib/datastruct.rb, line 166
def to_array
  self.class::PROPERTIES.map { |name| @data[name] }
end
Also aliased as: to_a
to_h(string_keys: false, deep: false)
Alias for: to_hash
to_hash(string_keys: false, deep: false) click to toggle source

Returns the properties of the object as a hash

@param [Boolean] string_keys Return keys as strings instead of symbols @param [Boolean] deep Go deep and serialize all DataStruct instances found

@return [Hash]

# File lib/datastruct.rb, line 180
def to_hash(string_keys: false, deep: false)
  # Builds a hash using property getters. This allows getters to override the
  # output of this function.
  data = Hash[self.class::PROPERTIES.map { |key| [key, self.get(key)] }]

  hashify = lambda { |obj|
    if obj.is_a? DataStruct and deep
      return obj.to_hash(string_keys: string_keys, deep: deep)

    elsif obj.is_a? Hash
      out = {}

      obj.each_pair { |key, value|
        # Only stringify the keys of the *top level* of the structure. We have
        # no business messing with the keys of nested hashes.
        key = key.to_s if string_keys and obj.equal? data

        out[key] = hashify[value]
      }

      return out

    elsif obj.is_a? Array
      return obj.map { |x| hashify[x] }

    else
      return obj
    end
  }

  return data if not string_keys and not deep
  return hashify[data]
end
Also aliased as: to_h
to_json(*args) click to toggle source

Dumps the properties of this object to JSON using Ruby's JSON module

@note JSON must be loaded for this function to work @see www.ruby-doc.org/stdlib-2.0/libdoc/json/rdoc/JSON.html JSON @return [String]

# File lib/datastruct.rb, line 223
def to_json(*args)
  self.to_hash.to_json(*args)
end
to_yaml(*args) click to toggle source

Dumps the properties of this object to YAML using Ruby's YAML module

@note YAML must be loaded for this function to work @see ruby-doc.org/stdlib-2.0.0/libdoc/yaml/rdoc/YAML.html YAML @return [String]

# File lib/datastruct.rb, line 234
def to_yaml(*args)
  self.to_hash.to_yaml(*args)
end
update(*args, **kwargs) click to toggle source

Updates the values of this object's properties

Both positional arguments and keyword arguments are used to update the property values of the object. Positional arguments should be passed in the same order as the defined properties.

@note Keyword arguments override posisional arguments @raise [ArgumentError] on invalid property names @return nil

# File lib/datastruct.rb, line 249
def update(*args, **kwargs)
  if args.length > self.class::PROPERTIES.length
    x = args.length
    y = self.class::PROPERTIES.length
    msg = "Too many arguments (you passed #{x} arguments for #{y} properties)"

    fail ArgumentError, msg
  end

  hash = Hash[self.class::PROPERTIES[0...args.length].zip(args)]
  hash.update(kwargs)

  hash.each_pair { |key, value|
    begin
      self.set(key, value)
    rescue KeyError => e
      fail ArgumentError, "Invalid property: #{key}"
    end
  }

  nil
end

Private Instance Methods

getter(sym) click to toggle source

@example

getter(:foo=)   # => :foo
getter(:foo)    # => :foo
# File lib/datastruct.rb, line 339
def getter(sym)
  if is_setter?(sym)
    sym.to_s[0..-2].to_sym
  else
    sym
  end
end
is_setter?(sym) click to toggle source

@example

is_setter?(:foo)    # => false
is_setter?(:foo=)   # => true
# File lib/datastruct.rb, line 322
def is_setter?(sym)
  sym.to_s.end_with?("=")
end
method_missing(name, *args, &block) click to toggle source

This makes the struct accept the defined properties as instance methods

Calls superclass method
# File lib/datastruct.rb, line 290
def method_missing(name, *args, &block)
  property = name
  set = false

  if is_setter?(property)
    property = getter(name)
    set = true
  end

  if valid_property? property
    if set
      @data[property] = args.first
    else
      @data[property]
    end
  else
    super
  end
end
setter(sym) click to toggle source

@example

setter(:foo)   # => :foo=
# File lib/datastruct.rb, line 330
def setter(sym)
  (sym.to_s + "=").to_sym
end
valid_property?(prop) click to toggle source

Returns true if prop is a valid property

# File lib/datastruct.rb, line 313
def valid_property?(prop)
  self.class::PROPERTIES.include? prop
end