validates_timeliness 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ = 1.1.0 [2009-01-01]
2
+ - Added between option
3
+
1
4
  = 1.0.0 [2008-12-06]
2
5
  - Gemified!
3
6
  - Refactor of plugin into a Data Mapper style validator class which makes for a cleaner implementation and possible future Merb\Data Mapper support
data/README.rdoc CHANGED
@@ -1,7 +1,7 @@
1
1
  = validates_timeliness
2
2
 
3
- * Source: http://github.com/adzap/validates_timeliness
4
- * Bugs: http://adzap.lighthouseapp.com/projects/14111-validates_timeliness
3
+ * Source: http://github.com/adzap/validates_timeliness
4
+ * Bugs: http://adzap.lighthouseapp.com/projects/14111-validates_timeliness
5
5
 
6
6
  == DESCRIPTION:
7
7
 
@@ -24,12 +24,14 @@ think should be a valid date or time string.
24
24
 
25
25
  * Respects new timezone features of Rails 2.1.
26
26
 
27
+ * Supports Rails 2.2 I18n for the error messages
28
+
27
29
 
28
30
  == INSTALLATION:
29
31
 
30
32
  As plugin (from master)
31
33
 
32
- ./script/plugin git://github.com/adzap/validates_timeliness
34
+ ./script/plugin git://github.com/adzap/validates_timeliness.git
33
35
 
34
36
  As gem
35
37
 
@@ -62,6 +64,7 @@ the valid range of dates or times allowed
62
64
  :on_or_before - Attribute must be equal to or before this value to be valid
63
65
  :after - Attribute must be after this value to be valid
64
66
  :on_or_after - Attribute must be equal to or after this value to be valid
67
+ :between - Attribute must be between the values to be valid
65
68
 
66
69
  Regular validation options:
67
70
  :allow_nil - Allow a nil value to be valid
@@ -77,6 +80,7 @@ the valid range of dates or times allowed
77
80
  :on_or_before_message
78
81
  :after_message
79
82
  :on_or_after_message
83
+ :between_message
80
84
 
81
85
  The temporal restrictions can take 4 different value types:
82
86
 
@@ -84,6 +88,7 @@ The temporal restrictions can take 4 different value types:
84
88
  * Date, Time, or DateTime object value
85
89
  * Proc or lambda object
86
90
  * A symbol matching the method name in the model
91
+ * Between option takes an array of two values or a range
87
92
 
88
93
  When an attribute value is compared to temporal restrictions, they are compared as
89
94
  the same type as the validation method type. So using validates_date means all
@@ -193,7 +198,7 @@ of d/my/yy. By default the plugin uses the US formats as this is the Ruby defaul
193
198
  when it does date interpretation, and is in keeping PoLS (principle of least
194
199
  surprise).
195
200
 
196
- To switch to using the :after => 1.day.from_nowEuropean (or Rest of The World) formats put this in an
201
+ To switch to using the European (or Rest of The World) formats put this in an
197
202
  initializer or environment.rb
198
203
 
199
204
  ValidatesTimeliness::Formats.remove_us_formats
@@ -264,7 +269,8 @@ For Rails 2.0/2.1:
264
269
  :before => "must be before %s",
265
270
  :on_or_before => "must be on or before %s",
266
271
  :after => "must be after %s",
267
- :on_or_after => "must be on or after %s"
272
+ :on_or_after => "must be on or after %s",
273
+ :between => "must be between %s and %s"
268
274
  )
269
275
 
270
276
  Where %s is the interpolation value for the restriction.
@@ -275,8 +281,9 @@ Rails 2.2+ using the I18n system to define new defaults:
275
281
  activerecord:
276
282
  errors:
277
283
  messages:
278
- on_or_before: "must equal to or before {{restriction}}"
279
- on_or_after: "must equal to or after {{restriction}}"
284
+ on_or_before: "must be equal to or before {{restriction}}"
285
+ on_or_after: "must be equal to or after {{restriction}}"
286
+ between: "must be between {{earliest}} and {{latest}}"
280
287
 
281
288
  The {{restriction}} signifies where the interpolation value for the restriction
282
289
  will be inserted.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'date'
5
5
  require 'spec/rake/spectask'
6
6
 
7
7
  GEM = "validates_timeliness"
8
- GEM_VERSION = "1.0.0"
8
+ GEM_VERSION = "1.1.0"
9
9
  AUTHOR = "Adam Meehan"
