module CustomFields::Source

Attributes

_custom_field_localize_diff[RW]
_custom_fields_diff[RW]

Public Instance Methods

apply_custom_fields_diff(name) click to toggle source

Apply the modifications collected from the custom fields by updating all the documents of the relation. The update uses the power of mongodb to make it fully optimized.

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

# File lib/custom_fields/source.rb, line 169
def apply_custom_fields_diff(name)
  # puts "==> apply_custom_fields_recipes for #{name}, #{self._custom_fields_diff[name].inspect}" # DEBUG

  operations = self._custom_fields_diff[name]
  operations['$set'].merge!({ 'custom_fields_recipe.version' => custom_fields_version(name) })
  collection = send(name).collection
  selector = send(name).criteria.selector

  # http://docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
  # The <update> document must contain only update operator expressions.
  %w[set unset rename].each do |operation_name|
    _fields = operations.delete("$#{operation_name}")

    next if _fields.empty?

    _operation = { "$#{operation_name}" => _fields }
    collection.find(selector).update_many _operation
  end
end
apply_custom_fields_localize_diff(name) click to toggle source

If the localized attribute has been changed in at least one of the custom fields, we have to upgrade all the records enhanced by custom_fields in order to make the values consistent with the mongoid localize option.

Ex: post.attributes = ‘Hello world’ => post.attributes = { en: ‘Hello world’ }

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

# File lib/custom_fields/source.rb, line 197
def apply_custom_fields_localize_diff(name)
  return if self._custom_field_localize_diff[name].empty?

  send(name).all.each do |record|
    updates = {}

    # puts "[apply_custom_fields_localize_diff] processing: record #{record._id} / #{self._custom_field_localize_diff[name].inspect}" # DEBUG
    self._custom_field_localize_diff[name].each do |changes|
      value = record.attributes[changes[:field]]
      if changes[:localized]
        updates[changes[:field]] = { Mongoid::Fields::I18n.locale.to_s => value }
      else
        # the other way around
        next if value.nil?

        updates[changes[:field]] = value[Mongoid::Fields::I18n.locale.to_s]
      end
    end

    next if updates.empty?

    collection = send(name).collection
    collection.find(record.atomic_selector).update_one({ '$set' => updates })
  end
end
bump_custom_fields_version(name) click to toggle source

When the fields have been modified and before the object is saved, we bump the version.

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

# File lib/custom_fields/source.rb, line 88
def bump_custom_fields_version(name)
  version = custom_fields_version(name) + 1
  send(:"#{name}_custom_fields_version=", version)
end
collect_custom_fields_diff(name, fields) click to toggle source

Collects all the modifications of the custom fields

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

@return [ Array ] An array of hashes storing the modifications

# File lib/custom_fields/source.rb, line 146
def collect_custom_fields_diff(name, fields)
  # puts "==> collect_custom_fields_diff for #{name}, #{fields.size}" # DEBUG

  memo = initialize_custom_fields_diff(name)

  fields.map do |field|
    field.collect_diff(memo)
  end

  # collect fields with a modified localized field
  fields.each do |field|
    if field.localized_changed? && field.persisted?
      self._custom_field_localize_diff[name] << { field: field.name, localized: field.localized? }
    end
  end
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 24
def custom_fields_for?(name)
  self.class.custom_fields_for?(name)
end
custom_fields_recipe_for(name) click to toggle source

Returns the recipe (meaning all the rules) needed to build the custom klass

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

@return [ Array ] An array of hashes

# File lib/custom_fields/source.rb, line 64
def custom_fields_recipe_for(name)
  {
    'name' => "#{relations[name.to_s].class_name.demodulize}#{_id}",
    'rules' => ordered_custom_fields(name).map(&:to_recipe),
    'version' => custom_fields_version(name),
    'model_name' => relations[name.to_s].class_name.constantize.model_name.to_s
  }
end
custom_fields_version(name) click to toggle source

Returns the number of the version for relation with custom fields

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

@return [ Integer ] The version number

# File lib/custom_fields/source.rb, line 79
def custom_fields_version(name)
  send(:"#{name}_custom_fields_version") || 0
end
initialize_custom_fields_diff(name) click to toggle source

Initializes the object tracking the modifications of the custom fields

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

# File lib/custom_fields/source.rb, line 133
def initialize_custom_fields_diff(name)
  self._custom_field_localize_diff ||= Hash.new([])

  self._custom_fields_diff ||= {}
  self._custom_fields_diff[name] = { '$set' => {}, '$unset' => {}, '$rename' => {} }
end
klass_with_custom_fields(name) click to toggle source

Returns the class enhanced by the custom fields. Be careful, call this method only if the source class has been saved with success.

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

@return [ Class ] The modified class.

# File lib/custom_fields/source.rb, line 36
def klass_with_custom_fields(name)
  # Rails.logger.debug "[CustomFields] klass_with_custom_fields #{self.send(name).metadata.klass} / #{self.send(name).metadata[:old_klass]}" if defined?(Rails) # DEBUG
  recipe    = custom_fields_recipe_for(name)
  _metadata = send(name)._association
  target    = _metadata.options[:original_klass] || _metadata.klass # avoid to use an already enhanced klass
  target.klass_with_custom_fields(recipe)
end
ordered_custom_fields(name) click to toggle source

Returns the ordered list of custom fields for a relation

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

person.ordered_custom_fields(:addresses)

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

@return [ Collection ] The ordered list.

# File lib/custom_fields/source.rb, line 53
def ordered_custom_fields(name)
  send(:"#{name}_custom_fields").sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
refresh_metadata_with_custom_fields(name) click to toggle source

Change the metadata of a relation enhanced by the custom fields. In Mongoid, all the instances of a same document share the same metadata objects.

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

# File lib/custom_fields/source.rb, line 98
def refresh_metadata_with_custom_fields(name)
  return if !persisted? || send(:"#{name}_custom_fields").blank?

  old_metadata = send(name)._association

  # puts "old_metadata = #{old_metadata.klass.inspect} / #{old_metadata.object_id.inspect}" # DEBUG

  # puts "[CustomFields] refresh_metadata_with_custom_fields, #{name.inspect}, self = #{self.inspect}"

  send(name)._association = old_metadata.clone.tap do |metadata|
    # Rails.logger.debug "[CustomFields] refresh_metadata_with_custom_fields #{metadata.klass}" if defined?(Rails) # DEBUG

    # backup the current klass
    metadata.instance_variable_set(:@options, metadata.options.dup)
    metadata.options[:original_klass] ||= metadata.klass

    metadata.instance_variable_set(:@klass, klass_with_custom_fields(name))
  end
  set_attribute_localization(name)
  # puts "new_metadata = #{self.send(name).metadata.klass.inspect} / #{self.send(name).metadata.object_id.inspect}" # DEBUG
end
set_attribute_localization(name) click to toggle source
# File lib/custom_fields/source.rb, line 120
def set_attribute_localization(name)
  klass_name = name.singularize.to_sym
  send(:"#{name}_custom_fields").each do |cf|
    I18n.backend.store_translations ::I18n.locale,
                                    { mongoid: { attributes: { klass_name => { cf.name.to_sym => cf.label } } } }
  end
end