Module: HasMetadata::ClassMethods

Defined in:
lib/has_metadata.rb

Overview

Class methods that are added to your model.

Instance Method Summary collapse

Instance Method Details

#has_metadata(fields) ⇒ Object

Defines a set of fields whose values exist in the associated Metadata record. Each key in the @fields@ hash is the name of a metadata field, and the value is a set of options to pass to the @validates@ method. If you do not want to perform any validation on a field, simply pass @true@ as its key value.

In addition to the normal @validates@ keys, you can also include a @:type@ key to restrict values to certain classes, or a @:default@ key to specify a value to return for the getter should none be set (normal default is @nil@).

Examples:

Three metadata fields, one basic, one validated, and one type-checked.

(optional: true, required: { presence: true }, number: { type: Fixnum })

Parameters:

  • fields (Hash<Symbol, Hash>)

    A mapping of field names to their validation options (and/or the @:type@ key).



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/has_metadata.rb', line 65

def (fields)
  if !respond_to?(:metadata_fields) then
    belongs_to :metadata, dependent: :destroy
    accepts_nested_attributes_for :metadata
    after_save :save_metadata, if: :metadata_changed?

    class_attribute :metadata_fields
    self. = fields.deep_clone

    define_method(:save_metadata) { .save! }
    define_method(:metadata_changed?) { .try :changed? }

    alias_method_chain :changed_attributes, :metadata
    alias_method_chain :attribute_will_change!, :metadata
  else
    raise "Cannot redefine existing metadata fields: #{(fields.keys & self..keys).to_sentence}" unless (fields.keys & self..keys).empty?
    self. = self..merge(fields)
  end

  fields.each do |name, options|
    # delegate all attribute methods to the metadata
    attribute_method_matchers.each { |matcher| delegate matcher.method_name(name), to: :metadata! }
    
    if options.kind_of?(Hash) then
      type = options.delete(:type)
      type_validate = !options.delete(:skip_type_validation)
      options.delete :default
      
      validate do |obj|
        value = obj.send(name)
        errors.add(name, :incorrect_type) unless
          HasMetadata.(value, type).kind_of?(type) or
            ((options[:allow_nil] and value.nil?) or (options[:allow_blank] and value.blank?))
      end if type && type_validate
      validates(name, options) unless options.empty? or (options.keys - [ :allow_nil, :allow_blank ]).empty?
    end
  end
end