chronic 0.4.1 → 0.5.0

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 (46) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +5 -0
  3. data/.yardopts +3 -0
  4. data/HISTORY.md +36 -2
  5. data/Manifest.txt +6 -2
  6. data/README.md +8 -2
  7. data/Rakefile +9 -101
  8. data/chronic.gemspec +12 -78
  9. data/lib/chronic/chronic.rb +225 -91
  10. data/lib/chronic/grabber.rb +12 -3
  11. data/lib/chronic/handlers.rb +94 -198
  12. data/lib/chronic/mini_date.rb +6 -2
  13. data/lib/chronic/ordinal.rb +15 -4
  14. data/lib/chronic/pointer.rb +12 -3
  15. data/lib/chronic/repeater.rb +28 -9
  16. data/lib/chronic/repeaters/repeater_day.rb +0 -1
  17. data/lib/chronic/repeaters/repeater_day_name.rb +0 -1
  18. data/lib/chronic/repeaters/repeater_day_portion.rb +0 -1
  19. data/lib/chronic/repeaters/repeater_fortnight.rb +0 -1
  20. data/lib/chronic/repeaters/repeater_hour.rb +0 -1
  21. data/lib/chronic/repeaters/repeater_minute.rb +0 -1
  22. data/lib/chronic/repeaters/repeater_month.rb +13 -2
  23. data/lib/chronic/repeaters/repeater_month_name.rb +13 -21
  24. data/lib/chronic/repeaters/repeater_season.rb +0 -19
  25. data/lib/chronic/repeaters/repeater_season_name.rb +0 -2
  26. data/lib/chronic/repeaters/repeater_second.rb +0 -1
  27. data/lib/chronic/repeaters/repeater_time.rb +1 -2
  28. data/lib/chronic/repeaters/repeater_week.rb +0 -1
  29. data/lib/chronic/repeaters/repeater_weekday.rb +0 -1
  30. data/lib/chronic/repeaters/repeater_weekend.rb +0 -1
  31. data/lib/chronic/repeaters/repeater_year.rb +19 -8
  32. data/lib/chronic/scalar.rb +29 -1
  33. data/lib/chronic/season.rb +37 -0
  34. data/lib/chronic/separator.rb +24 -7
  35. data/lib/chronic/tag.rb +10 -1
  36. data/lib/chronic/time_zone.rb +12 -3
  37. data/lib/chronic/token.rb +14 -4
  38. data/lib/chronic.rb +62 -49
  39. data/test/test_Chronic.rb +52 -2
  40. data/test/test_RepeaterMonth.rb +4 -0
  41. data/test/test_RepeaterSeason.rb +40 -0
  42. data/test/test_RepeaterYear.rb +7 -0
  43. data/test/test_Time.rb +1 -1
  44. data/test/test_parsing.rb +159 -113
  45. metadata +31 -29
  46. data/benchmark/benchmark.rb +0 -13
@@ -1,156 +1,10 @@
1
1
  module Chronic
2
+ module Handlers
3
+ module_function
2
4
 
