chronic 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/HISTORY.md +27 -0
  2. data/Manifest.txt +16 -5
  3. data/README.md +14 -8
  4. data/Rakefile +2 -8
  5. data/chronic.gemspec +8 -11
  6. data/lib/chronic.rb +21 -14
  7. data/lib/chronic/chronic.rb +38 -130
  8. data/lib/chronic/grabber.rb +11 -15
  9. data/lib/chronic/handlers.rb +63 -40
  10. data/lib/chronic/mini_date.rb +27 -0
  11. data/lib/chronic/numerizer.rb +120 -0
  12. data/lib/chronic/ordinal.rb +5 -10
  13. data/lib/chronic/pointer.rb +8 -10
  14. data/lib/chronic/repeater.rb +106 -109
  15. data/lib/chronic/repeaters/repeater_day.rb +43 -41
  16. data/lib/chronic/repeaters/repeater_day_name.rb +38 -36
  17. data/lib/chronic/repeaters/repeater_day_portion.rb +74 -73
  18. data/lib/chronic/repeaters/repeater_fortnight.rb +57 -55
  19. data/lib/chronic/repeaters/repeater_hour.rb +46 -44
  20. data/lib/chronic/repeaters/repeater_minute.rb +46 -44
  21. data/lib/chronic/repeaters/repeater_month.rb +52 -50
  22. data/lib/chronic/repeaters/repeater_month_name.rb +84 -80
  23. data/lib/chronic/repeaters/repeater_season.rb +97 -119
  24. data/lib/chronic/repeaters/repeater_season_name.rb +39 -39
  25. data/lib/chronic/repeaters/repeater_second.rb +32 -30
  26. data/lib/chronic/repeaters/repeater_time.rb +106 -101
  27. data/lib/chronic/repeaters/repeater_week.rb +60 -58
  28. data/lib/chronic/repeaters/repeater_weekday.rb +67 -58
  29. data/lib/chronic/repeaters/repeater_weekend.rb +54 -52
  30. data/lib/chronic/repeaters/repeater_year.rb +50 -48
  31. data/lib/chronic/scalar.rb +24 -16
  32. data/lib/chronic/separator.rb +15 -33
  33. data/lib/chronic/span.rb +31 -0
  34. data/lib/chronic/tag.rb +26 -0
  35. data/lib/chronic/time_zone.rb +7 -9
  36. data/lib/chronic/token.rb +35 -0
  37. data/test/helper.rb +5 -6
  38. data/test/test_Chronic.rb +5 -0
  39. data/test/test_Numerizer.rb +60 -39
  40. data/test/test_RepeaterHour.rb +4 -0
  41. data/test/test_parsing.rb +104 -13
  42. metadata +14 -20
  43. data/lib/chronic/numerizer/numerizer.rb +0 -97
@@ -1,26 +1,22 @@
1
- #module Chronic
2
-
3
- class Chronic::Grabber < Chronic::Tag #:nodoc:
4
- def self.scan(tokens)
1
+ module Chronic
2
+ class Grabber < Tag #:nodoc:
3
+ def self.scan(tokens, options)
5
4
  tokens.each_index do |i|
6
- if t = self.scan_for_all(tokens[i]) then tokens[i].tag(t); next end
5
+ if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
7
6
  end
8
- tokens
9
7
  end
10
8
 
11
9
  def self.scan_for_all(token)
12
- scanner = {/last/ => :last,
13
- /this/ => :this,
14
- /next/ => :next}
15
- scanner.keys.each do |scanner_item|
16
- return self.new(scanner[scanner_item]) if scanner_item =~ token.word
17
- end
18
- return nil
10
+ scan_for token, self,
11
+ {
12
+ /last/ => :last,
13
+ /this/ => :this,
14
+ /next/ => :next
15
+ }
19
16
  end
20
17
 
21
18
  def to_s
22
19
  'grabber-' << @type.to_s
23
20
  end
24
21
  end
