custom_fields 1.1.0.rc1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/README.textile +14 -6
  3. data/config/locales/fr.yml +5 -1
  4. data/lib/custom_fields.rb +11 -37
  5. data/lib/custom_fields/extensions/carrierwave.rb +25 -0
  6. data/lib/custom_fields/extensions/mongoid/document.rb +12 -50
  7. data/lib/custom_fields/extensions/mongoid/factory.rb +20 -0
  8. data/lib/custom_fields/extensions/mongoid/fields.rb +29 -0
  9. data/lib/custom_fields/extensions/mongoid/fields/i18n.rb +53 -0
  10. data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +84 -0
  11. data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +29 -0
  12. data/lib/custom_fields/field.rb +44 -175
  13. data/lib/custom_fields/source.rb +333 -0
  14. data/lib/custom_fields/target.rb +90 -0
  15. data/lib/custom_fields/types/boolean.rb +26 -3
  16. data/lib/custom_fields/types/date.rb +36 -24
  17. data/lib/custom_fields/types/default.rb +44 -24
  18. data/lib/custom_fields/types/file.rb +35 -17
  19. data/lib/custom_fields/types/select.rb +184 -0
  20. data/lib/custom_fields/types/string.rb +25 -6
  21. data/lib/custom_fields/types/text.rb +35 -8
  22. data/lib/custom_fields/types_old/boolean.rb +13 -0
  23. data/lib/custom_fields/{types → types_old}/category.rb +0 -0
  24. data/lib/custom_fields/types_old/date.rb +49 -0
  25. data/lib/custom_fields/types_old/default.rb +44 -0
  26. data/lib/custom_fields/types_old/file.rb +27 -0
  27. data/lib/custom_fields/{types → types_old}/has_many.rb +0 -0
  28. data/lib/custom_fields/{types → types_old}/has_many/proxy_collection.rb +0 -0
  29. data/lib/custom_fields/{types → types_old}/has_many/reverse_lookup_proxy_collection.rb +0 -0
  30. data/lib/custom_fields/{types → types_old}/has_one.rb +0 -0
  31. data/lib/custom_fields/types_old/string.rb +13 -0
  32. data/lib/custom_fields/types_old/text.rb +15 -0
  33. data/lib/custom_fields/version.rb +1 -1
  34. metadata +115 -91
  35. data/lib/custom_fields/custom_fields_for.rb +0 -350
  36. data/lib/custom_fields/extensions/mongoid/relations/accessors.rb +0 -31
  37. data/lib/custom_fields/extensions/mongoid/relations/builders.rb +0 -30
  38. data/lib/custom_fields/proxy_class/base.rb +0 -112
  39. data/lib/custom_fields/proxy_class/builder.rb +0 -60
  40. data/lib/custom_fields/proxy_class/helper.rb +0 -57
  41. data/lib/custom_fields/self_metadata.rb +0 -30