3
- class << self
4
-
5
- def definitions(options={}) #:nodoc:
6
- options[:endian_precedence] ||= [:middle, :little]
7
- # ensure the endian precedence is exactly two elements long
8
- raise ChronicPain, "More than two elements specified for endian precedence array" unless options[:endian_precedence].length == 2
9
-
10
- # handler for dd/mm/yyyy
11
- @little_endian_handler ||= Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
12
-
13
- # handler for mm/dd/yyyy
14
- @middle_endian_handler ||= Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy)
15
-
16
- # ensure we have valid endian values
17
- options[:endian_precedence].each do |e|
18
- raise ChronicPain, "Unknown endian type: #{e.to_s}" unless instance_variable_defined?(endian_variable_name_for(e))
19
- end
20
-
21
- @definitions ||= {
22
- :time => [
23
- Handler.new([:repeater_time, :repeater_day_portion?], nil)
24
- ],
25
-
26
- :date => [
27
- Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :separator_slash_or_dash?, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
28
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
29
- Handler.new([:repeater_month_name, :ordinal_day, :scalar_year], :handle_rmn_od_sy),
30
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
31
- Handler.new([:repeater_month_name, :ordinal_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_od_sy),
32
- Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
33
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :scalar_day], :handle_rmn_sd_on),
34
- Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
35
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :ordinal_day], :handle_rmn_od_on),
36
- Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),
37
- Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),
38
- @middle_endian_handler,
39
- @little_endian_handler,
40
- Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
41
- Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)
42
- ],
43
-
44
- # tonight at 7pm
45
- :anchor => [
46
- Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
47
- Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
48
- Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)
49
- ],
50
-
51
- # 3 weeks from now, in 2 months
52
- :arrow => [
53
- Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
54
- Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
55
- Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)
56
- ],
57
-
58
- # 3rd week in march
59
- :narrow => [
60
- Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
61
- Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)
62
- ]
63
- }
64
-
65
- apply_endian_precedences(options[:endian_precedence])
66
-
67
- @definitions
68
- end
69
-
70
- def tokens_to_span(tokens, options) #:nodoc:
71
- # maybe it's a specific date
72
-
73
- definitions = self.definitions(options)
74
- definitions[:date].each do |handler|
75
- if handler.match(tokens, definitions)
76
- puts "-date" if Chronic.debug
77
- good_tokens = tokens.select { |o| !o.get_tag Separator }
78
- return self.send(handler.handler_method, good_tokens, options)
79
- end
80
- end
81
-
82
- # I guess it's not a specific date, maybe it's just an anchor
83
-
84
- definitions[:anchor].each do |handler|
85
- if handler.match(tokens, definitions)
86
- puts "-anchor" if Chronic.debug
87
- good_tokens = tokens.select { |o| !o.get_tag Separator }
88
- return self.send(handler.handler_method, good_tokens, options)
89
- end
90
- end
91
-
92
- # not an anchor, perhaps it's an arrow
93
-
94
- definitions[:arrow].each do |handler|
95
- if handler.match(tokens, definitions)
96
- puts "-arrow" if Chronic.debug
97
- good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
98
- return self.send(handler.handler_method, good_tokens, options)
99
- end
100
- end
101
-
102
- # not an arrow, let's hope it's a narrow
103
-
104
- definitions[:narrow].each do |handler|
105
- if handler.match(tokens, definitions)
106
- puts "-narrow" if Chronic.debug
107
- #good_tokens = tokens.select { |o| !o.get_tag Separator }
108
- return self.send(handler.handler_method, tokens, options)
109
- end
110
- end
111
-
112
- # I guess you're out of luck!
113
- puts "-none" if Chronic.debug
114
- return nil
115
- end
116
-
117
- #--------------
118
-
119
- def apply_endian_precedences(precedences)
120
- date_defs = @definitions[:date]
121
-
122
- # map the precedence array to indices on @definitions[:date]
123
- indices = precedences.map { |e|
124
- handler = instance_variable_get(endian_variable_name_for(e))
125
- date_defs.index(handler)
126
- }
127
-
128
- # swap the handlers if we discover they are at odds with the desired preferences
129
- swap(date_defs, indices.first, indices.last) if indices.first > indices.last
130
- end
131
-
132
- def endian_variable_name_for(e)
133
- "@#{e.to_s}_endian_handler".to_sym
134
- end
135
-
136
- # exchange two elements in an array
137
- def swap(arr, a, b); arr[a], arr[b] = arr[b], arr[a]; end
138
-
139
- def day_or_time(day_start, time_tokens, options)
140
- outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
141
-
142
- if !time_tokens.empty?
143
- @now = outer_span.begin
144
- get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
145
- else
146
- outer_span
147
- end
148
- end
149
-
150
- #--------------
151
-
5
+ # Handle month/day
152
6
  def handle_m_d(month, day, time_tokens, options) #:nodoc:
153
- month.start = @now
7
+ month.start = Chronic.now
154
8
  span = month.this(options[:context])
155
9
 
156
10
  day_start = Chronic.time_class.local(span.begin.year, span.begin.month, day)
@@ -158,10 +12,12 @@ module Chronic
158
12
  day_or_time(day_start, time_tokens, options)
159
13
  end
160
14
 
15
+ # Handle repeater-month-name/scalar-day
161
16
  def handle_rmn_sd(tokens, options) #:nodoc:
