module CustomFields::Source::ClassMethods

Public Instance Methods

custom_fields_for(name) click to toggle source

Enhance a referenced collection OR the instance itself (by passing self) by providing methods to manage custom fields.

@param [ String, Symbol ] name The name of the relation.

@example

class Company
  embeds_many :employees
  custom_fields_for :employees
end

class Employee
  embedded_in :company, inverse_of: :employees
  field :name, String
end

company.employees_custom_fields.build label: 'His/her position', name: 'position', kind: 'string'
company.save
company.employees.build name: 'Michael Scott', position: 'Regional manager'
# File lib/custom_fields/source.rb, line 256
def custom_fields_for(name)
  declare_embedded_in_definition_in_custom_field(name)

  # stores the relation name
  _custom_fields_for << name.to_s

  extend_for_custom_fields(name)
end
custom_fields_for?(name) click to toggle source

Determines if the relation is enhanced by the custom fields

@example the Person class has somewhere in its code this: “custom_fields_for :addresses”

Person.custom_fields_for?(:addresses)

@param [ String, Symbol ] name The name of the relation.

@return [ true, false ] True if enhanced, false if not.

# File lib/custom_fields/source.rb, line 233
def custom_fields_for?(name)
  _custom_fields_for.include?(name.to_s)
end

Protected Instance Methods

declare_embedded_in_definition_in_custom_field(name) click to toggle source

An embedded relationship has to be defined on both side in order for it to work properly. But because custom_field can be embedded in different models that it’s not aware of, we have to declare manually the definition once we know the target class.

@param [ String, Symbol ] name The name of the relation.

@return [ Field ] The new field class.

# File lib/custom_fields/source.rb, line 337
      def declare_embedded_in_definition_in_custom_field(name)
        klass_name = dynamic_custom_field_class_name(name).split('::').last # Use only the class, ignore the modules

        source = safe_module_parents.size > 1 ? safe_module_parents.first : Object

        return if source.const_defined?(klass_name)

        (klass = Class.new(::CustomFields::Field)).class_eval <<-EOF, __FILE__, __LINE__ + 1
            embedded_in :#{self.name.demodulize.underscore}, inverse_of: :#{name}_custom_fields, class_name: '#{self.name}'
        EOF

        source.const_set(klass_name, klass)
      end
dynamic_custom_field_class_name(name) click to toggle source

Returns the class name of the custom field which is based both on the parent class name and the name of the relation in order to avoid name conflicts (with other classes)

@param [ Metadata ] metadata The relation’s old metadata.

@return [ String ] The class name

# File lib/custom_fields/source.rb, line 324
def dynamic_custom_field_class_name(name)
  "#{self.name}#{name.to_s.singularize.camelize}Field"
end
extend_for_custom_fields(name) click to toggle source

Extends / Decorates the current class in order to be fully custom_fields compliant. it declares news fields, adds new callbacks, …etc

@param [ String, Symbol ] name The name of the relation.

# File lib/custom_fields/source.rb, line 272
      def extend_for_custom_fields(name)
        class_eval do
          field :"#{name}_custom_fields_version", type: ::Integer, default: 0

          embeds_many :"#{name}_custom_fields", class_name: dynamic_custom_field_class_name(name) # , cascade_callbacks: true # FIXME ?????

          accepts_nested_attributes_for :"#{name}_custom_fields", allow_destroy: true
        end

        class_eval <<-EOV, __FILE__, __LINE__ + 1
          after_initialize  :refresh_#{name}_metadata
          before_update     :bump_#{name}_custom_fields_version
          before_update     :collect_#{name}_custom_fields_diff
          after_update      :apply_#{name}_custom_fields_diff
          after_update      :apply_#{name}_custom_fields_localize_diff

          def ordered_#{name}_custom_fields
            self.ordered_custom_fields('#{name}')
          end

          protected

          def refresh_#{name}_metadata
            self.refresh_metadata_with_custom_fields('#{name}')
          end

          def bump_#{name}_custom_fields_version
            self.bump_custom_fields_version('#{name}')
          end

          def collect_#{name}_custom_fields_diff
            self.collect_custom_fields_diff(:#{name}, self.#{name}_custom_fields)
          end

          def apply_#{name}_custom_fields_diff
            self.apply_custom_fields_diff(:#{name})
          end

          def apply_#{name}_custom_fields_localize_diff
            self.apply_custom_fields_localize_diff(:#{name})
          end

        EOV
      end
safe_module_parents() click to toggle source
# File lib/custom_fields/source.rb, line 351
def safe_module_parents
  respond_to?(:module_parents) ? module_parents : parents
end