custom_fields 2.12.1 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +4 -5
- data/lib/custom_fields/extensions/active_support.rb +22 -23
- data/lib/custom_fields/extensions/carrierwave.rb +9 -40
- data/lib/custom_fields/extensions/mongoid/association/referenced/has_many.rb +49 -0
- data/lib/custom_fields/extensions/mongoid/association/referenced/has_one.rb +17 -0
- data/lib/custom_fields/extensions/mongoid/association/relatable.rb +30 -0
- data/lib/custom_fields/extensions/mongoid/criteria/queryable/smash.rb +3 -6
- data/lib/custom_fields/extensions/mongoid/document.rb +4 -8
- data/lib/custom_fields/extensions/mongoid/factory.rb +7 -9
- data/lib/custom_fields/extensions/mongoid/fields/i18n.rb +17 -17
- data/lib/custom_fields/extensions/mongoid/fields/localized.rb +21 -21
- data/lib/custom_fields/extensions/mongoid/fields.rb +4 -7
- data/lib/custom_fields/extensions/mongoid/validatable/collection_size.rb +86 -0
- data/lib/custom_fields/extensions/mongoid/{validations → validatable}/macros.rb +3 -2
- data/lib/custom_fields/extensions/origin/smash.rb +4 -5
- data/lib/custom_fields/field.rb +39 -36
- data/lib/custom_fields/source.rb +46 -44
- data/lib/custom_fields/target.rb +11 -12
- data/lib/custom_fields/target_helpers.rb +21 -23
- data/lib/custom_fields/types/belongs_to.rb +6 -20
- data/lib/custom_fields/types/boolean.rb +5 -12
- data/lib/custom_fields/types/color.rb +4 -12
- data/lib/custom_fields/types/date.rb +20 -22
- data/lib/custom_fields/types/date_time.rb +16 -18
- data/lib/custom_fields/types/default.rb +16 -24
- data/lib/custom_fields/types/email.rb +6 -13
- data/lib/custom_fields/types/file.rb +26 -35
- data/lib/custom_fields/types/float.rb +10 -13
- data/lib/custom_fields/types/has_many.rb +12 -16
- data/lib/custom_fields/types/integer.rb +10 -13
- data/lib/custom_fields/types/json.rb +18 -29
- data/lib/custom_fields/types/many_to_many.rb +14 -19
- data/lib/custom_fields/types/money.rb +34 -35
- data/lib/custom_fields/types/password.rb +19 -23
- data/lib/custom_fields/types/relationship_default.rb +4 -16
- data/lib/custom_fields/types/select.rb +43 -51
- data/lib/custom_fields/types/string.rb +5 -13
- data/lib/custom_fields/types/tags.rb +11 -11
- data/lib/custom_fields/types/text.rb +4 -16
- data/lib/custom_fields/version.rb +4 -2
- data/lib/custom_fields.rb +16 -16
- metadata +38 -38
- data/lib/custom_fields/extensions/mongoid/relations/options.rb +0 -19
- data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +0 -18
- data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +0 -30
- data/lib/custom_fields/extensions/mongoid/validations/collection_size.rb +0 -43
data/lib/custom_fields/field.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
class Field
|
4
|
-
|
5
5
|
include ::Mongoid::Document
|
6
6
|
include ::Mongoid::Timestamps
|
7
7
|
|
8
|
-
AVAILABLE_TYPES = %w
|
9
|
-
|
8
|
+
AVAILABLE_TYPES = %w[default string text email date date_time boolean file select float integer
|
9
|
+
money tags color relationship_default belongs_to has_many many_to_many password json].freeze
|
10
10
|
|
11
11
|
## types ##
|
12
12
|
AVAILABLE_TYPES.each do |type|
|
@@ -19,14 +19,14 @@ module CustomFields
|
|
19
19
|
field :type
|
20
20
|
field :hint
|
21
21
|
field :position, type: ::Integer, default: 0
|
22
|
-
field :required, type:
|
23
|
-
field :unique, type:
|
24
|
-
field :localized, type:
|
22
|
+
field :required, type: 'Boolean', default: false
|
23
|
+
field :unique, type: 'Boolean', default: false
|
24
|
+
field :localized, type: 'Boolean', default: false
|
25
25
|
field :default
|
26
26
|
|
27
27
|
## validations ##
|
28
28
|
validates_presence_of :label, :type
|
29
|
-
validates_exclusion_of :name, in:
|
29
|
+
validates_exclusion_of :name, in: ->(_f) { CustomFields.options[:reserved_names].map(&:to_s) }
|
30
30
|
validates_inclusion_of :type, in: AVAILABLE_TYPES, allow_blank: true
|
31
31
|
validates_format_of :name, with: /^[a-z]([A-Za-z0-9_]+)?$/, multiline: true
|
32
32
|
validate :uniqueness_of_label_and_name
|
@@ -45,10 +45,10 @@ module CustomFields
|
|
45
45
|
# @return [ Hash ] The memo object upgraded
|
46
46
|
#
|
47
47
|
def collect_diff(memo)
|
48
|
-
method_name = :"collect_#{
|
48
|
+
method_name = :"collect_#{type}_diff"
|
49
49
|
|
50
|
-
if
|
51
|
-
|
50
|
+
if respond_to?(method_name)
|
51
|
+
send(method_name, memo)
|
52
52
|
else
|
53
53
|
collect_default_diff(memo)
|
54
54
|
end
|
@@ -61,20 +61,28 @@ module CustomFields
|
|
61
61
|
# @return [ Hash ] The hash
|
62
62
|
#
|
63
63
|
def to_recipe
|
64
|
-
method_name = :"#{
|
65
|
-
custom_to_recipe =
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
'
|
64
|
+
method_name = :"#{type}_to_recipe"
|
65
|
+
custom_to_recipe = begin
|
66
|
+
send(method_name)
|
67
|
+
rescue StandardError
|
68
|
+
{}
|
69
|
+
end
|
70
|
+
|
71
|
+
{ 'name' => name,
|
72
|
+
'type' => type,
|
73
|
+
'required' => required?,
|
74
|
+
'unique' => unique?,
|
75
|
+
'localized' => localized?,
|
76
|
+
'default' => default }.merge(custom_to_recipe)
|
73
77
|
end
|
74
78
|
|
75
79
|
def as_json(options = {})
|
76
|
-
method_name = :"#{
|
77
|
-
custom_as_json =
|
80
|
+
method_name = :"#{type}_as_json"
|
81
|
+
custom_as_json = begin
|
82
|
+
send(method_name)
|
83
|
+
rescue StandardError
|
84
|
+
{}
|
85
|
+
end
|
78
86
|
|
79
87
|
super(options).merge(custom_as_json)
|
80
88
|
end
|
@@ -82,28 +90,23 @@ module CustomFields
|
|
82
90
|
protected
|
83
91
|
|
84
92
|
def uniqueness_of_label_and_name
|
85
|
-
if
|
86
|
-
self.errors.add(:label, :taken)
|
87
|
-
end
|
93
|
+
errors.add(:label, :taken) if siblings.any? { |f| f.label == label && f._id != _id }
|
88
94
|
|
89
|
-
|
90
|
-
|
91
|
-
|
95
|
+
return unless siblings.any? { |f| f.name == name && f._id != _id }
|
96
|
+
|
97
|
+
errors.add(:name, :taken)
|
92
98
|
end
|
93
99
|
|
94
100
|
def set_name
|
95
|
-
return if
|
101
|
+
return if label.blank? && name.blank?
|
96
102
|
|
97
|
-
|
98
|
-
|
99
|
-
|
103
|
+
return unless name.blank?
|
104
|
+
|
105
|
+
self.name = ActiveSupport::Inflector.parameterize(label, separator: '_').gsub('-', '_').downcase
|
100
106
|
end
|
101
107
|
|
102
108
|
def siblings
|
103
|
-
|
104
|
-
self._parent.send(self.relation_metadata.name)
|
109
|
+
_parent.send(association_name)
|
105
110
|
end
|
106
|
-
|
107
111
|
end
|
108
|
-
|
109
112
|
end
|
data/lib/custom_fields/source.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
module Source
|
4
|
-
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
@@ -35,9 +35,9 @@ module CustomFields
|
|
35
35
|
#
|
36
36
|
def klass_with_custom_fields(name)
|
37
37
|
# Rails.logger.debug "[CustomFields] klass_with_custom_fields #{self.send(name).metadata.klass} / #{self.send(name).metadata[:old_klass]}" if defined?(Rails) # DEBUG
|
38
|
-
recipe =
|
39
|
-
_metadata =
|
40
|
-
target = _metadata[:original_klass] || _metadata.klass # avoid to use an already enhanced klass
|
38
|
+
recipe = custom_fields_recipe_for(name)
|
39
|
+
_metadata = send(name)._association
|
40
|
+
target = _metadata.options[:original_klass] || _metadata.klass # avoid to use an already enhanced klass
|
41
41
|
target.klass_with_custom_fields(recipe)
|
42
42
|
end
|
43
43
|
|
@@ -51,7 +51,7 @@ module CustomFields
|
|
51
51
|
# @return [ Collection ] The ordered list.
|
52
52
|
#
|
53
53
|
def ordered_custom_fields(name)
|
54
|
-
|
54
|
+
send(:"#{name}_custom_fields").sort { |a, b| (a.position || 0) <=> (b.position || 0) }
|
55
55
|
end
|
56
56
|
|
57
57
|
# Returns the recipe (meaning all the rules) needed to
|
@@ -63,10 +63,10 @@ module CustomFields
|
|
63
63
|
#
|
64
64
|
def custom_fields_recipe_for(name)
|
65
65
|
{
|
66
|
-
'name'
|
67
|
-
'rules'
|
68
|
-
'version'
|
69
|
-
'model_name' =>
|
66
|
+
'name' => "#{relations[name.to_s].class_name.demodulize}#{_id}",
|
67
|
+
'rules' => ordered_custom_fields(name).map(&:to_recipe),
|
68
|
+
'version' => custom_fields_version(name),
|
69
|
+
'model_name' => relations[name.to_s].class_name.constantize.model_name.to_s
|
70
70
|
}
|
71
71
|
end
|
72
72
|
|
@@ -77,7 +77,7 @@ module CustomFields
|
|
77
77
|
# @return [ Integer ] The version number
|
78
78
|
#
|
79
79
|
def custom_fields_version(name)
|
80
|
-
|
80
|
+
send(:"#{name}_custom_fields_version") || 0
|
81
81
|
end
|
82
82
|
|
83
83
|
# When the fields have been modified and before the object is saved,
|
@@ -86,8 +86,8 @@ module CustomFields
|
|
86
86
|
# @param [ String, Symbol ] name The name of the relation.
|
87
87
|
#
|
88
88
|
def bump_custom_fields_version(name)
|
89
|
-
version =
|
90
|
-
|
89
|
+
version = custom_fields_version(name) + 1
|
90
|
+
send(:"#{name}_custom_fields_version=", version)
|
91
91
|
end
|
92
92
|
|
93
93
|
# Change the metadata of a relation enhanced by the custom fields.
|
@@ -96,21 +96,22 @@ module CustomFields
|
|
96
96
|
# @param [ String, Symbol ] name The name of the relation.
|
97
97
|
#
|
98
98
|
def refresh_metadata_with_custom_fields(name)
|
99
|
-
return if !
|
99
|
+
return if !persisted? || send(:"#{name}_custom_fields").blank?
|
100
100
|
|
101
|
-
old_metadata =
|
101
|
+
old_metadata = send(name)._association
|
102
102
|
|
103
103
|
# puts "old_metadata = #{old_metadata.klass.inspect} / #{old_metadata.object_id.inspect}" # DEBUG
|
104
104
|
|
105
105
|
# puts "[CustomFields] refresh_metadata_with_custom_fields, #{name.inspect}, self = #{self.inspect}"
|
106
106
|
|
107
|
-
|
107
|
+
send(name)._association = old_metadata.clone.tap do |metadata|
|
108
108
|
# Rails.logger.debug "[CustomFields] refresh_metadata_with_custom_fields #{metadata.klass}" if defined?(Rails) # DEBUG
|
109
109
|
|
110
110
|
# backup the current klass
|
111
|
-
metadata
|
111
|
+
metadata.instance_variable_set(:@options, metadata.options.dup)
|
112
|
+
metadata.options[:original_klass] ||= metadata.klass
|
112
113
|
|
113
|
-
metadata.instance_variable_set(:@klass,
|
114
|
+
metadata.instance_variable_set(:@klass, klass_with_custom_fields(name))
|
114
115
|
end
|
115
116
|
set_attribute_localization(name)
|
116
117
|
# puts "new_metadata = #{self.send(name).metadata.klass.inspect} / #{self.send(name).metadata.object_id.inspect}" # DEBUG
|
@@ -118,9 +119,9 @@ module CustomFields
|
|
118
119
|
|
119
120
|
def set_attribute_localization(name)
|
120
121
|
klass_name = name.singularize.to_sym
|
121
|
-
|
122
|
+
send(:"#{name}_custom_fields").each do |cf|
|
122
123
|
I18n.backend.store_translations ::I18n.locale,
|
123
|
-
|
124
|
+
{ mongoid: { attributes: { klass_name => { cf.name.to_sym => cf.label } } } }
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
@@ -145,7 +146,7 @@ module CustomFields
|
|
145
146
|
def collect_custom_fields_diff(name, fields)
|
146
147
|
# puts "==> collect_custom_fields_diff for #{name}, #{fields.size}" # DEBUG
|
147
148
|
|
148
|
-
memo =
|
149
|
+
memo = initialize_custom_fields_diff(name)
|
149
150
|
|
150
151
|
fields.map do |field|
|
151
152
|
field.collect_diff(memo)
|
@@ -169,12 +170,13 @@ module CustomFields
|
|
169
170
|
# puts "==> apply_custom_fields_recipes for #{name}, #{self._custom_fields_diff[name].inspect}" # DEBUG
|
170
171
|
|
171
172
|
operations = self._custom_fields_diff[name]
|
172
|
-
operations['$set'].merge!({ 'custom_fields_recipe.version' =>
|
173
|
-
collection
|
173
|
+
operations['$set'].merge!({ 'custom_fields_recipe.version' => custom_fields_version(name) })
|
174
|
+
collection = send(name).collection
|
175
|
+
selector = send(name).criteria.selector
|
174
176
|
|
175
177
|
# http://docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
|
176
178
|
# The <update> document must contain only update operator expressions.
|
177
|
-
%w
|
179
|
+
%w[set unset rename].each do |operation_name|
|
178
180
|
_fields = operations.delete("$#{operation_name}")
|
179
181
|
|
180
182
|
next if _fields.empty?
|
@@ -195,31 +197,30 @@ module CustomFields
|
|
195
197
|
def apply_custom_fields_localize_diff(name)
|
196
198
|
return if self._custom_field_localize_diff[name].empty?
|
197
199
|
|
198
|
-
|
200
|
+
send(name).all.each do |record|
|
199
201
|
updates = {}
|
200
202
|
|
201
203
|
# puts "[apply_custom_fields_localize_diff] processing: record #{record._id} / #{self._custom_field_localize_diff[name].inspect}" # DEBUG
|
202
204
|
self._custom_field_localize_diff[name].each do |changes|
|
205
|
+
value = record.attributes[changes[:field]]
|
203
206
|
if changes[:localized]
|
204
|
-
value = record.read_attribute(changes[:field].to_sym)
|
205
207
|
updates[changes[:field]] = { Mongoid::Fields::I18n.locale.to_s => value }
|
206
208
|
else
|
207
209
|
# the other way around
|
208
|
-
value = record.read_attribute(changes[:field].to_sym)
|
209
210
|
next if value.nil?
|
211
|
+
|
210
212
|
updates[changes[:field]] = value[Mongoid::Fields::I18n.locale.to_s]
|
211
213
|
end
|
212
214
|
end
|
213
215
|
|
214
216
|
next if updates.empty?
|
215
217
|
|
216
|
-
collection =
|
218
|
+
collection = send(name).collection
|
217
219
|
collection.find(record.atomic_selector).update_one({ '$set' => updates })
|
218
220
|
end
|
219
221
|
end
|
220
222
|
|
221
223
|
module ClassMethods
|
222
|
-
|
223
224
|
# Determines if the relation is enhanced by the custom fields
|
224
225
|
#
|
225
226
|
# @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
|
@@ -230,7 +231,7 @@ module CustomFields
|
|
230
231
|
# @return [ true, false ] True if enhanced, false if not.
|
231
232
|
#
|
232
233
|
def custom_fields_for?(name)
|
233
|
-
|
234
|
+
_custom_fields_for.include?(name.to_s)
|
234
235
|
end
|
235
236
|
|
236
237
|
# Enhance a referenced collection OR the instance itself (by passing self) by providing methods to manage custom fields.
|
@@ -253,12 +254,12 @@ module CustomFields
|
|
253
254
|
# company.employees.build name: 'Michael Scott', position: 'Regional manager'
|
254
255
|
#
|
255
256
|
def custom_fields_for(name)
|
256
|
-
|
257
|
+
declare_embedded_in_definition_in_custom_field(name)
|
257
258
|
|
258
259
|
# stores the relation name
|
259
|
-
|
260
|
+
_custom_fields_for << name.to_s
|
260
261
|
|
261
|
-
|
262
|
+
extend_for_custom_fields(name)
|
262
263
|
end
|
263
264
|
|
264
265
|
protected
|
@@ -272,12 +273,12 @@ module CustomFields
|
|
272
273
|
class_eval do
|
273
274
|
field :"#{name}_custom_fields_version", type: ::Integer, default: 0
|
274
275
|
|
275
|
-
embeds_many :"#{name}_custom_fields", class_name:
|
276
|
+
embeds_many :"#{name}_custom_fields", class_name: dynamic_custom_field_class_name(name) # , cascade_callbacks: true # FIXME ?????
|
276
277
|
|
277
278
|
accepts_nested_attributes_for :"#{name}_custom_fields", allow_destroy: true
|
278
279
|
end
|
279
280
|
|
280
|
-
class_eval <<-EOV
|
281
|
+
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
281
282
|
after_initialize :refresh_#{name}_metadata
|
282
283
|
before_update :bump_#{name}_custom_fields_version
|
283
284
|
before_update :collect_#{name}_custom_fields_diff
|
@@ -334,21 +335,22 @@ module CustomFields
|
|
334
335
|
# @return [ Field ] The new field class.
|
335
336
|
#
|
336
337
|
def declare_embedded_in_definition_in_custom_field(name)
|
337
|
-
klass_name =
|
338
|
+
klass_name = dynamic_custom_field_class_name(name).split('::').last # Use only the class, ignore the modules
|
338
339
|
|
339
|
-
source =
|
340
|
+
source = safe_module_parents.size > 1 ? safe_module_parents.first : Object
|
340
341
|
|
341
|
-
|
342
|
-
|
342
|
+
return if source.const_defined?(klass_name)
|
343
|
+
|
344
|
+
(klass = Class.new(::CustomFields::Field)).class_eval <<-EOF, __FILE__, __LINE__ + 1
|
343
345
|
embedded_in :#{self.name.demodulize.underscore}, inverse_of: :#{name}_custom_fields, class_name: '#{self.name}'
|
344
|
-
|
346
|
+
EOF
|
345
347
|
|
346
|
-
|
347
|
-
end
|
348
|
+
source.const_set(klass_name, klass)
|
348
349
|
end
|
349
350
|
|
351
|
+
def safe_module_parents
|
352
|
+
respond_to?(:module_parents) ? module_parents : parents
|
353
|
+
end
|
350
354
|
end
|
351
|
-
|
352
355
|
end
|
353
|
-
|
354
356
|
end
|
data/lib/custom_fields/target.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
module Target
|
4
|
-
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
|
9
8
|
## types ##
|
10
|
-
%w
|
11
|
-
|
12
|
-
|
9
|
+
%w[default string text email date date_time boolean file select
|
10
|
+
float integer money color belongs_to has_many many_to_many
|
11
|
+
tags password json].each do |type|
|
13
12
|
include "CustomFields::Types::#{type.camelize}::Target".constantize
|
14
13
|
end
|
15
14
|
|
@@ -20,7 +19,6 @@ module CustomFields
|
|
20
19
|
end
|
21
20
|
|
22
21
|
module ClassMethods
|
23
|
-
|
24
22
|
# A document with custom fields always returns true.
|
25
23
|
#
|
26
24
|
# @return [ Boolean ] True
|
@@ -39,7 +37,7 @@ module CustomFields
|
|
39
37
|
def build_klass_with_custom_fields(recipe)
|
40
38
|
name = recipe['name']
|
41
39
|
# puts "CREATING #{name}, #{recipe.inspect}" # DEBUG
|
42
|
-
|
40
|
+
safe_module_parent.const_set(name, Class.new(self)).tap do |klass|
|
43
41
|
klass.cattr_accessor :version
|
44
42
|
|
45
43
|
klass.version = recipe['version']
|
@@ -49,10 +47,10 @@ module CustomFields
|
|
49
47
|
# klass.write_inheritable_attribute(:scopes, self.scopes)
|
50
48
|
|
51
49
|
recipe['rules'].each do |rule|
|
52
|
-
|
50
|
+
send(:"apply_#{rule['type']}_custom_field", klass, rule)
|
53
51
|
end
|
54
52
|
recipe_model_name = recipe['model_name']
|
55
|
-
model_name =
|
53
|
+
model_name = proc do
|
56
54
|
if recipe_model_name.is_a?(ActiveModel::Name)
|
57
55
|
recipe_model_name
|
58
56
|
else
|
@@ -93,8 +91,9 @@ module CustomFields
|
|
93
91
|
klass
|
94
92
|
end
|
95
93
|
|
94
|
+
def safe_module_parent
|
95
|
+
respond_to?(:module_parent) ? module_parent : parent
|
96
|
+
end
|
96
97
|
end
|
97
|
-
|
98
98
|
end
|
99
|
-
|
100
99
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
module TargetHelpers
|
4
|
-
|
5
5
|
# Return the list of the getters dynamically based on the
|
6
6
|
# custom_fields recipe in order to get the formatted values
|
7
7
|
# of the custom fields.
|
@@ -18,8 +18,8 @@ module CustomFields
|
|
18
18
|
# @return [ List ] a list of method names (string)
|
19
19
|
#
|
20
20
|
def custom_fields_methods(&filter)
|
21
|
-
|
22
|
-
method =
|
21
|
+
custom_fields_recipe['rules'].map do |rule|
|
22
|
+
method = custom_fields_getters_for rule['name'], rule['type']
|
23
23
|
if block_given?
|
24
24
|
filter.call(rule) ? method : nil
|
25
25
|
else
|
@@ -34,7 +34,7 @@ module CustomFields
|
|
34
34
|
# @return [ List ] a list of method names (string)
|
35
35
|
#
|
36
36
|
def custom_fields_safe_setters
|
37
|
-
|
37
|
+
custom_fields_recipe['rules'].map do |rule|
|
38
38
|
case rule['type'].to_sym
|
39
39
|
when :date, :date_time, :money then "formatted_#{rule['name']}"
|
40
40
|
when :file then [rule['name'], "remove_#{rule['name']}", "remote_#{rule['name']}_url"]
|
@@ -54,8 +54,9 @@ module CustomFields
|
|
54
54
|
#
|
55
55
|
def custom_fields_basic_attributes
|
56
56
|
{}.tap do |hash|
|
57
|
-
|
58
|
-
name
|
57
|
+
non_relationship_custom_fields.each do |rule|
|
58
|
+
name = rule['name']
|
59
|
+
type = rule['type'].to_sym
|
59
60
|
|
60
61
|
# method of the custom getter
|
61
62
|
method_name = "#{type}_attribute_get"
|
@@ -71,8 +72,9 @@ module CustomFields
|
|
71
72
|
# @param [ Hash ] The attributes for the custom fields and their related fields.
|
72
73
|
#
|
73
74
|
def custom_fields_basic_attributes=(attributes)
|
74
|
-
|
75
|
-
name
|
75
|
+
non_relationship_custom_fields.each do |rule|
|
76
|
+
name = rule['name']
|
77
|
+
type = rule['type'].to_sym
|
76
78
|
|
77
79
|
# method of the custom getter
|
78
80
|
method_name = "#{type}_attribute_set"
|
@@ -89,7 +91,7 @@ module CustomFields
|
|
89
91
|
# @return [ Boolean ] True if the rule is a "many" relationship kind.
|
90
92
|
#
|
91
93
|
def is_a_custom_field_many_relationship?(name)
|
92
|
-
rule =
|
94
|
+
rule = custom_fields_recipe['rules'].detect do |rule|
|
93
95
|
rule['name'] == name && _custom_field_many_relationship?(rule['type'])
|
94
96
|
end
|
95
97
|
end
|
@@ -99,8 +101,8 @@ module CustomFields
|
|
99
101
|
# @return [ Array ] List of rules (Hash)
|
100
102
|
#
|
101
103
|
def non_relationship_custom_fields
|
102
|
-
|
103
|
-
!%w
|
104
|
+
custom_fields_recipe['rules'].find_all do |rule|
|
105
|
+
!%w[belongs_to has_many many_to_many].include?(rule['type'])
|
104
106
|
end
|
105
107
|
end
|
106
108
|
|
@@ -109,8 +111,8 @@ module CustomFields
|
|
109
111
|
# @return [ Array ] List of rules (Hash)
|
110
112
|
#
|
111
113
|
def relationship_custom_fields
|
112
|
-
|
113
|
-
%w
|
114
|
+
custom_fields_recipe['rules'].find_all do |rule|
|
115
|
+
%w[belongs_to has_many many_to_many].include?(rule['type'])
|
114
116
|
end
|
115
117
|
end
|
116
118
|
|
@@ -173,20 +175,16 @@ module CustomFields
|
|
173
175
|
end
|
174
176
|
end
|
175
177
|
|
176
|
-
|
178
|
+
# :nodoc:
|
177
179
|
def _custom_field_many_relationship?(type)
|
178
|
-
%w
|
180
|
+
%w[has_many many_to_many].include?(type)
|
179
181
|
end
|
180
182
|
|
181
|
-
|
183
|
+
# :nodoc:
|
182
184
|
def group_custom_fields(type, &block)
|
183
|
-
unless block_given?
|
184
|
-
block = lambda { |rule| rule['name'] }
|
185
|
-
end
|
185
|
+
block = ->(rule) { rule['name'] } unless block_given?
|
186
186
|
|
187
|
-
|
187
|
+
custom_fields_recipe['rules'].find_all { |rule| rule['type'] == type }.map(&block)
|
188
188
|
end
|
189
|
-
|
190
189
|
end
|
191
|
-
|
192
190
|
end
|
@@ -1,33 +1,26 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
module Types
|
4
|
-
|
5
5
|
module BelongsTo
|
6
|
-
|
7
6
|
module Field
|
8
|
-
|
9
7
|
extend ActiveSupport::Concern
|
10
8
|
|
11
9
|
included do
|
12
|
-
|
13
10
|
def belongs_to_to_recipe
|
14
|
-
{ 'class_name' =>
|
11
|
+
{ 'class_name' => class_name, 'inverse_of' => inverse_of }
|
15
12
|
end
|
16
13
|
|
17
14
|
def belongs_to_is_relationship?
|
18
|
-
|
15
|
+
type == 'belongs_to'
|
19
16
|
end
|
20
|
-
|
21
17
|
end
|
22
|
-
|
23
18
|
end
|
24
19
|
|
25
20
|
module Target
|
26
|
-
|
27
21
|
extend ActiveSupport::Concern
|
28
22
|
|
29
23
|
module ClassMethods
|
30
|
-
|
31
24
|
# Adds a belongs_to relationship between 2 models
|
32
25
|
#
|
33
26
|
# @param [ Class ] klass The class to modify
|
@@ -43,26 +36,19 @@ module CustomFields
|
|
43
36
|
klass.field position_name, type: ::Integer, default: 0
|
44
37
|
|
45
38
|
options = { class_name: rule['class_name'], optional: true }
|
46
|
-
options[:inverse_of] = rule['inverse_of'] unless rule['inverse_of'].blank?
|
39
|
+
options[:inverse_of] = rule['inverse_of'] unless rule['inverse_of'].blank?
|
47
40
|
|
48
41
|
klass.belongs_to rule['name'].to_sym, options
|
49
42
|
|
50
|
-
if rule['required']
|
51
|
-
klass.validates_presence_of rule['name'].to_sym
|
52
|
-
end
|
43
|
+
klass.validates_presence_of rule['name'].to_sym if rule['required']
|
53
44
|
|
54
45
|
klass.before_create do |object|
|
55
46
|
position = (object.class.max(position_name.to_sym) || 0) + 1
|
56
47
|
object.send(:"#{position_name}=", position)
|
57
48
|
end
|
58
49
|
end
|
59
|
-
|
60
50
|
end
|
61
|
-
|
62
51
|
end
|
63
|
-
|
64
52
|
end
|
65
|
-
|
66
53
|
end
|
67
|
-
|
68
54
|
end
|
@@ -1,24 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CustomFields
|
3
4
|
module Types
|
4
|
-
|
5
5
|
module Boolean
|
6
|
-
|
7
6
|
module Field; end
|
8
7
|
|
9
8
|
module Target
|
10
|
-
|
11
9
|
extend ActiveSupport::Concern
|
12
10
|
|
13
11
|
module ClassMethods
|
14
|
-
|
15
12
|
# Adds a boolean field. It can not be required.
|
16
13
|
#
|
17
14
|
# @param [ Class ] klass The class to modify
|
18
15
|
# @param [ Hash ] rule It contains the name of the field.
|
19
16
|
#
|
20
17
|
def apply_boolean_custom_field(klass, rule)
|
21
|
-
klass.field rule['name'], type:
|
18
|
+
klass.field rule['name'], type: 'Boolean', localize: rule['localized'] || false,
|
19
|
+
default: rule['default'].nil? ? false : rule['default']
|
22
20
|
end
|
23
21
|
|
24
22
|
# Build a hash storing the boolean value (true / false) for
|
@@ -41,15 +39,10 @@ module CustomFields
|
|
41
39
|
# @param [ Hash ] attributes The attributes used to fetch the values
|
42
40
|
#
|
43
41
|
def boolean_attribute_set(instance, name, attributes)
|
44
|
-
|
42
|
+
default_attribute_set(instance, name, attributes)
|
45
43
|
end
|
46
|
-
|
47
44
|
end
|
48
|
-
|
49
45
|
end
|
50
|
-
|
51
46
|
end
|
52
|
-
|
53
47
|
end
|
54
|
-
|
55
48
|
end
|