lib/citeproc/attributes.rb in citeproc-0.0.2 vs lib/citeproc/attributes.rb in citeproc-0.0.3

- old
+ new

@@ -1,65 +1,140 @@ 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 [](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.encode(other, :symbolize_keys => true) + other = MulitJson.decode(other, :symbolize_keys => true) when Hash - other = other.deep_copy - else + # do nothing + when Attributes other = other.to_hash + else + raise ParseError, "failed to merge attributes and #{other.inspect}" end - other.each_pair { |k,v| attributes[k.to_sym] = v } + other.each_pair do |key, value| + attributes[filter_key(key)] = filter_value(value, key) + end self end alias update merge - def reverse_merge(other) - other.merge(self) - end + def reverse_merge(other) + fail "not implemented yet" + end - alias to_hash attributes + 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 + + # 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 + module ClassMethods - def create(parameters) - new.merge(parameters) - end - + def create(parameters) + create!(parameters) + rescue + nil + 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) end end - def attr_fields + def attr_fields(*arguments) arguments.flatten.each do |field| attr_field(*(field.is_a?(Hash) ? field.to_a.flatten : [field]).map(&:to_s)) end end def attr_field(field, default = nil, predicate = false) - method_id = field.downcase.gsub(/[-\s]+/, '_') + method_id = field.to_s.downcase.gsub(/[-\s]+/, '_') unless instance_methods.include?(method_id) if default define_method(method_id) do attributes[field.to_sym] @@ -75,14 +150,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 - ![nil, false, '', [], 'false', 'no', 'never'].include?(attributes[field.to_sym]) + v = attributes[field.to_sym] + !(v.nil? || (v.respond_to?(:empty?) && v.empty?) || v =~ FALSE_PATTERN) 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