custom_fields 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/README.textile +4 -4
  3. data/config/locales/de.yml +11 -5
  4. data/config/locales/en.yml +4 -0
  5. data/config/locales/fr.yml +4 -0
  6. data/config/locales/ru.yml +15 -0
  7. data/lib/custom_fields.rb +37 -27
  8. data/lib/custom_fields/extensions/active_support.rb +1 -1
  9. data/lib/custom_fields/extensions/carrierwave.rb +2 -2
  10. data/lib/custom_fields/extensions/mongoid/factory.rb +3 -3
  11. data/lib/custom_fields/extensions/mongoid/fields.rb +4 -3
  12. data/lib/custom_fields/extensions/mongoid/fields/localized.rb +39 -0
  13. data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +1 -1
  14. data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +3 -3
  15. data/lib/custom_fields/field.rb +14 -8
  16. data/lib/custom_fields/source.rb +23 -14
  17. data/lib/custom_fields/target.rb +16 -8
  18. data/lib/custom_fields/target_helpers.rb +6 -6
  19. data/lib/custom_fields/types/belongs_to.rb +4 -4
  20. data/lib/custom_fields/types/boolean.rb +1 -1
  21. data/lib/custom_fields/types/date.rb +2 -2
  22. data/lib/custom_fields/types/default.rb +5 -6
  23. data/lib/custom_fields/types/email.rb +60 -0
  24. data/lib/custom_fields/types/file.rb +2 -2
  25. data/lib/custom_fields/types/float.rb +52 -0
  26. data/lib/custom_fields/types/has_many.rb +6 -8
  27. data/lib/custom_fields/types/integer.rb +54 -0
  28. data/lib/custom_fields/types/many_to_many.rb +3 -3
  29. data/lib/custom_fields/types/money.rb +146 -0
  30. data/lib/custom_fields/types/relationship_default.rb +2 -2
  31. data/lib/custom_fields/types/select.rb +16 -13
  32. data/lib/custom_fields/types/tags.rb +35 -0
  33. data/lib/custom_fields/types/text.rb +1 -1
  34. data/lib/custom_fields/version.rb +1 -1
  35. metadata +65 -76
  36. data/init.rb +0 -2
  37. data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +0 -86
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 [Didier Lafforgue]
1
+ Copyright (c) 2013 [Didier Lafforgue]
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.textile CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  h1. CustomFields
4
4
 
5
- Manage custom fields to a mongoid document or a collection. This module is one of the core features we implemented in our custom cms named Locomotive.
5
+ Manage custom fields to a mongoid document or a collection. This module is one of the core features we implemented in our custom cms, LocomotiveCMS.
6
6
  Basically, its aim is to provide to editors a way to manage extra fields to a Mongoid document through, for instance, a web UI.
7
7
 
8
8
  The main goals:
@@ -12,7 +12,7 @@ The main goals:
12
12
 
13
13
  h2. Requirements
14
14
 
15
- ActiveSupport 3.2.9, MongoDB 2.0 and Mongoid 2.4.12
15
+ ActiveSupport 3.2.13, MongoDB 2.0 and Mongoid 3.1.3
16
16
 
17
17
  h2. Examples
18
18
 
@@ -42,7 +42,7 @@ company.save
42
42
  company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
43
43
 
44
44
  another_company = Company.new
45
- employee = company.employees.build
45
+ employee = another_company.employees.build
46
46
  employee.position # returns a "not defined method" error
47
47
 
48
48
  h3. On the class itself
@@ -67,4 +67,4 @@ h2. Contact
67
67
 
68
68
  Feel free to contact me at didier at nocoffee dot fr.
69
69
 
70
- Copyright (c) 2012 NoCoffee, released under the MIT license
70
+ Copyright (c) 2013 NoCoffee, released under the MIT license
@@ -1,9 +1,15 @@
1
1
  de:
2
2
  custom_fields:
3
3
  type:
4
- string: Einfache Texteingabe
5
- text: Text
6
- select: Auswahlbox
7
- boolean: Checkbox
4
+ string: Einfacher Text
5
+ text: Formatierter Text
6
+ select: Auswahl-Liste
7
+ boolean: Auswahl-Box
8
+ integer: Ganzzahl
9
+ float: Fließkommazahl
10
+ money: Währung
8
11
  date: Datum
