Module: CustomFields::Source

Extended by:
ActiveSupport::Concern
Defined in:
lib/custom_fields/source.rb

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#apply_custom_fields_diff(name) ⇒ Object

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.

Parameters:

  • name (String, Symbol)

    The name of the relation.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# 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) ⇒ Object

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’ }

Parameters:

  • name (String, Symbol)

    The name of the relation.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# 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) ⇒ Object

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

Parameters:

  • name (String, Symbol)

    The name of the relation.



88
89
90
91
# 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) ⇒ Array

Collects all the modifications of the custom fields

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (Array)

    An array of hashes storing the modifications



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# 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) ⇒ true, false

Determines if the relation is enhanced by the custom fields

Examples:

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

person.custom_fields_for?(:addresses)

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (true, false)

    True if enhanced, false if not.



24
25
26
# 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) ⇒ Array

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

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (Array)

    An array of hashes



64
65
66
67
68
69
70
71
# 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) ⇒ Integer

Returns the number of the version for relation with custom fields

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (Integer)

    The version number



79
80
81
# 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) ⇒ Object

Initializes the object tracking the modifications of the custom fields

Parameters:

  • name (String, Symbol)

    The name of the relation.



133
134
135
136
137
138
# 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) ⇒ Class

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

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (Class)

    The modified class.



36
37
38
39
40
41
42
# 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)
   = send(name)._association
  target    = .options[:original_klass] || .klass # avoid to use an already enhanced klass
  target.klass_with_custom_fields(recipe)
end

#ordered_custom_fields(name) ⇒ Collection

Returns the ordered list of custom fields for a relation

Examples:

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

person.ordered_custom_fields(:addresses)

Parameters:

  • name (String, Symbol)

    The name of the relation.

Returns:

  • (Collection)

    The ordered list.



53
54
55
# 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) ⇒ Object

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.

Parameters:

  • name (String, Symbol)

    The name of the relation.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/custom_fields/source.rb', line 98

def (name)
  return if !persisted? || send(:"#{name}_custom_fields").blank?

   = 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 = .clone.tap do ||
    # Rails.logger.debug "[CustomFields] refresh_metadata_with_custom_fields #{metadata.klass}" if defined?(Rails) # DEBUG

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

    .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) ⇒ Object



120
121
122
123
124
125
126
# 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