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.
- data/MIT-LICENSE +1 -1
- data/README.textile +4 -4
- data/config/locales/de.yml +11 -5
- data/config/locales/en.yml +4 -0
- data/config/locales/fr.yml +4 -0
- data/config/locales/ru.yml +15 -0
- data/lib/custom_fields.rb +37 -27
- data/lib/custom_fields/extensions/active_support.rb +1 -1
- data/lib/custom_fields/extensions/carrierwave.rb +2 -2
- data/lib/custom_fields/extensions/mongoid/factory.rb +3 -3
- data/lib/custom_fields/extensions/mongoid/fields.rb +4 -3
- data/lib/custom_fields/extensions/mongoid/fields/localized.rb +39 -0
- data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +1 -1
- data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +3 -3
- data/lib/custom_fields/field.rb +14 -8
- data/lib/custom_fields/source.rb +23 -14
- data/lib/custom_fields/target.rb +16 -8
- data/lib/custom_fields/target_helpers.rb +6 -6
- data/lib/custom_fields/types/belongs_to.rb +4 -4
- data/lib/custom_fields/types/boolean.rb +1 -1
- data/lib/custom_fields/types/date.rb +2 -2
- data/lib/custom_fields/types/default.rb +5 -6
- data/lib/custom_fields/types/email.rb +60 -0
- data/lib/custom_fields/types/file.rb +2 -2
- data/lib/custom_fields/types/float.rb +52 -0
- data/lib/custom_fields/types/has_many.rb +6 -8
- data/lib/custom_fields/types/integer.rb +54 -0
- data/lib/custom_fields/types/many_to_many.rb +3 -3
- data/lib/custom_fields/types/money.rb +146 -0
- data/lib/custom_fields/types/relationship_default.rb +2 -2
- data/lib/custom_fields/types/select.rb +16 -13
- data/lib/custom_fields/types/tags.rb +35 -0
- data/lib/custom_fields/types/text.rb +1 -1
- data/lib/custom_fields/version.rb +1 -1
- metadata +65 -76
- data/init.rb +0 -2
- data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +0 -86
data/MIT-LICENSE
CHANGED
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
|
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.
|
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 =
|
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)
|
70
|
+
Copyright (c) 2013 NoCoffee, released under the MIT license
|
data/config/locales/de.yml
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
de:
|
2
2
|
custom_fields:
|
3
3
|
type:
|
4
|
-
string:
|
5
|
-
text: Text
|
6
|
-
select:
|
7
|
-
boolean:
|
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
|
data/config/locales/en.yml
CHANGED
data/config/locales/fr.yml
CHANGED
@@ -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
|
-
:
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
@@ -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].
|
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
|
-
|
23
|
-
|
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
|
@@ -14,13 +14,13 @@ module Mongoid #:nodoc:
|
|
14
14
|
|
15
15
|
attributes ||= {}
|
16
16
|
|
17
|
-
attributes.merge!(:
|
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
|
data/lib/custom_fields/field.rb
CHANGED
@@ -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
|
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, :
|
19
|
-
field :required, :
|
20
|
-
field :
|
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, :
|
25
|
-
validates_format_of :name, :
|
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'
|
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
|
data/lib/custom_fields/source.rb
CHANGED
@@ -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] << { :
|
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
|
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] = { :
|
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.
|
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, :
|
240
|
+
# embedded_in :company, inverse_of: :employees
|
232
241
|
# field :name, String
|
233
242
|
# end
|
234
243
|
#
|
235
|
-
# company.employees_custom_fields.build :
|
244
|
+
# company.employees_custom_fields.build label: 'His/her position', name: 'position', kind: 'string'
|
236
245
|
# company.save
|
237
|
-
# company.employees.build :
|
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", :
|
266
|
+
field :"#{name}_custom_fields_version", type: ::Integer, default: 0
|
258
267
|
|
259
|
-
embeds_many :"#{name}_custom_fields", :
|
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", :
|
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}, :
|
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
|