custom_fields 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|