gnuside-custom_fields 2.3.1

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +70 -0
  4. data/config/locales/de.yml +15 -0
  5. data/config/locales/en.yml +21 -0
  6. data/config/locales/fr.yml +25 -0
  7. data/config/locales/pt-BR.yml +9 -0
  8. data/config/locales/ru.yml +15 -0
  9. data/lib/custom_fields/extensions/active_support.rb +28 -0
  10. data/lib/custom_fields/extensions/carrierwave.rb +25 -0
  11. data/lib/custom_fields/extensions/mongoid/document.rb +21 -0
  12. data/lib/custom_fields/extensions/mongoid/factory.rb +20 -0
  13. data/lib/custom_fields/extensions/mongoid/fields/i18n.rb +55 -0
  14. data/lib/custom_fields/extensions/mongoid/fields/localized.rb +39 -0
  15. data/lib/custom_fields/extensions/mongoid/fields.rb +31 -0
  16. data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +22 -0
  17. data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +34 -0
  18. data/lib/custom_fields/extensions/mongoid/validations/collection_size.rb +43 -0
  19. data/lib/custom_fields/extensions/mongoid/validations/macros.rb +25 -0
  20. data/lib/custom_fields/extensions/origin/smash.rb +33 -0
  21. data/lib/custom_fields/field.rb +106 -0
  22. data/lib/custom_fields/source.rb +347 -0
  23. data/lib/custom_fields/target.rb +99 -0
  24. data/lib/custom_fields/target_helpers.rb +192 -0
  25. data/lib/custom_fields/types/belongs_to.rb +65 -0
  26. data/lib/custom_fields/types/boolean.rb +55 -0
  27. data/lib/custom_fields/types/date.rb +97 -0
  28. data/lib/custom_fields/types/date_time.rb +97 -0
  29. data/lib/custom_fields/types/default.rb +103 -0
  30. data/lib/custom_fields/types/email.rb +60 -0
  31. data/lib/custom_fields/types/file.rb +74 -0
  32. data/lib/custom_fields/types/float.rb +52 -0
  33. data/lib/custom_fields/types/has_many.rb +74 -0
  34. data/lib/custom_fields/types/integer.rb +54 -0
  35. data/lib/custom_fields/types/many_to_many.rb +75 -0
  36. data/lib/custom_fields/types/money.rb +146 -0
  37. data/lib/custom_fields/types/relationship_default.rb +44 -0
  38. data/lib/custom_fields/types/select.rb +217 -0
  39. data/lib/custom_fields/types/string.rb +55 -0
  40. data/lib/custom_fields/types/tags.rb +35 -0
  41. data/lib/custom_fields/types/text.rb +65 -0
  42. data/lib/custom_fields/version.rb +6 -0
  43. data/lib/custom_fields.rb +74 -0
  44. metadata +244 -0
