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