module HasMetadataColumn::ClassMethods
Class methods that are added to your model.
Public Class Methods
# File lib/has_metadata_column.rb, line 136 def define_attribute_methods super metadata_column_fields.keys.each { |field| define_attribute_method field.to_s } end
# File lib/has_metadata_column.rb, line 141 def define_method_attribute(attr_name) return super unless metadata_column_fields.include?(attr_name.to_sym) generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 def __temp__#{attr_name} options = self.class.metadata_column_fields[:#{attr_name}] || {} default = options.include?(:default) ? options[:default] : nil _metadata_hash.include?('#{attr_name}') ? HasMetadataColumn.metadata_typecast(_metadata_hash['#{attr_name}'], options[:type]) : default end RUBY end
# File lib/has_metadata_column.rb, line 152 def define_method_attribute=(attr_name) return super unless metadata_column_fields.include?(attr_name.to_sym) generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 def __temp__#{attr_name}=(value) attribute_will_change! :#{attr_name} old = _metadata_hash['#{attr_name}'] send (self.class.metadata_column + '='), _metadata_hash.merge('#{attr_name}' => value).to_json @_metadata_hash = nil value end RUBY end
Public Instance Methods
Defines a set of fields whose values exist in the JSON metadata column. 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`). See {TYPES} for a list of valid values.
@overload has_metadata_column
(column, fields)
@param [Symbol] column (:metadata) The column containing the metadata information. @param [Hash<Symbol, Hash>] fields A mapping of field names to their validation options (and/or the `:type` key).
@raise [ArgumentError] If invalid arguments are given, or an invalid
class for the `:type` key.
@raise [StandardError] If invalid field names are given (see source).
@example Three metadata fields, one basic, one validated, and one type-checked.
has_metadata_column(optional: true, required: { presence: true }, number: { type: Fixnum })
# File lib/has_metadata_column.rb, line 93 def has_metadata_column(*args) fields = args.extract_options! column = args.shift raise ArgumentError, "has_metadata_column takes a column name and a hash of fields" unless args.empty? raise "Can't define Rails-magic timestamped columns as metadata" if Rails.version >= '3.2.0' && (fields.keys & [:created_at, :created_on, :updated_at, :updated_on]).any? classes = fields.values.select { |o| o[:type] && !TYPES.include?(o[:type]) } raise ArgumentError, "#{classes.to_sentence} cannot be serialized to JSON" if classes.any? if !respond_to?(:metadata_column_fields) then class_attribute :metadata_column_fields self.metadata_column_fields = fields.deep_clone class_attribute :metadata_column self.metadata_column = column || :metadata else raise "Cannot redefine existing metadata column #{self.metadata_column}" if column && column != self.metadata_column if metadata_column_fields.slice(*fields.keys) != fields raise "Cannot redefine existing metadata fields: #{(fields.keys & self.metadata_column_fields.keys).to_sentence}" unless (fields.keys & self.metadata_column_fields.keys).empty? self.metadata_column_fields = self.metadata_column_fields.merge(fields) end end fields.each do |name, options| if options.kind_of?(Hash) then type = options.delete(:type) type_validate = !options.delete(:skip_type_validation) options.delete :default attribute name validate do |obj| value = obj.send(name) if !HasMetadataColumn.metadata_typecast(value, type).kind_of?(type) && (!options[:allow_nil] || (options[:allow_nil] && !value.nil?)) && (!options[:allow_blank] || (options[:allow_blank] && !value.blank?)) errors.add(name, :incorrect_type) end end if type && type_validate validates(name, options) unless options.empty? or (options.keys - [:allow_nil, :allow_blank]).empty? end end class << self def define_attribute_methods super metadata_column_fields.keys.each { |field| define_attribute_method field.to_s } end def define_method_attribute(attr_name) return super unless metadata_column_fields.include?(attr_name.to_sym) generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 def __temp__#{attr_name} options = self.class.metadata_column_fields[:#{attr_name}] || {} default = options.include?(:default) ? options[:default] : nil _metadata_hash.include?('#{attr_name}') ? HasMetadataColumn.metadata_typecast(_metadata_hash['#{attr_name}'], options[:type]) : default end RUBY end def define_method_attribute=(attr_name) return super unless metadata_column_fields.include?(attr_name.to_sym) generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 def __temp__#{attr_name}=(value) attribute_will_change! :#{attr_name} old = _metadata_hash['#{attr_name}'] send (self.class.metadata_column + '='), _metadata_hash.merge('#{attr_name}' => value).to_json @_metadata_hash = nil value end RUBY end end end