szilm-validates_timeliness 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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