custom_fields 2.0.0.rc1 → 2.0.0.rc2
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/lib/custom_fields/extensions/mongoid/fields/i18n.rb +1 -1
- data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +4 -2
- data/lib/custom_fields/extensions/mongoid/fields.rb +2 -1
- data/lib/custom_fields/source.rb +152 -156
- data/lib/custom_fields/target.rb +1 -5
- data/lib/custom_fields/types/date.rb +9 -13
- data/lib/custom_fields/types/select.rb +26 -34
- data/lib/custom_fields/version.rb +3 -1
- metadata +26 -37
- data/lib/custom_fields/types_old/boolean.rb +0 -13
- data/lib/custom_fields/types_old/category.rb +0 -107
- data/lib/custom_fields/types_old/date.rb +0 -49
- data/lib/custom_fields/types_old/default.rb +0 -44
- data/lib/custom_fields/types_old/file.rb +0 -27
- data/lib/custom_fields/types_old/has_many/proxy_collection.rb +0 -103
- data/lib/custom_fields/types_old/has_many/reverse_lookup_proxy_collection.rb +0 -101
- data/lib/custom_fields/types_old/has_many.rb +0 -100
- data/lib/custom_fields/types_old/has_one.rb +0 -60
- data/lib/custom_fields/types_old/string.rb +0 -13
- data/lib/custom_fields/types_old/text.rb +0 -15
|
@@ -45,8 +45,9 @@ module Mongoid #:nodoc:
|
|
|
45
45
|
return nil if object.nil?
|
|
46
46
|
|
|
47
47
|
# puts "deserializing...#{locale.inspect} / #{object.inspect}" # DEBUG
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
value = if !object.respond_to?(:keys) # if no translation hash is given, we return the object itself
|
|
49
|
+
object
|
|
50
|
+
elsif I18n.fallbacks?
|
|
50
51
|
object[I18n.fallbacks[locale.to_sym].map(&:to_s).find { |loc| !object[loc].nil? }]
|
|
51
52
|
else
|
|
52
53
|
object[locale.to_s]
|
|
@@ -69,6 +70,7 @@ module Mongoid #:nodoc:
|
|
|
69
70
|
# puts "serializing...#{locale} / #{object.inspect} / #{options.inspect}" # DEBUG
|
|
70
71
|
|
|
71
72
|
value = self.original_field_type.serialize(object)
|
|
73
|
+
|
|
72
74
|
{ locale.to_s => value }
|
|
73
75
|
end
|
|
74
76
|
|
|
@@ -18,8 +18,9 @@ module Mongoid #:nodoc
|
|
|
18
18
|
#
|
|
19
19
|
# @since 2.1.0
|
|
20
20
|
def replace_field(name, type, localize = false)
|
|
21
|
+
# puts "fields[#{name}] = #{fields[name.to_s].inspect} / #{fields.keys.inspect}" # DEBUG
|
|
21
22
|
defaults.delete_one(name)
|
|
22
|
-
add_field(name, fields[name].options.merge(:type => type, :localize => localize))
|
|
23
|
+
add_field(name, fields[name.to_s].options.merge(:type => type, :localize => localize))
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
end
|
data/lib/custom_fields/source.rb
CHANGED
|
@@ -12,189 +12,185 @@ module CustomFields
|
|
|
12
12
|
attr_accessor :_custom_field_localize_diff
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self.class.custom_fields_for?(name)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Returns the class enhanced by the custom fields.
|
|
31
|
-
# Be careful, call this method only if the source class
|
|
32
|
-
# has been saved with success.
|
|
33
|
-
#
|
|
34
|
-
# @param [ String, Symbol ] name The name of the relation.
|
|
35
|
-
#
|
|
36
|
-
# @return [ Class ] The modified class.
|
|
37
|
-
#
|
|
38
|
-
def klass_with_custom_fields(name)
|
|
39
|
-
recipe = self.custom_fields_recipe_for(name)
|
|
40
|
-
target = self.send(name).metadata.klass
|
|
41
|
-
target.klass_with_custom_fields(recipe)
|
|
42
|
-
end
|
|
15
|
+
# Determines if the relation is enhanced by the custom fields
|
|
16
|
+
#
|
|
17
|
+
# @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
|
|
18
|
+
# person.custom_fields_for?(:addresses)
|
|
19
|
+
#
|
|
20
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
21
|
+
#
|
|
22
|
+
# @return [ true, false ] True if enhanced, false if not.
|
|
23
|
+
#
|
|
24
|
+
def custom_fields_for?(name)
|
|
25
|
+
self.class.custom_fields_for?(name)
|
|
26
|
+
end
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
28
|
+
# Returns the class enhanced by the custom fields.
|
|
29
|
+
# Be careful, call this method only if the source class
|
|
30
|
+
# has been saved with success.
|
|
31
|
+
#
|
|
32
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
33
|
+
#
|
|
34
|
+
# @return [ Class ] The modified class.
|
|
35
|
+
#
|
|
36
|
+
def klass_with_custom_fields(name)
|
|
37
|
+
recipe = self.custom_fields_recipe_for(name)
|
|
38
|
+
target = self.send(name).metadata.klass
|
|
39
|
+
target.klass_with_custom_fields(recipe)
|
|
40
|
+
end
|
|
56
41
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
end
|
|
42
|
+
# Returns the ordered list of custom fields for a relation
|
|
43
|
+
#
|
|
44
|
+
# @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
|
|
45
|
+
# person.ordered_custom_fields(:addresses)
|
|
46
|
+
#
|
|
47
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
48
|
+
#
|
|
49
|
+
# @return [ Collection ] The ordered list.
|
|
50
|
+
#
|
|
51
|
+
def ordered_custom_fields(name)
|
|
52
|
+
self.send(:"#{name}_custom_fields").sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
|
53
|
+
end
|
|
71
54
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
55
|
+
# Returns the recipe (meaning all the rules) needed to
|
|
56
|
+
# build the custom klass
|
|
57
|
+
#
|
|
58
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
59
|
+
#
|
|
60
|
+
# @return [ Array ] An array of hashes
|
|
61
|
+
#
|
|
62
|
+
def custom_fields_recipe_for(name)
|
|
63
|
+
{
|
|
64
|
+
'name' => "#{name.to_s.classify}#{self._id}",
|
|
65
|
+
'rules' => self.ordered_custom_fields(name).map(&:to_recipe),
|
|
66
|
+
'version' => self.custom_fields_version(name)
|
|
67
|
+
}
|
|
68
|
+
end
|
|
81
69
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
# Returns the number of the version for relation with custom fields
|
|
71
|
+
#
|
|
72
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
73
|
+
#
|
|
74
|
+
# @return [ Integer ] The version number
|
|
75
|
+
#
|
|
76
|
+
def custom_fields_version(name)
|
|
77
|
+
self.send(:"#{name}_custom_fields_version") || 0
|
|
78
|
+
end
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
# When the fields have been modified and before the object is saved,
|
|
81
|
+
# we bump the version.
|
|
82
|
+
#
|
|
83
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
84
|
+
#
|
|
85
|
+
def bump_custom_fields_version(name)
|
|
86
|
+
version = self.custom_fields_version(name) + 1
|
|
87
|
+
self.send(:"#{name}_custom_fields_version=", version)
|
|
88
|
+
end
|
|
99
89
|
|
|
100
|
-
|
|
90
|
+
# Change the metadata of a relation enhanced by the custom fields.
|
|
91
|
+
# In Mongoid, all the instances of a same document share the same metadata objects.
|
|
92
|
+
#
|
|
93
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
94
|
+
#
|
|
95
|
+
def refresh_metadata_with_custom_fields(name)
|
|
96
|
+
return if !self.persisted? || self.send(:"#{name}_custom_fields").blank? # do not generate a klass without all the information
|
|
101
97
|
|
|
102
|
-
|
|
98
|
+
old_metadata = self.send(name).metadata
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
metadata.instance_variable_set(:@klass, self.klass_with_custom_fields(name))
|
|
106
|
-
end
|
|
100
|
+
# puts "old_metadata = #{old_metadata.klass.inspect} / #{old_metadata.object_id.inspect}" # DEBUG
|
|
107
101
|
|
|
108
|
-
|
|
102
|
+
self.send(name).metadata = old_metadata.clone.tap do |metadata|
|
|
103
|
+
metadata.instance_variable_set(:@klass, self.klass_with_custom_fields(name))
|
|
109
104
|
end
|
|
110
105
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
# @param [ String, Symbol ] name The name of the relation.
|
|
115
|
-
#
|
|
116
|
-
def initialize_custom_fields_diff(name)
|
|
117
|
-
self._custom_field_localize_diff ||= Hash.new([])
|
|
106
|
+
# puts "new_metadata = #{self.send(name).metadata.klass.inspect} / #{self.send(name).metadata.object_id.inspect}" # DEBUG
|
|
107
|
+
end
|
|
118
108
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
# Initializes the object tracking the modifications
|
|
110
|
+
# of the custom fields
|
|
111
|
+
#
|
|
112
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
113
|
+
#
|
|
114
|
+
def initialize_custom_fields_diff(name)
|
|
115
|
+
self._custom_field_localize_diff ||= Hash.new([])
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#
|
|
127
|
-
# @return [ Array ] An array of hashes storing the modifications
|
|
128
|
-
#
|
|
129
|
-
def collect_custom_fields_diff(name, fields)
|
|
130
|
-
# puts "==> collect_custom_fields_diff for #{name}, #{fields.size}" # DEBUG
|
|
117
|
+
self._custom_fields_diff ||= {}
|
|
118
|
+
self._custom_fields_diff[name] = { '$set' => {}, '$unset' => {}, '$rename' => {} }
|
|
119
|
+
end
|
|
131
120
|
|
|
132
|
-
|
|
121
|
+
# Collects all the modifications of the custom fields
|
|
122
|
+
#
|
|
123
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
124
|
+
#
|
|
125
|
+
# @return [ Array ] An array of hashes storing the modifications
|
|
126
|
+
#
|
|
127
|
+
def collect_custom_fields_diff(name, fields)
|
|
128
|
+
# puts "==> collect_custom_fields_diff for #{name}, #{fields.size}" # DEBUG
|
|
133
129
|
|
|
134
|
-
|
|
135
|
-
field.collect_diff(memo)
|
|
136
|
-
end
|
|
130
|
+
memo = self.initialize_custom_fields_diff(name)
|
|
137
131
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
fields.map do |field|
|
|
133
|
+
field.collect_diff(memo)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# collect fields with a modified localized field
|
|
137
|
+
fields.each do |field|
|
|
138
|
+
if field.localized_changed? && field.persisted?
|
|
139
|
+
self._custom_field_localize_diff[name] << { :field => field.name, :localized => field.localized? }
|
|
143
140
|
end
|
|
144
141
|
end
|
|
142
|
+
end
|
|
145
143
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
144
|
+
# Apply the modifications collected from the custom fields by
|
|
145
|
+
# updating all the documents of the relation.
|
|
146
|
+
# The update uses the power of mongodb to make it fully optimized.
|
|
147
|
+
#
|
|
148
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
149
|
+
#
|
|
150
|
+
def apply_custom_fields_diff(name)
|
|
151
|
+
# puts "==> apply_custom_fields_recipes for #{name}, #{self._custom_fields_diff[name].inspect}" # DEBUG
|
|
154
152
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
operations = self._custom_fields_diff[name]
|
|
154
|
+
operations['$set'].merge!({ 'custom_fields_recipe.version' => self.custom_fields_version(name) })
|
|
155
|
+
collection, selector = self.send(name).collection, self.send(name).criteria.selector
|
|
158
156
|
|
|
159
|
-
|
|
157
|
+
# puts "selector = #{selector.inspect}, memo = #{attributes.inspect}" # DEBUG
|
|
160
158
|
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
collection.update selector, operations, :multi => true
|
|
160
|
+
end
|
|
163
161
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
end
|
|
162
|
+
# If the localized attribute has been changed in at least one of the custom fields,
|
|
163
|
+
# we have to upgrade all the records enhanced by custom_fields in order to make
|
|
164
|
+
# the values consistent with the mongoid localize option.
|
|
165
|
+
#
|
|
166
|
+
# Ex: post.attributes[:name] = 'Hello world' => post.attributes[:name] = { :en => 'Hello world' }
|
|
167
|
+
#
|
|
168
|
+
# @param [ String, Symbol ] name The name of the relation.
|
|
169
|
+
#
|
|
170
|
+
def apply_custom_fields_localize_diff(name)
|
|
171
|
+
return if self._custom_field_localize_diff[name].empty?
|
|
172
|
+
|
|
173
|
+
self.send(name).all.each do |record|
|
|
174
|
+
updates = {}
|
|
175
|
+
|
|
176
|
+
# puts "[apply_custom_fields_localize_diff] processing: record #{record._id} / #{self._custom_field_localize_diff[name].inspect}" # DEBUG
|
|
177
|
+
self._custom_field_localize_diff[name].each do |changes|
|
|
178
|
+
if changes[:localized]
|
|
179
|
+
value = record.read_attribute(changes[:field].to_sym)
|
|
180
|
+
updates[changes[:field]] = { I18n.locale.to_s => value }
|
|
181
|
+
else
|
|
182
|
+
# the other way around
|
|
183
|
+
value = record.read_attribute(changes[:field].to_sym)
|
|
184
|
+
next if value.nil?
|
|
185
|
+
updates[changes[:field]] = value[I18n.locale.to_s]
|
|
189
186
|
end
|
|
187
|
+
end
|
|
190
188
|
|
|
191
|
-
|
|
189
|
+
next if updates.empty?
|
|
192
190
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
end
|
|
191
|
+
collection = self.send(name).collection
|
|
192
|
+
collection.update record.atomic_selector, { '$set' => updates }
|
|
196
193
|
end
|
|
197
|
-
|
|
198
194
|
end
|
|
199
195
|
|
|
200
196
|
module ClassMethods
|
data/lib/custom_fields/target.rb
CHANGED
|
@@ -16,10 +16,6 @@ module CustomFields
|
|
|
16
16
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
module InstanceMethods
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
module ClassMethods
|
|
24
20
|
|
|
25
21
|
# A document with custom fields always returns true.
|
|
@@ -45,7 +41,7 @@ module CustomFields
|
|
|
45
41
|
klass.version = recipe['version']
|
|
46
42
|
|
|
47
43
|
# copy scopes from the parent class (scopes does not inherit automatically from the parents in mongoid)
|
|
48
|
-
klass.write_inheritable_attribute(:scopes, self.scopes)
|
|
44
|
+
# klass.write_inheritable_attribute(:scopes, self.scopes) # not needed it
|
|
49
45
|
|
|
50
46
|
recipe['rules'].each do |rule|
|
|
51
47
|
self.send(:"apply_#{rule['type']}_custom_field", klass, rule)
|
|
@@ -33,23 +33,19 @@ module CustomFields
|
|
|
33
33
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
protected
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
date = ::Date._strptime(value, I18n.t('date.formats.default'))
|
|
43
|
-
value = ::Date.new(date[:year], date[:mon], date[:mday])
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
self.send(:"#{name}=", value)
|
|
38
|
+
def _set_formatted_date(name, value)
|
|
39
|
+
if value.is_a?(::String) && !value.blank?
|
|
40
|
+
date = ::Date._strptime(value, I18n.t('date.formats.default'))
|
|
41
|
+
value = ::Date.new(date[:year], date[:mon], date[:mday])
|
|
47
42
|
end
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
44
|
+
self.send(:"#{name}=", value)
|
|
45
|
+
end
|
|
52
46
|
|
|
47
|
+
def _get_formatted_date(name)
|
|
48
|
+
self.send(name.to_sym).strftime(I18n.t('date.formats.default')) rescue nil
|
|
53
49
|
end
|
|
54
50
|
|
|
55
51
|
end
|
|
@@ -35,24 +35,20 @@ module CustomFields
|
|
|
35
35
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
self.select_options.sort { |a, b| (a.position || 0) <=> (b.position || 0) }.to_a
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def select_to_recipe
|
|
45
|
-
{
|
|
46
|
-
'select_options' => self.ordered_select_options.map do |option|
|
|
47
|
-
{ '_id' => option._id, 'name' => option.name_translations }
|
|
48
|
-
end
|
|
49
|
-
}
|
|
50
|
-
end
|
|
38
|
+
def ordered_select_options
|
|
39
|
+
self.select_options.sort { |a, b| (a.position || 0) <=> (b.position || 0) }.to_a
|
|
40
|
+
end
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
def select_to_recipe
|
|
43
|
+
{
|
|
44
|
+
'select_options' => self.ordered_select_options.map do |option|
|
|
45
|
+
{ '_id' => option._id, 'name' => option.name_translations }
|
|
46
|
+
end
|
|
47
|
+
}
|
|
48
|
+
end
|
|
55
49
|
|
|
50
|
+
def select_as_json(options = {})
|
|
51
|
+
{ 'select_options' => self.ordered_select_options.map(&:as_json) }
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
end
|
|
@@ -151,28 +147,24 @@ module CustomFields
|
|
|
151
147
|
|
|
152
148
|
end
|
|
153
149
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
self.send(:"#{name}_id")
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def _find_select_option(name, id_or_name)
|
|
161
|
-
self.class._select_options(name).detect do |option|
|
|
162
|
-
option['name'] == id_or_name || option['_id'].to_s == id_or_name.to_s
|
|
163
|
-
end
|
|
164
|
-
end
|
|
150
|
+
def _select_option_id(name)
|
|
151
|
+
self.send(:"#{name}_id")
|
|
152
|
+
end
|
|
165
153
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
option
|
|
154
|
+
def _find_select_option(name, id_or_name)
|
|
155
|
+
self.class._select_options(name).detect do |option|
|
|
156
|
+
option['name'] == id_or_name || option['_id'].to_s == id_or_name.to_s
|
|
169
157
|
end
|
|
158
|
+
end
|
|
170
159
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
def _get_select_option(name)
|
|
161
|
+
option = self._find_select_option(name, self._select_option_id(name))
|
|
162
|
+
option ? option['name'] : nil
|
|
163
|
+
end
|
|
175
164
|
|
|
165
|
+
def _set_select_option(name, value)
|
|
166
|
+
option = self._find_select_option(name, value)
|
|
167
|
+
self.send(:"#{name}_id=", option ? option['_id'] : nil)
|
|
176
168
|
end
|
|
177
169
|
|
|
178
170
|
end
|