10
10
  EMAIL = "adam.meehan@gmail.com"
11
11
  HOMEPAGE = "http://github.com/adzap/validates_timeliness"
data/TODO CHANGED
@@ -1,4 +1,3 @@
1
- - :between option
2
1
  - :format option
3
2
  - :with_date and :with_time options
4
3
  - Merb and Data Mapper support
@@ -56,7 +56,8 @@ module ValidatesTimeliness
56
56
  self.send("setup_for_rails_#{major}_#{minor}")
57
57
  self.default_timezone = ::ActiveRecord::Base.default_timezone
58
58
  rescue
59
- raise "Rails version #{Rails::VERSION::STRING} not yet supported by validates_timeliness plugin"
59
+ puts "Rails version #{Rails::VERSION::STRING} not explicitly supported by validates_timeliness plugin. You may encounter some problems."
60
+ resume
60
61
  end
61
62
  end
62
63
  end
@@ -237,8 +237,7 @@ module ValidatesTimeliness
237
237
 
238
238
  return Regexp.new(regexp), format_proc(order)
239
239
  rescue
240
- puts "The following format regular expression failed to compile: #{regexp}\n from format #{string_format}."
241
- raise
240
+ raise "The following format regular expression failed to compile: #{regexp}\n from format #{string_format}."
242
241
  end
243
242
 
244
243
  # Generates a proc which when executed maps the regexp capture groups to a
@@ -9,3 +9,4 @@ en:
9
9
  on_or_before: "must be on or before {{restriction}}"
10
10
  after: "must be after {{restriction}}"
11
11
  on_or_after: "must be on or after {{restriction}}"
12
+ between: "must be between {{earliest}} and {{latest}}"
@@ -2,120 +2,156 @@ module Spec
2
2
  module Rails
3
3
  module Matchers
4
4
  class ValidateTimeliness
5
- cattr_accessor :test_values
6
5
 
7
- @@test_values = {
6
+ VALIDITY_TEST_VALUES = {
8
7
  :date => {:pass => '2000-01-01', :fail => '2000-01-32'},
9
8
  :time => {:pass => '12:00', :fail => '25:00'},
10
9
  :datetime => {:pass => '2000-01-01 00:00:00', :fail => '2000-01-32 00:00:00'}
11
10
  }
12
11
 
12
+ OPTION_TEST_SETTINGS = {
13
+ :before => { :method => :-, :modify_on => :valid },
14
+ :after => { :method => :+, :modify_on => :valid },
15
+ :on_or_before => { :method => :+, :modify_on => :invalid },
16
+ :on_or_after => { :method => :-, :modify_on => :invalid }
17
+ }
18
+
13
19
  def initialize(attribute, options)
14
20
  @expected, @options = attribute, options
15
21
  @validator = ValidatesTimeliness::Validator.new(options)
16
- compile_error_messages
17
- end
18
-
19
- def compile_error_messages
20
- messages = validator.send(:error_messages)
21
- @messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h }
22
22
  end
23
23
 
24
24
  def matches?(record)
25
25
  @record = record
26
- type = options[:type]
26
+ @type = options[:type]
27
27
 
