lib/blobject.rb in blobject-0.3.7 vs lib/blobject.rb in blobject-0.4.0

- old
+ new

@@ -2,11 +2,11 @@ require 'yaml' require_relative 'blobject/version' # Wraps a hash to provide arbitrarily nested object-style attribute access class Blobject - + # filter :to_ary else Blobject#to_ary returns a # blobject which is not cool, especially if you are puts. ProhibitedNames = [:to_ary] # pass an optional hash of values to preload @@ -25,29 +25,29 @@ end yield self if block_given? end + # indicates whether the blobject contains any data def empty? @hash.empty? end # delegates to the internal Hash def inspect - + @hash.inspect end # access the internal hash. be careful, this is _not_ a copy def hash - @hash end # creates a recursive copy of the internal hash def to_hash - + h = hash.dup @hash.each do |name, node| h[name] = node.to_hash if node.respond_to? :to_hash end h @@ -61,11 +61,11 @@ case # assignment in conditionals is usually a bad smell, here it helps minimize regex matching when (name = method[/^\w+$/, 0]) && params.length == 0 # the call is an attribute reader - + return self.class.new.freeze if frozen? and not @hash.has_key?(method) self.class.send :__define_attribute__, name return send(method) if @hash.has_key? method @@ -97,54 +97,43 @@ super end def respond_to? method - - return true if self.methods.include?(method) - return false if ProhibitedNames.include?(method) + super || self.__respond_to__?(method) + end - method = method.to_s - - [/^(\w+)=$/, /^(\w+)\?$/, /^\w+$/].any? do |r| - r.match(method) - end + def respond_to_missing?(method, *) + super || self.__respond_to__?(method) end - # compares Blobjects to Blobjects or Hashes + # compares Blobjects to Blobjects or Hashes for equality def == other return @hash == other.hash if other.class <= Blobject return @hash == other if other.class <= Hash super end # hash-like access to the Blobject's attributes def [] name - + send name end - # hash-like attribtue setter + # hash-like attribute setter def []= name, value - + send "#{name.to_s}=", value end - + # freeze a Blobject to prevent it being modified def freeze - @hash.freeze + self.class.send(:__freeze_r__, @hash) unless frozen? super end - def freeze_r - self.class.send(:__freeze_r__, self) - freeze - end - - - # returns a hash which can be serialized as json. - # this is for use in rails controllers: `render json: blobject` + # for rails: `render json: blobject` def as_json *args return hash.as_json(*args) if hash.respond_to? :as_json to_hash end @@ -153,66 +142,66 @@ as_json.to_json *args end # serialize the Blobject as a yaml string def to_yaml - + as_yaml.to_yaml end - # get a Blobject from a json string - # if the yaml string describes an array, an array will be returned + # get a Blobject from a json string, if the yaml string describes an array, an array will be returned def self.from_json json - + __blobjectify__(JSON.parse(json)) end - # get a Blobject from a yaml string - # if the yaml string describes an array, an array will be returned + # get a Blobject from a yaml string, if the yaml string describes an array, an array will be returned def self.from_yaml yaml - + __blobjectify__(YAML.load(yaml)) end -private -# to avoid naming collisions private method names are prefixed and suffix with double unerscores (__) + protected + # to avoid naming collisions private method names are prefixed and suffix with double unerscores (__) - # Used to tag and reraise errors from a Blobject + # Used to tag and re-raise errors from a Blobject # Refer to "Tagging exceptions with modules" on p97 in Exceptional Ruby by Avdi Grimm # errors from this library can be handled with rescue Blobject::Error module Error; end - + def __tag_and_raise__ e raise e rescue e.extend Blobject::Error raise e end + def __respond_to__?(method) + return false if ProhibitedNames.include?(method) + + method = method.to_s + + [/^(\w+)=$/, /^(\w+)\?$/, /^\w+$/].any? do |r| + r.match(method) + end + end + class << self - private + protected def __freeze_r__ object - - case object - when Array - return object.each do |e| - e.freeze - __freeze_r__(e) - end - when Hash - return object.each do |k, v| + + if object.respond_to?(:each) && object.each.is_a?(Enumerator) + values = object.is_a?(Hash) ? object.values : object + values.each do |v| v.freeze __freeze_r__(v) end - when Blobject - object.freeze - __freeze_r__ object.hash - else - object.freeze end + + object.freeze end def __blobjectify__ object array = object if object.is_a? Array @@ -234,11 +223,11 @@ unless methods.include? setter_name self.send :define_method, setter_name do |value| begin value = self.class.send(:__blobjectify__, value) if value.is_a?(Hash) or value.is_a?(Array) @hash[name] = value - rescue ex + rescue => ex __tag_and_raise__(ex) end @store_in_parent.call unless @store_in_parent.nil? end end @@ -246,11 +235,11 @@ unless methods.include? name self.send :define_method, name do value = @hash[name] - if value.nil? + if value.nil? value = self.class.new @hash[name] = value unless frozen? end value @@ -258,13 +247,13 @@ end checker_name = (name.to_s + '?').to_sym unless methods.include? checker_name self.send :define_method, checker_name do - @hash.key?(name) + @hash[name] ? true : false end end name end end -end \ No newline at end of file +end