validates_timeliness 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/CHANGELOG +43 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +320 -0
  4. data/Rakefile +58 -0
  5. data/TODO +8 -0
  6. data/lib/validates_timeliness.rb +66 -0
  7. data/lib/validates_timeliness/action_view/instance_tag.rb +45 -0
  8. data/lib/validates_timeliness/active_record/attribute_methods.rb +157 -0
  9. data/lib/validates_timeliness/active_record/multiparameter_attributes.rb +64 -0
  10. data/lib/validates_timeliness/core_ext/date.rb +13 -0
  11. data/lib/validates_timeliness/core_ext/date_time.rb +13 -0
  12. data/lib/validates_timeliness/core_ext/time.rb +13 -0
  13. data/lib/validates_timeliness/formats.rb +310 -0
  14. data/lib/validates_timeliness/locale/en.yml +11 -0
  15. data/lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb +121 -0
  16. data/lib/validates_timeliness/validation_methods.rb +82 -0
  17. data/lib/validates_timeliness/validator.rb +120 -0
  18. data/spec/action_view/instance_tag_spec.rb +38 -0
  19. data/spec/active_record/attribute_methods_spec.rb +204 -0
  20. data/spec/active_record/multiparameter_attributes_spec.rb +48 -0
  21. data/spec/core_ext/dummy_time_spec.rb +31 -0
  22. data/spec/formats_spec.rb +274 -0
  23. data/spec/ginger_scenarios.rb +19 -0
  24. data/spec/resources/application.rb +2 -0
  25. data/spec/resources/person.rb +3 -0
  26. data/spec/resources/schema.rb +10 -0
  27. data/spec/resources/sqlite_patch.rb +19 -0
  28. data/spec/spec/rails/matchers/validate_timeliness_spec.rb +178 -0
  29. data/spec/spec_helper.rb +54 -0
  30. data/spec/time_travel/MIT-LICENSE +20 -0
  31. data/spec/time_travel/time_extensions.rb +33 -0
  32. data/spec/time_travel/time_travel.rb +12 -0
  33. data/spec/validation_methods_spec.rb +61 -0
  34. data/spec/validator_spec.rb +438 -0
  35. metadata +105 -0