28
- invalid_value = @@test_values[type][:fail]
29
- valid_value = parse_and_cast(@@test_values[type][:pass])
30
- valid = error_matching(invalid_value, /#{messages["invalid_#{type}".to_sym]}/) &&
31
- no_error_matching(valid_value, /#{messages["invalid_#{type}".to_sym]}/)
28
+ valid = test_validity
32
29
 
33
- valid = test_option(:before, :-) if options[:before] && valid
34
- valid = test_option(:after, :+) if options[:after] && valid
30
+ valid = test_option(:before) if @options[:before] && valid
31
+ valid = test_option(:after) if @options[:after] && valid
35
32
 
36
- valid = test_option(:on_or_before, :+, :modify_on => :invalid) if options[:on_or_before] && valid
37
- valid = test_option(:on_or_after, :-, :modify_on => :invalid) if options[:on_or_after] && valid
33
+ valid = test_option(:on_or_before) if @options[:on_or_before] && valid
34
+ valid = test_option(:on_or_after) if @options[:on_or_after] && valid
35
+
36
+ valid = test_between if @options[:between] && valid
38
37
 
39
38
  return valid
40
39
  end
41
40
 
42
41
  def failure_message
43
- "expected model to validate #{options[:type]} attribute #{expected.inspect} with #{last_failure}"
42
+ "expected model to validate #{@type} attribute #{@expected.inspect} with #{@last_failure}"
44
43
  end
45
44
 
46
45
  def negative_failure_message
47
- "expected not to validate #{options[:type]} attribute #{expected.inspect}"
46
+ "expected not to validate #{@type} attribute #{@expected.inspect}"
48
47
  end
49
48
 
50
49
  def description
51
- "have validated #{options[:type]} attribute #{expected.inspect}"
50
+ "have validated #{@type} attribute #{@expected.inspect}"
52
51
  end
53
52
 
54
53
  private
55
- attr_reader :actual, :expected, :record, :options, :messages, :last_failure, :validator
56
-
57
- def test_option(option, modifier, settings={})
58
- settings.reverse_merge!(:modify_on => :valid)
59
- boundary = parse_and_cast(options[option])
54
+
55
+ def test_validity
56
+ invalid_value = VALIDITY_TEST_VALUES[@type][:fail]
57
+ valid_value = parse_and_cast(VALIDITY_TEST_VALUES[@type][:pass])
58
+ error_matching(invalid_value, "invalid_#{@type}".to_sym) &&
59
+ no_error_matching(valid_value, "invalid_#{@type}".to_sym)
60
+ end
61
+
62
+ def test_option(option)
63
+ settings = OPTION_TEST_SETTINGS[option]
64
+ boundary = parse_and_cast(@options[option])
60
65
 
66
+ method = settings[:method]
67
+
61
68
  valid_value, invalid_value = if settings[:modify_on] == :valid
62
- [ boundary.send(modifier, 1), boundary ]
69
+ [ boundary.send(method, 1), boundary ]
63
70
  else
64
- [ boundary, boundary.send(modifier, 1) ]
71
+ [ boundary, boundary.send(method, 1) ]
65
72
  end
66
73
 
67
- message = messages[option]
68
- error_matching(invalid_value, /#{message}/) &&
69
- no_error_matching(valid_value, /#{message}/)
74
+ error_matching(invalid_value, option) &&
75
+ no_error_matching(valid_value, option)
76
+ end
77
+
78
+ def test_before
79
+ before = parse_and_cast(@options[:before])
80
+
81
+ error_matching(before - 1, :before) &&
82
+ no_error_matching(before, :before)
83
+ end
84
+
85
+ def test_between
86
+ between = parse_and_cast(@options[:between])
87
+
88
+ error_matching(between.first - 1, :between) &&
89
+ error_matching(between.last + 1, :between) &&
90
+ no_error_matching(between.first, :between) &&
91
+ no_error_matching(between.last, :between)
70
92
  end
71
93
 
72
94
  def parse_and_cast(value)
73
- value = validator.send(:restriction_value, value, record)
74
- validator.send(:type_cast_value, value)
95
+ value = @validator.send(:restriction_value, value, @record)
96
+ @validator.send(:type_cast_value, value)
75
97
  end
76
98
 
77
- def error_matching(value, match)
78
- record.send("#{expected}=", value)
79
- record.valid?
80
- errors = record.errors.on(expected)
81
- pass = [ errors ].flatten.any? {|error| match === error }
82
- @last_failure = "error matching #{match.inspect} when value is #{format_value(value)}" unless pass
99
+ def error_matching(value, option)
100
+ match = error_message_for(option)
101
+ @record.send("#{@expected}=", value)
102
+ @record.valid?
103
+ errors = @record.errors.on(@expected)
104
+ pass = [ errors ].flatten.any? {|error| /#{match}/ === error }
105
+ @last_failure = "error matching '#{match}' when value is #{format_value(value)}" unless pass
83
106
  pass
84
107
  end
85
108
 
86
- def no_error_matching(value, match)
87
- pass = !error_matching(value, match)
88
- @last_failure = "no error matching #{match.inspect} when value is #{format_value(value)}" unless pass
109
+ def no_error_matching(value, option)
110
+ pass = !error_matching(value, option)
111
+ unless pass
112
+ error = error_message_for(option)
113
+ @last_failure = "no error matching '#{error}' when value is #{format_value(value)}"
114
+ end
89
115
  pass
90
116
  end
117
+
118
+ def error_message_for(option)
119
+ msg = @validator.send(:error_messages)[option]
120
+ restriction = @validator.send(:restriction_value, @validator.configuration[option], @record)
121
+
122
+ if restriction
123
+ restriction = [restriction] unless restriction.is_a?(Array)
124
+ restriction.map! {|r| @validator.send(:type_cast_value, r) }
125
+ interpolate = @validator.send(:interpolation_values, option, restriction )
126
+ if defined?(I18n)
127
+ msg = @record.errors.generate_message(@expected, option, interpolate)
128
+ else
129
+ msg = msg % interpolate
130
+ end
131
+ end
132
+ msg
133
+ end
91
134
 
92
135
  def format_value(value)
93
136
  return value if value.is_a?(String)
94
- value.strftime(ValidatesTimeliness::Validator.error_value_formats[options[:type]])
137
+ value.strftime(ValidatesTimeliness::Validator.error_value_formats[@type])
95
138
  end
96
139
  end
97
140
 
98
141
  def validate_date(attribute, options={})
99
142
  options[:type] = :date
100
- validate_timeliness_of(attribute, options)
143
+ ValidateTimeliness.new(attribute, options)
101
144
  end
102
145
 
103
146
  def validate_time(attribute, options={})
104
147
  options[:type] = :time
105
- validate_timeliness_of(attribute, options)
148
+ ValidateTimeliness.new(attribute, options)
106
149
  end
107
150
 
108
151
  def validate_datetime(attribute, options={})
109
152
  options[:type] = :datetime
110
- validate_timeliness_of(attribute, options)
111
- end
112
-
113
- private
114
-
115
- def validate_timeliness_of(attribute, options={})
116
153
  ValidateTimeliness.new(attribute, options)
117
154
  end
118
-
119
155
  end
120
156
  end
121
157
  end
@@ -11,6 +11,14 @@ module ValidatesTimeliness
11
11
  :datetime => '%Y-%m-%d %H:%M:%S'
12
12
  }
13
13
 
14
+ RESTRICTION_METHODS = {
15
+ :before => :<,
16
+ :after => :>,
17
+ :on_or_before => :<=,
18
+ :on_or_after => :>=,
19
+ :between => lambda {|v, r| (r.first..r.last).include?(v) }
20
+ }
21
+
14
22
  attr_reader :configuration, :type
15
23
 
16
24
  def initialize(configuration)
@@ -40,21 +48,17 @@ module ValidatesTimeliness
40
48
  end
41
49
 
42
50
  def validate_restrictions(record, attr_name, value)
43
- restriction_methods = {:before => '<', :after => '>', :on_or_before => '<=', :on_or_after => '>='}
44
-
45
- display = self.class.error_value_formats[type]
46
-
47
51
  value = type_cast_value(value)
48
52
 
49
- restriction_methods.each do |option, method|
53
+ RESTRICTION_METHODS.each do |option, method|
50
54
  next unless restriction = configuration[option]
51
55
  begin
52
- compare = restriction_value(restriction, record)
53
- next if compare.nil?
54
- compare = type_cast_value(compare)
56
+ restriction = restriction_value(restriction, record)
57
+ next if restriction.nil?
58
+ restriction = type_cast_value(restriction)
55
59
 
56
- unless value.send(method, compare)
57
- add_error(record, attr_name, option, :restriction => compare.strftime(display))
60
+ unless evaluate_restriction(restriction, value, method)
61
+ add_error(record, attr_name, option, interpolation_values(option, restriction))
58
62
  end
59
63
  rescue
60
64
  unless self.class.ignore_restriction_errors
@@ -63,16 +67,42 @@ module ValidatesTimeliness
63
67
  end
64
68
  end
65
69
  end
66
-
67
- def add_error(record, attr_name, message, interpolate={})
68
- if Rails::VERSION::STRING < '2.2'
69
- message = error_messages[message] if message.is_a?(Symbol)
70
- message = message % interpolate.values unless interpolate.empty?
71
- record.errors.add(attr_name, message)
70
+
71
+ def interpolation_values(option, restriction)
72
+ format = self.class.error_value_formats[type]
73
+ restriction = [restriction] unless restriction.is_a?(Array)
74
+
75
+ if defined?(I18n)
76
+ message = custom_error_messages[option] || I18n.translate('activerecord.errors.messages')[option]
77
+ subs = message.scan(/\{\{([^\}]*)\}\}/)
78
+ interpolations = {}
79
+ subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
80
+ interpolations
72
81
  else
82
+ restriction.map {|r| r.strftime(format) }
83
+ end
84
+ end
85
+
86
+ def evaluate_restriction(restriction, value, comparator)
87
+ return true if restriction.nil?
88
+
89
+ case comparator
90
+ when Symbol
91
+ value.send(comparator, restriction)
92
+ when Proc
93
+ comparator.call(value, restriction)
94
+ end
95
+ end
96
+
97
+ def add_error(record, attr_name, message, interpolate=nil)
98
+ if defined?(I18n)
73
99
  # use i18n support in AR for message or use custom message passed to validation method
74
100
  custom = custom_error_messages[message]
75
- record.errors.add(attr_name, custom || message, interpolate)
101
+ record.errors.add(attr_name, custom || message, interpolate || {})
102
+ else
103
+ message = error_messages[message] if message.is_a?(Symbol)
104
+ message = message % interpolate
105
+ record.errors.add(attr_name, message)
76
106
  end
77
107
  end
78
108
 
@@ -83,7 +113,12 @@ module ValidatesTimeliness
83
113
 
84
114
  def custom_error_messages
85
115
  return @custom_error_messages if defined?(@custom_error_messages)
86
- @custom_error_messages = configuration.inject({}) {|h, (k, v)| h[$1.to_sym] = v if k.to_s =~ /(.*)_message$/;h }
116
+ @custom_error_messages = configuration.inject({}) {|msgs, (k, v)|
117
+ if md = /(.*)_message$/.match(k.to_s)
118
+ msgs[md[0].to_sym] = v
119
+ end
120
+ msgs
121
+ }
87
122
  end
88
123
 
89
124
  def restriction_value(restriction, record)
@@ -94,13 +129,20 @@ module ValidatesTimeliness
94
129
  restriction_value(record.send(restriction), record)
95
130
  when Proc
96
131
  restriction_value(restriction.call(record), record)
132
+ when Array
133
+ restriction.map {|r| restriction_value(r, record) }.sort
134
+ when Range
135
+ restriction_value([restriction.first, restriction.last], record)
97
136
  else
98
137
  record.class.parse_date_time(restriction, type, false)
99
138
  end
100
139
  end
101
140
 
102
141
  def type_cast_value(value)
103
- case type
142
+ if value.is_a?(Array)
143
+ value.map {|v| type_cast_value(v) }
144
+ else
145
+ case type
104
146
  when :time
105
147
  value.to_dummy_time
106
148
  when :date
@@ -109,10 +151,11 @@ module ValidatesTimeliness
109
151
  if value.is_a?(DateTime) || value.is_a?(Time)
110
152
  value.to_time
111
153
  else
112
- value.to_time(ValidatesTimelines.default_timezone)
154
+ value.to_time(ValidatesTimeliness.default_timezone)
113
155
  end
114
156
  else
115
157
  nil
158
+ end
116
159
  end
117
160
  end
118
161
 
@@ -6,13 +6,17 @@ end
6
6
  class WithValidation < Person
7
7
  validates_date :birth_date,
8
8
  :before => '2000-01-10', :after => '2000-01-01',
9
- :on_or_before => '2000-01-09', :on_or_after => '2000-01-02'
9
+ :on_or_before => '2000-01-09', :on_or_after => '2000-01-02',
10
+ :between => ['2000-01-01', '2000-01-03']
11
+
10
12
  validates_time :birth_time,
11
13
  :before => '23:00', :after => '09:00',
12
- :on_or_before => '22:00', :on_or_after => '10:00'
14
+ :on_or_before => '22:00', :on_or_after => '10:00',
15
+ :between => ['09:00', '17:00']
13
16
  validates_datetime :birth_date_and_time,
14
17
  :before => '2000-01-10 23:00', :after => '2000-01-01 09:00',
15
- :on_or_before => '2000-01-09 23:00', :on_or_after => '2000-01-02 09:00'
18
+ :on_or_before => '2000-01-09 23:00', :on_or_after => '2000-01-02 09:00',
19
+ :between => ['2000-01-01 09:00', '2000-01-01 17:00']
16
20
 
17
21
  end
18
22
 
@@ -27,20 +31,21 @@ end
27
31
  describe "ValidateTimeliness matcher" do
28
32
  attr_accessor :no_validation, :with_validation
29
33
 
34
+ @@attribute_for_type = { :date => :birth_date, :time => :birth_time, :datetime => :birth_date_and_time }
35
+
30
36
  before do
31
37
  @no_validation = NoValidation.new
32
38
  @with_validation = WithValidation.new
33
39
  end
34
40
 
35
41
  [:date, :time, :datetime].each do |type|
36
- attribute = type == :datetime ? :date_and_time : type
37
42
 
38
43
  it "should report that #{type} is validated" do
39
- with_validation.should self.send("validate_#{type}", "birth_#{attribute}".to_sym)
44
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type))
40
45
  end
41
46
 
42
47
  it "should report that #{type} is not validated" do
43
- no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}".to_sym)
48
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type))
44
49
  end