162
17
  handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(ScalarDay).type, tokens[2..tokens.size], options)
163
18
  end
164
19
 
20
+ # Handle repeater-month-name/scalar-day with separator-on
165
21
  def handle_rmn_sd_on(tokens, options) #:nodoc:
166
22
  if tokens.size > 3
167
23
  handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(ScalarDay).type, tokens[0..1], options)
@@ -170,10 +26,17 @@ module Chronic
170
26
  end
171
27
  end
172
28
 
29
+ # Handle repeater-month-name/ordinal-day
173
30
  def handle_rmn_od(tokens, options) #:nodoc:
174
31
  handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(OrdinalDay).type, tokens[2..tokens.size], options)
175
32
  end
176
33
 
34
+ # Handle ordinal-day/repeater-month-name
35
+ def handle_od_rmn(tokens, options) #:nodoc:
36
+ handle_m_d(tokens[1].get_tag(RepeaterMonthName), tokens[0].get_tag(OrdinalDay).type, tokens[2..tokens.size], options)
37
+ end
38
+
39
+ # Handle repeater-month-name/ordinal-day with separator-on
177
40
  def handle_rmn_od_on(tokens, options) #:nodoc:
178
41
  if tokens.size > 3
179
42
  handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(OrdinalDay).type, tokens[0..1], options)
@@ -182,6 +45,7 @@ module Chronic
182
45
  end
183
46
  end
184
47
 
48
+ # Handle repeater-month-name/scalar-year
185
49
  def handle_rmn_sy(tokens, options) #:nodoc:
186
50
  month = tokens[0].get_tag(RepeaterMonthName).index
187
51
  year = tokens[1].get_tag(ScalarYear).type
@@ -201,11 +65,13 @@ module Chronic
201
65
  end
202
66
  end
203
67
 
68
+ # Handle generic timestamp
204
69
  def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
205
- t = Chronic.time_class.parse(@text)
70
+ t = Chronic.time_class.parse(options[:text])
206
71
  Span.new(t, t + 1)
207
72
  end
208
73
 
74
+ # Handle repeater-month-name/scalar-day/scalar-year
209
75
  def handle_rmn_sd_sy(tokens, options) #:nodoc:
210
76
  month = tokens[0].get_tag(RepeaterMonthName).index
211
77
  day = tokens[1].get_tag(ScalarDay).type
@@ -221,6 +87,7 @@ module Chronic
221
87
  end
222
88
  end
223
89
 
90
+ # Handle repeater-month-name/ordinal-day/scalar-year
224
91
  def handle_rmn_od_sy(tokens, options) #:nodoc:
225
92
  month = tokens[0].get_tag(RepeaterMonthName).index
226
93
  day = tokens[1].get_tag(OrdinalDay).type
@@ -236,12 +103,30 @@ module Chronic
236
103
  end
237
104
  end
238
105
 
106
+ # Handle oridinal-day/repeater-month-name/scalar-year
107
+ def handle_od_rmn_sy(tokens, options) #:nodoc:
108
+ day = tokens[0].get_tag(OrdinalDay).type
109
+ month = tokens[1].get_tag(RepeaterMonthName).index
110
+ year = tokens[2].get_tag(ScalarYear).type
111
+
112
+ time_tokens = tokens.last(tokens.size - 3)
113
+
114
+ begin
115
+ day_start = Chronic.time_class.local(year, month, day)
116
+ day_or_time(day_start, time_tokens, options)
117
+ rescue ArgumentError
118
+ nil
119
+ end
120
+ end
121
+
122
+ # Handle scalar-day/repeater-month-name/scalar-year
239
123
  def handle_sd_rmn_sy(tokens, options) #:nodoc:
240
124
  new_tokens = [tokens[1], tokens[0], tokens[2]]
241
125
  time_tokens = tokens.last(tokens.size - 3)
242
- self.handle_rmn_sd_sy(new_tokens + time_tokens, options)
126
+ handle_rmn_sd_sy(new_tokens + time_tokens, options)
243
127
  end
244
128
 
