lib/blobject.rb in blobject-0.1.5 vs lib/blobject.rb in blobject-0.1.9

- old
+ new

@@ -1,175 +1,169 @@ -Dir["#{File.dirname __FILE__}/blobject/*.rb"].each {|file| require "#{file}" } +require 'blobject/version' def blobject *parameters, &block Blobject.new *parameters, &block -end +end -# similar to OpenStruct -# b.foo.bar = 8 automatically creates a blobject called "foo" on "b" -# to check whether a variable is defined use the '?' syntax i.e: b.is_it_here? => nil, b.foo? == b.foo -# calling an unassigned member returns a new blobject unless the blobject is frozen -# a frozen blobject cannot be assigned new members or populated from a hash -# does no cycle checking, intended for use in serialization class Blobject - class AssignToFrozenBlobjectException < Exception; end -public - - def frozen? - @blobject_frozen == true + def initialize hash = {}, &block + @hash = {} + merge hash + + @modifying = false + self.modify &block if block_given? end - def freeze - @blobject_frozen = true - self + def modify &block + + __r_modify_set__ true + + exception = nil + + begin + self.instance_eval &block + rescue Exception => e + exception = e + end + + __r_modify_set__ false + + raise exception unless exception.nil? + return self end - def unfreeze - @blobject_frozen = false - self - end - def defrost - unfreeze - end - def initialize hash_of_initial_values_or_an_object={}, array_of_methods_to_copy_data_from_object=[] - @values = {} - - if (hash_of_initial_values_or_an_object.class == Hash) - merge_hash hash_of_initial_values_or_an_object - else - array_of_methods_to_copy_data_from_object.each do |accessor| - @values[accessor.to_s] = hash_of_initial_values_or_an_object.send accessor - end - end - - if block_given? - yield self - end - self - end - - def method_missing(sym, *args, &block) - # fixes for ruby 1.9.2, otherwise blobjects can't be used in arrays that may have the flatten method called - super if [:to_ary].include? sym + def method_missing sym, *params, &block - str = sym.to_s + if match = /^has_(?<name>.+)\?/.match(sym) + return @hash.has_key? match[:name].to_sym + end - assignment = /.*(?=\=$)/.match str - question = /.*(?=\?$)/.match str - - if question - !@values[question.to_s].nil? - elsif assignment - raise AssignToFrozenBlobjectException if @blobject_frozen - @values[assignment.to_s] = args[0] - else - #return the value or a new blobject - value = @values[str] + modify_getter = -> { params.length == 0 && @modifying } + modify_assign = -> { params.length == 1 && @modifying } + + if @modifying - return value unless value.nil? - return nil if frozen? - @values[str] = Blobject.new - return @values[str] + case params.length + when 0 # get + return @hash[sym] if @hash.has_key? sym + + child = Blobject.new + parent = self + + child.__r_modify_set__ true + + store_in_parent = lambda { + + parent_hash = parent.instance_variable_get '@hash' + parent_hash[sym] = child + + parent_store_in_parent = parent.instance_variable_get :@__store_in_parent__ + parent_store_in_parent.call unless parent_store_in_parent.nil? + + child.method(:remove_instance_variable).call(:@__store_in_parent__) + } + + child.instance_variable_set :@__store_in_parent__, store_in_parent + + return block_given? ? child.modify(&block) : child + when 1 # set + @hash[sym] = params[0] + + store_in_parent = @__store_in_parent__ + store_in_parent.call unless store_in_parent.nil? + + return self + end + else + return @hash[sym] if @hash.has_key? sym end + + super end - def merge_hash hash - raise "cannot populate a frozen blobject" if @blobject_frozen + def merge hash hash.each do |key, value| - - if value.class==Hash - value = Blobject.new value - end - - if value.class==Array - value = Blobject.blobjectify_array value - end - - @values[key.to_s] = value + @hash[key.to_s.to_sym] = self.class.__blobjectify__ value end + self - end + end + def empty? + @hash.empty? + end + + def [] key + @hash[key] + end + + def []= key, value + send key, value + end + def to_hash - h = {} - @values.each do |key, value| - if value.class==Blobject - h[key] = value.to_hash - elsif value.class==Array - h[key] = Blobject.deblobjectify_array value - else - h[key] = value - end - end - h + Marshal.load(Marshal.dump(@hash)) end - #builds a hash for json conversion - def as_json *ps - to_hash + def from_hash hash + Blobject.new hash end - def self.from_json json, freeze=true - h = ActiveSupport::JSON.decode(json) - if h.class==Hash - b = Blobject.new h - b.freeze if freeze - return b - elsif h.class==Array - return blobjectify_array h - else - return h - end - end + def to_yaml *params + @hash.to_yaml *params + end - def self.blobjectify_array array - array.map do |e| - if e.class==Hash - Blobject.new(e) - elsif e.class==Array - blobjectify_array(e) - else - e - end - end + def self.from_yaml yaml + __blobjectify__ YAML.load(yaml) end - def self.deblobjectify_array array - array.map do |e| - if e.class==Blobject - e.to_hash - elsif e.class==Array - deblobjectify_array(e) - else - e - end - end + def to_json *params + @hash.to_json *params end - def [] key - @values[key] + def self.from_json json + __blobjectify__ JSON.load(json) end - def []= key, value - @values[key] = value + def dup + Blobject.new to_hash end - def blank? - empty? + def inspect + @hash.inspect end + - def empty? - @values.empty? || @values.values.empty? || !@values.values.any? do |v| - #if the value is a Blobject, Hash or Array return - #true if it is not empty. - #else just return true, the value is regarded as not empty. - if [Blobject, Array, Hash].include?(v.class) - !v.empty? - else - true + +protected + + def self.__blobjectify__ obj + + if obj.instance_of?(Hash) + + obj.each do |key, value| + obj[key] = __blobjectify__ value end + + return self.new obj end - end + + if obj.instance_of?(Array) + return obj.map do |e| + __blobjectify__ e + end + end + + obj + end -end + def __r_modify_set__ modifying + @modifying = modifying + @hash.values.each do |child| + if child.class <= Blobject + child.__r_modify_set__ modifying + end + end + end +end \ No newline at end of file