45
50
  end
46
51
 
@@ -52,18 +57,17 @@ describe "ValidateTimeliness matcher" do
52
57
  }
53
58
 
54
59
  [:date, :time, :datetime].each do |type|
55
- attribute = type == :datetime ? :date_and_time : type
56
60
 
57
61
  it "should report that #{type} is validated" do
58
- with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][0])
62
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][0])
59
63
  end
60
64
 
61
65
  it "should report that #{type} is not validated when option value is incorrect" do
62
- with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][1])
66
+ with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][1])
63
67
  end
64
68
 
65
69
  it "should report that #{type} is not validated with option" do
66
- no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][0])
70
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][0])
67
71
  end
68
72
  end
69
73
  end
@@ -76,18 +80,17 @@ describe "ValidateTimeliness matcher" do
76
80
  }
77
81
 
78
82
  [:date, :time, :datetime].each do |type|
79
- attribute = type == :datetime ? :date_and_time : type
80
83
 
81
84
  it "should report that #{type} is validated" do
82
- with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][0])
85
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][0])
83
86
  end
84
87
 
85
88
  it "should report that #{type} is not validated when option value is incorrect" do
86
- with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][1])
89
+ with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][1])
87
90
  end
88
91
 
89
92
  it "should report that #{type} is not validated with option" do