@@ -0,0 +1,54 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ $:.unshift(File.dirname(__FILE__))
3
+ $:.unshift(File.dirname(__FILE__) + '/resources')
4
+
5
+ ENV['RAILS_ENV'] = 'test'
6
+
7
+ require 'rubygems'
8
+ require 'spec'
9
+
10
+ vendored_rails = File.dirname(__FILE__) + '/../../../../vendor/rails'
11
+
12
+ if vendored = File.exists?(vendored_rails)
13
+ Dir.glob(vendored_rails + "/**/lib").each { |dir| $:.unshift dir }
14
+ else
15
+ begin
16
+ require 'ginger'
17
+ rescue LoadError
18
+ end
19
+ if ENV['VERSION']
20
+ gem 'rails', ENV['VERSION']
21
+ else
22
+ gem 'rails'
23
+ end
24
+ end
25
+
26
+ RAILS_ROOT = File.dirname(__FILE__)
27
+
28
+ require 'rails/version'
29
+ require 'active_record'
30
+ require 'active_record/version'
31
+ require 'action_controller'
32
+ require 'action_view'
33
+
34
+ require 'spec/rails'
35
+ require 'time_travel/time_travel'
36
+
37
+ ActiveRecord::Base.default_timezone = :utc
38
+ RAILS_VER = Rails::VERSION::STRING
39
+ puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
40
+
41
+ require 'validates_timeliness'
42
+
43
+ if RAILS_VER >= '2.1'
44
+ Time.zone_default = ActiveSupport::TimeZone['UTC']
45
+ ActiveRecord::Base.time_zone_aware_attributes = true
46
+ end
47
+
48
+ ActiveRecord::Migration.verbose = false
49
+ ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
50
+
51
+ require 'sqlite_patch' if RAILS_VER < '2.1'
52
+
53
+ require 'schema'
54
+ require 'person'
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Peter Yandell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ require 'time'
2
+
3
+ module TimeTravel
4
+ module TimeExtensions
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ base.class_eval do
9
+ class << self
10
+ alias_method :immutable_now, :now
11
+ alias_method :now, :mutable_now
12
+ end
13
+ end
14
+ base.now = nil
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ @@now = nil
20
+
21
+ def now=(time)
22
+ time = Time.parse(time) if time.instance_of?(String)
23
+ @@now = time
24
+ end
25
+
26
+ def mutable_now #:nodoc:
27
+ @@now || immutable_now
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ require 'time_travel/time_extensions'
2
+
3
+ Time.send(:include, TimeTravel::TimeExtensions)
4
+
5
+ def at_time(time)
6
+ Time.now = time
7
+ begin
8
+ yield
9
+ ensure
10
+ Time.now = nil
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe ValidatesTimeliness::ValidationMethods do
4
+ attr_accessor :person
5
+
6
+ describe "parse_date_time" do
7
+ it "should return time object for valid time string" do
8
+ parse_method("2000-01-01 12:13:14", :datetime).should be_kind_of(Time)
9
+ end
10
+
11
+ it "should return nil for time string with invalid date part" do
12
+ parse_method("2000-02-30 12:13:14", :datetime).should be_nil
13
+ end
14
+
15
+ it "should return nil for time string with invalid time part" do
16
+ parse_method("2000-02-01 25:13:14", :datetime).should be_nil
17
+ end
18
+
19
+ it "should return Time object when passed a Time object" do
20
+ parse_method(Time.now, :datetime).should be_kind_of(Time)
21
+ end
22
+
23
+ if RAILS_VER >= '2.1'
24
+ it "should convert time string into current timezone" do
25
+ Time.zone = 'Melbourne'
26
+ time = parse_method("2000-01-01 12:13:14", :datetime)
27
+ Time.zone.utc_offset.should == 10.hours
28
+ end
29
+ end
30
+
31
+ it "should return nil for invalid date string" do
32
+ parse_method("2000-02-30", :date).should be_nil
33
+ end
34
+
35
+ def parse_method(*args)
36
+ ActiveRecord::Base.parse_date_time(*args)
37
+ end
38
+ end
39
+
40
+ describe "make_time" do
41
+
42
+ if RAILS_VER >= '2.1'
43
+
44
+ it "should create time using current timezone" do
45
+ Time.zone = 'Melbourne'
46
+ time = ActiveRecord::Base.send(:make_time, [2000,1,1,12,0,0])
47
+ time.zone.should == "EST"
48
+ end
49
+
50
+ else
51
+
52
+ it "should create time using default timezone" do
53
+ time = ActiveRecord::Base.send(:make_time, [2000,1,1,12,0,0])
54
+ time.zone.should == "UTC"
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,438 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe ValidatesTimeliness::Validator do
4
+ attr_accessor :person, :validator
5
+
6
+ before :all do
7
+ # freezes time using time_travel plugin
8
+ Time.now = Time.utc(2000, 1, 1, 0, 0, 0)
9
+ end
10
+
11
+ after :all do
12
+ Time.now = nil
13
+ end
14
+
15
+ before :each do
16
+ @person = Person.new
17
+ end
18
+
19
+ describe "restriction_value" do
20
+ it "should return Time object when restriction is Time object" do
21
+ restriction_value(Time.now, :datetime).should be_kind_of(Time)
22
+ end
23
+
24
+ it "should return Time object when restriction is string" do
25
+ restriction_value("2007-01-01 12:00", :datetime).should be_kind_of(Time)
26
+ end
27
+
28
+ it "should return Time object when restriction is method and method returns Time object" do
29
+ person.stub!(:datetime_attr).and_return(Time.now)
30
+ restriction_value(:datetime_attr, :datetime).should be_kind_of(Time)
31
+ end
32
+
33
+ it "should return Time object when restriction is method and method returns string" do
34
+ person.stub!(:datetime_attr).and_return("2007-01-01 12:00")
35
+ restriction_value(:datetime_attr, :datetime).should be_kind_of(Time)
36
+ end
37
+
38
+ it "should return Time object when restriction is proc which returns Time object" do
39
+ restriction_value(lambda { Time.now }, :datetime).should be_kind_of(Time)
40
+ end
41
+
42
+ it "should return Time object when restriction is proc which returns string" do
43
+ restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
44
+ end
45
+
46
+ def restriction_value(restriction, type)
47
+ configure_validator(:type => type)
48
+ validator.send(:restriction_value, restriction, person)
49
+ end
50
+ end
51
+
52
+ describe "instance with defaults" do
53
+
54
+ describe "for datetime type" do
55
+ before do
56
+ configure_validator(:type => :datetime)
57
+ end
58
+
59
+ it "should have invalid error when date component is invalid" do
60
+ validate_with(:birth_date_and_time, "2000-01-32 01:02:03")
61
+ should_have_error(:birth_date_and_time, :invalid_datetime)
62
+ end
63
+
64
+ it "should have invalid error when time component is invalid" do
65
+ validate_with(:birth_date_and_time, "2000-01-01 25:02:03")
66
+ should_have_error(:birth_date_and_time, :invalid_datetime)
67
+ end
68
+
69
+ it "should have blank error when value is nil" do
70
+ validate_with(:birth_date_and_time, nil)
71
+ should_have_error(:birth_date_and_time, :blank)
72
+ end
73
+
74
+ it "should have no errors when value is valid" do
75
+ validate_with(:birth_date_and_time, "2000-01-01 12:00:00")
76
+ should_have_no_error(:birth_date_and_time, :invalid_datetime)
77
+ end
78
+ end
79
+
80
+ describe "for date type" do
81
+ before do
82
+ configure_validator(:type => :date)
83
+ end
84
+
85
+ it "should have invalid error when value is invalid" do
86
+ validate_with(:birth_date, "2000-01-32")
87
+ should_have_error(:birth_date, :invalid_date)
88
+ end
89
+
90
+ it "should have blank error when value is nil" do
91
+ validate_with(:birth_date, nil)
92
+ should_have_error(:birth_date, :blank)
93
+ end
94
+
95
+ it "should have no error when value is valid" do
96
+ validate_with(:birth_date, "2000-01-31")
97
+ should_have_no_error(:birth_date, :invalid_date)
98
+ end
99
+ end
100
+
101
+ describe "for time type" do
102
+ before do
103
+ configure_validator(:type => :time)
104
+ end
105
+
106
+ it "should have invalid error when value is invalid" do
107
+ validate_with(:birth_time, "25:00")
108
+ should_have_error(:birth_time, :invalid_time)
109
+ end
110
+
111
+ it "should have blank error when value is nil" do
112
+ validate_with(:birth_time, nil)
113
+ should_have_error(:birth_time, :blank)
114
+ end
115
+
116
+ it "should have no errors when value is valid" do
117
+ validate_with(:birth_date_and_time, "12:00")
118
+ should_have_no_error(:birth_time, :invalid_time)
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ describe "instance with before and after restrictions" do
125
+
126
+ describe "for datetime type" do
127
+ before :each do
128
+ configure_validator(:before => lambda { Time.now }, :after => lambda { 1.day.ago})
129
+ end
130
+
131
+ it "should have before error when value is past :before restriction" do
132
+ validate_with(:birth_date_and_time, 1.minute.from_now)
133
+ should_have_error(:birth_date_and_time, :before)
134
+ end
135
+
136
+ it "should have before error when value is on boundary of :before restriction" do
137
+ validate_with(:birth_date_and_time, Time.now)
138
+ should_have_error(:birth_date_and_time, :before)
139
+ end
140
+
141
+ it "should have after error when value is before :after restriction" do
142
+ validate_with(:birth_date_and_time, 2.days.ago)
143
+ should_have_error(:birth_date_and_time, :after)
144
+ end
145
+
146
+ it "should have after error when value is on boundary of :after restriction" do
147
+ validate_with(:birth_date_and_time, 1.day.ago)
148
+ should_have_error(:birth_date_and_time, :after)
149
+ end
150
+ end
151
+
152
+ describe "for date type" do
153
+ before :each do
154
+ configure_validator(:before => 1.day.from_now, :after => 1.day.ago, :type => :date)
155
+ end
156
+
157
+ it "should have error when value is past :before restriction" do
158
+ validate_with(:birth_date, 2.days.from_now)
159
+ should_have_error(:birth_date, :before)
160
+ end
161
+
162
+ it "should have error when value is before :after restriction" do
163
+ validate_with(:birth_date, 2.days.ago)
164
+ should_have_error(:birth_date, :after)
165
+ end
166
+
167
+ it "should have no error when value is before :before restriction" do
168
+ validate_with(:birth_date, Time.now)
169
+ should_have_no_error(:birth_date, :before)
170
+ end
171
+
172
+ it "should have no error when value is after :after restriction" do
173
+ validate_with(:birth_date, Time.now)
174
+ should_have_no_error(:birth_date, :after)
175
+ end
176
+ end
177
+
178
+ describe "for time type" do
179
+ before :each do
180
+ configure_validator(:before => "23:00", :after => "06:00", :type => :time)
181
+ end
182
+
183
+ it "should have error when value is on boundary of :before restriction" do
184
+ validate_with(:birth_time, "23:00")
185
+ should_have_error(:birth_time, :before)
186
+ end
187
+
188
+ it "should have error when value is on boundary of :after restriction" do
189
+ validate_with(:birth_time, "06:00")
190
+ should_have_error(:birth_time, :after)
191
+ end
192
+
193
+ it "should have error when value is past :before restriction" do
194
+ validate_with(:birth_time, "23:01")
195
+ should_have_error(:birth_time, :before)
196
+ end
197
+
198
+ it "should have error when value is before :after restriction" do
199
+ validate_with(:birth_time, "05:59")
200
+ should_have_error(:birth_time, :after)
201
+ end
202
+
203
+ it "should not have error when value is before :before restriction" do
204
+ validate_with(:birth_time, "22:59")
205
+ should_have_no_error(:birth_time, :before)
206
+ end
207
+
208
+ it "should have error when value is before :after restriction" do
209
+ validate_with(:birth_time, "06:01")
210
+ should_have_no_error(:birth_time, :before)
211
+ end
212
+ end
213
+ end
214
+
215
+ describe "instance with on_or_before and on_or_after restrictions" do
216
+
217
+ describe "for datetime type" do
218
+ before do
219
+ configure_validator(:on_or_before => Time.now.at_midnight, :on_or_after => 1.day.ago)
220
+ end
221
+
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)
225
+ end
226
+
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)
230
+ end
231
+
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)
235
+ end
236
+
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)
240
+ end
241
+ end
242
+
243
+ 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)
246
+ end
247
+
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)
251
+ end
252
+
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)
256
+ end
257
+
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)
261
+ end
262
+
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)
266
+ end
267
+ end
268
+
269
+ describe "for time type" do
270
+ before :each do
271
+ configure_validator(:on_or_before => "23:00", :on_or_after => "06:00", :type => :time)
272
+ end
273
+
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)
277
+ end
278
+
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)
282
+ end
283
+
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)
287
+ end
288
+
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)
292
+ end
293
+ end
294
+ end
295
+
296
+ describe "instance with mixed value and restriction types" do
297
+
298
+ it "should validate datetime attribute with Date restriction" do
299
+ configure_validator(:type => :datetime, :on_or_before => Date.new(2000,1,1))
300
+ validate_with(:birth_date_and_time, "2000-01-01 00:00:00")
301
+ should_have_no_error(:birth_date_and_time, :on_or_before)
302
+ end
303
+
304
+ it "should validate date attribute with DateTime restriction value" do
305
+ configure_validator(:type => :date, :on_or_before => DateTime.new(2000, 1, 1, 0,0,0))
306
+ validate_with(:birth_date, "2000-01-01")
307
+ should_have_no_error(:birth_date, :on_or_before)
308
+ end
309
+
310
+ it "should validate date attribute with Time restriction value" do
311
+ configure_validator(:type => :date, :on_or_before => Time.utc(2000, 1, 1, 0,0,0))
312
+ validate_with(:birth_date, "2000-01-01")
313
+ should_have_no_error(:birth_date, :on_or_before)
314
+ end
315
+
316
+ it "should validate time attribute with DateTime restriction value" do
317
+ configure_validator(:type => :time, :on_or_before => DateTime.new(2000, 1, 1, 12,0,0))
318
+ validate_with(:birth_time, "12:00")
319
+ should_have_no_error(:birth_time, :on_or_before)
320
+ end
321
+
322
+ it "should validate time attribute with Time restriction value" do
323
+ configure_validator(:type => :time, :on_or_before => Time.utc(2000, 1, 1, 12,0,0))
324
+ validate_with(:birth_time, "12:00")
325
+ should_have_no_error(:birth_time, :on_or_before)
326
+ end
327
+ end
328
+
329
+ describe "restriction errors" do
330
+ before :each do
331
+ configure_validator(:type => :date, :before => lambda { raise })
332
+ end
333
+
334
+ it "should be added by default for invalid restriction" do
335
+ ValidatesTimeliness::Validator.ignore_restriction_errors = false
336
+ validate_with(:birth_date, Date.today)
337
+ person.errors.on(:birth_date).should match(/restriction 'before' value was invalid/)
338
+ end
339
+
340
+ it "should not be added when ignore switch is true and restriction is invalid" do
341
+ ValidatesTimeliness::Validator.ignore_restriction_errors = true
342
+ person.should be_valid
343
+ end
344
+
345
+ after :all do
346
+ ValidatesTimeliness::Validator.ignore_restriction_errors = false
347
+ end
348
+ end
349
+
350
+ describe "restriction value error message" do
351
+
352
+ describe "default formats" do
353
+
354
+ it "should format datetime value of restriction" do
355
+ configure_validator(:type => :datetime, :after => 1.day.from_now)
356
+ validate_with(:birth_date_and_time, Time.now)
357
+ person.errors.on(:birth_date_and_time).should match(/after \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\Z/)
358
+ end
359
+
360
+ it "should format date value of restriction" do
361
+ configure_validator(:type => :date, :after => 1.day.from_now)
362
+ validate_with(:birth_date, Time.now)
363
+ person.errors.on(:birth_date).should match(/after \d{4}-\d{2}-\d{2}\Z/)
364
+ end
365
+
366
+ it "should format time value of restriction" do
367
+ configure_validator(:type => :time, :after => '12:00')
368
+ validate_with(:birth_time, '11:59')
369
+ person.errors.on(:birth_time).should match(/after \d{2}:\d{2}:\d{2}\Z/)
370
+ end
371
+ end
372
+
373
+ describe "custom formats" do
374
+
375
+ before :all do
376
+ @@formats = ValidatesTimeliness::Validator.error_value_formats
377
+ ValidatesTimeliness::Validator.error_value_formats = {
378
+ :time => '%H:%M %p',
379
+ :date => '%d-%m-%Y',
380
+ :datetime => '%d-%m-%Y %H:%M %p'
381
+ }
382
+ end
383
+
384
+ it "should format datetime value of restriction" do
385
+ configure_validator(:type => :datetime, :after => 1.day.from_now)
386
+ validate_with(:birth_date_and_time, Time.now)
387
+ person.errors.on(:birth_date_and_time).should match(/after \d{2}-\d{2}-\d{4} \d{2}:\d{2} (AM|PM)\Z/)
388
+ end
389
+
390
+ it "should format date value of restriction" do
391
+ configure_validator(:type => :date, :after => 1.day.from_now)
392
+ validate_with(:birth_date, Time.now)
393
+ person.errors.on(:birth_date).should match(/after \d{2}-\d{2}-\d{4}\Z/)
394
+ end
395
+
396
+ it "should format time value of restriction" do
397
+ configure_validator(:type => :time, :after => '12:00')
398
+ validate_with(:birth_time, '11:59')
399
+ person.errors.on(:birth_time).should match(/after \d{2}:\d{2} (AM|PM)\Z/)
400
+ end
401
+
402
+ after :all do
403
+ ValidatesTimeliness::Validator.error_value_formats = @@formats
404
+ end
405
+ end
406
+
407
+ end
408
+
409
+ def configure_validator(options={})
410
+ @validator = ValidatesTimeliness::Validator.new(options)
411
+ end
412
+
413
+ def validate_with(attr_name, value)
414
+ person.send("#{attr_name}=", value)
415
+ validator.call(person, attr_name)
416
+ end
417
+
418
+ def should_have_error(attr_name, error)
419
+ message = error_messages[error]
420
+ person.errors.on(attr_name).should match(/#{message}/)
421
+ end
422
+
423
+ def should_have_no_error(attr_name, error)
424
+ message = error_messages[error]
425
+ errors = person.errors.on(attr_name)
426
+ if errors
427
+ errors.should_not match(/#{message}/)
428
+ else
429
+ errors.should be_nil
430
+ end
431
+ end
432
+
433
+ def error_messages
434
+ return @error_messages if defined?(@error_messages)
435
+ messages = validator.send(:error_messages)
436
+ @error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h }
437
+ end
438
+ end