szilm-validates_timeliness 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/CHANGELOG +116 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +403 -0
  4. data/Rakefile +52 -0
  5. data/TODO +8 -0
  6. data/lib/validates_timeliness.rb +49 -0
  7. data/lib/validates_timeliness/action_view/instance_tag.rb +52 -0
  8. data/lib/validates_timeliness/active_record/attribute_methods.rb +77 -0
  9. data/lib/validates_timeliness/active_record/multiparameter_attributes.rb +69 -0
  10. data/lib/validates_timeliness/formats.rb +365 -0
  11. data/lib/validates_timeliness/locale/en.yml +18 -0
  12. data/lib/validates_timeliness/matcher.rb +1 -0
  13. data/lib/validates_timeliness/parser.rb +44 -0
  14. data/lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb +162 -0
  15. data/lib/validates_timeliness/validation_methods.rb +46 -0
  16. data/lib/validates_timeliness/validator.rb +230 -0
  17. data/lib/validates_timeliness/version.rb +3 -0
  18. data/spec/action_view/instance_tag_spec.rb +194 -0
  19. data/spec/active_record/attribute_methods_spec.rb +157 -0
  20. data/spec/active_record/multiparameter_attributes_spec.rb +118 -0
  21. data/spec/formats_spec.rb +306 -0
  22. data/spec/ginger_scenarios.rb +19 -0
  23. data/spec/parser_spec.rb +61 -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 +245 -0
  29. data/spec/spec_helper.rb +58 -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/validator_spec.rb +713 -0
  34. metadata +102 -0
