validates_timeliness 3.0.0.beta.3 → 3.0.0.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|