25
-
26
- #end
22
+ end
@@ -3,8 +3,7 @@ module Chronic
3
3
  class << self
4
4
 
5
5
  def definitions(options={}) #:nodoc:
6
- options[:endian_precedence] = [:middle, :little] if options[:endian_precedence].nil?
7
-
6
+ options[:endian_precedence] ||= [:middle, :little]
8
7
  # ensure the endian precedence is exactly two elements long
9
8
  raise ChronicPain, "More than two elements specified for endian precedence array" unless options[:endian_precedence].length == 2
10
9
 
@@ -19,36 +18,48 @@ module Chronic
19
18
  raise ChronicPain, "Unknown endian type: #{e.to_s}" unless instance_variable_defined?(endian_variable_name_for(e))
20
19
  end
21
20
 
22
- @definitions ||=
23
- {:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],
24
-
25
- :date => [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),
26
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
27
- Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
28
- Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
29
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :scalar_day], :handle_rmn_sd_on),
30
- Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
31
- Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :ordinal_day], :handle_rmn_od_on),
32
- Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),
33
- Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),
34
- @middle_endian_handler,
35
- @little_endian_handler,
36
- Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
37
- Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],
38
-
39
- # tonight at 7pm
40
- :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
41
- Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
42
- Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],
43
-
44
- # 3 weeks from now, in 2 months
45
- :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
46
- Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
47
- Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],
48
-
49
- # 3rd week in march
50
- :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
51
- Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]
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
+ ]
52
63
  }
53
64
 
54
65
  apply_endian_precedences(options[:endian_precedence])
@@ -130,10 +141,9 @@ module Chronic
130
141
 
131
142
  if !time_tokens.empty?
132
143
  @now = outer_span.begin
133
- time = get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
134
- return time
144
+ get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
135
145
  else
136
- return outer_span
146
+ outer_span
137
147
  end
138
148
  end
139
149
 
@@ -211,6 +221,21 @@ module Chronic
211
221
  end
212
222
  end
213
223
 
224
+ def handle_rmn_od_sy(tokens, options) #:nodoc:
225
+ month = tokens[0].get_tag(RepeaterMonthName).index
226
+ day = tokens[1].get_tag(OrdinalDay).type
227
+ year = tokens[2].get_tag(ScalarYear).type
228
+
229
+ time_tokens = tokens.last(tokens.size - 3)
230
+
231
+ begin
232
+ day_start = Chronic.time_class.local(year, month, day)
233
+ day_or_time(day_start, time_tokens, options)
234
+ rescue ArgumentError
235
+ nil
236
+ end
237
+ end
238
+
214
239
  def handle_sd_rmn_sy(tokens, options) #:nodoc:
215
240
  new_tokens = [tokens[1], tokens[0], tokens[2]]
216
241
  time_tokens = tokens.last(tokens.size - 3)
@@ -363,7 +388,7 @@ module Chronic
363
388
  when :last
364
389
  outer_span = head.next(:past)
365
390
  when :this
366
- if repeaters.size > 0
391
+ if options[:context] != :past and repeaters.size > 0
367
392
  outer_span = head.this(:none)
368
393
  else
369
394
  outer_span = head.this(options[:context])
@@ -398,10 +423,8 @@ module Chronic
398
423
  head.start = pointer == :future ? span.begin : span.end
399
424
  h = head.this(:none)
400
425
 
401
- if span.include?(h.begin) || span.include?(h.end)
402
- return find_within(rest, h, pointer)
403
- else
404
- return nil
426
+ if span.cover?(h.begin) || span.cover?(h.end)
427
+ find_within(rest, h, pointer)
405
428
  end
406
429
  end
407
430
 