@@ -1,350 +0,0 @@
1
- module CustomFields
2
-
3
- module CustomFieldsFor
4
-
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- cattr_accessor :_custom_fields_for
9
-
10
- self._custom_fields_for = []
11
- end
12
-
13
- module InstanceMethods
14
-
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
27
-
28
- # Returns the class enhanced by the custom fields defined in the parent class.
29
- #
30
- # @param [ String, Symbol ] name The name of the relation.
31
- #
32
- # @return [ Class ] The modified class.
33
- #
34
- def klass_with_custom_fields(name)
35
- self.class.klass_with_custom_fields(name, self)
36
- end
37
-
38
- # Returns the ordered list of custom fields for a relation
39
- #
40
- # @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
41
- # person.ordered_custom_fields(:addresses)
42
- #
43
- # @param [ String, Symbol ] name The name of the relation.
44
- #
45
- # @return [ Collection ] The ordered list.
46
- #
47
- def ordered_custom_fields(name)
48
- self.send(:"#{name}_custom_fields").sort { |a, b| (a.position || 0) <=> (b.position || 0) }
49
- end
50
-
51
- # Marks all the custom fields as persisted. Actually, this is a patch
52
- # for mongoid since for the update, it runs the reset_persisted_children method after
53
- # the callbacks unlike for the create.
54
- # We assume that all the fields have been validated in a previous step
55
- #
56
- # @param [ String, Symbol ] name The name of the relation.
57
- #
58
- def mark_custom_fields_as_persisted(name)
59
- self.send(:"#{name}_custom_fields").each do |field|
60
- field.instance_variable_set(:@new_record, false) unless field.persisted?
61
- end
62
- end
63
-
64
- # Builds the class enhanced by the custom fields defined in the parent class.
65
- # The new class inherits from the original one.
66
- #
67
- # @param [ String, Symbol ] name The name of the relation.
68
- # @param [ Metadata ] metadata The relation's metadata.
69
- #
70
- # @return [ Class ] The modified class.
71
- #
72
- def build_klass_with_custom_fields(name, metadata)
73
- custom_fields = self.ordered_custom_fields(name)
74
-
75
- metadata.klass.to_klass_with_custom_fields(name, self, custom_fields)
76
- end
77
-
78
- # Marks the class enhanced by the custom fields as invalidated
79
- #
80
- # @param [ String, Symbol ] name The name of the relation.
81
- #
82
- def mark_klass_with_custom_fields_as_invalidated(name)
83
- self.send(:"invalidate_#{name}_klass_flag=", true)
84
- end
85
-
86
- # Reset the flag telling if the class enhanced by the custom fields is invalidated
87
- #
88
- # @param [ String, Symbol ] name The name of the relation.
89
- #
90
- def reset_klass_with_custom_fields_invalidated_flag(name)
91
- # puts "* reset_klass_with_custom_fields_invalidated_flag #{name}"
92
- self.send(:"invalidate_#{name}_klass_flag=", false)
93
- end
94
-
95
- # Determines if the enhanced class has to be invalidated.
96
- #
97
- # @param [ String, Symbol ] name The name of the relation.
98
- #
99
- # @return [ true, false ] True if enhanced, false if not.
100
- #
101
- def invalidate_klass_with_custom_fields?(name)
102
- !!self.send(:"invalidate_#{name}_klass_flag")
103
- end
104
-
105
- # Destroy the class enhanced by the custom fields so that next time we need it,
106
- # we have a fresh new one.
107
- #
108
- # @param [ String, Symbol ] name The name of the relation.
109
- #
110
- def invalidate_klass_with_custom_fields(name)
111
- self.class.invalidate_klass_with_custom_fields(name, self)
112
- end
113
-
114
- # Duplicates a metadata and assigns the enhanced class to it.
115
- #
116
- # @param [ Metadata ] metadata The relation's old metadata.
117
- #
118
- # @return [ Metadata ] The relation's new metadata
119
- #
120
- def clone_metadata_for_custom_fields(metadata)
121
- # puts "-> clone_metadata_for_custom_fields #{metadata.name}"
122
-
123
- klass = self.build_klass_with_custom_fields(metadata.name, metadata)
124
-
125
- # we do not want that other instances of the parent class have the same metadata
126
- metadata.clone.tap do |metadata|
127
- metadata.instance_variable_set(:@klass, klass)
128
- end
129
- end
130
-
131
- # When the fields have been modified and before the object is saved,
132
- # we bump the version.
133
- #
134
- # @param [ String, Symbol ] name The name of the relation.
135
- #
136
- def bump_custom_fields_version(name)
137
- if self.invalidate_klass_with_custom_fields?(name)
138
- # puts "%%% bump_custom_fields_version #{name} #{self.send(:"#{name}_custom_fields_version").inspect}"
139
- version = self.send(:"#{name}_custom_fields_version") || 0
140
- self.send(:"#{name}_custom_fields_version=", version + 1)
141
- end
142
- end
143
-
144
- # Increments by 1 the counter couting the number of added custom fields
145
- # for a relation
146
- #
147
- # @param [ String, Symbol ] name The name of the relation.
148
- #
149
- # @return [ Integer ] The new value of the counter
150
- #
151
- def bump_custom_fields_counter(name)
152
- counter = self.send(:"#{name}_custom_fields_counter") || 0
153
- self.send(:"#{name}_custom_fields_counter=", counter + 1)
154
- end
155
-
156
- # Builds a new relation so that the builder takes the last version of
157
- # the enhanced class when creating new instances
158
- #
159
- # @param [ String, Symbol ] name The name of the relation.
160
- #
161
- def rebuild_custom_fields_relation(name)
162
- # metadata = self.clone_metadata_for_custom_fields(self.relations[name.to_s])
163
-
164
- # puts "rebuild_custom_fields_relation #{name}"
165
-
166
- metadata = self.relations[name.to_s]
167
- self.build(name, nil, metadata)
168
- end
169
-
170
- end
171
-
172
- module ClassMethods
173
-
174
- # Determines if the relation is enhanced by the custom fields
175
- #
176
- # @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
177
- # Person.custom_fields_for?(:addresses)
178
- #
179
- # @param [ String, Symbol ] name The name of the relation.
180
- #
181
- # @return [ true, false ] True if enhanced, false if not.
182
- #
183
- def custom_fields_for?(name)
184
- self._custom_fields_for.include?(name.to_s)
185
- end
186
-
187
- # Enhance an embedded collection OR the instance itself (by passing self) by providing methods to manage custom fields.
188
- #
189
- # @param [ String, Symbol ] name The name of the relation.
190
- #
191
- # @example
192
- # class Company
193
- # embeds_many :employees
194
- # custom_fields_for :employees
195
- # end
196
- #
197
- # class Employee
198
- # embedded_in :company, :inverse_of => :employees
199
- # field :name, String
200
- # end
201
- #
202
- # company.employees_custom_fields.build :label => 'His/her position', :_alias => 'position', :kind => 'string'
203
- # company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
204
- #
205
- def custom_fields_for(name)
206
- self.declare_embedded_in_definition_in_custom_field(name)
207
-
208
- # stores the relation name
209
- self._custom_fields_for << name.to_s
210
-
211
- self.extend_for_custom_fields(name)
212
- end
213
-
214
- # Enhances the class itself
215
- #
216
- # @example
217
- # class Company
218
- # custom_fields_for_itself
219
- # end
220
- #
221
- # company.self_metadata_custom_fields.build :label => 'Shipping Address', :_alias => 'address', :kind => 'text'
222
- # company.self_metadata.address = '700 S Laflin, 60607 Chicago'
223
- # other_company.self_metadata.address # returns a "not defined method" error
224
- #
225
- def custom_fields_for_itself
226
- self.embeds_one :self_metadata, :class_name => '::CustomFields::SelfMetadata'
227
-
228
- class_eval do
229
- def self_metadata_with_automatic_build
230
- object = self_metadata_without_automatic_build
231
- object || self.build_self_metadata
232
- end
233
- alias_method_chain :self_metadata, :automatic_build
234
- end
235
-
236
- self.custom_fields_for('self_metadata')
237
- end
238
-
239
- protected
240
-
241
- # Extends / Decorates the current class in order to be fully custom_fields compliant.
242
- # it declares news fields, adds new callbacks, ...etc
243
- #
244
- # @param [ String, Symbol ] name The name of the relation.
245
- #
246
- def extend_for_custom_fields(name)
247
- class_eval do
248
- field :"#{name}_custom_fields_counter", :type => Integer, :default => 0
249
- field :"#{name}_custom_fields_version", :type => Integer, :default => 0
250
-
251
- embeds_many :"#{name}_custom_fields", :class_name => self.dynamic_custom_field_class_name(name), :cascade_callbacks => true
252
-
253
- attr_accessor :"invalidate_#{name}_klass_flag" # flag for invalidating the custom class
254
-
255
- accepts_nested_attributes_for :"#{name}_custom_fields", :allow_destroy => true
256
- end
257
-
258
- class_eval <<-EOV
259
-
260
- before_save :bump_#{name}_custom_fields_version
261
- after_save :mark_#{name}_custom_fields_as_persisted
262
- after_save :rebuild_#{name}_relation
263
- after_save :reset_#{name}_klass_invalidated_flag
264
- after_destroy :invalidate_#{name}_klass
265
-
266
- def #{name}_klass
267
- self.klass_with_custom_fields('#{name}')
268
- end
269
-
270
- def #{name}_klass_name
271
- self.class.klass_name_with_custom_fields('#{name}', self)
272
- end
273
-
274
- def #{name}_klass_out_of_date?
275
- self.#{name}_klass.nil? || self.#{name}_klass.version != self.#{name}_custom_fields_version
276
- end
277
-
278
- def invalidate_#{name}_klass
279
- self.invalidate_klass_with_custom_fields('#{name}')
280
- end
281
-
282
- def invalidate_#{name}_klass?
283
- self.invalidate_klass_with_custom_fields?('#{name}')
284
- end
285
-
286
- def rebuild_#{name}_relation
287
- # puts "--> AFTER SAVE (#{name})"
288
- if self.#{name}_klass_out_of_date?
289
- # puts 'rebuild relation for #{name} after save'
290
- self.rebuild_custom_fields_relation('#{name}')
291
- end
292
- end
293
-
294
- protected
295
-
296
- def bump_#{name}_custom_fields_version
297
- # puts "--> BEFORE SAVE (#{name})"
298
- self.bump_custom_fields_version('#{name}')
299
- end
300
-
301
- def mark_#{name}_custom_fields_as_persisted
302
- self.mark_custom_fields_as_persisted('#{name}')
303
- end
304
-
305
- def reset_#{name}_klass_invalidated_flag
306
- self.reset_klass_with_custom_fields_invalidated_flag('#{name}')
307
- end
308
-
309
- EOV
310
- end
311
-
312
- # Returns the class name of the custom field which is based both on the parent class name
313
- # and the name of the relation in order to avoid name conflicts (with other classes)
314
- #
315
- # @param [ Metadata ] metadata The relation's old metadata.
316
- #
317
- # @return [ String ] The class name
318
- #
319
- def dynamic_custom_field_class_name(name)
320
- "#{self.name}#{name.to_s.singularize.camelize}Field"
321
- end
322
-
323
- # An embedded relationship has to be defined on both side in order for it
324
- # to work properly. But because custom_field can be embedded in different
325
- # models that it's not aware of, we have to declare manually the definition
326
- # once we know the target class.
327
- #
328
- # @param [ String, Symbol ] name The name of the relation.
329
- #
330
- # @return [ Field ] The new field class.
331
- #
332
- def declare_embedded_in_definition_in_custom_field(name)
333
- klass_name = self.dynamic_custom_field_class_name(name)
334
-
335
- unless Object.const_defined?(klass_name)
336
- (klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
337
- embedded_in :#{self.name.underscore}, :inverse_of => :#{name}_custom_fields
338
- EOF
339
-
340
- Object.const_set(klass_name, klass)
341
- end
342
- end
343
-
344
- end
345
-
346
- end
347
-
348
- end
349
-
350
-
@@ -1,31 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid # :nodoc:
3
- module Relations #:nodoc:
4
-
5
- module Accessors
6
-
7
- # Builds the related document and creates the relation based on the class
8
- # enhanced by the custom_fields functionnality (if set up for the relation).
9
- #
10
- # @param [ String, Symbol ] name The name of the relation.
11
- # @param [ Hash, BSON::ObjectId ] object The id or attributes to use.
12
- # @param [ Metadata ] metadata The relation's metadata.
13
- # @param [ true, false ] building If we are in a build operation.
14
- #
15
- # @return [ Proxy ] The relation.
16
- #
17
- # @since 2.0.0.rc.1
18
- def build_with_custom_fields(name, object, metadata)
19
- if self.respond_to?(:custom_fields_for?) && self.custom_fields_for?(metadata.name)
20
- # puts "[Accessors] build_with_custom_fields #{name}"
21
- metadata = self.clone_metadata_for_custom_fields(metadata)
22
- end
23
-
24
- build_without_custom_fields(name, object, metadata)
25
- end
26
-
27
- alias_method_chain :build, :custom_fields
28
- end
29
-
30
- end
31
- end
@@ -1,30 +0,0 @@
1
- module Mongoid # :nodoc:
2
- module Relations #:nodoc:
3
-
4
- module Builders
5
-
6
- module ClassMethods #:nodoc:
7
-
8
- def builder_with_custom_fields(name, metadata)
9
- tap do
10
- define_method("build_#{name}") do |*args|
11
- if self.custom_fields_for?(metadata.name)
12
- metadata = self.clone_metadata_for_custom_fields(metadata)
13
- end
14
-
15
- attributes = args.first || {}
16
- options = args.size > 1 ? args[1] : {}
17
- document = Factory.build(metadata.klass, attributes, options)
18
- _building do
19
- send("#{name}=", document)
20
- end
21
- end
22
- end
23
- end
24
-
25
- alias_method_chain :builder, :custom_fields
26
-
27
- end
28
- end
29
- end
30
- end
@@ -1,112 +0,0 @@
1
- module CustomFields
2
- module ProxyClass
3
-
4
- module Base
5
-
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- cattr_accessor :custom_fields, :_parent, :association_name, :built_at, :version
10
- end
11
-
12
- module InstanceMethods
13
-
14
- # Returns the list of the custom fields used to build this class
15
- #
16
- # @return [ List ] the list of custom fields
17
- #
18
- def custom_fields
19
- self.class.custom_fields
20
- end
21
-
22
- # Returns the fields specified by the custom fields and their values
23
- #
24
- # @return [ Hash ] the hash
25
- #
26
- def aliased_attributes
27
- hash = { :created_at => self.created_at, :updated_at => self.updated_at } rescue {}
28
-
29
- (self.custom_fields || []).each do |field|
30
- case field.kind
31
- when 'file' then hash[field._alias] = self.send(field._name.to_sym).url
32
- else
33
- hash[field._alias] = self.send(field._name.to_sym)
34
- end
35
- end
36
-
37
- hash
38
- end
39
-
40
- end
41
-
42
- module ClassMethods
43
-
44
- # Returns the fields specified by the custom fields and their values
45
- #
46
- def model_name
47
- @_model_name ||= ActiveModel::Name.new(self.superclass)
48
- end
49
-
50
- # Declares a field within the class based on the information given by the custom field
51
- #
52
- # @param [ Field ] field The custom field
53
- #
54
- def apply_custom_field(field)
55
- # puts "field #{field._name} persisted? #{field.persisted?} / valid ? #{field.quick_valid?}"
56
- return if !field.persisted? || !field.quick_valid?
57
-
58
- self.custom_fields ||= []
59
-
60
- if self.lookup_custom_field(field._name).nil?
61
- self.custom_fields << field
62
- field.apply(self)
63
- end
64
- end
65
-
66
- # Determines if the class has already declared the field
67
- #
68
- # @param [ String ] name The name of the custom field
69
- #
70
- # @return [ true, false ] True if found it, false if not.
71
- #
72
- def lookup_custom_field(name)
73
- self.custom_fields.detect { |f| f._name == name }
74
- end
75
-
76
- # Convenient method to get the name of a custom field from its alias
77
- #
78
- # @param [ String ] value The _alias value of the custom field
79
- #
80
- # @return [ String ] The name of the custom field.
81
- #
82
- def custom_field_alias_to_name(value)
83
- self.custom_fields.detect { |f| f._alias == value }._name
84
- end
85
-
86
- # Convenient method to get the _alias of a custom field from its name
87
- #
88
- # @param [ String ] value The name value of the custom field
89
- #
90
- # @return [ String ] The _alias of the custom field.
91
- #
92
- def custom_field_name_to_alias(value)
93
- self.custom_fields.detect { |f| f._name == value }._alias
94
- end
95
-
96
- # Tells Mongoid that this class is a child of a super class (even if it is no true)
97
- #
98
- def hereditary?
99
- false
100
- end
101
-
102
- end
103
-
104
- end
105
-
106
- end
107
- end
108
-
109
-
110
-
111
-
112
-