lib/citeproc/attributes.rb in citeproc-0.0.8 vs lib/citeproc/attributes.rb in citeproc-0.0.9

- old
+ new

@@ -1,124 +1,131 @@ - module CiteProc - - # TODO refactor using a Struct instead of a hash. This will have to convert - # the CiteProc/CSL names which are no proper method names. - - + module Attributes extend Forwardable - - FALSE_PATTERN = (/^(false|no|never)$/i).freeze - def self.included(base) base.extend(ClassMethods) end def attributes @attributes ||= {} end - def_delegators :attributes, :length, :empty? + def_delegators :attributes, :length, :empty?, :values_at, :key?, :value? - def [](key) - attributes[filter_key(key)] - end - - def []=(key, value) - attributes[filter_key(key)] = filter_value(value) - end - - def filter_key(key) - key.to_sym - end - - def filter_value(value, key = nil) - value.respond_to?(:deep_copy) ? value.deep_copy : value.dup - rescue - value - end - + alias size length + + def [](key) + attributes[filter_key(key)] + end + + def []=(key, value) + attributes[filter_key(key)] = filter_value(value) + end + + def filter_key(key) + key.to_sym + end + + def filter_value(value, key = nil) + value.respond_to?(:deep_copy) ? value.deep_copy : value.dup + rescue + value + end + def merge(other) return self if other.nil? - case other - when String, /^\s*\{/ - other = MulitJson.decode(other, :symbolize_keys => true) - when Hash - # do nothing - when Attributes + case + when other.is_a?(String) && /^\s*\{/ =~ other + other = MultiJson.decode(other, :symbolize_keys => true) + when other.respond_to?(:each_pair) + # do nothing + when other.respond_to?(:to_hash) other = other.to_hash - else - raise ParseError, "failed to merge attributes and #{other.inspect}" + else + raise ParseError, "failed to merge attributes and #{other.inspect}" end other.each_pair do |key, value| - attributes[filter_key(key)] = filter_value(value, key) - end - + attributes[filter_key(key)] = filter_value(value, key) + end + self end alias update merge - def reverse_merge(other) - fail "not implemented yet" - end + def reverse_merge(other) + fail "not implemented yet" + end - def to_hash - attributes.deep_copy - end + def to_hash + attributes.deep_copy + end - def to_citeproc - Hash[*attributes.map { |k,v| - [k.to_s, v.respond_to?(:to_citeproc) ? v.to_citeproc : v.to_s] - }.flatten(1)] - end - - def to_json - MultiJson.encode(to_citeproc) - end + # @return [Hash] a hash-based representation of the attributes + def to_citeproc + Hash[attributes.map { |k,v| + [k.to_s, v.respond_to?(:to_citeproc) ? v.to_citeproc : v.to_s] + }] + end + + # @return [String] a JSON string representation of the attributes + def to_json + MultiJson.encode(to_citeproc) + end - # Don't expose internals to public API - private :filter_key, :filter_value - - # initialize_copy should be able to access attributes - protected :attributes - + # Don't expose internals to public API + private :filter_key, :filter_value + + # initialize_copy should be able to access attributes + protected :attributes - - # def eql?(other) - # case - # when equal?(other) - # true - # when self.class != other.class, length != other.length - # false - # else - # other.attributes.each_pair do |key, value| - # return false unless attributes[key].eql?(value) - # end - # - # true - # end - # end - # - # def hash - # end - + + # Two Attribute-based objects are equal if they are the same object, + # or if all their attributes are equal using _#eql?_. + # + # @param other [Object] the other object + # @return [Boolean] whether or not self and passed-in object are equal + def eql?(other) + case + when equal?(other) + true + when self.class != other.class, length != other.length + false + else + other.attributes.each_pair do |key, value| + return false unless attributes[key].eql?(value) + end + + true + end + end + + # @return [Fixnum] a hash value based on the object's attributes + def hash + digest = size + attributes.each do |attribute| + digest ^= attribute.hash + end + + digest + end + module ClassMethods - def create(parameters) - create!(parameters) - rescue - nil - end + def create(parameters) + create!(parameters) + rescue + nil + end - def create!(parameters) - new.merge(parameters) - end + def create!(parameters) + new.merge(parameters) + end def attr_predicates(*arguments) arguments.flatten.each do |field| field, default = *(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s) attr_field(field, default, true) @@ -150,15 +157,15 @@ unless instance_methods.include?(writer_id) define_method(writer_id) do |value| attributes[field.to_sym] = value end end - + predicate_id = [method_id, '?'].join if predicate && !instance_methods.include?(predicate_id) define_method(predicate_id) do - v = attributes[field.to_sym] - !(v.nil? || (v.respond_to?(:empty?) && v.empty?) || v =~ FALSE_PATTERN) + v = attributes[field.to_sym] + !(!v || (v.respond_to?(:empty?) && v.empty?) || v.to_s =~ /^(false|no|never)$/i) end has_predicate = ['has_', predicate_id].join alias_method(has_predicate, predicate_id) unless instance_methods.include?(has_predicate) end \ No newline at end of file