@@ -0,0 +1,58 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ $:.unshift(File.dirname(__FILE__))
3
+ $:.unshift(File.dirname(__FILE__) + '/resources')
4
+
5
+ RAILS_ENV = ENV['RAILS_ENV'] = 'test'
6
+
7
+ require 'rubygems'
8
+ require 'spec/autorun'
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
+ require 'action_mailer'
34
+
35
+ require 'spec/rails'
36
+ require 'time_travel/time_travel'
37
+
38
+ ActiveRecord::Base.default_timezone = :utc
39
+ RAILS_VER = Rails::VERSION::STRING
40
+ puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
41
+
42
+ if RAILS_VER >= '2.1'
43
+ Time.zone_default = ActiveSupport::TimeZone['UTC']
44
+ ActiveRecord::Base.time_zone_aware_attributes = true
45
+ end
46
+
47
+ require 'validates_timeliness'
48
+ require 'validates_timeliness/matcher'
49
+
50
+ ValidatesTimeliness.enable_datetime_select_extension!
51
+
52
+ ActiveRecord::Migration.verbose = false
53
+ ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
54
+
55
+ require 'sqlite_patch' if RAILS_VER < '2.1'
56
+
57
+ require 'schema'
58
+ 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,713 @@
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
+ def person
16
+ @person ||= Person.new
17
+ end
18
+
19
+ describe "option keys validation" do
20
+ before(:all) do
21
+ ActiveSupport::Deprecation.silenced = true
22
+ end
23
+
24
+ before do
25
+ keys = ValidatesTimeliness::Validator::VALID_OPTION_KEYS - [:invalid_date_message, :invalid_time_message, :with_date, :with_time]
26
+ @valid_options = keys.inject({}) {|hash, opt| hash[opt] = nil; hash }
27
+ end
28
+
29
+ it "should raise error if invalid option key passed" do
30
+ @valid_options.update(:invalid_key => 'will not open lock')
31
+ lambda { Person.validates_datetime(@valid_options) }.should raise_error(ArgumentError)
32
+ end
33
+
34
+ it "should not raise error if option keys are valid" do
35
+ lambda { Person.validates_datetime(@valid_options) }.should_not raise_error(ArgumentError)
36
+ end
37
+
38
+ it "should display deprecation notice for :equal_to" do
39
+ ::ActiveSupport::Deprecation.should_receive(:warn)
40
+ Person.validates_datetime :equal_to => Time.now
41
+ end
42
+
43
+ after(:all) do
44
+ ActiveSupport::Deprecation.silenced = false
45
+ end
46
+ end
47
+
48
+ describe "evaluate_option_value" do
49
+ it "should return Time object when restriction is Time object" do
50
+ evaluate_option_value(Time.now, :datetime).should be_kind_of(Time)
51
+ end
52
+
53
+ it "should return Time object when restriction is string" do
54
+ evaluate_option_value("2007-01-01 12:00", :datetime).should be_kind_of(Time)
55
+ end
56
+
57
+ it "should return Time object when restriction is method and method returns Time object" do
58
+ person.stub!(:datetime_attr).and_return(Time.now)
59
+ evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
60
+ end
61
+
62
+ it "should return Time object when restriction is method and method returns string" do
63
+ person.stub!(:datetime_attr).and_return("2007-01-01 12:00")
64
+ evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
65
+ end
66
+
67
+ it "should return Time object when restriction is proc which returns Time object" do
68
+ evaluate_option_value(lambda { Time.now }, :datetime).should be_kind_of(Time)
69
+ end
70
+
71
+ it "should return Time object when restriction is proc which returns string" do
72
+ evaluate_option_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
73
+ end
74
+
75
+ it "should return array of Time objects when restriction is array of Time objects" do
76
+ time1, time2 = Time.now, 1.day.ago
77
+ evaluate_option_value([time1, time2], :datetime).should == [time2, time1]
78
+ end
79
+
80
+ it "should return array of Time objects when restriction is array of strings" do
81
+ time1, time2 = "2000-01-02", "2000-01-01"
82
+ evaluate_option_value([time1, time2], :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
83
+ end
84
+
85
+ it "should return array of Time objects when restriction is Range of Time objects" do
86
+ time1, time2 = Time.now, 1.day.ago
87
+ evaluate_option_value(time1..time2, :datetime).should == [time2, time1]
88
+ end
89
+
90
+ it "should return array of Time objects when restriction is Range of time strings" do
91
+ time1, time2 = "2000-01-02", "2000-01-01"
92
+ evaluate_option_value(time1..time2, :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
93
+ end
94
+ def evaluate_option_value(restriction, type)
95
+ configure_validator(:type => type)
96
+ validator.class.send(:evaluate_option_value, restriction, type, person)
97
+ end
98
+ end
99
+
100
+ describe "instance with defaults" do
101
+
102
+ describe "for datetime type" do
103
+ before do
104
+ configure_validator(:type => :datetime)
105
+ end
106
+
107
+ it "should have invalid error when date component is invalid" do
108
+ validate_with(:birth_date_and_time, "2000-01-32 01:02:03")
109
+ should_have_error(:birth_date_and_time, :invalid_datetime)
110
+ end
111
+
112
+ it "should have invalid error when time component is invalid" do
113
+ validate_with(:birth_date_and_time, "2000-01-01 25:02:03")
114
+ should_have_error(:birth_date_and_time, :invalid_datetime)
115
+ end
116
+
117
+ it "should have blank error when value is nil" do
118
+ validate_with(:birth_date_and_time, nil)
119
+ should_have_error(:birth_date_and_time, :blank)
120
+ end
121
+
122
+ it "should have no errors when value is valid" do
123
+ validate_with(:birth_date_and_time, "2000-01-01 12:00:00")
124
+ should_have_no_error(:birth_date_and_time, :invalid_datetime)
125
+ end
126
+ end
127
+
128
+ describe "for date type" do
129
+ before do
130
+ configure_validator(:type => :date)
131
+ end
132
+
133
+ it "should have invalid error when value is invalid" do
134
+ validate_with(:birth_date, "2000-01-32")
135
+ should_have_error(:birth_date, :invalid_date)
136
+ end
137
+
138
+ it "should have blank error when value is nil" do
139
+ validate_with(:birth_date, nil)
140
+ should_have_error(:birth_date, :blank)
141
+ end
142
+
143
+ it "should have no error when value is valid" do
144
+ validate_with(:birth_date, "2000-01-31")
145
+ should_have_no_error(:birth_date, :invalid_date)
146
+ end
147
+ end
148
+
149
+ describe "for time type" do
150
+ before do
151
+ configure_validator(:type => :time)
152
+ end
153
+
154
+ it "should have invalid error when value is invalid" do
155
+ validate_with(:birth_time, "25:00")
156
+ should_have_error(:birth_time, :invalid_time)
157
+ end
158
+
159
+ it "should have blank error when value is nil" do
160
+ validate_with(:birth_time, nil)
161
+ should_have_error(:birth_time, :blank)
162
+ end
163
+
164
+ it "should have no errors when value is valid" do
165
+ validate_with(:birth_date_and_time, "12:00")
166
+ should_have_no_error(:birth_time, :invalid_time)
167
+ end
168
+ end
169
+
170
+ end
171
+
172
+ describe "instance with before and after restrictions" do
173
+
174
+ describe "for datetime type" do
175
+ before :each do
176
+ configure_validator(:before => lambda { Time.now }, :after => lambda { 1.day.ago})
177
+ end
178
+
179
+ it "should have before error when value is past :before restriction" do
180
+ validate_with(:birth_date_and_time, 1.minute.from_now)
181
+ should_have_error(:birth_date_and_time, :before)
182
+ end
183
+
184
+ it "should have before error when value is on boundary of :before restriction" do
185
+ validate_with(:birth_date_and_time, Time.now)
186
+ should_have_error(:birth_date_and_time, :before)
187
+ end
188
+
189
+ it "should have after error when value is before :after restriction" do
190
+ validate_with(:birth_date_and_time, 2.days.ago)
191
+ should_have_error(:birth_date_and_time, :after)
192
+ end
193
+
194
+ it "should have after error when value is on boundary of :after restriction" do
195
+ validate_with(:birth_date_and_time, 1.day.ago)
196
+ should_have_error(:birth_date_and_time, :after)
197
+ end
198
+ end
199
+
200
+ describe "for date type" do
201
+ before :each do
202
+ configure_validator(:before => 1.day.from_now, :after => 1.day.ago, :type => :date)
203
+ end
204
+
205
+ it "should have error when value is past :before restriction" do
206
+ validate_with(:birth_date, 2.days.from_now)
207
+ should_have_error(:birth_date, :before)
208
+ end
209
+
210
+ it "should have error when value is before :after restriction" do
211
+ validate_with(:birth_date, 2.days.ago)
212
+ should_have_error(:birth_date, :after)
213
+ end
214
+
215
+ it "should have no error when value is before :before restriction" do
216
+ validate_with(:birth_date, Time.now)
217
+ should_have_no_error(:birth_date, :before)
218
+ end
219
+
220
+ it "should have no error when value is after :after restriction" do
221
+ validate_with(:birth_date, Time.now)
222
+ should_have_no_error(:birth_date, :after)
223
+ end
224
+ end
225
+
226
+ describe "for time type" do
227
+ before :each do
228
+ configure_validator(:before => "23:00", :after => "06:00", :type => :time)
229
+ end
230
+
231
+ it "should have error when value is on boundary of :before restriction" do
232
+ validate_with(:birth_time, "23:00")
233
+ should_have_error(:birth_time, :before)
234
+ end
235
+
236
+ it "should have error when value is on boundary of :after restriction" do
237
+ validate_with(:birth_time, "06:00")
238
+ should_have_error(:birth_time, :after)
239
+ end
240
+
241
+ it "should have error when value is past :before restriction" do
242
+ validate_with(:birth_time, "23:01")
243
+ should_have_error(:birth_time, :before)
244
+ end
245
+
246
+ it "should have error when value is before :after restriction" do
247
+ validate_with(:birth_time, "05:59")
248
+ should_have_error(:birth_time, :after)
249
+ end
250
+
251
+ it "should not have error when value is before :before restriction" do
252
+ validate_with(:birth_time, "22:59")
253
+ should_have_no_error(:birth_time, :before)
254
+ end
255
+
256
+ it "should have error when value is before :after restriction" do
257
+ validate_with(:birth_time, "06:01")
258
+ should_have_no_error(:birth_time, :before)
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "instance with between restriction" do
264
+
265
+ describe "for datetime type" do
266
+ before do
267
+ configure_validator(:between => [1.day.ago.at_midnight, 1.day.from_now.at_midnight])
268
+ end
269
+
270
+ it "should have error when value is before earlist :between restriction" do
271
+ validate_with(:birth_date_and_time, 2.days.ago)
272
+ should_have_error(:birth_date_and_time, :between)
273
+ end
274
+
275
+ it "should have error when value is after latest :between restriction" do
276
+ validate_with(:birth_date_and_time, 2.days.from_now)
277
+ should_have_error(:birth_date_and_time, :between)
278
+ end
279
+
280
+ it "should be valid when value is equal to earliest :between restriction" do
281
+ validate_with(:birth_date_and_time, 1.day.ago.at_midnight)
282
+ should_have_no_error(:birth_date_and_time, :between)
283
+ end
284
+
285
+ it "should be valid when value is equal to latest :between restriction" do
286
+ validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
287
+ should_have_no_error(:birth_date_and_time, :between)
288
+ end
289
+
290
+ it "should allow a range for between restriction" do
291
+ configure_validator(:type => :datetime, :between => (1.day.ago.at_midnight)..(1.day.from_now.at_midnight))
292
+ validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
293
+ should_have_no_error(:birth_date_and_time, :between)
294
+ end
295
+ end
296
+
297
+ describe "for date type" do
298
+ before do
299
+ configure_validator(:type => :date, :between => [1.day.ago.to_date, 1.day.from_now.to_date])
300
+ end
301
+
302
+ it "should have error when value is before earlist :between restriction" do
303
+ validate_with(:birth_date, 2.days.ago.to_date)
304
+ should_have_error(:birth_date, :between)
305
+ end
306
+
307
+ it "should have error when value is after latest :between restriction" do
308
+ validate_with(:birth_date, 2.days.from_now.to_date)
309
+ should_have_error(:birth_date, :between)
310
+ end
311
+
312
+ it "should be valid when value is equal to earliest :between restriction" do
313
+ validate_with(:birth_date, 1.day.ago.to_date)
314
+ should_have_no_error(:birth_date, :between)
315
+ end
316
+
317
+ it "should be valid when value is equal to latest :between restriction" do
318
+ validate_with(:birth_date, 1.day.from_now.to_date)
319
+ should_have_no_error(:birth_date, :between)
320
+ end
321
+
322
+ it "should allow a range for between restriction" do
323
+ configure_validator(:type => :date, :between => (1.day.ago.to_date)..(1.day.from_now.to_date))
324
+ validate_with(:birth_date, 1.day.from_now.to_date)
325
+ should_have_no_error(:birth_date, :between)
326
+ end
327
+ end
328
+
329
+ describe "for time type" do
330
+ before do
331
+ configure_validator(:type => :time, :between => ["09:00", "17:00"])
332
+ end
333
+
334
+ it "should have error when value is before earlist :between restriction" do
335
+ validate_with(:birth_time, "08:59")
336
+ should_have_error(:birth_time, :between)
337
+ end
338
+
339
+ it "should have error when value is after latest :between restriction" do
340
+ validate_with(:birth_time, "17:01")
341
+ should_have_error(:birth_time, :between)
342
+ end
343
+
344
+ it "should be valid when value is equal to earliest :between restriction" do
345
+ validate_with(:birth_time, "09:00")
346
+ should_have_no_error(:birth_time, :between)
347
+ end
348
+
349
+ it "should be valid when value is equal to latest :between restriction" do
350
+ validate_with(:birth_time, "17:00")
351
+ should_have_no_error(:birth_time, :between)
352
+ end
353
+
354
+ it "should allow a range for between restriction" do
355
+ configure_validator(:type => :time, :between => "09:00".."17:00")
356
+ validate_with(:birth_time, "17:00")
357
+ should_have_no_error(:birth_time, :between)
358
+ end
359
+ end
360
+ end
361
+
362
+ describe "instance with :is_at restriction" do
363
+
364
+ describe "for datetime type" do
365
+ before do
366
+ configure_validator(:is_at => Time.now)
367
+ end
368
+
369
+ it "should have error when value not equal to :is_at restriction" do
370
+ validate_with(:birth_date_and_time, Time.now + 1)
371
+ should_have_error(:birth_date_and_time, :is_at)
372
+ end
373
+
374
+ it "should be valid when value is equal to :is_at restriction" do
375
+ validate_with(:birth_date_and_time, Time.now)
376
+ should_have_no_error(:birth_date_and_time, :is_at)
377
+ end
378
+ end
379
+
380
+ describe "for date type" do
381
+ before do
382
+ configure_validator(:type => :date, :is_at => Date.today)
383
+ end
384
+
385
+ it "should have error when value is not equal to :is_at restriction" do
386
+ validate_with(:birth_date, Date.today + 1)
387
+ should_have_error(:birth_date, :is_at)
388
+ end
389
+
390
+ it "should be valid when value is equal to :is_at restriction" do
391
+ validate_with(:birth_date, Date.today)
392
+ should_have_no_error(:birth_date, :is_at)
393
+ end
394
+ end
395
+
396
+ describe "for time type" do
397
+ before do
398
+ configure_validator(:type => :time, :is_at => "09:00:00")
399
+ end
400
+
401
+ it "should have error when value is not equal to :is_at restriction" do
402
+ validate_with(:birth_time, "09:00:01")
403
+ should_have_error(:birth_time, :is_at)
404
+ end
405
+
406
+ it "should be valid when value is equal to :is_at restriction" do
407
+ validate_with(:birth_time, "09:00:00")
408
+ should_have_no_error(:birth_time, :is_at)
409
+ end
410
+ end
411
+ end
412
+
413
+ describe "instance with :ignore_usec option" do
414
+
415
+ it "should ignore usec on time values when evaluated" do
416
+ configure_validator(:is_at => Time.utc(2000, 1, 1, 0, 0, 0, 0), :ignore_usec => true)
417
+ validate_with(:birth_date_and_time, Time.utc(2000, 1, 1, 0, 0, 0, 500))
418
+ should_have_no_error(:birth_date_and_time, :is_at)
419
+ end
420
+
421
+ end
422
+
423
+ describe "instance with :with_time option" do
424
+
425
+ it "should validate date attribute as datetime combining value of :with_time against restrictions " do
426
+ configure_validator(:type => :date, :with_time => '12:31', :on_or_before => Time.mktime(2000,1,1,12,30))
427
+ validate_with(:birth_date, "2000-01-01")
428
+ should_have_error(:birth_date, :on_or_before)
429
+ end
430
+
431
+ it "should skip restriction validation if :with_time value is nil" do
432
+ configure_validator(:type => :date, :with_time => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
433
+ validate_with(:birth_date, "2000-01-01")
434
+ should_have_no_error(:birth_date, :on_or_before)
435
+ end
436
+
437
+ it "should should ignore usec value on combined value if :ignore_usec option is true" do
438
+ configure_validator(:type => :date, :with_time => Time.mktime(2000,1,1,12,30,0,500), :is_at => Time.mktime(2000,1,1,12,30), :ignore_usec => true)
439
+ validate_with(:birth_date, "2000-01-01")
440
+ should_have_no_error(:birth_date, :is_at)
441
+ end
442
+ end
443
+
444
+ describe "instance with :with_date option" do
445
+
446
+ it "should validate time attribute as datetime combining value of :with_date against restrictions " do
447
+ configure_validator(:type => :time, :with_date => '2009-01-01', :on_or_before => Time.mktime(2000,1,1,12,30))
448
+ validate_with(:birth_time, "12:30")
449
+ should_have_error(:birth_time, :on_or_before)
450
+ end
451
+
452
+ it "should skip restriction validation if :with_date value is nil" do
453
+ configure_validator(:type => :time, :with_date => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
454
+ validate_with(:birth_time, "12:30")
455
+ should_have_no_error(:birth_time, :on_or_before)
456
+ end
457
+
458
+ it "should should ignore usec value on combined value if :ignore_usec option is true" do
459
+ configure_validator(:type => :time, :with_date => Date.new(2000,1,1), :on_or_before => Time.mktime(2000,1,1,12,30), :ignore_usec => true)
460
+ validate_with(:birth_time, Time.mktime(2000,1,1,12,30,0,50))
461
+ should_have_no_error(:birth_time, :on_or_before)
462
+ end
463
+ end
464
+
465
+ describe "instance with mixed value and restriction types" do
466
+
467
+ it "should validate datetime attribute with Date restriction" do
468
+ configure_validator(:type => :datetime, :on_or_before => Date.new(2000,1,1))
469
+ validate_with(:birth_date_and_time, "2000-01-01 00:00:00")
470
+ should_have_no_error(:birth_date_and_time, :on_or_before)
471
+ end
472
+
473
+ it "should validate date attribute with DateTime restriction value" do
474
+ configure_validator(:type => :date, :on_or_before => DateTime.new(2000, 1, 1, 0,0,0))
475
+ validate_with(:birth_date, "2000-01-01")
476
+ should_have_no_error(:birth_date, :on_or_before)
477
+ end
478
+
479
+ it "should validate date attribute with Time restriction value" do
480
+ configure_validator(:type => :date, :on_or_before => Time.utc(2000, 1, 1, 0,0,0))
481
+ validate_with(:birth_date, "2000-01-01")
482
+ should_have_no_error(:birth_date, :on_or_before)
483
+ end
484
+
485
+ it "should validate time attribute with DateTime restriction value" do
486
+ configure_validator(:type => :time, :on_or_before => DateTime.new(2000, 1, 1, 12,0,0))
487
+ validate_with(:birth_time, "12:00")
488
+ should_have_no_error(:birth_time, :on_or_before)
489
+ end
490
+
491
+ it "should validate time attribute with Time restriction value" do
492
+ configure_validator(:type => :time, :on_or_before => Time.utc(2000, 1, 1, 12,0,0))
493
+ validate_with(:birth_time, "12:00")
494
+ should_have_no_error(:birth_time, :on_or_before)
495
+ end
496
+ end
497
+
498
+ describe "instance with format option" do
499
+ it "should validate attribute when value matches format" do
500
+ configure_validator(:type => :time, :format => 'hh:nn:ss')
501
+ validate_with(:birth_time, "12:00:00")
502
+ should_have_no_error(:birth_time, :invalid_time)
503
+ end
504
+
505
+ it "should not validate attribute when value does not match format" do
506
+ configure_validator(:type => :time, :format => 'hh:nn:ss')
507
+ validate_with(:birth_time, "12:00")
508
+ should_have_error(:birth_time, :invalid_time)
509
+ end
510
+ end
511
+
512
+ if defined?(I18n)
513
+
514
+ describe "localized error messages" do
515
+ before(:all) do
516
+ translations = {
517
+ :activerecord => {:errors => {:messages => { :after => 'retfa {{restriction}}' }}},
518
+ :validates_timeliness => {:error_value_formats => {}}
519
+ }
520
+ I18n.backend.store_translations 'zz', translations
521
+ I18n.locale = :zz
522
+ end
523
+
524
+ it "should be used if defined" do
525
+ configure_validator(:type => :date, :after => Date.today)
526
+ validate_with(:birth_date, 1.day.ago)
527
+ person.errors.on(:birth_date).should match(/retfa/)
528
+ end
529
+
530
+ it "should use I18n translation missing message when not defined" do
531
+ configure_validator(:type => :date, :on_or_after => Date.today)
532
+ validate_with(:birth_date, 1.day.ago)
533
+ person.errors.on(:birth_date).should match(/translation missing/)
534
+ end
535
+
536
+ after(:all) do
537
+ I18n.locale = :en
538
+ end
539
+
540
+ end
541
+
542
+ end
543
+
544
+ describe "custom_error_messages" do
545
+ it "should return hash of custom error messages from configuration with _message truncated from keys" do
546
+ configure_validator(:type => :date, :invalid_date_message => 'thats no date')
547
+ validator.send(:custom_error_messages)[:invalid_date].should == 'thats no date'
548
+ end
549
+
550
+ it "should return empty hash if no custom error messages in configuration" do
551
+ configure_validator(:type => :date)
552
+ validator.send(:custom_error_messages).should be_empty
553
+ end
554
+ end
555
+
556
+ describe "interpolation_values" do
557
+ if defined?(I18n)
558
+ it "should return hash of interpolation keys with restriction values" do
559
+ before = '1900-01-01'
560
+ configure_validator(:type => :date, :before => before)
561
+ validator.send(:interpolation_values, :before, before.to_date).should == {:restriction => before}
562
+ end
563
+ else
564
+ it "should return array of interpolation values" do
565
+ before = '1900-01-01'
566
+ configure_validator(:type => :date, :before => before)
567
+ validator.send(:interpolation_values, :before, before.to_date).should == [before]
568
+ end
569
+ end
570
+ end
571
+
572
+ describe "restriction errors" do
573
+ before :each do
574
+ configure_validator(:type => :date, :before => lambda { raise })
575
+ end
576
+
577
+ it "should be added by default for invalid restriction" do
578
+ ValidatesTimeliness::Validator.ignore_restriction_errors = false
579
+ validate_with(:birth_date, Date.today)
580
+ person.errors.on(:birth_date).should match(/restriction 'before' value was invalid/)
581
+ end
582
+
583
+ it "should not be added when ignore switch is true and restriction is invalid" do
584
+ ValidatesTimeliness::Validator.ignore_restriction_errors = true
585
+ person.should be_valid
586
+ end
587
+
588
+ after :all do
589
+ ValidatesTimeliness::Validator.ignore_restriction_errors = false
590
+ end
591
+ end
592
+
593
+ describe "restriction value error message" do
594
+
595
+ describe "default formats" do
596
+
597
+ it "should format datetime value of restriction" do
598
+ configure_validator(:type => :datetime, :after => 1.day.from_now)
599
+ validate_with(:birth_date_and_time, Time.now)
600
+ person.errors.on(:birth_date_and_time).should match(/after \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\Z/)
601
+ end
602
+
603
+ it "should format date value of restriction" do
604
+ configure_validator(:type => :date, :after => 1.day.from_now)
605
+ validate_with(:birth_date, Time.now)
606
+ person.errors.on(:birth_date).should match(/after \d{4}-\d{2}-\d{2}\Z/)
607
+ end
608
+
609
+ it "should format time value of restriction" do
610
+ configure_validator(:type => :time, :after => '12:00')
611
+ validate_with(:birth_time, '11:59')
612
+ person.errors.on(:birth_time).should match(/after \d{2}:\d{2}:\d{2}\Z/)
613
+ end
614
+
615
+ if defined?(I18n)
616
+
617
+ describe "I18n" do
618
+ it "should use global default if locale format missing" do
619
+ I18n.backend.store_translations 'zz', :activerecord => {:errors => {:messages => { :after => 'after {{restriction}}' }}}
620
+ I18n.locale = :zz
621
+ configure_validator(:type => :datetime, :after => 1.day.from_now)
622
+ validate_with(:birth_date_and_time, Time.now)
623
+ person.errors.on(:birth_date_and_time).should match(/after \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\Z/)
624
+ end
625
+
626
+ after do
627
+ I18n.locale = :en
628
+ end
629
+ end
630
+
631
+ end
632
+ end
633
+
634
+ describe "custom formats" do
635
+
636
+ before :all do
637
+ custom = {
638
+ :time => '%H:%M %p',
639
+ :date => '%d-%m-%Y',
640
+ :datetime => '%d-%m-%Y %H:%M %p'
641
+ }
642
+
643
+ if defined?(I18n)
644
+ I18n.backend.store_translations 'en', :validates_timeliness => { :error_value_formats => custom }
645
+ else
646
+ @@formats = ValidatesTimeliness::Validator.error_value_formats
647
+ ValidatesTimeliness::Validator.error_value_formats = custom
648
+ end
649
+ end
650
+
651
+ it "should format datetime value of restriction" do
652
+ configure_validator(:type => :datetime, :after => 1.day.from_now)
653
+ validate_with(:birth_date_and_time, Time.now)
654
+ person.errors.on(:birth_date_and_time).should match(/after \d{2}-\d{2}-\d{4} \d{2}:\d{2} (AM|PM)\Z/)
655
+ end
656
+
657
+ it "should format date value of restriction" do
658
+ configure_validator(:type => :date, :after => 1.day.from_now)
659
+ validate_with(:birth_date, Time.now)
660
+ person.errors.on(:birth_date).should match(/after \d{2}-\d{2}-\d{4}\Z/)
661
+ end
662
+
663
+ it "should format time value of restriction" do
664
+ configure_validator(:type => :time, :after => '12:00')
665
+ validate_with(:birth_time, '11:59')
666
+ person.errors.on(:birth_time).should match(/after \d{2}:\d{2} (AM|PM)\Z/)
667
+ end
668
+
669
+ after :all do
670
+ if defined?(I18n)
671
+ I18n.reload!
672
+ else
673
+ ValidatesTimeliness::Validator.error_value_formats = @@formats
674
+ end
675
+ end
676
+ end
677
+
678
+ end
679
+
680
+ def parse(*args)
681
+ ValidatesTimeliness::Parser.parse(*args)
682
+ end
683
+
684
+ def configure_validator(options={})
685
+ @validator = ValidatesTimeliness::Validator.new(options)
686
+ end
687
+
688
+ def validate_with(attr_name, value)
689
+ person.send("#{attr_name}=", value)
690
+ validator.call(person, attr_name, value)
691
+ end
692
+
693
+ def should_have_error(attr_name, error)
694
+ message = error_messages[error]
695
+ person.errors.on(attr_name).should match(/#{message}/)
696
+ end
697
+
698
+ def should_have_no_error(attr_name, error)
699
+ message = error_messages[error]
700
+ errors = person.errors.on(attr_name)
701
+ if errors
702
+ errors.should_not match(/#{message}/)
703
+ else
704
+ errors.should be_nil
705
+ end
706
+ end
707
+
708
+ def error_messages
709
+ return @error_messages if defined?(@error_messages)
710
+ messages = defined?(I18n) ? I18n.t('activerecord.errors.messages') : validator.send(:error_messages)
711
+ @error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.sub(/ (\%s|\{\{\w*\}\}).*/, ''); h }
712
+ end
713
+ end