gnuside-custom_fields 2.3.1

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