9
- file: Datei
12
+ file: Datei
13
+ belongs_to: gehört zu
14
+ has_many: Hat viele
15
+ many_to_many: Hat und gehört zu vielen
@@ -6,7 +6,11 @@ en:
6
6
  select: Select
7
7
  boolean: Checkbox
8
8
  date: Date
9
+ integer: Integer
9
10
  file: File
11
+ integer: Integer
12
+ float: Float
13
+ money: Money
10
14
  belongs_to: Belongs to
11
15
  has_many: Has many
12
16
  many_to_many: Many To Many
@@ -6,7 +6,11 @@ fr:
6
6
  select: Liste déroulante
7
7
  boolean: Case à cocher
8
8
  date: Date
9
+ integer: Nombre
9
10
  file: Fichier
11
+ integer: Entier
12
+ float: Décimal
13
+ money: Monnaie
10
14
  belongs_to: Appartient à
11
15
  has_many: A plusieurs
12
16
  many_to_many: A plusieurs et appartient à
@@ -0,0 +1,15 @@
1
+ ru:
2
+ custom_fields:
3
+ type:
4
+ string: Простая строка
5
+ text: Текст
6
+ select: Выбор
7
+ boolean: Чекбокс
8
+ date: Дата
9
+ file: Файл
10
+ integer: Целое число
11
+ float: Число с дробной частью
12
+ money: Деньги
13
+ belongs_to: Относится к
14
+ has_many: Имеет много
15
+ many_to_many: Многие ко многим
data/lib/custom_fields.rb CHANGED
@@ -2,11 +2,13 @@ $:.unshift File.expand_path(File.dirname(__FILE__))
2
2
 
3
3
  require 'active_support'
4
4
  require 'carrierwave/mongoid'
5
+ require 'money'
5
6
 
6
7
  module CustomFields
7
8
 
8
9
  @@options = {
9
- :reserved_names => Mongoid.destructive_fields + %w(id _id send class)
10
+ reserved_names: Mongoid.destructive_fields + %w(id _id send class),
11
+ default_currency: 'EUR'
10
12
  }
11
13
 
12
14
  def self.options=(options)
@@ -19,31 +21,39 @@ module CustomFields
19
21
 
20
22
  end
21
23
 
22
- require 'custom_fields/version'
23
- require 'custom_fields/extensions/active_support'
24
- require 'custom_fields/extensions/carrierwave'
25
- require 'custom_fields/extensions/mongoid/document'
26
- require 'custom_fields/extensions/mongoid/factory'
27
- require 'custom_fields/extensions/mongoid/relations/referenced/many'
28
- require 'custom_fields/extensions/mongoid/relations/referenced/in'
29
- require 'custom_fields/extensions/mongoid/fields.rb'
30
- require 'custom_fields/extensions/mongoid/fields/i18n.rb'
31
- require 'custom_fields/extensions/mongoid/fields/internal/localized.rb'
32
- require 'custom_fields/types/default'
33
- require 'custom_fields/types/string'
34
- require 'custom_fields/types/text'
35
- require 'custom_fields/types/date'
36
- require 'custom_fields/types/boolean'
37
- require 'custom_fields/types/file'
38
- require 'custom_fields/types/select'
39
- require 'custom_fields/types/relationship_default'
40
- require 'custom_fields/types/belongs_to'
41
- require 'custom_fields/types/has_many'
42
- require 'custom_fields/types/many_to_many'
43
- require 'custom_fields/field'
44
- require 'custom_fields/source'
45
- require 'custom_fields/target_helpers'
46
- require 'custom_fields/target'
24
+ %w( version
25
+ extensions/active_support
26
+ extensions/carrierwave
27
+ extensions/mongoid/document
28
+ extensions/mongoid/factory
29
+ extensions/mongoid/relations/referenced/many
30
+ extensions/mongoid/relations/referenced/in
31
+ extensions/mongoid/fields.rb
32
+ extensions/mongoid/fields/i18n.rb
33
+ extensions/mongoid/fields/localized.rb
34
+ types/default
35
+ types/string
36
+ types/text
37
+ types/email
38
+ types/date
39
+ types/boolean
40
+ types/file
41
+ types/select
42
+ types/integer
43
+ types/float
44
+ types/money
45
+ types/relationship_default
46
+ types/belongs_to
47
+ types/has_many
48
+ types/many_to_many
49
+ types/tags
50
+ field
51
+ source
52
+ target_helpers
53
+ target
54
+ ).each do |lib|
55
+ require_relative "./custom_fields/#{lib}"
56
+ end
47
57
 