129
+ # Handle scalar-month/scalar-day/scalar-year (endian middle)
245
130
  def handle_sm_sd_sy(tokens, options) #:nodoc:
246
131
  month = tokens[0].get_tag(ScalarMonth).type
247
132
  day = tokens[1].get_tag(ScalarDay).type
@@ -257,18 +142,21 @@ module Chronic
257
142
  end
258
143
  end
259
144
 
145
+ # Handle scalar-day/scalar-month/scalar-year (endian little)
260
146
  def handle_sd_sm_sy(tokens, options) #:nodoc:
261
147
  new_tokens = [tokens[1], tokens[0], tokens[2]]
262
148
  time_tokens = tokens.last(tokens.size - 3)
263
- self.handle_sm_sd_sy(new_tokens + time_tokens, options)
149
+ handle_sm_sd_sy(new_tokens + time_tokens, options)
264
150
  end
265
151
 
152
+ # Handle scalar-year/scalar-month/scalar-day
266
153
  def handle_sy_sm_sd(tokens, options) #:nodoc:
267
154
  new_tokens = [tokens[1], tokens[2], tokens[0]]
268
155
  time_tokens = tokens.last(tokens.size - 3)
269
- self.handle_sm_sd_sy(new_tokens + time_tokens, options)
156
+ handle_sm_sd_sy(new_tokens + time_tokens, options)
270
157
  end
271
158
 
159
+ # Handle scalar-month/scalar-year
272
160
  def handle_sm_sy(tokens, options) #:nodoc:
273
161
  month = tokens[0].get_tag(ScalarMonth).type
274
162
  year = tokens[1].get_tag(ScalarYear).type
@@ -290,18 +178,21 @@ module Chronic
290
178
 
291
179
  # anchors
292
180
 
181
+ # Handle repeaters
293
182
  def handle_r(tokens, options) #:nodoc:
294
183
  dd_tokens = dealias_and_disambiguate_times(tokens, options)
295
- self.get_anchor(dd_tokens, options)
184
+ get_anchor(dd_tokens, options)
296
185
  end
297
186
 
187
+ # Handle repeater/grabber/repeater
298
188
  def handle_r_g_r(tokens, options) #:nodoc:
299
189
  new_tokens = [tokens[1], tokens[0], tokens[2]]
300
- self.handle_r(new_tokens, options)
190
+ handle_r(new_tokens, options)
301
191
  end
302
192
 
303
193
  # arrows
304
194
 
195
+ # Handle scalar/repeater/pointer helper
305
196
  def handle_srp(tokens, span, options) #:nodoc:
306
197
  distance = tokens[0].get_tag(Scalar).type
307
198
  repeater = tokens[1].get_tag(Repeater)
@@ -310,38 +201,29 @@ module Chronic
310
201
  repeater.offset(span, distance, pointer)
311
202
  end
312
203
 
204
+ # Handle scalar/repeater/pointer
313
205
  def handle_s_r_p(tokens, options) #:nodoc:
314
206
  repeater = tokens[1].get_tag(Repeater)
207
+ span = Span.new(Chronic.now, Chronic.now + 1)
315
208
 
316
- # span =
317
- # case true
318
- # when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class)
319
- # self.parse("this hour", :guess => false, :now => @now)
320
- # when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class)
321
- # self.parse("this minute", :guess => false, :now => @now)
322
- # when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
323
- # self.parse("this second", :guess => false, :now => @now)
324
- # else
325
- # raise(ChronicPain, "Invalid repeater: #{repeater.class}")
326
- # end
327
-
328
- span = Span.new(@now, @now + 1)
329
-
330
- self.handle_srp(tokens, span, options)
209
+ handle_srp(tokens, span, options)
331
210
  end
332
211
 
212
+ # Handle pointer/scalar/repeater
333
213
  def handle_p_s_r(tokens, options) #:nodoc:
334
214
  new_tokens = [tokens[1], tokens[2], tokens[0]]
335
- self.handle_s_r_p(new_tokens, options)
215
+ handle_s_r_p(new_tokens, options)
336
216
  end
337
217
 
218
+ # Handle scalar/repeater/pointer/anchor
338
219
  def handle_s_r_p_a(tokens, options) #:nodoc:
