custom_fields 2.1.0 → 2.2.0
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.
- data/MIT-LICENSE +1 -1
- data/README.textile +4 -4
- data/config/locales/de.yml +11 -5
- data/config/locales/en.yml +4 -0
- data/config/locales/fr.yml +4 -0
- data/config/locales/ru.yml +15 -0
- data/lib/custom_fields.rb +37 -27
- data/lib/custom_fields/extensions/active_support.rb +1 -1
- data/lib/custom_fields/extensions/carrierwave.rb +2 -2
- data/lib/custom_fields/extensions/mongoid/factory.rb +3 -3
- data/lib/custom_fields/extensions/mongoid/fields.rb +4 -3
- data/lib/custom_fields/extensions/mongoid/fields/localized.rb +39 -0
- data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +1 -1
- data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +3 -3
- data/lib/custom_fields/field.rb +14 -8
- data/lib/custom_fields/source.rb +23 -14
- data/lib/custom_fields/target.rb +16 -8
- data/lib/custom_fields/target_helpers.rb +6 -6
- data/lib/custom_fields/types/belongs_to.rb +4 -4
- data/lib/custom_fields/types/boolean.rb +1 -1
- data/lib/custom_fields/types/date.rb +2 -2
- data/lib/custom_fields/types/default.rb +5 -6
- data/lib/custom_fields/types/email.rb +60 -0
- data/lib/custom_fields/types/file.rb +2 -2
- data/lib/custom_fields/types/float.rb +52 -0
- data/lib/custom_fields/types/has_many.rb +6 -8
- data/lib/custom_fields/types/integer.rb +54 -0
- data/lib/custom_fields/types/many_to_many.rb +3 -3
- data/lib/custom_fields/types/money.rb +146 -0
- data/lib/custom_fields/types/relationship_default.rb +2 -2
- data/lib/custom_fields/types/select.rb +16 -13
- data/lib/custom_fields/types/tags.rb +35 -0
- data/lib/custom_fields/types/text.rb +1 -1
- data/lib/custom_fields/version.rb +1 -1
- metadata +65 -76
- data/init.rb +0 -2
- data/lib/custom_fields/extensions/mongoid/fields/internal/localized.rb +0 -86
data/lib/custom_fields/target.rb
CHANGED
@@ -7,15 +7,15 @@ module CustomFields
|
|
7
7
|
included do
|
8
8
|
|
9
9
|
## types ##
|
10
|
-
%w(default string text date boolean file select
|
11
|
-
|
10
|
+
%w(default string text email date boolean file select float integer money
|
11
|
+
belongs_to has_many many_to_many tags).each do |type|
|
12
|
+
include "CustomFields::Types::#{type.camelize}::Target".constantize
|
12
13
|
end
|
13
14
|
|
14
15
|
include ::CustomFields::TargetHelpers
|
15
16
|
|
16
17
|
## fields ##
|
17
|
-
field :custom_fields_recipe, :
|
18
|
-
|
18
|
+
field :custom_fields_recipe, type: Hash
|
19
19
|
end
|
20
20
|
|
21
21
|
module ClassMethods
|
@@ -33,13 +33,11 @@ module CustomFields
|
|
33
33
|
#
|
34
34
|
# @param [ Hash ] recipe The recipe describing the fields to add
|
35
35
|
#
|
36
|
-
# @return [ Class] the anonymous custom klass
|
36
|
+
# @return [ Class ] the anonymous custom klass
|
37
37
|
#
|
38
38
|
def build_klass_with_custom_fields(recipe)
|
39
39
|
name = recipe['name']
|
40
|
-
|
41
40
|
# puts "CREATING #{name}, #{recipe.inspect}" # DEBUG
|
42
|
-
|
43
41
|
parent.const_set(name, Class.new(self)).tap do |klass|
|
44
42
|
klass.cattr_accessor :version
|
45
43
|
|
@@ -52,6 +50,16 @@ module CustomFields
|
|
52
50
|
recipe['rules'].each do |rule|
|
53
51
|
self.send(:"apply_#{rule['type']}_custom_field", klass, rule)
|
54
52
|
end
|
53
|
+
recipe_model_name = recipe['model_name']
|
54
|
+
model_name = Proc.new do
|
55
|
+
if recipe_model_name.is_a?(ActiveModel::Name)
|
56
|
+
recipe_model_name
|
57
|
+
else
|
58
|
+
recipe_model_name.constantize.model_name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
klass.send :define_method, :model_name, model_name
|
62
|
+
klass.send :define_singleton_method, :model_name, model_name
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
@@ -86,4 +94,4 @@ module CustomFields
|
|
86
94
|
|
87
95
|
end
|
88
96
|
|
89
|
-
end
|
97
|
+
end
|
@@ -36,7 +36,7 @@ module CustomFields
|
|
36
36
|
def custom_fields_safe_setters
|
37
37
|
self.custom_fields_recipe['rules'].map do |rule|
|
38
38
|
case rule['type'].to_sym
|
39
|
-
when :date
|
39
|
+
when :date, :money then "formatted_#{rule['name']}"
|
40
40
|
when :file then [rule['name'], "remove_#{rule['name']}"]
|
41
41
|
when :select, :belongs_to then ["#{rule['name']}_id", "position_in_#{rule['name']}"]
|
42
42
|
when :has_many, :many_to_many then nil
|
@@ -164,10 +164,10 @@ module CustomFields
|
|
164
164
|
#
|
165
165
|
def custom_fields_getters_for(name, type)
|
166
166
|
case type.to_sym
|
167
|
-
when :select
|
168
|
-
when :date
|
169
|
-
when :file
|
170
|
-
when :belongs_to
|
167
|
+
when :select then [name, "#{name}_id"]
|
168
|
+
when :date, :money then "formatted_#{name}"
|
169
|
+
when :file then "#{name}_url"
|
170
|
+
when :belongs_to then "#{name}_id"
|
171
171
|
else
|
172
172
|
name
|
173
173
|
end
|
@@ -189,4 +189,4 @@ module CustomFields
|
|
189
189
|
|
190
190
|
end
|
191
191
|
|
192
|
-
end
|
192
|
+
end
|
@@ -34,15 +34,15 @@ module CustomFields
|
|
34
34
|
# @param [ Hash ] rule It contains the name of the field and if it is required or not
|
35
35
|
#
|
36
36
|
def apply_belongs_to_custom_field(klass, rule)
|
37
|
-
# puts "#{klass.inspect}.belongs_to #{rule['name'].inspect}, :
|
37
|
+
# puts "#{klass.inspect}.belongs_to #{rule['name'].inspect}, class_name: #{rule['class_name'].inspect}" # DEBUG
|
38
38
|
|
39
39
|
position_name = "position_in_#{rule['name'].underscore}"
|
40
40
|
|
41
41
|
# puts "#{klass.inspect}.field :#{position_name}" # DEBUG
|
42
42
|
|
43
|
-
klass.field position_name, :
|
43
|
+
klass.field position_name, type: ::Integer, default: 0
|
44
44
|
|
45
|
-
klass.belongs_to rule['name'].to_sym, :
|
45
|
+
klass.belongs_to rule['name'].to_sym, class_name: rule['class_name']
|
46
46
|
|
47
47
|
if rule['required']
|
48
48
|
klass.validates_presence_of rule['name'].to_sym
|
@@ -62,4 +62,4 @@ module CustomFields
|
|
62
62
|
|
63
63
|
end
|
64
64
|
|
65
|
-
end
|
65
|
+
end
|
@@ -18,7 +18,7 @@ module CustomFields
|
|
18
18
|
# @param [ Hash ] rule It contains the name of the field.
|
19
19
|
#
|
20
20
|
def apply_boolean_custom_field(klass, rule)
|
21
|
-
klass.field rule['name'], :
|
21
|
+
klass.field rule['name'], type: ::Boolean, localize: rule['localized'] || false, default: false
|
22
22
|
end
|
23
23
|
|
24
24
|
# Build a hash storing the boolean value (true / false) for
|
@@ -20,7 +20,7 @@ module CustomFields
|
|
20
20
|
def apply_date_custom_field(klass, rule)
|
21
21
|
name = rule['name']
|
22
22
|
|
23
|
-
klass.field name, :
|
23
|
+
klass.field name, type: ::Date, localize: rule['localized'] || false
|
24
24
|
|
25
25
|
# other methods
|
26
26
|
klass.send(:define_method, :"formatted_#{name}") { _get_formatted_date(name) }
|
@@ -90,4 +90,4 @@ module CustomFields
|
|
90
90
|
|
91
91
|
end
|
92
92
|
|
93
|
-
end
|
93
|
+
end
|
@@ -19,8 +19,8 @@ module CustomFields
|
|
19
19
|
if self.destroyed?
|
20
20
|
memo['$unset'][self.name] = 1
|
21
21
|
elsif self.changed?
|
22
|
-
if self.changes.key?(
|
23
|
-
old_name, new_name = self.changes[
|
22
|
+
if self.changes.key?('name')
|
23
|
+
old_name, new_name = self.changes['name']
|
24
24
|
memo['$rename'][old_name] = new_name
|
25
25
|
end
|
26
26
|
end
|
@@ -47,11 +47,10 @@ module CustomFields
|
|
47
47
|
# @param [ Hash ] rule It contains the name of the field and if it is required or not
|
48
48
|
#
|
49
49
|
def apply_custom_field(klass, rule)
|
50
|
-
klass.field rule['name'], :
|
50
|
+
klass.field rule['name'], localize: rule['localized'] || false
|
51
51
|
|
52
|
-
if rule['required']
|
53
|
-
|
54
|
-
end
|
52
|
+
klass.validates_presence_of rule['name'] if rule['required']
|
53
|
+
klass.validates_uniqueness_of rule['name'], scope: :_type if rule['unique']
|
55
54
|
end
|
56
55
|
|
57
56
|
# Build a hash storing the formatted (or not) values
|
@@ -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
|
@@ -39,7 +39,7 @@ module CustomFields
|
|
39
39
|
# @return [ Hash ] field name => url or empty hash if no file
|
40
40
|
#
|
41
41
|
def file_attribute_get(instance, name)
|
42
|
-
if instance.send(:"#{name}?")
|
42
|
+
if instance.send(:"#{name}?") #"
|
43
43
|
value = instance.send(name.to_sym).url
|
44
44
|
{ name => value, "#{name}_url" => value }
|
45
45
|
else
|
@@ -71,4 +71,4 @@ module CustomFields
|
|
71
71
|
|
72
72
|
end
|
73
73
|
|
74
|
-
end
|
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
|
@@ -34,14 +34,13 @@ module CustomFields
|
|
34
34
|
# @param [ Hash ] rule It contains the name of the relation and if it is required or not
|
35
35
|
#
|
36
36
|
def apply_has_many_custom_field(klass, rule)
|
37
|
-
# puts "#{klass.inspect}.has_many #{rule['name'].inspect}, :
|
38
|
-
|
37
|
+
# puts "#{klass.inspect}.has_many #{rule['name'].inspect}, class_name: #{rule['class_name'].inspect}, inverse_of: #{rule['inverse_of']}" # DEBUG
|
39
38
|
position_name = "position_in_#{rule['inverse_of']}"
|
40
39
|
|
41
40
|
_order_by = rule['order_by'] || position_name.to_sym.asc
|
42
41
|
_inverse_of = rule['inverse_of'].blank? ? nil : rule['inverse_of'] # an empty String can cause weird behaviours
|
43
42
|
|
44
|
-
klass.has_many rule['name'], :
|
43
|
+
klass.has_many rule['name'], class_name: rule['class_name'], inverse_of: _inverse_of, order: _order_by do
|
45
44
|
|
46
45
|
def filtered(conditions = {}, order_by = nil)
|
47
46
|
list = conditions.empty? ? self : self.where(conditions)
|
@@ -49,8 +48,7 @@ module CustomFields
|
|
49
48
|
if order_by
|
50
49
|
list.order_by(order_by)
|
51
50
|
else
|
52
|
-
|
53
|
-
list.order(metadata.order)
|
51
|
+
list.order_by(metadata.order)
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -58,10 +56,10 @@ module CustomFields
|
|
58
56
|
|
59
57
|
end
|
60
58
|
|
61
|
-
klass.accepts_nested_attributes_for rule['name'], :
|
59
|
+
klass.accepts_nested_attributes_for rule['name'], allow_destroy: true
|
62
60
|
|
63
61
|
if rule['required']
|
64
|
-
klass.validates_length_of rule['name'], :
|
62
|
+
klass.validates_length_of rule['name'], minimum: 1
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
@@ -73,4 +71,4 @@ module CustomFields
|
|
73
71
|
|
74
72
|
end
|
75
73
|
|
76
|
-
end
|
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
|
@@ -34,9 +34,9 @@ module CustomFields
|
|
34
34
|
# @param [ Hash ] rule It contains the name of the relation and if it is required or not
|
35
35
|
#
|
36
36
|
def apply_many_to_many_custom_field(klass, rule)
|
37
|
-
# puts "#{klass.inspect}.many_to_many #{rule['name'].inspect}, :
|
37
|
+
# puts "#{klass.inspect}.many_to_many #{rule['name'].inspect}, class_name: #{rule['class_name'].inspect} / #{rule['order_by']}" # DEBUG
|
38
38
|
|
39
|
-
klass.has_and_belongs_to_many rule['name'], :
|
39
|
+
klass.has_and_belongs_to_many rule['name'], class_name: rule['class_name'], inverse_of: rule['inverse_of'], order: rule['order_by'] do
|
40
40
|
|
41
41
|
def filtered(conditions = {}, order_by = nil)
|
42
42
|
list = conditions.empty? ? self : self.where(conditions)
|
@@ -56,7 +56,7 @@ module CustomFields
|
|
56
56
|
end
|
57
57
|
|
58
58
|
if rule['required']
|
59
|
-
klass.validates_length_of rule['name'], :
|
59
|
+
klass.validates_length_of rule['name'], minimum: 1
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module CustomFields
|
2
|
+
module Types
|
3
|
+
module Money
|
4
|
+
|
5
|
+
module Field
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
# allow_currency_from_symbol means that the user is able
|
10
|
+
# to provide another currency instead of the the default
|
11
|
+
# e.g. default is 'EUR' and User sets '100.11 USD'
|
12
|
+
field :default_currency
|
13
|
+
field :allow_currency_from_symbol, type: ::Boolean, default: false
|
14
|
+
|
15
|
+
before_validation :set_default
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def set_default
|
20
|
+
self.default_currency ||= CustomFields.options[:default_currency]
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_currency
|
24
|
+
::Money::Currency.find( self.default_currency )
|
25
|
+
end
|
26
|
+
|
27
|
+
end # included
|
28
|
+
|
29
|
+
def money_to_recipe
|
30
|
+
{ 'default_currency' => self.default_currency,
|
31
|
+
'allow_currency_from_symbol' => self.allow_currency_from_symbol }
|
32
|
+
end
|
33
|
+
|
34
|
+
def money_as_json(options = {})
|
35
|
+
money_to_recipe
|
36
|
+
end
|
37
|
+
|
38
|
+
end # module Field
|
39
|
+
|
40
|
+
|
41
|
+
module Target
|
42
|
+
extend ActiveSupport::Concern
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
|
46
|
+
# Adds a Money field
|
47
|
+
#
|
48
|
+
# uses the money gem (https://github.com/RubyMoney/money)
|
49
|
+
#
|
50
|
+
# @param [ Class ] klass The class to modify
|
51
|
+
# @param [ Hash ] rule It contains the name of the field and if it is required or not
|
52
|
+
#
|
53
|
+
# if allow_currency_from_symbol is set the formatted_name_field will return the amount
|
54
|
+
# and the currency
|
55
|
+
|
56
|
+
def apply_money_custom_field(klass, rule)
|
57
|
+
|
58
|
+
# the field names
|
59
|
+
name = rule['name']
|
60
|
+
names = {
|
61
|
+
name: name.to_sym,
|
62
|
+
cents_field: "#{name}_cents".to_sym,
|
63
|
+
currency_field: "#{name}_currency".to_sym,
|
64
|
+
formatted_name_field: "formatted_#{name}".to_sym,
|
65
|
+
allow_currency_from_symbol: "#{name}_allow_currency_from_symbol".to_sym,
|
66
|
+
default_currency: "#{name}_default_currency".to_sym
|
67
|
+
}
|
68
|
+
|
69
|
+
# fields
|
70
|
+
klass.field names[:cents_field], type: ::Integer, localize: false
|
71
|
+
klass.field names[:currency_field], type: ::String, localize: false
|
72
|
+
|
73
|
+
# getters and setters
|
74
|
+
klass.send( :define_method, name ) { _get_money(names) }
|
75
|
+
klass.send( :define_method, :"#{name}=" ) { |value| _set_money(value, names) }
|
76
|
+
|
77
|
+
klass.send( :define_method, names[:formatted_name_field] ) { _get_formatted_money(names) }
|
78
|
+
klass.send( :define_method, :"#{names[:formatted_name_field]}=" ) { |value| _set_money(value, names) }
|
79
|
+
|
80
|
+
klass.send( :define_method, names[:allow_currency_from_symbol] ) { rule['allow_currency_from_symbol'] }
|
81
|
+
klass.send( :define_method, names[:default_currency] ) { rule['default_currency'] }
|
82
|
+
|
83
|
+
# validations
|
84
|
+
klass.validate { _check_money( names ) } if rule['required']
|
85
|
+
klass.validates_presence_of( names[:cents_field], names[:currency_field] ) if rule['required']
|
86
|
+
klass.validates_numericality_of names[:cents_field], only_integer: true, if: names[:cents_field]
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def money_attribute_get(instance, name)
|
91
|
+
if value = instance.send(:"formatted_#{name}")
|
92
|
+
{ name => value, "formatted_#{name}" => value }
|
93
|
+
else
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def money_attribute_set(instance, name, attributes)
|
99
|
+
return unless attributes.key?(name) || attributes.key?("formatted_#{name}")
|
100
|
+
value = attributes[name] || attributes["formatted_#{name}"]
|
101
|
+
instance.send(:"formatted_#{name}=", value)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
def _set_money_defaults( names )
|
110
|
+
::Money.assume_from_symbol = self.send( names[:allow_currency_from_symbol] )
|
111
|
+
::Money.default_currency = self.send( names[:default_currency] )
|
112
|
+
end
|
113
|
+
|
114
|
+
def _get_money( names )
|
115
|
+
_set_money_defaults( names )
|
116
|
+
::Money.new( self.read_attribute( names[:cents_field] ), self.read_attribute( names[:currency_field] ) || ::Money.default_currency )
|
117
|
+
end
|
118
|
+
|
119
|
+
def _check_money( names )
|
120
|
+
if [nil, ''].include? self.read_attribute.names[:cents_field]
|
121
|
+
raise ArgumentError.new 'Unrecognized amount'
|
122
|
+
end
|
123
|
+
_get_money( names )
|
124
|
+
rescue
|
125
|
+
self.errors.add( names[:name], "#{$!}" )
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def _set_money( _money, names )
|
130
|
+
return if _money.blank?
|
131
|
+
_set_money_defaults( names )
|
132
|
+
money = _money.kind_of?( Money ) ? _money : ::Money.parse( _money )
|
133
|
+
self.write_attribute( names[:cents_field], money.cents )
|
134
|
+
self.write_attribute( names[:currency_field], money.currency.iso_code )
|
135
|
+
rescue
|
136
|
+
self.errors.add( names[:name], "#{$!}" )
|
137
|
+
end
|
138
|
+
|
139
|
+
def _get_formatted_money( names )
|
140
|
+
_get_money( names ).format( symbol: self.send( names[:allow_currency_from_symbol] ), no_cents_if_whole: true ) rescue nil
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|