48
58
  # Load all the translation files
49
59
  I18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', 'config', 'locales', '*.yml')]
@@ -57,4 +67,4 @@ module MyBenchmark
57
67
  returned_value
58
68
  end
59
69
 
60
- end
70
+ end
@@ -25,4 +25,4 @@ class String
25
25
  end
26
26
 
27
27
  alias_method_chain :constantize, :custom_fields
28
- end
28
+ end
@@ -12,7 +12,7 @@ module CarrierWave
12
12
 
13
13
  value = read_attribute(name.to_sym)
14
14
  unless value.nil?
15
- self.class.fields[name.to_s].deserialize(value)
15
+ self.class.fields[name.to_s].demongoize(value)
16
16
  else
17
17
  nil
18
18
  end
@@ -22,4 +22,4 @@ module CarrierWave
22
22
  alias_method_chain :mount_uploader, :localization
23
23
  end
24
24
 
25
- end
25
+ end
@@ -4,11 +4,11 @@ module Mongoid #:nodoc:
4
4
  # Instantiates documents that came from the database.
5
5
  module Factory
6
6
 
7
- def from_db_with_custom_fields(klass, attributes = {})
7
+ def from_db_with_custom_fields(klass, attributes = {}, criteria_instance_id = nil)
8
8
  if klass.with_custom_fields?
9
9
  klass.klass_with_custom_fields(attributes['custom_fields_recipe'])
10
10
  end
11
- from_db_without_custom_fields(klass, attributes)
11
+ from_db_without_custom_fields(klass, attributes, criteria_instance_id)
12
12
  end
13
13
 
14
14
  # equivalent for "alias_method_chain :from_db, :custom_fields"
@@ -17,4 +17,4 @@ module Mongoid #:nodoc:
17
17
 
18
18
  end
19
19
 
20
- end
20
+ end
@@ -19,12 +19,13 @@ module Mongoid #:nodoc
19
19
  # @since 2.1.0
20
20
  def replace_field(name, type, localize = false)
21
21
  # puts "fields[#{name}] = #{fields[name.to_s].inspect} / #{fields.keys.inspect}" # DEBUG
22
- defaults.delete_one(name)
23
- add_field(name, fields[name.to_s].options.merge(:type => type, :localize => localize))
22
+ #attribute_names.delete_one(name)
23
+ remove_defaults(name)
24
+ add_field(name, fields[name.to_s].options.merge(type: type, localize: localize))
24
25
  end
25
26
 
26
27
  end
27
28
 
28
29
  end
29
30
 
30
- end
31
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Fields #:nodoc:
4
+
5
+ # The behaviour of the Localized fields in the custom fields gem is different
6
+ # because we do not rely on I18n directly but on a slight version Mongoid::Fields::I18n.
7
+ # The main reason is only practical to handle the following case:
8
+ # -> Back-office in English and editing content in French.
9
+ #
10
+ # TODO: use this gem instead https://github.com/simi/mongoid-localizer
11
+ #
12
+ class Localized < Standard
13
+
14
+ def mongoize(object)
15
+ { locale.to_s => type.mongoize(object) }
16
+ end
17
+
18
+ private
19
+
20
+ def lookup(object)
21
+ if !object.respond_to?(:keys) # if no translation hash is given, we return the object itself
22
+ object
23
+ elsif object.has_key?(locale.to_s)
24
+ object[locale.to_s]
25
+ elsif I18n.fallbacks?
26
+ object[I18n.fallbacks[locale].map(&:to_s).find { |loc| !object[loc].nil? }]
27
+ else
28
+ nil
29
+ end
30
+ end
31
+
32
+ def locale
33
+ # be careful, it does not return ::I18n.locale
34
+ I18n.locale
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -17,4 +17,4 @@ module Mongoid # :nodoc:
17
17
 
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -14,13 +14,13 @@ module Mongoid #:nodoc:
14
14
 
15
15
  attributes ||= {}