339
220
  anchor_span = get_anchor(tokens[3..tokens.size - 1], options)
340
- self.handle_srp(tokens, anchor_span, options)
221
+ handle_srp(tokens, anchor_span, options)
341
222
  end
342
223
 
343
224
  # narrows
344
225
 
226
+ # Handle oridinal repeaters
345
227
  def handle_orr(tokens, outer_span, options) #:nodoc:
346
228
  repeater = tokens[1].get_tag(Repeater)
347
229
  repeater.start = outer_span.begin - 1
@@ -357,11 +239,13 @@ module Chronic
357
239
  span
358
240
  end
359
241
 
242
+ # Handle ordinal/repeater/separator/repeater
360
243
  def handle_o_r_s_r(tokens, options) #:nodoc:
361
244
  outer_span = get_anchor([tokens[3]], options)
362
245
  handle_orr(tokens[0..1], outer_span, options)
363
246
  end
364
247
 
248
+ # Handle ordinal/repeater/grabber/repeater
365
249
  def handle_o_r_g_r(tokens, options) #:nodoc:
366
250
  outer_span = get_anchor(tokens[2..3], options)
367
251
  handle_orr(tokens[0..1], outer_span, options)
@@ -369,11 +253,22 @@ module Chronic
369
253
 
370
254
  # support methods
371
255
 
256
+ def day_or_time(day_start, time_tokens, options)
257
+ outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
258
+
259
+ if !time_tokens.empty?
260
+ Chronic.now = outer_span.begin
261
+ get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
262
+ else
263
+ outer_span
264
+ end
265
+ end
266
+
372
267
  def get_anchor(tokens, options) #:nodoc:
373
268
  grabber = Grabber.new(:this)
374
269
  pointer = :future
375
270
 
376
- repeaters = self.get_repeaters(tokens)
271
+ repeaters = get_repeaters(tokens)
377
272
  repeaters.size.times { tokens.pop }
378
273
 
379
274
  if tokens.first && tokens.first.get_tag(Grabber)
@@ -382,7 +277,7 @@ module Chronic
382
277
  end
383
278
 
384
279
  head = repeaters.shift
385
- head.start = @now
280
+ head.start = Chronic.now
386
281
 
387
282
  case grabber.type
388
283
  when :last
@@ -404,8 +299,8 @@ module Chronic
404
299
 
405
300
  def get_repeaters(tokens) #:nodoc:
406
301
  repeaters = []
407
- tokens.each do |token|
408
- if t = token.get_tag(Repeater)
302
+ tokens.each do |token|
303
+ if t = token.get_tag(Repeater)
409
304
  repeaters << t
410
305
  end
411
306
  end
@@ -464,21 +359,6 @@ module Chronic
464
359
  end
465
360
  end
466
361
 
467
- # tokens.each_with_index do |t0, i|
468
- # t1 = tokens[i + 1]
469
- # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
470
- # if [:morning].include?(t1tag.type)
471
- # puts '--morning->am' if Chronic.debug
472
- # t1.untag(RepeaterDayPortion)
473
- # t1.tag(RepeaterDayPortion.new(:am))
474
- # elsif [:afternoon, :evening, :night].include?(t1tag.type)
475
- # puts "--#{t1tag.type}->pm" if Chronic.debug
476
- # t1.untag(RepeaterDayPortion)
477
- # t1.tag(RepeaterDayPortion.new(:pm))
478
- # end
479
- # end
480
- # end
481
-
482
362
  # handle ambiguous times if :ambiguous_time_range is specified
483
363
  if options[:ambiguous_time_range] != :none
484
364
  ttokens = []
@@ -499,24 +379,38 @@ module Chronic
499
379
 
500
380
  end
501
381
 
502
- class Handler #:nodoc:
503
- attr_accessor :pattern, :handler_method
382
+ class Handler
383
+ # @return [Array] A list of patterns
384
+ attr_accessor :pattern
385
+
386
+ # @return [Symbol] The method which handles this list of patterns.
387
+ # This method should exist inside the {Handlers} module
388
+ attr_accessor :handler_method
504
389
 