@@ -0,0 +1,55 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module Boolean
6
+
7
+ module Field; end
8
+
9
+ module Target
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+
15
+ # Adds a boolean field. It can not be required.
16
+ #
17
+ # @param [ Class ] klass The class to modify
18
+ # @param [ Hash ] rule It contains the name of the field.
19
+ #
20
+ def apply_boolean_custom_field(klass, rule)
21
+ klass.field rule['name'], type: ::Boolean, localize: rule['localized'] || false, default: false
22
+ end
23
+
24
+ # Build a hash storing the boolean value (true / false) for
25
+ # a boolean custom field of an instance.
26
+ #
27
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
28
+ # @param [ String ] name The name of the boolean custom field
29
+ #
30
+ # @return [ Hash ] field name => true / false
31
+ #
32
+ def boolean_attribute_get(instance, name)
33
+ default_attribute_get(instance, name)
34
+ end
35
+
36
+ # Set the value for the instance and the boolean field specified by
37
+ # the 2 params.
38
+ #
39
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
40
+ # @param [ String ] name The name of the boolean custom field
41
+ # @param [ Hash ] attributes The attributes used to fetch the values
42
+ #
43
+ def boolean_attribute_set(instance, name, attributes)
44
+ self.default_attribute_set(instance, name, attributes)
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,97 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module Date
6
+
7
+ module Field; end
8
+
9
+ module Target
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+
15
+ # Adds a date field
16
+ #
17
+ # @param [ Class ] klass The class to modify
18
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
19
+ #
20
+ def apply_date_custom_field(klass, rule)
21
+ name = rule['name']
22
+
23
+ klass.field name, type: ::Date, localize: rule['localized'] || false
24
+
25
+ # other methods
26
+ klass.send(:define_method, :"formatted_#{name}") { _get_formatted_date(name) }
27
+ klass.send(:define_method, :"formatted_#{name}=") { |value| _set_formatted_date(name, value) }
28
+
29
+ if rule['required']
30
+ klass.validates_presence_of name, :"formatted_#{name}"
31
+ end
32
+ end
33
+
34
+ # Build a hash storing the formatted value for
35
+ # a date custom field of an instance.
36
+ #
37
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
38
+ # @param [ String ] name The name of the date custom field
39
+ #
40
+ # @return [ Hash ] field name => formatted date
41
+ #
42
+ def date_attribute_get(instance, name)
43
+ if value = instance.send(:"formatted_#{name}")
44
+ { name => value, "formatted_#{name}" => value }
45
+ else
46
+ {}
47
+ end
48
+ end
49
+
50
+ # Set the value for the instance and the date field specified by
51
+ # the 2 params.
52
+ #
53
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
54
+ # @param [ String ] name The name of the date custom field
55
+ # @param [ Hash ] attributes The attributes used to fetch the values
56
+ #
57
+ def date_attribute_set(instance, name, attributes)
58
+ return unless attributes.key?(name) || attributes.key?("formatted_#{name}")
59
+
60
+ value = attributes[name] || attributes["formatted_#{name}"]
61
+
62
+ instance.send(:"formatted_#{name}=", value)
63
+ end
64
+
65
+ end
66
+
67
+ protected
68
+
69
+ def _set_formatted_date(name, value)
70
+ if value.is_a?(::String) && !value.blank?
71
+ date = ::Date._strptime(value, self._formatted_date_format)
72
+
73
+ if date
74
+ value = ::Date.new(date[:year], date[:mon], date[:mday])
75
+ else
76
+ value = ::Date.parse(value) rescue nil
77
+ end
78
+ end
79
+
80
+ self.send(:"#{name}=", value)
81
+ end
82
+
83
+ def _get_formatted_date(name)
84
+ self.send(name.to_sym).strftime(self._formatted_date_format) rescue nil
85
+ end
86
+
87
+ def _formatted_date_format
88
+ I18n.t('date.formats.default')
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,97 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module DateTime
6
+
7
+ module Field; end
8
+
9
+ module Target
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+
15
+ # Adds a date_time field
16
+ #
17
+ # @param [ Class ] klass The class to modify
18
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
19
+ #
20
+ def apply_date_time_custom_field(klass, rule)
21
+ name = rule['name']
22
+
23
+ klass.field name, type: ::DateTime, localize: rule['localized'] || false
24
+
25
+ # other methods
26
+ klass.send(:define_method, :"formatted_#{name}") { _get_formatted_date_time(name) }
27
+ klass.send(:define_method, :"formatted_#{name}=") { |value| _set_formatted_date_time(name, value) }
28
+
29
+ if rule['required']
30
+ klass.validates_presence_of name, :"formatted_#{name}"
31
+ end
32
+ end
33
+
34
+ # Build a hash storing the formatted value for
35
+ # a date_time custom field of an instance.
36
+ #
37
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
38
+ # @param [ String ] name The name of the date_time custom field
39
+ #
40
+ # @return [ Hash ] field name => formatted date_time
41
+ #
42
+ def date_time_attribute_get(instance, name)
43
+ if value = instance.send(:"formatted_#{name}")
44
+ { name => value, "formatted_#{name}" => value }
45
+ else
46
+ {}
47
+ end
48
+ end
49
+
50
+ # Set the value for the instance and the date_time field specified by
51
+ # the 2 params.
52
+ #
53
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
54
+ # @param [ String ] name The name of the date_time custom field
55
+ # @param [ Hash ] attributes The attributes used to fetch the values
56
+ #
57
+ def date_time_attribute_set(instance, name, attributes)
58
+ return unless attributes.key?(name) || attributes.key?("formatted_#{name}")
59
+
60
+ value = attributes[name] || attributes["formatted_#{name}"]
61
+
62
+ instance.send(:"formatted_#{name}=", value)
63
+ end
64
+
65
+ end
66
+
67
+ protected
68
+
69
+ def _set_formatted_date_time(name, value)
70
+ if value.is_a?(::String) && !value.blank?
71
+ date_time = ::DateTime._strptime(value, self._formatted_date_time_format)
72
+
73
+ if date_time
74
+ value = ::Time.zone.local(date_time[:year], date_time[:mon], date_time[:mday], date_time[:hour], date_time[:min], date_time[:sec] || 0)#, date_time[:zone] || "")
75
+ else
76
+ value = ::Time.zone.parse(value) rescue nil
77
+ end
78
+ end
79
+
80
+ self.send(:"#{name}=", value)
81
+ end
82
+
83
+ def _get_formatted_date_time(name)
84
+ self.send(name.to_sym).strftime(self._formatted_date_time_format) rescue nil
85
+ end
86
+
87
+ def _formatted_date_time_format
88
+ I18n.t('time.formats.default')
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,103 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module Default
6
+
7
+ module Field
8
+
9
+ # Build the mongodb updates based on
10
+ # the new state of the field
11
+ #
12
+ # @param [ Hash ] memo Store the updates
13
+ #
14
+ # @return [ Hash ] The memo object upgraded
15
+ #
16
+ def collect_default_diff(memo)
17
+ # puts "collect_default_diff #{self.name}: #{self.persisted?} / #{self.destroyed?}" # DEBUG
18
+ if self.persisted?
19
+ if self.destroyed?
20
+ memo['$unset'][self.name] = 1
21
+ elsif self.changed?
22
+ if self.changes.key?('name')
23
+ old_name, new_name = self.changes['name']
24
+ memo['$rename'][old_name] = new_name
25
+ end
26
+ end
27
+ end
28
+
29
+ (memo['$set']['custom_fields_recipe.rules'] ||= []) << self.to_recipe
30
+
31
+ memo
32
+ end
33
+
34
+ end
35
+
36
+ module Target
37
+
38
+ extend ActiveSupport::Concern
39
+
40
+ module ClassMethods
41
+
42
+ # Modify the target class according to the rule.
43
+ # By default, it declares the field and a validator
44
+ # if specified by the rule
45
+ #
46
+ # @param [ Class ] klass The class to modify
47
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
48
+ #
49
+ def apply_custom_field(klass, rule)
50
+ klass.field rule['name'], localize: rule['localized'] || false
51
+
52
+ klass.validates_presence_of rule['name'] if rule['required']
53
+ klass.validates_uniqueness_of rule['name'], scope: :_type if rule['unique']
54
+ end
55
+
56
+ # Build a hash storing the formatted (or not) values
57
+ # for a custom field of an instance.
58
+ # Since aliases are accepted, we return a hash. Beside,
59
+ # it is more convenient to use (ex: API).
60
+ # By default, it only returns hash with only one entry
61
+ # whose key is the second parameter and the value the
62
+ # value of the field in the instance given in first parameter.
63
+ #
64
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
65
+ # @param [ String ] name The name of the custom field
66
+ #
67
+ # @return [ Hash ] field name => formatted value or empty hash if no value
68
+ #
69
+ def default_attribute_get(instance, name)
70
+ unless (value = instance.send(name.to_sym)).nil?
71
+ { name => instance.send(name.to_sym) }
72
+ else
73
+ {}
74
+ end
75
+ end
76
+
77
+ # Set the value for the instance and the field specified by
78
+ # the 2 params.
79
+ # Since the value can come from different attributes and other
80
+ # params can modify the instance too, we need to pass a hash
81
+ # instead of a single value.
82
+ #
83
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
84
+ # @param [ String ] name The name of the custom field
85
+ # @param [ Hash ] attributes The attributes used to fetch the values
86
+ #
87
+ def default_attribute_set(instance, name, attributes)
88
+ # do not go further if the name is not one of the attributes keys.
89
+ return unless attributes.key?(name)
90
+
91
+ # simple assign
92
+ instance.send(:"#{name}=", attributes[name])
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,60 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module Email
6
+
7
+ module Field; end
8
+
9
+ module Target
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+
15
+ # Add a string field
16
+ #
17
+ # @param [ Class ] klass The class to modify
18
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
19
+ #
20
+ def apply_email_custom_field(klass, rule)
21
+ name = rule['name']
22
+
23
+ klass.field name, type: ::String, localize: rule['localized'] || false
24
+ klass.validates_presence_of name if rule['required']
25
+ klass.validates_format_of name, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, allow_blank: !rule['required']
26
+ klass.validates_uniqueness_of rule['name'], scope: :_type if rule['unique']
27
+ end
28
+
29
+ # Build a hash storing the raw value for
30
+ # a string custom field of an instance.
31
+ #
32
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
33
+ # @param [ String ] name The name of the string custom field
34
+ #
35
+ # @return [ Hash ] field name => raw value
36
+ #
37
+ def email_attribute_get(instance, name)
38
+ self.default_attribute_get(instance, name)
39
+ end
40
+
41
+ # Set the value for the instance and the string field specified by
42
+ # the 2 params.
43
+ #
44
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
45
+ # @param [ String ] name The name of the string custom field
46
+ # @param [ Hash ] attributes The attributes used to fetch the values
47
+ #
48
+ def email_attribute_set(instance, name, attributes)
49
+ self.default_attribute_set(instance, name, attributes)
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,74 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module File
6
+
7
+ module Field; end
8
+
9
+ module Target
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ module ClassMethods
14
+
15
+ # Adds a file field (using carrierwave)
16
+ #
17
+ # @param [ Class ] klass The class to modify
18
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
19
+ #
20
+ def apply_file_custom_field(klass, rule)
21
+ name = rule['name']
22
+
23
+ klass.mount_uploader name, FileUploader
24
+
25
+ if rule['localized'] == true
26
+ klass.replace_field name, ::String, true
27
+ end
28
+
29
+ if rule['required']
30
+ klass.validates_presence_of name
31
+ end
32
+ end
33
+
34
+ # Build a hash storing the url for a file custom field of an instance.
35
+ #
36
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
37
+ # @param [ String ] name The name of the file custom field
38
+ #
39
+ # @return [ Hash ] field name => url or empty hash if no file
40
+ #
41
+ def file_attribute_get(instance, name)
42
+ if instance.send(:"#{name}?") #"
43
+ value = instance.send(name.to_sym).url
44
+ { name => value, "#{name}_url" => value }
45
+ else
46
+ {}
47
+ end
48
+ end
49
+
50
+ # Set the value for the instance and the file field specified by
51
+ # the 2 params.
52
+ #
53
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
54
+ # @param [ String ] name The name of the file custom field
55
+ # @param [ Hash ] attributes The attributes used to fetch the values
56
+ #
57
+ def file_attribute_set(instance, name, attributes)
58
+ [name, "remote_#{name}_url", "remove_#{name}"].each do |_name|
59
+ self.default_attribute_set(instance, _name, attributes)
60
+ end.compact
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ class FileUploader < ::CarrierWave::Uploader::Base
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,52 @@
1
+ module CustomFields
2
+ module Types
3
+ module Float
4
+
5
+ module Field; end
6
+
7
+ module Target
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+
12
+ # Add a string field
13
+ #
14
+ # @param [ Class ] klass The class to modify
15
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
16
+ #
17
+ def apply_float_custom_field(klass, rule)
18
+ klass.field rule['name'], type: ::Float, localize: rule['localized'] || false
19
+ klass.validates_presence_of rule['name'] if rule['required']
20
+ klass.validates rule['name'], numericality: true, if: ->(x){ rule['required'] }
21
+ end
22
+
23
+ # Build a hash storing the raw value for
24
+ # a string custom field of an instance.
25
+ #
26
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
27
+ # @param [ String ] name The name of the string custom field
28
+ #
29
+ # @return [ Hash ] field name => raw value
30
+ #
31
+ def float_attribute_get(instance, name)
32
+ self.default_attribute_get(instance, name)
33
+ end
34
+
35
+ # Set the value for the instance and the string field specified by
36
+ # the 2 params.
37
+ #
38
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
39
+ # @param [ String ] name The name of the string custom field
40
+ # @param [ Hash ] attributes The attributes used to fetch the values
41
+ #
42
+ def float_attribute_set(instance, name, attributes)
43
+ self.default_attribute_set(instance, name, attributes)
44
+ end
45
+
46
+ end # ClassMethods
47
+
48
+ end # Target
49
+
50
+ end # Float
51
+ end # Types
52
+ end # CustomFields
@@ -0,0 +1,74 @@
1
+ module CustomFields
2
+
3
+ module Types
4
+
5
+ module HasMany
6
+
7
+ module Field
8
+
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+
13
+ def has_many_to_recipe
14
+ { 'class_name' => self.class_name, 'inverse_of' => self.inverse_of, 'order_by' => self.order_by }
15
+ end
16
+
17
+ def has_many_is_relationship?
18
+ self.type == 'has_many'
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ module Target
26
+
27
+ extend ActiveSupport::Concern
28
+
29
+ module ClassMethods
30
+
31
+ # Adds a has_many relationship between 2 mongoid models
32
+ #
33
+ # @param [ Class ] klass The class to modify
34
+ # @param [ Hash ] rule It contains the name of the relation and if it is required or not
35
+ #
36
+ def apply_has_many_custom_field(klass, rule)
37
+ # puts "#{klass.inspect}.has_many #{rule['name'].inspect}, class_name: #{rule['class_name'].inspect}, inverse_of: #{rule['inverse_of']}" # DEBUG
38
+ position_name = "position_in_#{rule['inverse_of']}"
39
+
40
+ _order_by = rule['order_by'] || position_name.to_sym.asc
41
+ _inverse_of = rule['inverse_of'].blank? ? nil : rule['inverse_of'] # an empty String can cause weird behaviours
42
+
43
+ klass.has_many rule['name'], class_name: rule['class_name'], inverse_of: _inverse_of, order: _order_by do
44
+
45
+ def filtered(conditions = {}, order_by = nil)
46
+ list = conditions.empty? ? self.unscoped : self.where(conditions)
47
+
48
+ if order_by
49
+ list.order_by(order_by)
50
+ else
51
+ list.order_by(metadata.order)
52
+ end
53
+ end
54
+
55
+ alias :ordered :filtered # backward compatibility + semantic purpose
56
+
57
+ end
58
+
59
+ klass.accepts_nested_attributes_for rule['name'], allow_destroy: true
60
+
61
+ if rule['required']
62
+ klass.validates_collection_size_of rule['name'], minimum: 1, message: :at_least_one_element, on: :update
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,54 @@
1
+ module CustomFields
2
+ module Types
3
+ module Integer
4
+
5
+ module Field; end
6
+
7
+ module Target
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+
12
+ # Add a integer field
13
+ #
14
+ # @param [ Class ] klass The class to modify
15
+ # @param [ Hash ] rule It contains the name of the field and if it is required or not
16
+ #
17
+ def apply_integer_custom_field(klass, rule)
18
+ name = rule['name']
19
+
20
+ klass.field name, type: ::Integer, localize: rule['localized'] || false
21
+ klass.validates_presence_of name if rule['required']
22
+ klass.validates name, numericality: { only_integer: true }, if: ->(x){ rule['required'] }
23
+ end
24
+
25
+ # Build a hash storing the raw value for
26
+ # a string custom field of an instance.
27
+ #
28
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
29
+ # @param [ String ] name The name of the string custom field
30
+ #
31
+ # @return [ Hash ] field name => raw value
32
+ #
33
+ def integer_attribute_get(instance, name)
34
+ self.default_attribute_get(instance, name)
35
+ end
36
+
37
+ # Set the value for the instance and the string field specified by
38
+ # the 2 params.
39
+ #
40
+ # @param [ Object ] instance An instance of the class enhanced by the custom_fields
41
+ # @param [ String ] name The name of the string custom field
42
+ # @param [ Hash ] attributes The attributes used to fetch the values
43
+ #
44
+ def integer_attribute_set(instance, name, attributes)
45
+ self.default_attribute_set(instance, name, attributes)
46
+ end
47
+
48
+ end # ClassMethods
49
+
50
+ end # Target
51
+
52
+ end # Integer
53
+ end # Types
54
+ end # CustomFields