16
16
 
17
- attributes.merge!(:custom_fields_recipe => recipe)
17
+ attributes.merge!(custom_fields_recipe: recipe)
18
18
 
19
19
  # build the class with custom_fields for the first time
20
20
  type = metadata.klass.klass_with_custom_fields(recipe)
21
21
  end
22
-
23
22
  build_without_custom_fields(attributes, options, type)
23
+
24
24
  end
25
25
 
26
26
  alias_method_chain :build, :custom_fields
@@ -31,4 +31,4 @@ module Mongoid #:nodoc:
31
31
 
32
32
  end
33
33
  end
34
- end
34
+ end
@@ -6,7 +6,8 @@ module CustomFields
6
6
  include ::Mongoid::Timestamps
7
7
 
8
8
  ## types ##
9
- %w(default string text date boolean file select relationship_default belongs_to has_many many_to_many).each do |type|
9
+ %w(default string text email date boolean file select float integer money
10
+ relationship_default belongs_to has_many many_to_many).each do |type|
10
11
  include "CustomFields::Types::#{type.classify}::Field".constantize
11
12
  end
12
13
 
@@ -15,14 +16,15 @@ module CustomFields
15
16
  field :name
16
17
  field :type
17
18
  field :hint
18
- field :position, :type => Integer, :default => 0
19
- field :required, :type => Boolean, :default => false
20
- field :localized, :type => Boolean, :default => false
19
+ field :position, type: ::Integer, default: 0
20
+ field :required, type: ::Boolean, default: false
21
+ field :unique, type: ::Boolean, default: false
22
+ field :localized, type: ::Boolean, default: false
21
23
 
22
24
  ## validations ##
23
25
  validates_presence_of :label, :type
24
- validates_exclusion_of :name, :in => lambda { |f| CustomFields.options[:reserved_names].map(&:to_s) }
25
- validates_format_of :name, :with => /^[a-z]([A-Za-z0-9_]+)?$/
26
+ validates_exclusion_of :name, in: lambda { |f| CustomFields.options[:reserved_names].map(&:to_s) }
27
+ validates_format_of :name, with: /^[a-z]([A-Za-z0-9_]+)?$/
26
28
  validate :uniqueness_of_label_and_name
27
29
 
28
30
  ## callbacks ##
@@ -58,7 +60,11 @@ module CustomFields
58
60
  method_name = :"#{self.type}_to_recipe"
59
61
  custom_to_recipe = self.send(method_name) rescue {}
60
62
 
61
- { 'name' => self.name, 'type' => self.type, 'required' => self.required?, 'localized' => self.localized? }.merge(custom_to_recipe)
63
+ { 'name' => self.name,
64
+ 'type' => self.type,
65
+ 'required' => self.required?,
66
+ 'unique' => self.unique?,
67
+ 'localized' => self.localized? }.merge(custom_to_recipe)
62
68
  end
63
69
 
64
70
  def as_json(options = {})
@@ -94,4 +100,4 @@ module CustomFields
94
100
 
95
101
  end
96
102
 
97
- end
103
+ end
@@ -65,7 +65,8 @@ module CustomFields
65
65
  {
66
66
  'name' => "#{self.relations[name.to_s].class_name.demodulize}#{self._id}",
67
67
  'rules' => self.ordered_custom_fields(name).map(&:to_recipe),
68
- 'version' => self.custom_fields_version(name)
68
+ 'version' => self.custom_fields_version(name),
69
+ 'model_name' => self.relations[name.to_s].class_name.constantize.model_name
69
70
  }
70
71
  end
71
72
 
@@ -111,10 +112,18 @@ module CustomFields
111
112
 
112
113
  metadata.instance_variable_set(:@klass, self.klass_with_custom_fields(name))
113
114
  end
114
-
115
+ set_attribute_localization(name)
115
116
  # puts "new_metadata = #{self.send(name).metadata.klass.inspect} / #{self.send(name).metadata.object_id.inspect}" # DEBUG
116
117
  end
117
118
 
119
+ def set_attribute_localization(name)
120
+ klass_name = name.singularize.to_sym
121
+ self.send(:"#{name}_custom_fields").each do |cf|
122
+ I18n.backend.store_translations ::I18n.locale,
123
+ {mongoid: { attributes: {klass_name => {cf.name.to_sym => cf.label}}}}
124
+ end
125
+ end
126
+
118
127
  # Initializes the object tracking the modifications