@@ -0,0 +1,27 @@
1
+ module Chronic
2
+ class MiniDate
3
+ attr_accessor :month, :day
4
+
5
+ def initialize(month, day)
6
+ @month = month
7
+ @day = day
8
+ end
9
+
10
+ def is_between?(md_start, md_end)
11
+ return true if (@month == md_start.month and @day >= md_start.day) ||
12
+ (@month == md_end.month and @day <= md_end.day)
13
+
14
+ i = md_start.month + 1
15
+ until i == md_end.month
16
+ return true if @month == i
17
+ i = (i+1) % 12
18
+ end
19
+
20
+ return false
21
+ end
22
+
23
+ def equals?(other)
24
+ @month == other.month and day == other.day
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,120 @@
1
+ require 'strscan'
2
+
3
+ module Chronic
4
+ class Numerizer
5
+
6
+ DIRECT_NUMS = [
7
+ ['eleven', '11'],
8
+ ['twelve', '12'],
9
+ ['thirteen', '13'],
10
+ ['fourteen', '14'],
11
+ ['fifteen', '15'],
12
+ ['sixteen', '16'],
13
+ ['seventeen', '17'],
14
+ ['eighteen', '18'],
15
+ ['nineteen', '19'],
16
+ ['ninteen', '19'], # Common mis-spelling
17
+ ['zero', '0'],
18
+ ['one', '1'],
19
+ ['two', '2'],
20
+ ['three', '3'],
21
+ ['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
22
+ ['five', '5'],
23
+ ['six(\W|$)', '6\1'],
24
+ ['seven(\W|$)', '7\1'],
25
+ ['eight(\W|$)', '8\1'],
26
+ ['nine(\W|$)', '9\1'],
27
+ ['ten', '10'],
28
+ ['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
29
+ ]
30
+
31
+ ORDINALS = [
32
+ ['first', '1'],
33
+ ['third', '3'],
34
+ ['fourth', '4'],
35
+ ['fifth', '5'],
36
+ ['sixth', '6'],
37
+ ['seventh', '7'],
38
+ ['eighth', '8'],
39
+ ['ninth', '9'],
40
+ ['tenth', '10']
41
+ ]
42
+
43
+ TEN_PREFIXES = [
44
+ ['twenty', 20],
45
+ ['thirty', 30],
46
+ ['forty', 40],
47
+ ['fourty', 40], # Common mis-spelling
48
+ ['fifty', 50],
49
+ ['sixty', 60],
50
+ ['seventy', 70],
51
+ ['eighty', 80],
52
+ ['ninety', 90]
53
+ ]
54
+
55
+ BIG_PREFIXES = [
56
+ ['hundred', 100],
57
+ ['thousand', 1000],
58
+ ['million', 1_000_000],
59
+ ['billion', 1_000_000_000],
60
+ ['trillion', 1_000_000_000_000],
61
+ ]
62
+
63
+ def self.numerize(string)
64
+ string = string.dup
65
+
66
+ # preprocess
67
+ string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
68
+ string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
69
+
70
+ # easy/direct replacements
71
+
72
+ DIRECT_NUMS.each do |dn|
73
+ string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
74
+ end
75
+
76
+ ORDINALS.each do |on|
77
+ string.gsub!(/#{on[0]}/i, '<num>' + on[1] + on[0][-2, 2])
78
+ end
79
+
80
+ # ten, twenty, etc.
81
+
82
+ TEN_PREFIXES.each do |tp|
83
+ string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
84
+ end
85
+
86
+ TEN_PREFIXES.each do |tp|
87
+ string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
88
+ end
89
+
90
+ # hundreds, thousands, millions, etc.
91
+
92
+ BIG_PREFIXES.each do |bp|
93
+ string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
94
+ andition(string)
95
+ end
96
+
97
+ # fractional addition
98
+ # I'm not combining this with the previous block as using float addition complicates the strings
99
+ # (with extraneous .0's and such )
100
+ string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
101
+
102
+ string.gsub(/<num>/, '')
103
+ end
104
+
105
+ class << self
106
+ private
107
+
108
+ def andition(string)
109
+ sc = StringScanner.new(string)
110
+ while(sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i))
111
+ if sc[2] =~ /and/ || sc[1].size > sc[3].size
112
+ string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
113
+ sc.reset
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -1,29 +1,24 @@
1
1
  module Chronic
2
2
 
3
3
  class Ordinal < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ def self.scan(tokens, options)
5
5
  # for each token
6
6
  tokens.each_index do |i|
7
- if t = self.scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
8
- if t = self.scan_for_days(tokens[i]) then tokens[i].tag(t) end
7
+ if t = scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
8
+ if t = scan_for_days(tokens[i]) then tokens[i].tag(t) end
9
9
  end
10
- tokens
11
10
  end
12
11
 
13
12
  def self.scan_for_ordinals(token)
14
- if token.word =~ /^(\d*)(st|nd|rd|th)$/
15
- return Ordinal.new($1.to_i)
16
- end
17
- return nil
13
+ Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
18
14
  end
19
15
 
20
16
  def self.scan_for_days(token)
21
17
  if token.word =~ /^(\d*)(st|nd|rd|th)$/
22
18
  unless $1.to_i > 31 || $1.to_i < 1
23
- return OrdinalDay.new(token.word.to_i)
19
+ OrdinalDay.new(token.word.to_i)
24
20
  end
25
21
  end
26
- return nil
27
22
  end
28
23
 
29
24
  def to_s
@@ -1,22 +1,20 @@
1
1
  module Chronic
2
2
 
3
3
  class Pointer < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ def self.scan(tokens, options)
5
5
  # for each token
6
6
  tokens.each_index do |i|
7
- if t = self.scan_for_all(tokens[i]) then tokens[i].tag(t) end
7
+ if t = scan_for_all(tokens[i]) then tokens[i].tag(t) end
8
8
  end
9
- tokens
10
9
  end
11
10
 
12
11
  def self.scan_for_all(token)
13
- scanner = {/\bpast\b/ => :past,
14
- /\bfuture\b/ => :future,
15
- /\bin\b/ => :future}
16
- scanner.keys.each do |scanner_item|
17
- return self.new(scanner[scanner_item]) if scanner_item =~ token.word
18
- end
19
- return nil
12
+ scan_for token, self,
13
+ {
14
+ /\bpast\b/ => :past,
15
+ /\bfuture\b/ => :future,
16
+ /\bin\b/ => :future
17
+ }
20
18
  end
21
19
 
22
20
  def to_s
@@ -1,129 +1,126 @@
1
- class Chronic::Repeater < Chronic::Tag #:nodoc:
2
- def self.scan(tokens, options)
3
- # for each token
4
- tokens.each_index do |i|
5
- if t = self.scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
6
- if t = self.scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
7
- if t = self.scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
8
- if t = self.scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
9
- if t = self.scan_for_times(tokens[i], options) then tokens[i].tag(t); next end
10
- if t = self.scan_for_units(tokens[i]) then tokens[i].tag(t); next end
1
+ module Chronic
2
+ class Repeater < Tag #:nodoc:
3
+ def self.scan(tokens, options)
4
+ # for each token
5
+ tokens.each_index do |i|
6
+ if t = scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
7
+ if t = scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
8
+ if t = scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
9
+ if t = scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
10
+ if t = scan_for_times(tokens[i]) then tokens[i].tag(t); next end
11
+ if t = scan_for_units(tokens[i]) then tokens[i].tag(t); next end
12
+ end
11
13
  end
12
- tokens
13
- end
14
14
 
15
- def self.scan_for_season_names(token)
16
- scanner = {/^springs?$/ => :spring,
17
- /^summers?$/ => :summer,
18
- /^(autumn)|(fall)s?$/ => :autumn,
19
- /^winters?$/ => :winter}
20
- scanner.keys.each do |scanner_item|
21
- return Chronic::RepeaterSeasonName.new(scanner[scanner_item]) if scanner_item =~ token.word
15
+ def self.scan_for_season_names(token)
16
+ scan_for token, RepeaterSeasonName,
17
+ {
18
+ /^springs?$/ => :spring,
19
+ /^summers?$/ => :summer,
20
+ /^(autumn)|(fall)s?$/ => :autumn,
21
+ /^winters?$/ => :winter
22
+ }
22
23
  end
23
24
 
24
- return nil
25
- end
26
-
27
- def self.scan_for_month_names(token)
28
- scanner = {/^jan\.?(uary)?$/ => :january,
29
- /^feb\.?(ruary)?$/ => :february,
30
- /^mar\.?(ch)?$/ => :march,
31
- /^apr\.?(il)?$/ => :april,
32
- /^may$/ => :may,
33
- /^jun\.?e?$/ => :june,
34
- /^jul\.?y?$/ => :july,
35
- /^aug\.?(ust)?$/ => :august,
36
- /^sep\.?(t\.?|tember)?$/ => :september,
37
- /^oct\.?(ober)?$/ => :october,
38
- /^nov\.?(ember)?$/ => :november,
39
- /^dec\.?(ember)?$/ => :december}
40
- scanner.keys.each do |scanner_item|
41
- return Chronic::RepeaterMonthName.new(scanner[scanner_item]) if scanner_item =~ token.word
25
+ def self.scan_for_month_names(token)
26
+ scan_for token, RepeaterMonthName,
27
+ {
28
+ /^jan\.?(uary)?$/ => :january,
29
+ /^feb\.?(ruary)?$/ => :february,
30
+ /^mar\.?(ch)?$/ => :march,
31
+ /^apr\.?(il)?$/ => :april,
32
+ /^may$/ => :may,
33
+ /^jun\.?e?$/ => :june,
34
+ /^jul\.?y?$/ => :july,
35
+ /^aug\.?(ust)?$/ => :august,
36
+ /^sep\.?(t\.?|tember)?$/ => :september,
37
+ /^oct\.?(ober)?$/ => :october,
38
+ /^nov\.?(ember)?$/ => :november,
39
+ /^dec\.?(ember)?$/ => :december
40
+ }
42
41
  end
43
- return nil
44
- end
45
42
 
46
- def self.scan_for_day_names(token)
47
- scanner = {/^m[ou]n(day)?$/ => :monday,
48
- /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
49
- /^tue$/ => :tuesday,
50
- /^we(dnes|nds|nns)day$/ => :wednesday,
51
- /^wed$/ => :wednesday,
52
- /^th(urs|ers)day$/ => :thursday,
53
- /^thu$/ => :thursday,
54
- /^fr[iy](day)?$/ => :friday,
55
- /^sat(t?[ue]rday)?$/ => :saturday,
56
- /^su[nm](day)?$/ => :sunday}
57
- scanner.keys.each do |scanner_item|
58
- return Chronic::RepeaterDayName.new(scanner[scanner_item]) if scanner_item =~ token.word
43
+ def self.scan_for_day_names(token)
44
+ scan_for token, RepeaterDayName,
45
+ {
46
+ /^m[ou]n(day)?$/ => :monday,
47
+ /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
48
+ /^tue$/ => :tuesday,
49
+ /^we(dnes|nds|nns)day$/ => :wednesday,
50
+ /^wed$/ => :wednesday,
51
+ /^th(urs|ers)day$/ => :thursday,
52
+ /^thu(rs)?$/ => :thursday,
53
+ /^fr[iy](day)?$/ => :friday,
54
+ /^sat(t?[ue]rday)?$/ => :saturday,
55
+ /^su[nm](day)?$/ => :sunday
56
+ }
59
57
  end
60
- return nil
61
- end
62
58
 
63
- def self.scan_for_day_portions(token)
64
- scanner = {/^ams?$/ => :am,
65
- /^pms?$/ => :pm,
66
- /^mornings?$/ => :morning,
67
- /^afternoons?$/ => :afternoon,
68
- /^evenings?$/ => :evening,
69
- /^(night|nite)s?$/ => :night}
70
- scanner.keys.each do |scanner_item|
71
- return Chronic::RepeaterDayPortion.new(scanner[scanner_item]) if scanner_item =~ token.word
59
+ def self.scan_for_day_portions(token)
60
+ scan_for token, RepeaterDayPortion,
61
+ {
62
+ /^ams?$/ => :am,
63
+ /^pms?$/ => :pm,
64
+ /^mornings?$/ => :morning,
65
+ /^afternoons?$/ => :afternoon,
66
+ /^evenings?$/ => :evening,
67
+ /^(night|nite)s?$/ => :night
68
+ }
72
69
  end
73
- return nil
74
- end
75
70
 
76
- def self.scan_for_times(token, options)
77
- if token.word =~ /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
78
- return Chronic::RepeaterTime.new(token.word, options)
71
+ def self.scan_for_times(token)
72
+ if token.word =~ /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
73
+ return Chronic::RepeaterTime.new(token.word)
74
+ end
75
+ return nil
79
76
  end
80
- return nil
81
- end
82
77
 
83
- def self.scan_for_units(token)
84
- scanner = {/^years?$/ => :year,
85
- /^seasons?$/ => :season,
86
- /^months?$/ => :month,
87
- /^fortnights?$/ => :fortnight,
88
- /^weeks?$/ => :week,
89
- /^weekends?$/ => :weekend,
90
- /^(week|business)days?$/ => :weekday,
91
- /^days?$/ => :day,
92
- /^hours?$/ => :hour,
93
- /^minutes?$/ => :minute,
94
- /^seconds?$/ => :second}
95
- scanner.keys.each do |scanner_item|
96
- if scanner_item =~ token.word
97
- klass_name = 'Chronic::Repeater' + scanner[scanner_item].to_s.capitalize
98
- klass = eval(klass_name)
99
- return klass.new(scanner[scanner_item])
78
+ def self.scan_for_units(token)
79
+ {
80
+ /^years?$/ => :year,
81
+ /^seasons?$/ => :season,
82
+ /^months?$/ => :month,
83
+ /^fortnights?$/ => :fortnight,
84
+ /^weeks?$/ => :week,
85
+ /^weekends?$/ => :weekend,
86
+ /^(week|business)days?$/ => :weekday,
87
+ /^days?$/ => :day,
88
+ /^hours?$/ => :hour,
89
+ /^minutes?$/ => :minute,
90
+ /^seconds?$/ => :second
91
+ }.each do |item, symbol|
92
+ if item =~ token.word
93
+ klass_name = 'Repeater' + symbol.to_s.capitalize
94
+ klass = Chronic.const_get(klass_name)
95
+ return klass.new(symbol)
96
+ end
100
97
  end
98
+ return nil
101
99
  end
102
- return nil
103
- end
104
100
 
105
- def <=>(other)
106
- width <=> other.width
107
- end
101
+ def <=>(other)
102
+ width <=> other.width
103
+ end
108
104
 
109
- # returns the width (in seconds or months) of this repeatable.
110
- def width
111
- raise("Repeatable#width must be overridden in subclasses")
112
- end
105
+ # returns the width (in seconds or months) of this repeatable.
106
+ def width
107
+ raise("Repeatable#width must be overridden in subclasses")
108
+ end
113
109
 
114
- # returns the next occurance of this repeatable.
115
- def next(pointer)
116
- !@now.nil? || raise("Start point must be set before calling #next")
117
- [:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
118
- #raise("Repeatable#next must be overridden in subclasses")
119
- end
110
+ # returns the next occurance of this repeatable.
111
+ def next(pointer)
112
+ !@now.nil? || raise("Start point must be set before calling #next")
113
+ [:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
114
+ #raise("Repeatable#next must be overridden in subclasses")
115
+ end
120
116
 
121
- def this(pointer)
122
- !@now.nil? || raise("Start point must be set before calling #this")
123
- [:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
124
- end
117
+ def this(pointer)
118
+ !@now.nil? || raise("Start point must be set before calling #this")
119
+ [:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
120
+ end
125
121
 
126
- def to_s
127
- 'repeater'
122
+ def to_s
123
+ 'repeater'
124
+ end
128
125
  end
129
- end
126
+ end