class SknUtils::NestedResult
Public Class Methods
# File lib/skn_utils/nested_result.rb, line 107 def initialize(params={}, speed=true) reset_from_empty!(params, speed) end
# File lib/skn_utils/nested_result.rb, line 103 def self.with_instance_vars(args) new(args, true) end
## Onlye good for the first level
# File lib/skn_utils/nested_result.rb, line 100 def self.with_methods(args) new(args, false) end
Public Instance Methods
Ruby basic Class methods
# File lib/skn_utils/nested_result.rb, line 151 def ==(other) return false unless other.is_a?(NestedResult) to_hash.eql?(other.to_hash) end
# File lib/skn_utils/nested_result.rb, line 111 def [](attr) container[key_as_sym(attr)] end
Feature: if a new attribute is added, on first read method_missing
will create getters/setters
# File lib/skn_utils/nested_result.rb, line 116 def []=(attr, value) container.store(key_as_sym(attr), value) end
# File lib/skn_utils/nested_result.rb, line 120 def delete_field(name) # protect public methods sym = key_as_sym(name) unless !sym.is_a?(Symbol) || self.class.method_defined?(sym) singleton_class.send(:remove_method, "#{sym.to_s}=".to_sym, sym) rescue nil container.delete(sym) end end
YAML/Psych load support, chance to re-initialize value methods
Use our unwrapped/original input Hash
when yaml'ing
# File lib/skn_utils/nested_result.rb, line 175 def encode_with(coder) coder['container'] = attributes end
# File lib/skn_utils/nested_result.rb, line 157 def eql?(other) return false unless other.is_a?(NestedResult) to_hash.eql?(other.to_hash) end
# File lib/skn_utils/nested_result.rb, line 162 def hash to_hash.hash end
returns hash from any root key starting point: object.root_key
-
protected to reasonably ensure key is a symbol
# File lib/skn_utils/nested_result.rb, line 189 def hash_from(sym) starting_sym = key_as_sym(sym) bundle = ((starting_sym == container) ? container : { starting_sym => container[starting_sym] }) bundle.keys.each_with_object({}) do |attr,collector| value = bundle[attr] case value when NestedResult, self.class value = value.to_hash when Array value = value.map {|ele| array_to_hash(ele) } end collector[attr] = value # new copy end end
Use our hash from above to fully re-initialize this instance
# File lib/skn_utils/nested_result.rb, line 180 def init_with(coder) case coder.tag when '!ruby/object:SknUtils::NestedResult', "!ruby/object:#{self.class.name}" reset_from_empty!( coder.map['container'] ) end end
Feature: returns keys from root input Hash
# File lib/skn_utils/nested_result.rb, line 167 def keys container.keys end
Exporters
# File lib/skn_utils/nested_result.rb, line 131 def to_hash attributes end
# File lib/skn_utils/nested_result.rb, line 137 def to_json(*args) attributes.to_json(*args) end
Returns a string containing a detailed summary of the keys and values.
# File lib/skn_utils/nested_result.rb, line 144 def to_s attributes.to_s end
Protected Instance Methods
Marshal.load()/.dump() support, chance to re-initialize value methods
# File lib/skn_utils/nested_result.rb, line 215 def marshal_dump to_hash end
Using the String
from above create and return an instance of this class
# File lib/skn_utils/nested_result.rb, line 220 def marshal_load(hash) reset_from_empty!(hash) end
# File lib/skn_utils/nested_result.rb, line 206 def reset_from_empty!(params={}, speed=true) @container = Concurrent::Hash.new() speed ? initialize_for_speed(params) : initialize_from_hash(params) end
# File lib/skn_utils/nested_result.rb, line 224 def respond_to_missing?(method, incl_private=false) method_nsym = method.is_a?(Symbol) ? method.to_s[0..-2].to_sym : method container[key_as_sym(method)] || container[method_nsym] || super end
Private Instance Methods
Feature: unwrap array of array-of-hashes/object
# File lib/skn_utils/nested_result.rb, line 294 def array_to_hash(array) case array when self.class array.to_hash when Array array.map { |element| array_to_hash(element) } else array end end
Feature: attribute must exist and have a non-blank value to cause this method to return true
# File lib/skn_utils/nested_result.rb, line 232 def attribute?(attr) return false unless container.key?(key_as_sym(attr)) ![ "", " ", nil, [],[""], [" "], self.class.new({}), [[]]].any? {|a| a == container[key_as_sym(attr)] } end
Feature: returns a hash of all attributes and their current values
# File lib/skn_utils/nested_result.rb, line 238 def attributes hash_from(container) end
# File lib/skn_utils/nested_result.rb, line 242 def container @container ||= Concurrent::Hash.new() end
Feature: enables dot.notation and creates matching getter/setters
# File lib/skn_utils/nested_result.rb, line 247 def enable_dot_notation(sym) name = key_as_sym(sym) unless !name.is_a?(Symbol) || singleton_class.method_defined?(name) singleton_class.send(:define_method, name) do container[name] end singleton_class.send(:define_method, "#{name.to_s}=".to_sym) do |x| container[name] = x end end name end
Don't create methods until first access
# File lib/skn_utils/nested_result.rb, line 262 def initialize_for_speed(hash) hash.each_pair do |k,v| key = key_as_sym(k) case v when Array value = v.map { |element| translate_value(element) } container.store(key, value) when Hash container.store(key, NestedResult.new(v)) else container.store(key, v) end end end
# File lib/skn_utils/nested_result.rb, line 277 def initialize_from_hash(hash) hash.each_pair do |k,v| key = key_as_sym(k) enable_dot_notation(key) case v when Array value = v.map { |element| translate_value(element) } container.store(key, value) when Hash container.store(key, NestedResult.new(v)) else container.store(key, v) end end end
# File lib/skn_utils/nested_result.rb, line 317 def key_as_sym(key) case key when Symbol key when String key.to_sym else key # no change, allows Fixnum and Object instances end end
Feature: post-assign key/value pair, <attr>?? predicate, create getter/setter on first access
# File lib/skn_utils/nested_result.rb, line 329 def method_missing(method, *args, &block) method_sym = key_as_sym(method) method_nsym = method_sym.is_a?(Symbol) ? method.to_s[0..-2].to_sym : method if method.to_s.end_with?("=") # add new key/value pair, transform value if Hash or Array initialize_from_hash({method_nsym => args.first}) # Add Reader/Writer one first need elsif container.key?(method_sym) container[method_sym] # Add Reader/Writer one first need elsif method.to_s.end_with?('?') # order of tests is significant, attribute?(method_nsym) else # TODO: replace following with nil to match OpenStruct or Hash behavior when key not found nil # e = NoMethodError.new "undefined method `#{method}' for #{self.class.name}", method, args # e.set_backtrace caller(1) # raise e end end
Feature: wrap array of array-of-hashes/object
# File lib/skn_utils/nested_result.rb, line 306 def translate_value(value) case value when Hash self.class.new(value) when Array value.map { |element| translate_value(element) } else value end end