390
+ # @param [Array] pattern A list of patterns to match tokens against
391
+ # @param [Symbol] handler_method The method to be invoked when patterns
392
+ # are matched. This method should exist inside the {Handlers} module
505
393
  def initialize(pattern, handler_method)
506
394
  @pattern = pattern
507
395
  @handler_method = handler_method
508
396
  end
509
397
 
398
+ # @param [#to_s] The snake_case name representing a Chronic constant
399
+ # @return [Class] The class represented by `name`
400
+ # @raise [NameError] Raises if this constant could not be found
510
401
  def constantize(name)
511
- camel = name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
512
- ::Chronic.module_eval(camel, __FILE__, __LINE__)
402
+ Chronic.const_get name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
513
403
  end
514
404
 
405
+ # @param [Array] tokens
406
+ # @param [Hash] definitions
407
+ # @return [Boolean]
408
+ # @see Chronic.tokens_to_span
515
409
  def match(tokens, definitions)
516
410
  token_index = 0
517
411
  @pattern.each do |element|
518
412
  name = element.to_s
519
- optional = name.reverse[0..0] == '?'
413
+ optional = name[-1, 1] == '?'
520
414
  name = name.chop if optional
521
415
  if element.instance_of? Symbol
522
416
  klass = constantize(name)
@@ -539,8 +433,10 @@ module Chronic
539
433
  return true
540
434
  end
541
435
 
436
+ # @param [Handler] The handler to compare
437
+ # @return [Boolean] True if these handlers match
542
438
  def ==(other)
543
- self.pattern == other.pattern
439
+ @pattern == other.pattern
544
440
  end
545
441
  end
546
442
 
@@ -3,13 +3,17 @@ module Chronic
3
3
  attr_accessor :month, :day
4
4
 
5
5
  def initialize(month, day)
6
- raise(InvalidArgumentException, "1..12 are valid months") unless (1..12).include?(month)
6
+ unless (1..12).include?(month)
7
+ raise(InvalidArgumentException, "1..12 are valid months")
8
+ end
9
+
7
10
  @month = month
8
11
  @day = day
9
12
  end
10
13
 
11
14
  def is_between?(md_start, md_end)
12
- return false if (@month==md_start.month && @month==md_end.month && (@day < md_start.day || @day > md_end.day))
15
+ return false if (@month == md_start.month && @month == md_end.month &&
16
+ (@day < md_start.day || @day > md_end.day))
13
17
  return true if (@month == md_start.month and @day >= md_start.day) ||
14
18
  (@month == md_end.month and @day <= md_end.day)
15
19
  i = (md_start.month % 12) + 1
@@ -1,16 +1,27 @@
1
1
  module Chronic
2
- class Ordinal < Tag #:nodoc:
2
+ class Ordinal < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Ordinal tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
- tokens.each_index do |i|
5
- if t = scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
6
- if t = scan_for_days(tokens[i]) then tokens[i].tag(t) end
11
+ tokens.each do |token|
12
+ if t = scan_for_ordinals(token) then token.tag(t) end
13
+ if t = scan_for_days(token) then token.tag(t) end
7
14
  end
8
15
  end
9
16
 
17
+ # @param [Token] token
18
+ # @return [Ordinal, nil]
10
19
  def self.scan_for_ordinals(token)
11
20
  Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
12
21
  end
13
22
 
23
+ # @param [Token] token
24
+ # @return [OrdinalDay, nil]
14
25
  def self.scan_for_days(token)
15
26
  if token.word =~ /^(\d*)(st|nd|rd|th)$/
16
27
  unless $1.to_i > 31 || $1.to_i < 1
@@ -1,11 +1,20 @@
1
1
  module Chronic
2
- class Pointer < Tag #:nodoc:
2
+ class Pointer < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Pointer tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
- tokens.each_index do |i|
5
- if t = scan_for_all(tokens[i]) then tokens[i].tag(t) end
11
+ tokens.each do |token|
12
+ if t = scan_for_all(token) then token.tag(t) end
6
13
  end
7
14
  end
8
15
 
16
+ # @param [Token] token
17
+ # @return [Pointer, nil]
9
18
  def self.scan_for_all(token)
10
19
  scan_for token, self,
