ae-validates_timeliness 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Appraisals +11 -0
- data/CHANGELOG.rdoc +190 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +178 -0
- data/LICENSE +20 -0
- data/README.md +329 -0
- data/Rakefile +30 -0
- data/ae-validates_timeliness.gemspec +25 -0
- data/autotest/discover.rb +1 -0
- data/gemfiles/rails_4_0.gemfile +15 -0
- data/gemfiles/rails_4_0.gemfile.lock +152 -0
- data/gemfiles/rails_4_1.gemfile +15 -0
- data/gemfiles/rails_4_1.gemfile.lock +156 -0
- data/gemfiles/rails_4_2.gemfile +15 -0
- data/gemfiles/rails_4_2.gemfile.lock +178 -0
- data/init.rb +1 -0
- data/lib/ae-validates_timeliness.rb +1 -0
- data/lib/ae-validates_timeliness/version.rb +3 -0
- data/lib/generators/validates_timeliness/install_generator.rb +16 -0
- data/lib/generators/validates_timeliness/templates/en.yml +16 -0
- data/lib/generators/validates_timeliness/templates/validates_timeliness.rb +40 -0
- data/lib/validates_timeliness.rb +70 -0
- data/lib/validates_timeliness/attribute_methods.rb +97 -0
- data/lib/validates_timeliness/conversion.rb +70 -0
- data/lib/validates_timeliness/extensions.rb +14 -0
- data/lib/validates_timeliness/extensions/date_time_select.rb +61 -0
- data/lib/validates_timeliness/extensions/multiparameter_handler.rb +80 -0
- data/lib/validates_timeliness/helper_methods.rb +23 -0
- data/lib/validates_timeliness/orm/active_record.rb +94 -0
- data/lib/validates_timeliness/orm/mongoid.rb +63 -0
- data/lib/validates_timeliness/railtie.rb +15 -0
- data/lib/validates_timeliness/validator.rb +117 -0
- metadata +159 -0
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'validates_timeliness'
|
@@ -0,0 +1 @@
|
|
1
|
+
require "validates_timeliness"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc "Copy ValidatesTimeliness default files"
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializers
|
8
|
+
copy_file 'validates_timeliness.rb', 'config/initializers/validates_timeliness.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_locale_file
|
12
|
+
copy_file 'en.yml', 'config/locales/validates_timeliness.en.yml'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
en:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
invalid_date: "is not a valid date"
|
5
|
+
invalid_time: "is not a valid time"
|
6
|
+
invalid_datetime: "is not a valid datetime"
|
7
|
+
is_at: "must be at %{restriction}"
|
8
|
+
before: "must be before %{restriction}"
|
9
|
+
on_or_before: "must be on or before %{restriction}"
|
10
|
+
after: "must be after %{restriction}"
|
11
|
+
on_or_after: "must be on or after %{restriction}"
|
12
|
+
validates_timeliness:
|
13
|
+
error_value_formats:
|
14
|
+
date: '%Y-%m-%d'
|
15
|
+
time: '%H:%M:%S'
|
16
|
+
datetime: '%Y-%m-%d %H:%M:%S'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
ValidatesTimeliness.setup do |config|
|
2
|
+
# Extend ORM/ODMs for full support (:active_record, :mongoid).
|
3
|
+
# config.extend_orms = [ :active_record ]
|
4
|
+
#
|
5
|
+
# Default timezone
|
6
|
+
# config.default_timezone = :utc
|
7
|
+
#
|
8
|
+
# Set the dummy date part for a time type values.
|
9
|
+
# config.dummy_date_for_time_type = [ 2000, 1, 1 ]
|
10
|
+
#
|
11
|
+
# Ignore errors when restriction options are evaluated
|
12
|
+
# config.ignore_restriction_errors = false
|
13
|
+
#
|
14
|
+
# Re-display invalid values in date/time selects
|
15
|
+
# config.enable_date_time_select_extension!
|
16
|
+
#
|
17
|
+
# Handle multiparameter date/time values strictly
|
18
|
+
# config.enable_multiparameter_extension!
|
19
|
+
#
|
20
|
+
# Shorthand date and time symbols for restrictions
|
21
|
+
# config.restriction_shorthand_symbols.update(
|
22
|
+
# :now => lambda { Time.current },
|
23
|
+
# :today => lambda { Date.current }
|
24
|
+
# )
|
25
|
+
#
|
26
|
+
# Use the plugin date/time parser which is stricter and extendable
|
27
|
+
# config.use_plugin_parser = false
|
28
|
+
#
|
29
|
+
# Add one or more formats making them valid. e.g. add_formats(:date, 'd(st|rd|th) of mmm, yyyy')
|
30
|
+
# config.parser.add_formats()
|
31
|
+
#
|
32
|
+
# Remove one or more formats making them invalid. e.g. remove_formats(:date, 'dd/mm/yyy')
|
33
|
+
# config.parser.remove_formats()
|
34
|
+
#
|
35
|
+
# Change the amiguous year threshold when parsing a 2 digit year
|
36
|
+
# config.parser.ambiguous_year_threshold = 30
|
37
|
+
#
|
38
|
+
# Treat ambiguous dates, such as 01/02/1950, as a Non-US date.
|
39
|
+
# config.parser.remove_us_formats
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/module'
|
4
|
+
require 'active_support/core_ext/hash/except'
|
5
|
+
require 'active_support/core_ext/string/conversions'
|
6
|
+
require 'active_support/core_ext/date/acts_like'
|
7
|
+
require 'active_support/core_ext/date/conversions'
|
8
|
+
require 'active_support/core_ext/time/acts_like'
|
9
|
+
require 'active_support/core_ext/time/conversions'
|
10
|
+
require 'active_support/core_ext/date_time/acts_like'
|
11
|
+
require 'active_support/core_ext/date_time/conversions'
|
12
|
+
require 'timeliness'
|
13
|
+
|
14
|
+
Timeliness.module_eval do
|
15
|
+
class << self
|
16
|
+
alias :dummy_date_for_time_type :date_for_time_type
|
17
|
+
alias :dummy_date_for_time_type= :date_for_time_type=
|
18
|
+
alias :remove_us_formats :use_euro_formats
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ValidatesTimeliness
|
23
|
+
autoload :VERSION, 'validates_timeliness/version'
|
24
|
+
|
25
|
+
class << self
|
26
|
+
delegate :default_timezone, :default_timezone=, :dummy_date_for_time_type, :dummy_date_for_time_type=, :to => Timeliness
|
27
|
+
|
28
|
+
attr_accessor :extend_orms, :ignore_restriction_errors, :restriction_shorthand_symbols, :use_plugin_parser
|
29
|
+
end
|
30
|
+
|
31
|
+
# Extend ORM/ODMs for full support (:active_record, :mongoid).
|
32
|
+
self.extend_orms = []
|
33
|
+
|
34
|
+
# Ignore errors when restriction options are evaluated
|
35
|
+
self.ignore_restriction_errors = false
|
36
|
+
|
37
|
+
# Shorthand time and date symbols for restrictions
|
38
|
+
self.restriction_shorthand_symbols = {
|
39
|
+
:now => lambda { Time.current },
|
40
|
+
:today => lambda { Date.current }
|
41
|
+
}
|
42
|
+
|
43
|
+
# Use the plugin date/time parser which is stricter and extensible
|
44
|
+
self.use_plugin_parser = false
|
45
|
+
|
46
|
+
# Default timezone
|
47
|
+
self.default_timezone = :utc
|
48
|
+
|
49
|
+
# Set the dummy date part for a time type values.
|
50
|
+
self.dummy_date_for_time_type = [ 2000, 1, 1 ]
|
51
|
+
|
52
|
+
# Setup method for plugin configuration
|
53
|
+
def self.setup
|
54
|
+
yield self
|
55
|
+
load_orms
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.load_orms
|
59
|
+
extend_orms.each {|orm| require "validates_timeliness/orm/#{orm}" }
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.parser; Timeliness end
|
63
|
+
end
|
64
|
+
|
65
|
+
require 'validates_timeliness/conversion'
|
66
|
+
require 'validates_timeliness/validator'
|
67
|
+
require 'validates_timeliness/helper_methods'
|
68
|
+
require 'validates_timeliness/attribute_methods'
|
69
|
+
require 'validates_timeliness/extensions'
|
70
|
+
require 'validates_timeliness/railtie' if defined?(Rails)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module AttributeMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class << self
|
7
|
+
attr_accessor :timeliness_validated_attributes
|
8
|
+
end
|
9
|
+
self.timeliness_validated_attributes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
public
|
15
|
+
# Override in ORM shim
|
16
|
+
def timeliness_attribute_timezone_aware?(attr_name)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
# Override in ORM shim
|
21
|
+
def timeliness_attribute_type(attr_name)
|
22
|
+
:datetime
|
23
|
+
end
|
24
|
+
|
25
|
+
def undefine_attribute_methods
|
26
|
+
super
|
27
|
+
undefine_timeliness_attribute_methods
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def define_timeliness_methods(before_type_cast=false)
|
33
|
+
return if timeliness_validated_attributes.blank?
|
34
|
+
timeliness_validated_attributes.each do |attr_name|
|
35
|
+
define_attribute_timeliness_methods(attr_name, before_type_cast)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_attribute_timeliness_methods(attr_name, before_type_cast=false)
|
40
|
+
define_timeliness_write_method(attr_name)
|
41
|
+
define_timeliness_before_type_cast_method(attr_name) if before_type_cast
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_timeliness_write_method(attr_name)
|
45
|
+
method_body, line = <<-EOV, __LINE__ + 1
|
46
|
+
def #{attr_name}=(value)
|
47
|
+
original_value = value
|
48
|
+
@timeliness_cache ||= {}
|
49
|
+
@timeliness_cache["#{attr_name}"] = original_value
|
50
|
+
#{ "if value.is_a?(String)\n#{timeliness_type_cast_code(attr_name, 'value')}\nend" if ValidatesTimeliness.use_plugin_parser }
|
51
|
+
|
52
|
+
super(value)
|
53
|
+
end
|
54
|
+
EOV
|
55
|
+
generated_timeliness_methods.module_eval(method_body, __FILE__, line)
|
56
|
+
end
|
57
|
+
|
58
|
+
def define_timeliness_before_type_cast_method(attr_name)
|
59
|
+
method_body, line = <<-EOV, __LINE__ + 1
|
60
|
+
def #{attr_name}_before_type_cast
|
61
|
+
_timeliness_raw_value_for('#{attr_name}') || begin
|
62
|
+
a = @attributes['#{attr_name}']
|
63
|
+
a.respond_to?(:value_before_type_cast) ? a.value_before_type_cast : a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
EOV
|
67
|
+
generated_timeliness_methods.module_eval(method_body, __FILE__, line)
|
68
|
+
end
|
69
|
+
|
70
|
+
def timeliness_type_cast_code(attr_name, var_name)
|
71
|
+
type = timeliness_attribute_type(attr_name)
|
72
|
+
timezone_aware = timeliness_attribute_timezone_aware?(attr_name)
|
73
|
+
timezone = :current if timezone_aware
|
74
|
+
|
75
|
+
"#{var_name} = Timeliness::Parser.parse(#{var_name}, :#{type}, :zone => #{timezone.inspect})"
|
76
|
+
end
|
77
|
+
|
78
|
+
def generated_timeliness_methods
|
79
|
+
@generated_timeliness_methods ||= Module.new.tap { |m| include(m) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def undefine_timeliness_attribute_methods
|
83
|
+
generated_timeliness_methods.module_eval do
|
84
|
+
instance_methods.each { |m| undef_method(m) }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _timeliness_raw_value_for(attr_name)
|
90
|
+
@timeliness_cache && @timeliness_cache[attr_name]
|
91
|
+
end
|
92
|
+
|
93
|
+
def _clear_timeliness_cache
|
94
|
+
@timeliness_cache = {}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Conversion
|
3
|
+
|
4
|
+
def type_cast_value(value, type)
|
5
|
+
return nil if value.nil? || !value.respond_to?(:to_time)
|
6
|
+
|
7
|
+
value = value.in_time_zone if value.acts_like?(:time) && @timezone_aware
|
8
|
+
value = case type
|
9
|
+
when :time
|
10
|
+
dummy_time(value)
|
11
|
+
when :date
|
12
|
+
value.to_date
|
13
|
+
when :datetime
|
14
|
+
value.is_a?(Time) ? value : value.to_time
|
15
|
+
end
|
16
|
+
if options[:ignore_usec] && value.is_a?(Time)
|
17
|
+
Timeliness::Parser.make_time(Array(value).reverse[4..9], (:current if @timezone_aware))
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def dummy_time(value)
|
24
|
+
time = if value.acts_like?(:time)
|
25
|
+
value = value.in_time_zone if @timezone_aware
|
26
|
+
[value.hour, value.min, value.sec]
|
27
|
+
else
|
28
|
+
[0,0,0]
|
29
|
+
end
|
30
|
+
values = ValidatesTimeliness.dummy_date_for_time_type + time
|
31
|
+
Timeliness::Parser.make_time(values, (:current if @timezone_aware))
|
32
|
+
end
|
33
|
+
|
34
|
+
def evaluate_option_value(value, record)
|
35
|
+
case value
|
36
|
+
when Time, Date
|
37
|
+
value
|
38
|
+
when String
|
39
|
+
parse(value)
|
40
|
+
when Symbol
|
41
|
+
if !record.respond_to?(value) && restriction_shorthand?(value)
|
42
|
+
ValidatesTimeliness.restriction_shorthand_symbols[value].call
|
43
|
+
else
|
44
|
+
evaluate_option_value(record.send(value), record)
|
45
|
+
end
|
46
|
+
when Proc
|
47
|
+
result = value.arity > 0 ? value.call(record) : value.call
|
48
|
+
evaluate_option_value(result, record)
|
49
|
+
else
|
50
|
+
value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def restriction_shorthand?(symbol)
|
55
|
+
ValidatesTimeliness.restriction_shorthand_symbols.keys.include?(symbol)
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse(value)
|
59
|
+
return nil if value.nil?
|
60
|
+
if ValidatesTimeliness.use_plugin_parser
|
61
|
+
Timeliness::Parser.parse(value, @type, :zone => (:current if @timezone_aware), :format => options[:format], :strict => false)
|
62
|
+
else
|
63
|
+
@timezone_aware ? Time.zone.parse(value, Time.zone.now) : value.to_time(ValidatesTimeliness.default_timezone)
|
64
|
+
end
|
65
|
+
rescue ArgumentError, TypeError
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Extensions
|
3
|
+
autoload :DateTimeSelect, 'validates_timeliness/extensions/date_time_select'
|
4
|
+
autoload :MultiparameterHandler, 'validates_timeliness/extensions/multiparameter_handler'
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.enable_date_time_select_extension!
|
8
|
+
::ActionView::Helpers::InstanceTag.send(:include, ValidatesTimeliness::Extensions::DateTimeSelect)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.enable_multiparameter_extension!
|
12
|
+
::ActiveRecord::Base.send(:include, ValidatesTimeliness::Extensions::MultiparameterHandler)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Extensions
|
3
|
+
module DateTimeSelect
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Intercepts the date and time select helpers to reuse the values from
|
7
|
+
# the params rather than the parsed value. This allows invalid date/time
|
8
|
+
# values to be redisplayed instead of blanks to aid correction by the user.
|
9
|
+
# It's a minor usability improvement which is rarely an issue for the user.
|
10
|
+
|
11
|
+
included do
|
12
|
+
alias_method_chain :datetime_selector, :timeliness
|
13
|
+
alias_method_chain :value, :timeliness
|
14
|
+
end
|
15
|
+
|
16
|
+
class TimelinessDateTime
|
17
|
+
attr_accessor :year, :month, :day, :hour, :min, :sec
|
18
|
+
|
19
|
+
def initialize(year, month, day, hour, min, sec)
|
20
|
+
@year, @month, @day, @hour, @min, @sec = year, month, day, hour, min, sec
|
21
|
+
end
|
22
|
+
|
23
|
+
# adapted from activesupport/lib/active_support/core_ext/date_time/calculations.rb, line 36 (3.0.7)
|
24
|
+
def change(options)
|
25
|
+
TimelinessDateTime.new(
|
26
|
+
options[:year] || year,
|
27
|
+
options[:month] || month,
|
28
|
+
options[:day] || day,
|
29
|
+
options[:hour] || hour,
|
30
|
+
options[:min] || (options[:hour] ? 0 : min),
|
31
|
+
options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def datetime_selector_with_timeliness(*args)
|
37
|
+
@timeliness_date_or_time_tag = true
|
38
|
+
datetime_selector_without_timeliness(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def value_with_timeliness(object)
|
42
|
+
unless @timeliness_date_or_time_tag && @template_object.params[@object_name]
|
43
|
+
return value_without_timeliness(object)
|
44
|
+
end
|
45
|
+
|
46
|
+
@template_object.params[@object_name]
|
47
|
+
|
48
|
+
pairs = @template_object.params[@object_name].select {|k,v| k =~ /^#{@method_name}\(/ }
|
49
|
+
return value_without_timeliness(object) if pairs.empty?
|
50
|
+
|
51
|
+
values = [nil] * 6
|
52
|
+
pairs.map do |(param, value)|
|
53
|
+
position = param.scan(/\((\d+)\w+\)/).first.first
|
54
|
+
values[position.to_i-1] = value.to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
TimelinessDateTime.new(*values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Extensions
|
3
|
+
module MultiparameterHandler
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Stricter handling of date and time values from multiparameter
|
7
|
+
# assignment from the date/time select view helpers
|
8
|
+
|
9
|
+
included do
|
10
|
+
alias_method_chain :instantiate_time_object, :timeliness
|
11
|
+
alias_method :execute_callstack_for_multiparameter_attributes, :execute_callstack_for_multiparameter_attributes_with_timeliness
|
12
|
+
alias_method :read_value_from_parameter, :read_value_from_parameter_with_timeliness
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def invalid_multiparameter_date_or_time_as_string(values)
|
18
|
+
value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-")
|
19
|
+
value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty?
|
20
|
+
value
|
21
|
+
end
|
22
|
+
|
23
|
+
def instantiate_time_object_with_timeliness(name, values)
|
24
|
+
validate_multiparameter_date_values(values) {
|
25
|
+
instantiate_time_object_without_timeliness(name, values)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def instantiate_date_object(name, values)
|
30
|
+
validate_multiparameter_date_values(values) {
|
31
|
+
Date.new(*values)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Yield if date values are valid
|
36
|
+
def validate_multiparameter_date_values(values)
|
37
|
+
if values[0..2].all?{ |v| v.present? } && Date.valid_civil?(*values[0..2])
|
38
|
+
yield
|
39
|
+
else
|
40
|
+
invalid_multiparameter_date_or_time_as_string(values)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_value_from_parameter_with_timeliness(name, values_from_param)
|
45
|
+
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
46
|
+
values = values_from_param.is_a?(Hash) ? values_from_param.to_a.sort_by(&:first).map(&:last) : values_from_param
|
47
|
+
|
48
|
+
if values.empty? || values.all?{ |v| v.nil? }
|
49
|
+
nil
|
50
|
+
elsif klass == Time
|
51
|
+
instantiate_time_object(name, values)
|
52
|
+
elsif klass == Date
|
53
|
+
instantiate_date_object(name, values)
|
54
|
+
else
|
55
|
+
if respond_to?(:read_other_parameter_value)
|
56
|
+
read_date_parameter_value(name, values_from_param)
|
57
|
+
else
|
58
|
+
klass.new(*values)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
|
64
|
+
errors = []
|
65
|
+
callstack.each do |name, values_with_empty_parameters|
|
66
|
+
begin
|
67
|
+
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
|
68
|
+
rescue => ex
|
69
|
+
values = values_with_empty_parameters.is_a?(Hash) ? values_with_empty_parameters.values : values_with_empty_parameters
|
70
|
+
errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
unless errors.empty?
|
74
|
+
raise ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|