validates_timeliness 3.0.0.beta.3 → 3.0.0.beta.4
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/README.rdoc +46 -18
- data/lib/generators/validates_timeliness/templates/validates_timeliness.rb +13 -1
- data/lib/validates_timeliness/attribute_methods.rb +29 -15
- data/lib/validates_timeliness/extensions/date_time_select.rb +2 -2
- data/lib/validates_timeliness/helper_methods.rb +10 -21
- data/lib/validates_timeliness/orm/active_record.rb +33 -10
- data/lib/validates_timeliness/orm/mongoid.rb +36 -7
- data/lib/validates_timeliness/parser.rb +0 -8
- data/lib/validates_timeliness/validator.rb +20 -3
- data/lib/validates_timeliness/version.rb +1 -1
- data/lib/validates_timeliness.rb +5 -1
- data/spec/spec_helper.rb +15 -13
- data/spec/test_model.rb +22 -21
- data/spec/validates_timeliness/attribute_methods_spec.rb +21 -4
- data/spec/validates_timeliness/helper_methods_spec.rb +9 -24
- data/spec/validates_timeliness/orm/active_record_spec.rb +29 -4
- data/spec/validates_timeliness/orm/mongoid_spec.rb +35 -12
- data/spec/validates_timeliness/parser_spec.rb +0 -16
- data/spec/validates_timeliness/validator_spec.rb +19 -1
- data/validates_timeliness.gemspec +2 -2
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -5,23 +5,23 @@
|
|
5
5
|
|
6
6
|
== Description
|
7
7
|
|
8
|
-
|
8
|
+
Complete validation of dates, times and datetimes for Rails 3.x and ActiveModel.
|
9
9
|
|
10
10
|
If you a looking for the old version for Rails 2.x go here[http://github.com/adzap/validates_timeliness/tree/v2.3].
|
11
11
|
|
12
12
|
|
13
13
|
== Features
|
14
14
|
|
15
|
-
* Adds
|
15
|
+
* Adds validation for dates, times and datetimes to ActiveModel
|
16
16
|
|
17
|
-
*
|
17
|
+
* Adds extensions to fix Rails date/time select issues
|
18
|
+
|
19
|
+
* Includes extensible date/time parser
|
18
20
|
|
19
21
|
* Supports timezone handling
|
20
22
|
|
21
23
|
* Supports I18n for the error messages
|
22
24
|
|
23
|
-
* Adds before_type_cast method on validated attributes, if ORM supports it.
|
24
|
-
|
25
25
|
|
26
26
|
== Installation
|
27
27
|
|
@@ -41,24 +41,29 @@ Then run
|
|
41
41
|
|
42
42
|
$ rails generate validates_timeliness:install
|
43
43
|
|
44
|
-
This creates configuration initializer and locale files. In the initializer, you there are a number of config
|
44
|
+
This creates configuration initializer and locale files. In the initializer, you there are a number of config
|
45
|
+
options to customize the plugin.
|
45
46
|
|
46
|
-
ValidatesTimeliness.setup do |config|
|
47
47
|
|
48
|
-
|
49
|
-
# config.extend_orms = [ :active_record ]
|
48
|
+
== Examples
|
50
49
|
|
51
|
-
|
50
|
+
validates_datetime :occurred_at
|
51
|
+
|
52
|
+
validates_date :date_of_birth :before => lambda { 18.years.ago },
|
53
|
+
:before_message => "must be at least 18 years old"
|
52
54
|
|
53
|
-
|
54
|
-
another ORM then look at the {wiki page}[http://github.com/adzap/validates_timeliness/wiki/ORM-Support] for more information.
|
55
|
+
validates_datetime :finish_time, :after => :start_time # Method symbol
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
successfully, please send me a pull request to add the shim to the plugin or let me know where to find it.
|
57
|
+
validates_date :booked_at, :on => :create, :on_or_after => :today # See Restriction Shorthand.
|
58
|
+
validates_time :booked_at, :between => ['9.00am', '5:00pm']
|
59
59
|
|
60
|
+
validates_time :breakfast_time, :on_or_after => '6:00am',
|
61
|
+
:on_or_after_message => 'must be after opening time',
|
62
|
+
:before => :lunchtime,
|
63
|
+
:before_message => 'must be before lunch time'
|
60
64
|
|
61
|
-
|
65
|
+
|
66
|
+
== Usage
|
62
67
|
|
63
68
|
To validate a model with a date, time or datetime attribute you just use the
|
64
69
|
validation method
|
@@ -90,13 +95,13 @@ Temporal options (or restrictions):
|
|
90
95
|
:after - Attribute must be after this value to be valid
|
91
96
|
:on_or_after - Attribute must be equal to or after this value to be valid
|
92
97
|
:between - Attribute must be between the values to be valid. Range or Array of 2 values.
|
93
|
-
Uses :on_or_after and :on_of_before for error messages on lower and upper bounds respectively.
|
94
98
|
|
95
99
|
Regular validation options:
|
96
100
|
:allow_nil - Allow a nil value to be valid
|
97
101
|
:allow_blank - Allows a nil or empty string value to be valid
|
98
102
|
:if - Execute validation when :if evaluates true
|
99
103
|
:unless - Execute validation when :unless evaluates false
|
104
|
+
:on - Specify validation context e.g :save, :create or :update. Default is :save.
|
100
105
|
|
101
106
|
Special options:
|
102
107
|
:ignore_usec - Ignores microsecond value on datetime restrictions
|
@@ -116,6 +121,29 @@ values are compared as dates.
|
|
116
121
|
|
117
122
|
== Configuration
|
118
123
|
|
124
|
+
=== ORM/ODM Support
|
125
|
+
|
126
|
+
The plugin adds date/time validation to ActiveModel for any ORM/ODM that supports the ActiveModel validations component.
|
127
|
+
However, there is an issue with most ORM/ODMs which does not allow 100% date/time validation by default. Specifically, when you
|
128
|
+
assign an invalid date/time value to an attribute, most ORM/ODMs will only store a nil value for the attribute. This causes an
|
129
|
+
issue for date/time validation, since we need to know that a value was assigned but was invalid. To fix this, we need to cache
|
130
|
+
the original invalid value to know that the attribute is not just nil.
|
131
|
+
|
132
|
+
Each ORM/ODM requires a specific shim to fix it. The plugin includes a shim for ActiveRecord and Mongoid. You can activate them
|
133
|
+
like so
|
134
|
+
|
135
|
+
ValidatesTimeliness.setup do |config|
|
136
|
+
|
137
|
+
# Extend ORM/ODMs for full support (:active_record, :mongoid).
|
138
|
+
config.extend_orms = [ :mongoid ]
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
By default the plugin extends ActiveRecord if loaded. If you wish to extend another ORM then look at the {wiki page}[http://github.com/adzap/validates_timeliness/wiki/ORM-Support] for more information.
|
143
|
+
|
144
|
+
It is not required that you use a shim, but you will not catch errors when the attribute value is invalid and evaluated to nil.
|
145
|
+
|
146
|
+
|
119
147
|
=== Error Messages
|
120
148
|
|
121
149
|
Using the I18n system to define new defaults:
|
@@ -164,7 +192,7 @@ By default the parser is switched off. To switch it on:
|
|
164
192
|
See the wiki[http://github.com/adzap/validates_timeliness/wiki/Plugin-Parser] for all the details about the parser.
|
165
193
|
|
166
194
|
|
167
|
-
=== Restriction
|
195
|
+
=== Restriction Shorthand
|
168
196
|
|
169
197
|
It is common to restrict an attribute to being on or before the current time or current day.
|
170
198
|
To specify this you need to use a lambda as an option value e.g. <tt>lambda { Time.now }</tt>.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
ValidatesTimeliness.setup do |config|
|
2
|
-
#
|
2
|
+
# Extend ORM/ODMs for full support (:active_record, :mongoid).
|
3
3
|
# config.extend_orms = [ :active_record ]
|
4
4
|
#
|
5
5
|
# User the plugin date/time parser which is stricter and extendable
|
@@ -22,4 +22,16 @@ ValidatesTimeliness.setup do |config|
|
|
22
22
|
# :now => lambda { Time.now },
|
23
23
|
# :today => lambda { Date.today }
|
24
24
|
# )
|
25
|
+
#
|
26
|
+
# Add one or more formats making them valid. e.g. add_formats(:date, 'd(st|rd|th) of mmm, yyyy')
|
27
|
+
# config.parser.add_formats()
|
28
|
+
#
|
29
|
+
# Remove one or more formats making them invalid. e.g. remove_formats(:date, 'dd/mm/yyy')
|
30
|
+
# config.parser.remove_formats()
|
31
|
+
#
|
32
|
+
# Change the amiguous year threshold when parsing a 2 digit year
|
33
|
+
# config.parser.ambiguous_year_threshold = 30
|
34
|
+
#
|
35
|
+
# Treat ambiguous dates, such as 01/02/1950, as a Non-US date.
|
36
|
+
# config.parser.remove_us_formats
|
25
37
|
end
|
@@ -2,23 +2,42 @@ module ValidatesTimeliness
|
|
2
2
|
module AttributeMethods
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
included do
|
6
|
+
class_inheritable_accessor :timeliness_validated_attributes
|
7
|
+
self.timeliness_validated_attributes = []
|
8
|
+
end
|
9
|
+
|
5
10
|
module ClassMethods
|
6
11
|
|
12
|
+
public
|
13
|
+
# Override in ORM shim
|
14
|
+
def timeliness_attribute_timezone_aware?(attr_name)
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Override in ORM shim
|
19
|
+
def timeliness_attribute_type(attr_name)
|
20
|
+
:datetime
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
7
25
|
def define_timeliness_methods(before_type_cast=false)
|
8
26
|
return if timeliness_validated_attributes.blank?
|
9
|
-
timeliness_validated_attributes.each do |attr_name
|
10
|
-
define_timeliness_write_method(attr_name
|
27
|
+
timeliness_validated_attributes.each do |attr_name|
|
28
|
+
define_timeliness_write_method(attr_name)
|
11
29
|
define_timeliness_before_type_cast_method(attr_name) if before_type_cast
|
12
30
|
end
|
13
31
|
end
|
14
32
|
|
15
|
-
|
33
|
+
def define_timeliness_write_method(attr_name)
|
34
|
+
type = timeliness_attribute_type(attr_name)
|
35
|
+
timezone_aware = timeliness_attribute_timezone_aware?(attr_name)
|
16
36
|
|
17
|
-
def define_timeliness_write_method(attr_name, type, timezone_aware)
|
18
37
|
method_body, line = <<-EOV, __LINE__ + 1
|
19
38
|
def #{attr_name}=(value)
|
20
|
-
@
|
21
|
-
@
|
39
|
+
@timeliness_cache ||= {}
|
40
|
+
@timeliness_cache["#{attr_name}"] = value
|
22
41
|
#{ "value = ValidatesTimeliness::Parser.parse(value, :#{type}, :timezone_aware => #{timezone_aware}) if value.is_a?(String)" if ValidatesTimeliness.use_plugin_parser }
|
23
42
|
super
|
24
43
|
end
|
@@ -34,21 +53,16 @@ module ValidatesTimeliness
|
|
34
53
|
EOV
|
35
54
|
class_eval(method_body, __FILE__, line)
|
36
55
|
end
|
37
|
-
|
38
|
-
public
|
39
|
-
|
40
|
-
def timeliness_attribute_timezone_aware?(attr_name)
|
41
|
-
false
|
42
|
-
end
|
43
|
-
|
44
56
|
end
|
45
57
|
|
46
58
|
module InstanceMethods
|
47
|
-
|
48
59
|
def _timeliness_raw_value_for(attr_name)
|
49
|
-
@
|
60
|
+
@timeliness_cache && @timeliness_cache[attr_name.to_s]
|
50
61
|
end
|
51
62
|
|
63
|
+
def _clear_timeliness_cache
|
64
|
+
@timeliness_cache = {}
|
65
|
+
end
|
52
66
|
end
|
53
67
|
|
54
68
|
end
|
@@ -3,10 +3,10 @@ module ValidatesTimeliness
|
|
3
3
|
module DateTimeSelect
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
# Intercepts the date and time select helpers to reuse the values from
|
6
|
+
# Intercepts the date and time select helpers to reuse the values from
|
7
7
|
# the params rather than the parsed value. This allows invalid date/time
|
8
8
|
# values to be redisplayed instead of blanks to aid correction by the user.
|
9
|
-
#
|
9
|
+
# It's a minor usability improvement which is rarely an issue for the user.
|
10
10
|
|
11
11
|
included do
|
12
12
|
alias_method_chain :datetime_selector, :timeliness
|
@@ -1,15 +1,7 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
extend ActiveSupport::Concern
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
4
3
|
|
5
|
-
|
6
|
-
include ValidationMethods
|
7
|
-
extend ValidationMethods
|
8
|
-
class_inheritable_hash :timeliness_validated_attributes
|
9
|
-
self.timeliness_validated_attributes = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
module ValidationMethods
|
4
|
+
module HelperMethods
|
13
5
|
def validates_date(*attr_names)
|
14
6
|
timeliness_validation_for attr_names, :date
|
15
7
|
end
|
@@ -23,17 +15,14 @@ module ValidatesTimeliness
|
|
23
15
|
end
|
24
16
|
|
25
17
|
def timeliness_validation_for(attr_names, type)
|
26
|
-
options = _merge_attributes(attr_names)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
}
|
33
|
-
self.timeliness_validated_attributes = attributes
|
34
|
-
validates_with Validator, options
|
18
|
+
options = _merge_attributes(attr_names).merge(:type => type)
|
19
|
+
if respond_to?(:timeliness_validated_attributes)
|
20
|
+
self.timeliness_validated_attributes ||= []
|
21
|
+
self.timeliness_validated_attributes += (attr_names - self.timeliness_validated_attributes)
|
22
|
+
end
|
23
|
+
validates_with ValidatesTimeliness::Validator, options
|
35
24
|
end
|
36
|
-
|
37
25
|
end
|
26
|
+
|
38
27
|
end
|
39
28
|
end
|
@@ -1,14 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module ORM
|
3
|
+
module ActiveRecord
|
4
|
+
extend ActiveSupport::Concern
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
module ClassMethods
|
7
|
+
def define_attribute_methods
|
8
|
+
super
|
9
|
+
# Define write method and before_type_cast method
|
10
|
+
define_timeliness_methods(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
def timeliness_attribute_timezone_aware?(attr_name)
|
14
|
+
attr_name = attr_name.to_s
|
15
|
+
create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
|
16
|
+
end
|
17
|
+
|
18
|
+
def timeliness_attribute_type(attr_name)
|
19
|
+
columns_hash[attr_name.to_s].type
|
20
|
+
end
|
21
|
+
end
|
10
22
|
|
11
|
-
|
12
|
-
|
23
|
+
module InstanceMethods
|
24
|
+
def reload(*args)
|
25
|
+
_clear_timeliness_cache
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
13
31
|
end
|
14
32
|
end
|
33
|
+
|
34
|
+
class ActiveRecord::Base
|
35
|
+
include ValidatesTimeliness::AttributeMethods
|
36
|
+
include ValidatesTimeliness::ORM::ActiveRecord
|
37
|
+
end
|
@@ -7,29 +7,58 @@ module ValidatesTimeliness
|
|
7
7
|
# field value in Mongoid. Parser will return nil rather than error.
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
+
# Mongoid has no bulk attribute method definition hook. It defines
|
11
|
+
# them with each field definition. So we likewise define them after
|
12
|
+
# each validation is defined.
|
13
|
+
#
|
10
14
|
def timeliness_validation_for(attr_names, type)
|
11
15
|
super
|
12
|
-
attr_names.each { |attr_name| define_timeliness_write_method(attr_name
|
16
|
+
attr_names.each { |attr_name| define_timeliness_write_method(attr_name) }
|
13
17
|
end
|
14
18
|
|
15
|
-
def define_timeliness_write_method(attr_name
|
19
|
+
def define_timeliness_write_method(attr_name)
|
20
|
+
type = timeliness_attribute_type(attr_name)
|
16
21
|
method_body, line = <<-EOV, __LINE__ + 1
|
17
22
|
def #{attr_name}=(value)
|
18
|
-
@
|
19
|
-
@
|
23
|
+
@timeliness_cache ||= {}
|
24
|
+
@timeliness_cache["#{attr_name}"] = value
|
20
25
|
#{ "value = ValidatesTimeliness::Parser.parse(value, :#{type}) if value.is_a?(String)" if ValidatesTimeliness.use_plugin_parser }
|
21
26
|
write_attribute(:#{attr_name}, value)
|
22
27
|
end
|
23
28
|
EOV
|
24
29
|
class_eval(method_body, __FILE__, line)
|
25
30
|
end
|
31
|
+
|
32
|
+
def timeliness_attribute_type(attr_name)
|
33
|
+
{
|
34
|
+
Date => :date,
|
35
|
+
Time => :datetime,
|
36
|
+
DateTime => :datetime
|
37
|
+
}[fields[attr_name.to_s].type] || :datetime
|
38
|
+
end
|
26
39
|
end
|
40
|
+
|
27
41
|
end
|
28
42
|
end
|
29
43
|
end
|
30
44
|
|
31
45
|
module Mongoid::Document
|
32
|
-
|
33
|
-
|
34
|
-
|
46
|
+
# Due to how Mongoid misuses ActiveSupport::Concern,
|
47
|
+
# the only way to override a core component method is
|
48
|
+
# using an append_features hook.
|
49
|
+
#
|
50
|
+
module TimelinessConcern
|
51
|
+
def append_features(base)
|
52
|
+
super
|
53
|
+
base.send :include, ValidatesTimeliness::AttributeMethods
|
54
|
+
base.send :include, ValidatesTimeliness::ORM::Mongoid
|
55
|
+
end
|
56
|
+
end
|
57
|
+
extend TimelinessConcern
|
58
|
+
|
59
|
+
def reload_with_timeliness
|
60
|
+
_clear_timeliness_cache
|
61
|
+
reload_without_timeliness
|
62
|
+
end
|
63
|
+
alias_method_chain :reload, :timeliness
|
35
64
|
end
|
@@ -23,14 +23,6 @@ module ValidatesTimeliness
|
|
23
23
|
cattr_accessor :ambiguous_year_threshold
|
24
24
|
self.ambiguous_year_threshold = 30
|
25
25
|
|
26
|
-
# Set the dummy date part for a time type value. Should be an array of 3 values
|
27
|
-
# being year, month and day in that order.
|
28
|
-
#
|
29
|
-
# Default: [ 2000, 1, 1 ] same as ActiveRecord
|
30
|
-
#
|
31
|
-
cattr_accessor :dummy_date_for_time_type
|
32
|
-
self.dummy_date_for_time_type = [ 2000, 1, 1 ]
|
33
|
-
|
34
26
|
# Format tokens:
|
35
27
|
# y = year
|
36
28
|
# m = month
|
@@ -14,6 +14,12 @@ module ValidatesTimeliness
|
|
14
14
|
:on_or_after => :>=,
|
15
15
|
}.freeze
|
16
16
|
|
17
|
+
DEFAULT_ERROR_VALUE_FORMATS = {
|
18
|
+
:date => '%Y-%m-%d',
|
19
|
+
:time => '%H:%M:%S',
|
20
|
+
:datetime => '%Y-%m-%d %H:%M:%S'
|
21
|
+
}.freeze
|
22
|
+
|
17
23
|
def self.kind
|
18
24
|
:timeliness
|
19
25
|
end
|
@@ -31,10 +37,10 @@ module ValidatesTimeliness
|
|
31
37
|
end
|
32
38
|
|
33
39
|
def validate_each(record, attr_name, value)
|
34
|
-
raw_value = record
|
40
|
+
raw_value = attribute_raw_value(record, attr_name) || value
|
35
41
|
return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?)
|
36
42
|
|
37
|
-
@timezone_aware =
|
43
|
+
@timezone_aware = timezone_aware?(record, attr_name)
|
38
44
|
value = parse(raw_value) if value.is_a?(String) || options[:format]
|
39
45
|
value = type_cast_value(value, @type)
|
40
46
|
|
@@ -56,10 +62,21 @@ module ValidatesTimeliness
|
|
56
62
|
end
|
57
63
|
|
58
64
|
def format_error_value(value)
|
59
|
-
format = I18n.t(@type, :scope => 'validates_timeliness.error_value_formats')
|
65
|
+
format = I18n.t(@type, :default => DEFAULT_ERROR_VALUE_FORMATS[@type], :scope => 'validates_timeliness.error_value_formats')
|
60
66
|
value.strftime(format)
|
61
67
|
end
|
62
68
|
|
69
|
+
def attribute_raw_value(record, attr_name)
|
70
|
+
if record.respond_to?(:_timeliness_raw_value_for)
|
71
|
+
record._timeliness_raw_value_for(attr_name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def timezone_aware?(record, attr_name)
|
76
|
+
record.class.respond_to?(:timeliness_attribute_timezone_aware?) &&
|
77
|
+
record.class.timeliness_attribute_timezone_aware?(attr_name)
|
78
|
+
end
|
79
|
+
|
63
80
|
end
|
64
81
|
end
|
65
82
|
|
data/lib/validates_timeliness.rb
CHANGED
@@ -12,7 +12,7 @@ module ValidatesTimeliness
|
|
12
12
|
autoload :Parser, 'validates_timeliness/parser'
|
13
13
|
autoload :VERSION, 'validates_timeliness/version'
|
14
14
|
|
15
|
-
#
|
15
|
+
# Extend ORM/ODMs for full support (:active_record, :mongoid).
|
16
16
|
mattr_accessor :extend_orms
|
17
17
|
@@extend_orms = [ defined?(ActiveRecord) && :active_record ].compact
|
18
18
|
|
@@ -39,6 +39,10 @@ module ValidatesTimeliness
|
|
39
39
|
:today => lambda { Date.today }
|
40
40
|
}
|
41
41
|
|
42
|
+
def self.parser
|
43
|
+
Parser
|
44
|
+
end
|
45
|
+
|
42
46
|
# Setup method for plugin configuration
|
43
47
|
def self.setup
|
44
48
|
yield self
|
data/spec/spec_helper.rb
CHANGED
@@ -24,15 +24,11 @@ LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/../lib/generators/vali
|
|
24
24
|
I18n.load_path.unshift(LOCALE_PATH)
|
25
25
|
|
26
26
|
# Extend TestModel as you would another ORM/ODM module
|
27
|
-
module
|
28
|
-
|
27
|
+
module TestModelShim
|
28
|
+
extend ActiveSupport::Concern
|
29
29
|
include ValidatesTimeliness::AttributeMethods
|
30
30
|
|
31
|
-
|
32
|
-
base.extend HookMethods
|
33
|
-
end
|
34
|
-
|
35
|
-
module HookMethods
|
31
|
+
module ClassMethods
|
36
32
|
# Hook method for attribute method generation
|
37
33
|
def define_attribute_methods(attr_names)
|
38
34
|
super
|
@@ -48,11 +44,17 @@ end
|
|
48
44
|
|
49
45
|
class Person
|
50
46
|
include TestModel
|
51
|
-
|
47
|
+
attribute :birth_date, :date
|
48
|
+
attribute :birth_time, :time
|
49
|
+
attribute :birth_datetime, :datetime
|
52
50
|
validates_date :birth_date
|
53
51
|
validates_time :birth_time
|
54
52
|
validates_datetime :birth_datetime
|
55
|
-
define_attribute_methods model_attributes
|
53
|
+
define_attribute_methods model_attributes.keys
|
54
|
+
end
|
55
|
+
|
56
|
+
class PersonWithShim < Person
|
57
|
+
include TestModelShim
|
56
58
|
end
|
57
59
|
|
58
60
|
ActiveRecord::Base.time_zone_aware_attributes = true
|
@@ -62,8 +64,8 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
62
64
|
create_table :employees, :force => true do |t|
|
63
65
|
t.string :first_name
|
64
66
|
t.string :last_name
|
65
|
-
t.
|
66
|
-
t.
|
67
|
+
t.date :birth_date
|
68
|
+
t.time :birth_time
|
67
69
|
t.datetime :birth_datetime
|
68
70
|
end
|
69
71
|
end
|
@@ -80,10 +82,10 @@ Rspec.configure do |c|
|
|
80
82
|
c.include(RspecTagMatchers)
|
81
83
|
c.before do
|
82
84
|
Person.reset_callbacks(:validate)
|
83
|
-
|
85
|
+
PersonWithShim.timeliness_validated_attributes = []
|
84
86
|
Person._validators.clear
|
85
87
|
Employee.reset_callbacks(:validate)
|
86
|
-
Employee.timeliness_validated_attributes =
|
88
|
+
Employee.timeliness_validated_attributes = []
|
87
89
|
Employee._validators.clear
|
88
90
|
end
|
89
91
|
end
|
data/spec/test_model.rb
CHANGED
@@ -1,41 +1,32 @@
|
|
1
1
|
module TestModel
|
2
|
-
extend
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
extend ActiveModel::Translation
|
4
|
+
include ActiveModel::Validations
|
5
|
+
include ActiveModel::AttributeMethods
|
3
6
|
|
4
7
|
included do
|
5
|
-
extend ActiveModel::Translation
|
6
|
-
include ActiveModel::Validations
|
7
|
-
include ActiveModel::AttributeMethods
|
8
|
-
include DynamicMethods
|
9
|
-
|
10
8
|
attribute_method_suffix ""
|
11
9
|
attribute_method_suffix "="
|
12
10
|
cattr_accessor :model_attributes
|
13
11
|
end
|
14
12
|
|
15
13
|
module ClassMethods
|
14
|
+
def attribute(name, type)
|
15
|
+
self.model_attributes ||= {}
|
16
|
+
self.model_attributes[name] = type
|
17
|
+
end
|
18
|
+
|
16
19
|
def define_method_attribute=(attr_name)
|
17
|
-
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast(new_value); end", __FILE__, __LINE__)
|
20
|
+
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast('#{attr_name}', new_value); end", __FILE__, __LINE__)
|
18
21
|
end
|
19
22
|
|
20
23
|
def define_method_attribute(attr_name)
|
21
24
|
generated_attribute_methods.module_eval("def #{attr_name}; @attributes['#{attr_name}']; end", __FILE__, __LINE__)
|
22
25
|
end
|
23
26
|
|
24
|
-
def type_cast(value)
|
27
|
+
def type_cast(attr_name, value)
|
25
28
|
return value unless value.is_a?(String)
|
26
|
-
value.
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module DynamicMethods
|
31
|
-
def method_missing(method_id, *args, &block)
|
32
|
-
if !self.class.attribute_methods_generated?
|
33
|
-
self.class.define_attribute_methods self.class.model_attributes.map(&:to_s)
|
34
|
-
method_name = method_id.to_s
|
35
|
-
send(method_id, *args, &block)
|
36
|
-
else
|
37
|
-
super
|
38
|
-
end
|
29
|
+
value.send("to_#{model_attributes[attr_name.to_sym]}") rescue nil
|
39
30
|
end
|
40
31
|
end
|
41
32
|
|
@@ -57,5 +48,15 @@ module TestModel
|
|
57
48
|
end
|
58
49
|
end
|
59
50
|
|
51
|
+
def method_missing(method_id, *args, &block)
|
52
|
+
if !self.class.attribute_methods_generated?
|
53
|
+
self.class.define_attribute_methods self.class.model_attributes.keys.map(&:to_s)
|
54
|
+
method_name = method_id.to_s
|
55
|
+
send(method_id, *args, &block)
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
60
61
|
end
|
61
62
|
|
@@ -2,13 +2,27 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe ValidatesTimeliness::AttributeMethods do
|
4
4
|
it 'should define _timeliness_raw_value_for instance method' do
|
5
|
-
|
5
|
+
PersonWithShim.instance_methods.should include('_timeliness_raw_value_for')
|
6
|
+
end
|
7
|
+
|
8
|
+
describe ".timeliness_validated_attributes" do
|
9
|
+
it 'should return attributes validated with plugin validator' do
|
10
|
+
PersonWithShim.timeliness_validated_attributes = []
|
11
|
+
PersonWithShim.validates_date :birth_date
|
12
|
+
PersonWithShim.validates_time :birth_time
|
13
|
+
PersonWithShim.validates_datetime :birth_datetime
|
14
|
+
|
15
|
+
PersonWithShim.timeliness_validated_attributes.should == [ :birth_date, :birth_time, :birth_datetime ]
|
16
|
+
end
|
6
17
|
end
|
7
18
|
|
8
19
|
context "attribute write method" do
|
9
20
|
class PersonWithCache
|
10
21
|
include TestModel
|
11
|
-
|
22
|
+
include TestModelShim
|
23
|
+
attribute :birth_date, :date
|
24
|
+
attribute :birth_time, :time
|
25
|
+
attribute :birth_datetime, :datetime
|
12
26
|
validates_date :birth_date
|
13
27
|
validates_time :birth_time
|
14
28
|
validates_datetime :birth_datetime
|
@@ -23,7 +37,10 @@ describe ValidatesTimeliness::AttributeMethods do
|
|
23
37
|
context "with plugin parser" do
|
24
38
|
class PersonWithParser
|
25
39
|
include TestModel
|
26
|
-
|
40
|
+
include TestModelShim
|
41
|
+
attribute :birth_date, :date
|
42
|
+
attribute :birth_time, :time
|
43
|
+
attribute :birth_datetime, :datetime
|
27
44
|
validates_date :birth_date
|
28
45
|
validates_time :birth_time
|
29
46
|
validates_datetime :birth_datetime
|
@@ -53,7 +70,7 @@ describe ValidatesTimeliness::AttributeMethods do
|
|
53
70
|
|
54
71
|
context "before_type_cast method" do
|
55
72
|
it 'should not be defined if ORM does not support it' do
|
56
|
-
|
73
|
+
PersonWithShim.instance_methods(false).should_not include("birth_datetime_before_type_cast")
|
57
74
|
end
|
58
75
|
end
|
59
76
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ValidatesTimeliness
|
4
|
-
it 'should define class validation methods
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
describe ValidatesTimeliness, 'HelperMethods' do
|
4
|
+
it 'should define class validation methods' do
|
5
|
+
Person.should respond_to(:validates_date)
|
6
|
+
Person.should respond_to(:validates_time)
|
7
|
+
Person.should respond_to(:validates_datetime)
|
8
8
|
end
|
9
9
|
|
10
|
-
it 'should define instance validation methods
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
it 'should define instance validation methods' do
|
11
|
+
Person.instance_methods.should include('validates_date')
|
12
|
+
Person.instance_methods.should include('validates_time')
|
13
|
+
Person.instance_methods.should include('validates_datetime')
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should validate instance when validation method called' do
|
@@ -18,19 +18,4 @@ describe ValidatesTimeliness::HelperMethods do
|
|
18
18
|
r.validates_date :birth_date
|
19
19
|
r.errors[:birth_date].should_not be_empty
|
20
20
|
end
|
21
|
-
|
22
|
-
describe ".timeliness_validated_attributes" do
|
23
|
-
it 'should return attributes validated with plugin validator' do
|
24
|
-
Person.timeliness_validated_attributes = {}
|
25
|
-
Person.validates_date :birth_date
|
26
|
-
Person.validates_time :birth_time
|
27
|
-
Person.validates_datetime :birth_datetime
|
28
|
-
|
29
|
-
Person.timeliness_validated_attributes.should == {
|
30
|
-
"birth_date" => :date,
|
31
|
-
"birth_time" => :time,
|
32
|
-
"birth_datetime" => :datetime
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
21
|
end
|
@@ -1,8 +1,23 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe ValidatesTimeliness, 'ActiveRecord' do
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
context "validation methods" do
|
6
|
+
it 'should be defined for the class' do
|
7
|
+
ActiveRecord::Base.should respond_to(:validates_date)
|
8
|
+
ActiveRecord::Base.should respond_to(:validates_time)
|
9
|
+
ActiveRecord::Base.should respond_to(:validates_datetime)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should defines for the instance' do
|
13
|
+
ActiveRecord::Base.instance_methods.should include('validates_date')
|
14
|
+
ActiveRecord::Base.instance_methods.should include('validates_time')
|
15
|
+
ActiveRecord::Base.instance_methods.should include('validates_datetime')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should determine type for attribute' do
|
20
|
+
Employee.timeliness_attribute_type(:birth_date).should == :date
|
6
21
|
end
|
7
22
|
|
8
23
|
context "attribute write method" do
|
@@ -36,16 +51,26 @@ describe ValidatesTimeliness, 'ActiveRecord' do
|
|
36
51
|
|
37
52
|
it 'should parse string as current timezone' do
|
38
53
|
r = EmployeeWithParser.new
|
39
|
-
r.birth_datetime = '2010-
|
40
|
-
r.birth_datetime.
|
54
|
+
r.birth_datetime = '2010-06-01 12:00'
|
55
|
+
r.birth_datetime.utc_offset.should == 10.hours
|
41
56
|
end
|
42
57
|
|
43
58
|
after :all do
|
59
|
+
Time.zone = 'Australia/Melbourne'
|
44
60
|
ValidatesTimeliness.use_plugin_parser = false
|
45
61
|
end
|
46
62
|
end
|
47
63
|
end
|
48
64
|
|
65
|
+
context "cached value" do
|
66
|
+
it 'should be cleared on reload' do
|
67
|
+
r = Employee.create!
|
68
|
+
r.birth_date = '2010-01-01'
|
69
|
+
r.reload
|
70
|
+
r._timeliness_raw_value_for(:birth_date).should be_nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
49
74
|
context "before_type_cast method" do
|
50
75
|
it 'should be defined on class if ORM supports it' do
|
51
76
|
Employee.instance_methods(false).should include("birth_datetime_before_type_cast")
|
@@ -10,22 +10,36 @@ Mongoid.configure do |config|
|
|
10
10
|
config.persist_in_safe_mode = false
|
11
11
|
end
|
12
12
|
|
13
|
+
class Article
|
14
|
+
::ValidatesTimeliness.use_plugin_parser = true
|
15
|
+
include Mongoid::Document
|
16
|
+
field :publish_date, :type => Date
|
17
|
+
field :publish_time, :type => Time
|
18
|
+
field :publish_datetime, :type => DateTime
|
19
|
+
validates_date :publish_date, :allow_nil => true
|
20
|
+
validates_time :publish_time, :allow_nil => true
|
21
|
+
validates_datetime :publish_datetime, :allow_nil => true
|
22
|
+
::ValidatesTimeliness.use_plugin_parser = false
|
23
|
+
end
|
24
|
+
|
13
25
|
describe ValidatesTimeliness, 'Mongoid' do
|
14
26
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
context "validation methods" do
|
28
|
+
it 'should be defined on the class' do
|
29
|
+
Article.should respond_to(:validates_date)
|
30
|
+
Article.should respond_to(:validates_time)
|
31
|
+
Article.should respond_to(:validates_datetime)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should be defined on the instance' do
|
35
|
+
Article.instance_methods.should include('validates_date')
|
36
|
+
Article.instance_methods.should include('validates_time')
|
37
|
+
Article.instance_methods.should include('validates_datetime')
|
38
|
+
end
|
25
39
|
end
|
26
40
|
|
27
|
-
it 'should
|
28
|
-
Article.
|
41
|
+
it 'should determine type for attribute' do
|
42
|
+
Article.timeliness_attribute_type(:publish_date).should == :date
|
29
43
|
end
|
30
44
|
|
31
45
|
context "attribute write method" do
|
@@ -58,6 +72,15 @@ describe ValidatesTimeliness, 'Mongoid' do
|
|
58
72
|
end
|
59
73
|
end
|
60
74
|
|
75
|
+
context "cached value" do
|
76
|
+
it 'should be cleared on reload' do
|
77
|
+
r = Article.create!
|
78
|
+
r.publish_date = '2010-01-01'
|
79
|
+
r.reload
|
80
|
+
r._timeliness_raw_value_for(:publish_date).should be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
61
84
|
context "before_type_cast method" do
|
62
85
|
it 'should not be defined if ORM does not support it' do
|
63
86
|
Article.instance_methods(false).should_not include("birth_datetime_before_type_cast")
|
@@ -177,22 +177,6 @@ describe ValidatesTimeliness::Parser do
|
|
177
177
|
ValidatesTimeliness::Parser.ambiguous_year_threshold = default
|
178
178
|
end
|
179
179
|
end
|
180
|
-
|
181
|
-
context "with custom dummy date values" do
|
182
|
-
before(:all) do
|
183
|
-
@old_dummy_date = ValidatesTimeliness.dummy_date_for_time_type
|
184
|
-
ValidatesTimeliness.dummy_date_for_time_type = [2009,1,1]
|
185
|
-
end
|
186
|
-
|
187
|
-
it "should return time array with custom dummy date" do
|
188
|
-
time_array = formats._parse('12:13:14', :time, :strict => true)
|
189
|
-
time_array.should == [2009,1,1,12,13,14,0]
|
190
|
-
end
|
191
|
-
|
192
|
-
after(:all) do
|
193
|
-
ValidatesTimeliness.dummy_date_for_time_type = @old_dummy_date
|
194
|
-
end
|
195
|
-
end
|
196
180
|
end
|
197
181
|
|
198
182
|
describe "parse" do
|
@@ -101,7 +101,10 @@ describe ValidatesTimeliness::Validator do
|
|
101
101
|
describe ":format option" do
|
102
102
|
class PersonWithFormatOption
|
103
103
|
include TestModel
|
104
|
-
|
104
|
+
include TestModelShim
|
105
|
+
attribute :birth_date, :date
|
106
|
+
attribute :birth_time, :time
|
107
|
+
attribute :birth_datetime, :datetime
|
105
108
|
validates_date :birth_date, :format => 'dd-mm-yyyy'
|
106
109
|
end
|
107
110
|
|
@@ -169,6 +172,21 @@ describe ValidatesTimeliness::Validator do
|
|
169
172
|
validator.format_error_value(Time.mktime(2010,1,1,12,34,56)).should == '2010-01-01 12:34:56'
|
170
173
|
end
|
171
174
|
end
|
175
|
+
|
176
|
+
describe "with missing translation" do
|
177
|
+
before :all do
|
178
|
+
I18n.locale = :es
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should use the default format for the type' do
|
182
|
+
validator = ValidatesTimeliness::Validator.new(:attributes => [:birth_date], :type => :date)
|
183
|
+
validator.format_error_value(Date.new(2010,1,1)).should == '2010-01-01'
|
184
|
+
end
|
185
|
+
|
186
|
+
after :all do
|
187
|
+
I18n.locale = :en
|
188
|
+
end
|
189
|
+
end
|
172
190
|
end
|
173
191
|
|
174
192
|
context "custom error message" do
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{validates_timeliness}
|
5
|
-
s.version = "3.0.0.beta.
|
5
|
+
s.version = "3.0.0.beta.4"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Adam Meehan"]
|
9
|
-
s.date = %q{2010-09-
|
9
|
+
s.date = %q{2010-09-29}
|
10
10
|
s.description = %q{Date and time validation plugin for Rails which allows custom formats}
|
11
11
|
s.email = %q{adam.meehan@gmail.com}
|
12
12
|
s.extra_rdoc_files = ["README.rdoc", "LICENSE", "CHANGELOG"]
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validates_timeliness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196427
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- beta
|
11
|
-
-
|
12
|
-
version: 3.0.0.beta.
|
11
|
+
- 4
|
12
|
+
version: 3.0.0.beta.4
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Adam Meehan
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-09-
|
20
|
+
date: 2010-09-29 00:00:00 +10:00
|
21
21
|
default_executable:
|
22
22
|
dependencies: []
|
23
23
|
|