11
20
  {
@@ -1,16 +1,25 @@
1
1
  module Chronic
2
- class Repeater < Tag #:nodoc:
2
+ class Repeater < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Repeater tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
- tokens.each_index do |i|
5
- if t = scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
6
- if t = scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
7
- if t = scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
8
- if t = scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
9
- if t = scan_for_times(tokens[i]) then tokens[i].tag(t); next end
10
- if t = scan_for_units(tokens[i]) then tokens[i].tag(t); next end
11
+ tokens.each do |token|
12
+ if t = scan_for_season_names(token) then token.tag(t); next end
13
+ if t = scan_for_month_names(token) then token.tag(t); next end
14
+ if t = scan_for_day_names(token) then token.tag(t); next end
15
+ if t = scan_for_day_portions(token) then token.tag(t); next end
16
+ if t = scan_for_times(token) then token.tag(t); next end
17
+ if t = scan_for_units(token) then token.tag(t); next end
11
18
  end
12
19
  end
13
20
 
21
+ # @param [Token] token
22
+ # @return [RepeaterSeasonName, nil]
14
23
  def self.scan_for_season_names(token)
15
24
  scan_for token, RepeaterSeasonName,
16
25
  {
@@ -21,6 +30,8 @@ module Chronic
21
30
  }
22
31
  end
23
32
 
33
+ # @param [Token] token
34
+ # @return [RepeaterMonthName, nil]
24
35
  def self.scan_for_month_names(token)
25
36
  scan_for token, RepeaterMonthName,
26
37
  {
@@ -39,6 +50,8 @@ module Chronic
39
50
  }
40
51
  end
41
52
 
53
+ # @param [Token] token
54
+ # @return [RepeaterDayName, nil]
42
55
  def self.scan_for_day_names(token)
43
56
  scan_for token, RepeaterDayName,
44
57
  {
@@ -55,6 +68,8 @@ module Chronic
55
68
  }
56
69
  end
57
70
 
71
+ # @param [Token] token
72
+ # @return [RepeaterDayPortion, nil]
58
73
  def self.scan_for_day_portions(token)
59
74
  scan_for token, RepeaterDayPortion,
60
75
  {
@@ -67,10 +82,14 @@ module Chronic
67
82
  }
68
83
  end
69
84
 
85
+ # @param [Token] token
86
+ # @return [RepeaterTime, nil]
70
87
  def self.scan_for_times(token)
71
88
  scan_for token, RepeaterTime, /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
72
89
  end
73
90
 
91
+ # @param [Token] token
92
+ # @return [Repeater] A new instance of a subclass of Repeater
74
93
  def self.scan_for_units(token)
75
94
  {
76
95
  /^years?$/ => :year,
@@ -100,7 +119,7 @@ module Chronic
100
119
 
101
120
  # returns the width (in seconds or months) of this repeatable.
102
121
  def width
103
- raise("Repeatable#width must be overridden in subclasses")
122
+ raise("Repeater#width must be overridden in subclasses")
104
123
  end
105
124
 
106
125
  # returns the next occurance of this repeatable.
@@ -4,7 +4,6 @@ module Chronic
4
4
 
5
5
  def initialize(type)
6
6
  super
7
- @current_day_start = nil
8
7
  end
9
8
 
10
9
  def next(pointer)
@@ -4,7 +4,6 @@ module Chronic
4
4
 
5
5
  def initialize(type)
6
6
  super
7
- @current_day_start = nil
8
7
  end
9
8
 
10
9
  def next(pointer)
@@ -11,7 +11,6 @@ module Chronic
11
11
 
12
12
  def initialize(type)
13
13
  super
14
- @current_span = nil
15
14
 
16
15
  if type.kind_of? Integer
17
16
  @range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
@@ -4,7 +4,6 @@ module Chronic
4
4
 
5
5
  def initialize(type)
6
6
  super
7
- @current_fortnight_start = nil
8
7
  end
9
8
 
10
9
  def next(pointer)
@@ -4,7 +4,6 @@ module Chronic
4
4
 
5
5
  def initialize(type)
6
6
  super
7
- @current_hour_start = nil
8
7
  end
9
8
 
10
9
  def next(pointer)