class SDStruct
Alternative to OpenStruct that is more strict and go deeper.
@author Adrian Setyadi
Constants
- VERSION
Attributes
Public Class Methods
Creates a new SDStruct
object. By default, the resulting SDStruct
object will have no attributes.
The optional hash
, if given, will generate attributes and values (can be a Hash, an SDStruct
or a Struct). For example:
require 'sd_struct' # or require 'sd_struct/base' hash = { "name" => "Matz", "coding language" => :ruby, :age => "old" } data = SDStruct.new(hash) p data # -> #<SDStruct .name="Matz", ['coding language']=:ruby, .age="old">
# File lib/sd_struct/base.rb, line 44 def initialize(hash = nil, opts = {}) opts = { deep: true, symbolize_keys: true }.merge(opts) @deep = opts[:deep] @table = {} if hash hash.each_pair do |k, v| @table[naturalize(k)] = structurize(v) # @deep is used in this method end end end
Public Instance Methods
# File lib/sd_struct/base.rb, line 177 def ==(other) return false unless other.kind_of?(self.class) @table == other.table end
Returns the value of a member.
person = SDStruct.new('name' => 'Matz', 'lang' => 'ruby') person[:lang] # => ruby, same as person.lang
# File lib/sd_struct/base.rb, line 130 def [](name) @table[name] || @table[naturalize(name)] end
Sets the value of a member.
person = SDStruct.new('name' => 'Matz', 'lang' => 'python') person[:lang] = 'ruby' # => equivalent to person.lang = 'ruby' person.lang # => ruby
# File lib/sd_struct/base.rb, line 141 def []=(name, value) unless self[name].nil? || value.is_a?(self[name].class) warn("You're assigning a value with different type as the previous value.") end @table[new_struct_member(naturalize(name))] = structurize(value) end
Deletes specified field or key
# File lib/sd_struct/base.rb, line 226 def delete_field(name) name = @table.has_key?(name) ? name : naturalize(name) @table.delete(name) do raise NameError.new("no field `#{name}' in #{self}", name) end singleton_class.__send__(:remove_method, name, "#{name}=") end
Digs the content of @table which is a hash
@param [Symbol] multiple symbols @return [SDStruct,Hash,Array,String,Integer,Float,Boolean,nil] first matched result
# File lib/sd_struct/deep_search.rb, line 68 def dig(*args) @table.dig(*args) end
Digs deep into array until non-Array and non-Hash primitive data is found
@param [Symbol] multiple symbols @return [String,Integer,Float,Boolean,nil] first matched result
# File lib/sd_struct/deep_search.rb, line 11 def dig_deep(*args) full_args = args.dup parent_key = args.shift result = nil if parent_key.is_a?(Integer) result = dig(parent_key) unless result.nil? || args.length.zero? result = result.dig_deep(*args) end end if result.nil? each do |x| if x.respond_to?(:dig_deep) || x.is_a?(Array) result = x.dig_deep(*full_args) end return result unless result.nil? end end return result end
Yields all attributes (as a symbol) along with the corresponding values or returns an enumerator if not block is given. Example:
require 'sd_struct' data = SDStruct.new("name" => "Matz", "coding language" => :ruby) data.each_pair.to_a # => [[:name, "Matz"], ["coding language", :ruby]]
# File lib/sd_struct/base.rb, line 90 def each_pair return to_enum(__method__) { @table.size } unless block_given? @table.each_pair{|p| yield p} end
Finds value with keys specified like xpath
@param [String] key string @param [Hash] option Hash @return [SDStruct,Hash,Array,String,Integer,Float,Boolean,nil] first matched result
# File lib/sd_struct/deep_search.rb, line 79 def find(key_str, opts = {}) opts = { separator: "/" }.merge(opts) sep = Regexp.quote(opts[:separator]) args = begin key_str.gsub(/^#{sep}(?!#{sep})|#{sep}+$/, '') .split(/#{sep}{2,}/) .map do |ks| ks.split(/#{sep}/) .map do |x| x.strip! if !!x[/\A[-+]?\d+\z/] x.to_i else if x[/^$|^[A-Z]|\s+/] x else x.underscore.to_sym end end end end end if !(parent_key = args.shift) # args == [], key_str == "" return else # e.g. args == [[], ..] or [[.., ..], [..]] result = dig(*parent_key) unless parent_key.empty? unless args.length.zero? args.each do |a| result = result.dig_deep(*a) rescue result = dig_deep(*a) end end end return result end
Computes a hash-code for this SDStruct
. Two hashes with the same content will have the same hash code (and will be eql?).
# File lib/sd_struct/base.rb, line 197 def hash @table.hash end
Duplicates an SDStruct
object members.
# File lib/sd_struct/base.rb, line 62 def initialize_copy(orig) super @table = @table.dup end
Returns a string containing a detailed summary of the keys and values.
# File lib/sd_struct/base.rb, line 151 def inspect str = "#<#{self.class}" ids = (Thread.current[:sd_struct] ||= []) if ids.include?(object_id) return str << ' ...>' end ids << object_id begin first = true for k,v in @table str << "," unless first first = false str << " #{k[/\s+/] ? "['#{k}']" : ".#{k}"}=#{v.inspect}" end return str << '>' ensure ids.pop end end
Exposes all keys
# File lib/sd_struct/base.rb, line 219 def keys @table.keys end
Provides marshalling support for use by the Marshal library.
# File lib/sd_struct/base.rb, line 70 def marshal_dump to_h end
Provides marshalling support for use by the Marshal library.
# File lib/sd_struct/base.rb, line 77 def marshal_load(x) @table = x.map{|a| structurize(a) }.original_to_h end
# File lib/sd_struct/base.rb, line 235 def method_missing(m_id, *args) m_nat = naturalize(m_id) if args.length.zero? if @table.key?(m_nat) @table[new_struct_member(m_nat)] end else err = NoMethodError.new "undefined method `#{m_id}' for #{self}", m_id, args err.set_backtrace caller(1) raise err end end
Exposes keys without space(s)
# File lib/sd_struct/base.rb, line 211 def non_spaced_keys methods(false).select{|x| x[/^\S+[^=]$/]} end
Exposes keys with space(s)
# File lib/sd_struct/base.rb, line 204 def spaced_keys @table.keys - non_spaced_keys end
# File lib/sd_struct/deep_convert.rb, line 6 def to_h(camelize_keys = false) map{|x| x.respond_to?(:to_h) ? x.to_h(camelize_keys) : x } end
# File lib/sd_struct/deep_convert.rb, line 28 def to_json(opts = {}) opts = { camelize_keys: true, exclude_blank_values: true, values_to_exclude: [0, [""], [{}]] }.merge(opts) to_h(opts).to_json end
Protected Instance Methods
# File lib/sd_struct/base.rb, line 106 def naturalize(key) unless key[/^[A-Z]|\s+/] key.to_s.underscore.to_sym else key end end
Used internally to define field properties
# File lib/sd_struct/base.rb, line 98 def new_struct_member(name) unless respond_to?(name) define_singleton_method(name) { @table[name] } define_singleton_method("#{name}=") { |x| @table[name] = x } end name end
Calls to struct to a value if it is an Array or a Hash and @deep is true
# File lib/sd_struct/base.rb, line 117 def structurize(value) ( @deep && (value.is_a?(Hash) || value.is_a?(Array)) ) ? value.to_struct : value end