90
- no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][0])
93
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][0])
91
94
  end
92
95
  end
93
96
  end
@@ -100,18 +103,17 @@ describe "ValidateTimeliness matcher" do
100
103
  }
101
104
 
102
105
  [:date, :time, :datetime].each do |type|
103
- attribute = type == :datetime ? :date_and_time : type
104
106
 
105
107
  it "should report that #{type} is validated" do
106
- with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][0])
108
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][0])
107
109
  end
108
110
 
109
111
  it "should report that #{type} is not validated when option value is incorrect" do
110
- with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][1])
112
+ with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][1])
111
113
  end
112
114
 
113
115
  it "should report that #{type} is not validated with option" do
114
- no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][0])
116
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][0])
115
117
  end
116
118
  end
117
119
  end
@@ -124,22 +126,44 @@ describe "ValidateTimeliness matcher" do
124
126
  }
125
127
 
126
128
  [:date, :time, :datetime].each do |type|
127
- attribute = type == :datetime ? :date_and_time : type
128
129
 
129
130
  it "should report that #{type} is validated" do
130
- with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][0])
131
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][0])
131
132
  end
132
133
 
133
134
  it "should report that #{type} is not validated when option value is incorrect" do
134
- with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][1])
135
+ with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][1])
135
136
  end
136
137
 