119
128
  # of the custom fields
120
129
  #
@@ -145,7 +154,7 @@ module CustomFields
145
154
  # collect fields with a modified localized field
146
155
  fields.each do |field|
147
156
  if field.localized_changed? && field.persisted?
148
- self._custom_field_localize_diff[name] << { :field => field.name, :localized => field.localized? }
157
+ self._custom_field_localize_diff[name] << { field: field.name, localized: field.localized? }
149
158
  end
150
159
  end
151
160
  end
@@ -165,14 +174,14 @@ module CustomFields
165
174
 
166
175
  # puts "selector = #{selector.inspect}, memo = #{attributes.inspect}" # DEBUG
167
176
 
168
- collection.update selector, operations, :multi => true
177
+ collection.find(selector).update operations, multi: true
169
178
  end
170
179
 
171
180
  # If the localized attribute has been changed in at least one of the custom fields,
172
181
  # we have to upgrade all the records enhanced by custom_fields in order to make
173
182
  # the values consistent with the mongoid localize option.
174
183
  #
175
- # Ex: post.attributes[:name] = 'Hello world' => post.attributes[:name] = { :en => 'Hello world' }
184
+ # Ex: post.attributes[:name] = 'Hello world' => post.attributes[:name] = { en: 'Hello world' }
176
185
  #
177
186
  # @param [ String, Symbol ] name The name of the relation.
178
187
  #
@@ -198,7 +207,7 @@ module CustomFields
198
207
  next if updates.empty?
199
208
 
200
209
  collection = self.send(name).collection
201
- collection.update record.atomic_selector, { '$set' => updates }
210
+ collection.find(record.atomic_selector).update({ '$set' => updates })
202
211
  end
203
212
  end
204
213
 
@@ -228,13 +237,13 @@ module CustomFields
228
237
  # end
229
238
  #
230
239
  # class Employee
231
- # embedded_in :company, :inverse_of => :employees
240
+ # embedded_in :company, inverse_of: :employees
232
241
  # field :name, String
233
242
  # end
234
243
  #
235
- # company.employees_custom_fields.build :label => 'His/her position', :name => 'position', :kind => 'string'
244
+ # company.employees_custom_fields.build label: 'His/her position', name: 'position', kind: 'string'
236
245
  # company.save
237
- # company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
246
+ # company.employees.build name: 'Michael Scott', position: 'Regional manager'
238
247
  #
239
248
  def custom_fields_for(name)
240
249
  self.declare_embedded_in_definition_in_custom_field(name)
@@ -254,11 +263,11 @@ module CustomFields
254
263
  #
255
264
  def extend_for_custom_fields(name)
256
265
  class_eval do
257
- field :"#{name}_custom_fields_version", :type => Integer, :default => 0
266
+ field :"#{name}_custom_fields_version", type: ::Integer, default: 0
258
267
 
259
- embeds_many :"#{name}_custom_fields", :class_name => self.dynamic_custom_field_class_name(name) #, :cascade_callbacks => true # FIXME ?????
268
+ embeds_many :"#{name}_custom_fields", class_name: self.dynamic_custom_field_class_name(name) #, cascade_callbacks: true # FIXME ?????
260
269
 
261
- accepts_nested_attributes_for :"#{name}_custom_fields", :allow_destroy => true
270
+ accepts_nested_attributes_for :"#{name}_custom_fields", allow_destroy: true
262
271
  end
263
272
 
264
273
  class_eval <<-EOV
@@ -324,7 +333,7 @@ module CustomFields
324
333
 
325
334
  unless source.const_defined?(klass_name)
326
335
  (klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
327
- embedded_in :#{self.name.demodulize.underscore}, :inverse_of => :#{name}_custom_fields, :class_name => '#{self.name}'
336
+ embedded_in :#{self.name.demodulize.underscore}, inverse_of: :#{name}_custom_fields, class_name: '#{self.name}'
328
337
  EOF
329
338
 
330
339
  source.const_set(klass_name, klass)
@@ -335,4 +344,4 @@ module CustomFields
335
344
 
336
345
  end
337
346
 
338
- end
347
+ end