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