137
138
  it "should report that #{type} is not validated with option" do
138
- no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][0])
139
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][0])
139
140
  end
140
141
  end
141
142
  end
142
143
 
144
+ describe "between option" do
145
+ test_values = {
146
+ :date => [ ['2000-01-01', '2000-01-03'], ['2000-01-01', '2000-01-04'] ],
147
+ :time => [ ['09:00', '17:00'], ['09:00', '17:01'] ],
148
+ :datetime => [ ['2000-01-01 09:00', '2000-01-01 17:00'], ['2000-01-01 09:00', '2000-01-01 17:01'] ]
149
+ }
150
+
151
+ [:date, :time, :datetime].each do |type|
152
+
153
+ it "should report that #{type} is validated" do
154
+ with_validation.should self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][0])
155
+ end
156
+
157
+ it "should report that #{type} is not validated when option value is incorrect" do
158
+ with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][1])
159
+ end
160
+
161
+ it "should report that #{type} is not validated with option" do
162
+ no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][0])
163
+ end
164
+ end
165
+ end
166
+
143
167
  describe "custom messages" do
144
168
 
145
169
  before do
@@ -175,4 +199,8 @@ describe "ValidateTimeliness matcher" do
175
199
  end
176
200
 
177
201
  end
202
+
203
+ def attribute_for_type(type)
204
+ @@attribute_for_type[type.to_sym]
205
+ end
178
206
  end
@@ -43,6 +43,25 @@ describe ValidatesTimeliness::Validator do
43
43
  restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
44
44
  end
45
45
 
46
+ it "should return array of Time objects when restriction is array of Time objects" do
47
+ time1, time2 = Time.now, 1.day.ago
48
+ restriction_value([time1, time2], :datetime).should == [time2, time1]
49
+ end
50
+
51
+ it "should return array of Time objects when restriction is array of strings" do
52
+ time1, time2 = "2000-01-02", "2000-01-01"
53
+ restriction_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
54
+ end
55
+
56
+ it "should return array of Time objects when restriction is Range of Time objects" do
57
+ time1, time2 = Time.now, 1.day.ago
58
+ restriction_value(time1..time2, :datetime).should == [time2, time1]
59
+ end
60
+
61
+ it "should return array of Time objects when restriction is Range of time strings" do
62
+ time1, time2 = "2000-01-02", "2000-01-01"
63
+ restriction_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
64
+ end
46
65
  def restriction_value(restriction, type)
47
66
  configure_validator(:type => type)
48
67
  validator.send(:restriction_value, restriction, person)
@@ -212,83 +231,101 @@ describe ValidatesTimeliness::Validator do
212
231
  end
213
232
  end
214
233
 
215
- describe "instance with on_or_before and on_or_after restrictions" do
234
+ describe "instance with between restriction" do
216
235
 
217
236
  describe "for datetime type" do
218
237
  before do
219
- configure_validator(:on_or_before => Time.now.at_midnight, :on_or_after => 1.day.ago)
238
+ configure_validator(:between => [1.day.ago.at_midnight, 1.day.from_now.at_midnight])
220
239
  end
221
240
 
222
- it "should have error when value is past :on_or_before restriction" do
223
- validate_with(:birth_date_and_time, Time.now.at_midnight + 1)
224
- should_have_error(:birth_date_and_time, :on_of_before)
241
+ it "should have error when value is before earlist :between restriction" do
242
+ validate_with(:birth_date_and_time, 2.days.ago)
243
+ should_have_error(:birth_date_and_time, :between)
225
244
  end
226
245
 
227
- it "should be valid when value is equal to :on_or_before restriction" do
228
- validate_with(:birth_date_and_time, Time.now.at_midnight)
229
- should_have_no_error(:birth_date_and_time, :on_of_before)
246
+ it "should have error when value is after latest :between restriction" do
247
+ validate_with(:birth_date_and_time, 2.days.from_now)
248
+ should_have_error(:birth_date_and_time, :between)
230
249
  end
231
250
 
232
- it "should have error when value is before :on_or_after restriction" do
233
- validate_with(:birth_date_and_time, 1.days.ago - 1)
234
- should_have_error(:birth_date_and_time, :on_of_after)
251
+ it "should be valid when value is equal to earliest :between restriction" do
252
+ validate_with(:birth_date_and_time, 1.day.ago.at_midnight)
253
+ should_have_no_error(:birth_date_and_time, :between)
235
254
  end
236
255
 
237
- it "should be valid when value is value equal to :on_or_after restriction" do
238
- validate_with(:birth_date_and_time, 1.day.ago)
239
- should_have_no_error(:birth_date_and_time, :on_of_after)
256
+ it "should be valid when value is equal to latest :between restriction" do
257
+ validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
258
+ should_have_no_error(:birth_date_and_time, :between)
259
+ end
260
+
261
+ it "should allow a range for between restriction" do
262
+ configure_validator(:type => :datetime, :between => (1.day.ago.at_midnight)..(1.day.from_now.at_midnight))
263
+ validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
264
+ should_have_no_error(:birth_date_and_time, :between)
240
265
  end
241
266
  end
242
267
 
243
268
  describe "for date type" do
244
- before :each do
245
- configure_validator(:on_or_before => 1.day.from_now, :on_or_after => 1.day.ago, :type => :date)
269
+ before do
270
+ configure_validator(:type => :date, :between => [1.day.ago.to_date, 1.day.from_now.to_date])
246
271
  end
247
272
 
248
- it "should have error when value is past :on_or_before restriction" do
249
- validate_with(:birth_date, 2.days.from_now)
250
- should_have_error(:birth_date, :on_or_before)
273
+ it "should have error when value is before earlist :between restriction" do
274
+ validate_with(:birth_date, 2.days.ago.to_date)
275
+ should_have_error(:birth_date, :between)
251
276
  end
252
277
 
253
- it "should have error when value is before :on_or_after restriction" do
254
- validate_with(:birth_date, 2.days.ago)
255
- should_have_error(:birth_date, :on_or_after)
278
+ it "should have error when value is after latest :between restriction" do
279
+ validate_with(:birth_date, 2.days.from_now.to_date)
280
+ should_have_error(:birth_date, :between)
256
281
  end
257
282
 
258
- it "should be valid when value is equal to :on_or_before restriction" do
259
- validate_with(:birth_date, 1.day.from_now)
260
- should_have_no_error(:birth_date, :on_or_before)
283
+ it "should be valid when value is equal to earliest :between restriction" do
284
+ validate_with(:birth_date, 1.day.ago.to_date)
285
+ should_have_no_error(:birth_date, :between)
261
286
  end
262
287
 
263
- it "should be valid when value value is equal to :on_or_after restriction" do
264
- validate_with(:birth_date, 1.day.ago)
265
- should_have_no_error(:birth_date, :on_or_before)
288
+ it "should be valid when value is equal to latest :between restriction" do
289
+ validate_with(:birth_date, 1.day.from_now.to_date)
290
+ should_have_no_error(:birth_date, :between)
291
+ end
292
+
293
+ it "should allow a range for between restriction" do
294
+ configure_validator(:type => :date, :between => (1.day.ago.to_date)..(1.day.from_now.to_date))
295
+ validate_with(:birth_date, 1.day.from_now.to_date)
296
+ should_have_no_error(:birth_date, :between)
266
297
  end
267
298
  end
268
299
 
269
300
  describe "for time type" do
270
- before :each do
271
- configure_validator(:on_or_before => "23:00", :on_or_after => "06:00", :type => :time)
301
+ before do
302
+ configure_validator(:type => :time, :between => ["09:00", "17:00"])
272
303
  end
273
304
 
274
- it "should have error when value is past :on_or_before restriction" do
275
- validate_with(:birth_time, "23:01")
276
- should_have_error(:birth_time, :on_or_before)
305
+ it "should have error when value is before earlist :between restriction" do
306
+ validate_with(:birth_time, "08:59")
307
+ should_have_error(:birth_time, :between)
277
308
  end
278
309
 
279
- it "should have error when value is before :on_or_after restriction" do
280
- validate_with(:birth_time, "05:59")
281
- should_have_error(:birth_time, :on_or_after)
310
+ it "should have error when value is after latest :between restriction" do
311
+ validate_with(:birth_time, "17:01")
312
+ should_have_error(:birth_time, :between)
282
313
  end
283
314
 
284
- it "should be valid when value is on boundary of :on_or_before restriction" do
285
- validate_with(:birth_time, "23:00")
286
- should_have_no_error(:birth_time, :on_or_before)
315
+ it "should be valid when value is equal to earliest :between restriction" do
316
+ validate_with(:birth_time, "09:00")
317
+ should_have_no_error(:birth_time, :between)
287
318
  end
288
319
 
289
- it "should be valid when value is on boundary of :on_or_after restriction" do
290
- validate_with(:birth_time, "06:00")
291
- should_have_no_error(:birth_time, :on_or_after)
320
+ it "should be valid when value is equal to latest :between restriction" do
321
+ validate_with(:birth_time, "17:00")
322
+ should_have_no_error(:birth_time, :between)
323
+ end
324
+
325
+ it "should allow a range for between restriction" do
326
+ configure_validator(:type => :time, :between => "09:00".."17:00")
327
+ validate_with(:birth_time, "17:00")
328
+ should_have_no_error(:birth_time, :between)
292
329
  end
293
330
  end
294
331
  end
@@ -433,6 +470,6 @@ describe ValidatesTimeliness::Validator do
433
470
  def error_messages
434
471
  return @error_messages if defined?(@error_messages)
435
472
  messages = validator.send(:error_messages)
436
- @error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h }
473
+ @error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.sub(/ (\%s|\{\{\w*\}\}).*/, ''); h }
437
474
  end
438
475
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validates_timeliness
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Meehan
@@ -9,7 +9,7 @@ autorequire: validates_timeliness
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-07 00:00:00 +11:00
12
+ date: 2009-01-02 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies: []
15
15