chronic 0.6.3 → 0.10.2

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -3
  3. data/.travis.yml +8 -0
  4. data/HISTORY.md +76 -0
  5. data/README.md +47 -42
  6. data/Rakefile +30 -12
  7. data/chronic.gemspec +10 -4
  8. data/lib/chronic/date.rb +82 -0
  9. data/lib/chronic/grabber.rb +9 -7
  10. data/lib/chronic/handler.rb +47 -40
  11. data/lib/chronic/handlers.rb +240 -28
  12. data/lib/chronic/numerizer.rb +11 -2
  13. data/lib/chronic/ordinal.rb +28 -23
  14. data/lib/chronic/parser.rb +268 -0
  15. data/lib/chronic/pointer.rb +9 -7
  16. data/lib/chronic/repeater.rb +58 -48
  17. data/lib/chronic/repeaters/repeater_day.rb +4 -3
  18. data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
  19. data/lib/chronic/repeaters/repeater_day_portion.rb +33 -18
  20. data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
  21. data/lib/chronic/repeaters/repeater_hour.rb +4 -3
  22. data/lib/chronic/repeaters/repeater_minute.rb +4 -3
  23. data/lib/chronic/repeaters/repeater_month.rb +5 -4
  24. data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
  25. data/lib/chronic/repeaters/repeater_season.rb +5 -3
  26. data/lib/chronic/repeaters/repeater_second.rb +4 -3
  27. data/lib/chronic/repeaters/repeater_time.rb +35 -25
  28. data/lib/chronic/repeaters/repeater_week.rb +4 -3
  29. data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
  30. data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
  31. data/lib/chronic/repeaters/repeater_year.rb +6 -5
  32. data/lib/chronic/scalar.rb +40 -68
  33. data/lib/chronic/season.rb +1 -12
  34. data/lib/chronic/separator.rb +142 -23
  35. data/lib/chronic/sign.rb +49 -0
  36. data/lib/chronic/span.rb +2 -2
  37. data/lib/chronic/tag.rb +10 -15
  38. data/lib/chronic/time.rb +40 -0
  39. data/lib/chronic/time_zone.rb +9 -7
  40. data/lib/chronic/token.rb +16 -10
  41. data/lib/chronic.rb +109 -70
  42. data/test/helper.rb +8 -2
  43. data/test/{test_Chronic.rb → test_chronic.rb} +69 -34
  44. data/test/{test_DaylightSavings.rb → test_daylight_savings.rb} +1 -1
  45. data/test/{test_Handler.rb → test_handler.rb} +38 -14
  46. data/test/{test_MiniDate.rb → test_mini_date.rb} +9 -9
  47. data/test/{test_Numerizer.rb → test_numerizer.rb} +16 -2
  48. data/test/test_parsing.rb +392 -22
  49. data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +1 -1
  50. data/test/test_repeater_day_portion.rb +254 -0
  51. data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +1 -1
  52. data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +1 -1
  53. data/test/{test_RepeaterMinute.rb → test_repeater_minute.rb} +1 -1
  54. data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +1 -1
  55. data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +1 -1
  56. data/test/{test_RepeaterSeason.rb → test_repeater_season.rb} +1 -1
  57. data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +19 -1
  58. data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +1 -1
  59. data/test/{test_RepeaterWeekday.rb → test_repeater_weekday.rb} +1 -1
  60. data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +1 -1
  61. data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +1 -1
  62. data/test/{test_Span.rb → test_span.rb} +2 -2
  63. data/test/{test_Token.rb → test_token.rb} +1 -1
  64. metadata +129 -87
  65. data/.gemtest +0 -0
  66. data/.yardopts +0 -3
  67. data/lib/chronic/chronic.rb +0 -323
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d0bcdd2460eb86f8898212a9eca5e325c9aa5e61
4
+ data.tar.gz: d8abd19da58e28473139b8f137991cb0ee150c49
5
+ SHA512:
6
+ metadata.gz: 04bfd4f32ce8f8ef6585042425ea64cc95d07998287fb16fa45b8d0a06d5dd48f8b85e6fecbf00e69101e560f21527ef6539a8994a9457357d25c2c832c7b87f
7
+ data.tar.gz: 87db3c22292ed9d5a22ecc84e6b14dafb2ed1c7a4c2f829873593b86b37707999adfaf43a5e0d801daffaaec9c7b599beade2d366d108c60bf2e8989998ed4b4
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
- pkg
2
1
  *.rbc
3
- rdoc
4
2
  .yardoc
5
- doc
3
+ /pkg/
4
+ /coverage/
5
+ /doc/
6
+ /tags/
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - 1.8.7
6
+ - jruby-19mode
7
+ - jruby-18mode
8
+ - rbx-19mode
data/HISTORY.md CHANGED
@@ -1,3 +1,79 @@
1
+ # 0.10.2 / 2013-09-09
2
+
3
+ * Fix 1.8.7 support (due to be dropped in 0.11.0)
4
+ * Bugfix for times with negative zones
5
+
6
+ # 0.10.1 / 2013-08-27
7
+
8
+ * Support `ActiveSupport::TimeZone` (#209, #208)
9
+
10
+ # 0.10.0 / 2013-08-25
11
+
12
+ * Chronic will parse subseconds correctly
13
+ for all supported date/time formats (#195, #198 and #200)
14
+ * Support for date format: dd.mm.yyyy (#197)
15
+ * Option `:hours24` to parse as 24 hour clock (#201 and #202)
16
+ * `:guess` option allows to specify which part of Span to return.
17
+ (accepted values `false`,`true`,`:begin`, `:middle`, `:end`)
18
+ * Replace `rcov` with `SimpleCov` for coverage generation
19
+ * Add more tests
20
+ * Various changes in codebase (#202 and #206)
21
+
22
+ # 0.9.1 / 2013-02-25
23
+
24
+ * Ensure Chronic strips periods from day portions (#173)
25
+ * Properly numerize "twelfth", "twentieth" etc. (#172, James McKinney)
26
+ * Ensure Chronic is compatible with Ruby 2.0.0 (#165, Ravil Bayramgalin)
27
+
28
+ # 0.9.0 / 2012-12-21
29
+
30
+ * Implement Chronic::Parser class and create an instance of this class
31
+ instead of leaving all data in the class level of Chronic
32
+ * Various bug fixes
33
+ * Add support for excel date formats (#149, @jmondo)
34
+ * Added support for time expressions such as '10 till' or 'half
35
+ past two' (#146, @chicagogrooves)
36
+ * Add support for RepeaterDayName, RepeaterMonthName,
37
+ Ordinal/ScalarDay and Time (#153, @kareemk)
38
+
39
+ # 0.8.0 / 2012-09-16
40
+
41
+ * Support parsing "<ordinal> of this month" (#109)
42
+ * Support parsing ISO 8601 format (#115)
43
+ * Support parsing "on <day>" without a timestamp (#117)
44
+ * Fix time parsing regexp (#125)
45
+ * Support time when parsing dd-mm-yyy <time> (#126)
46
+ * Allow anchor handler to accept any separators (at, on) (#128)
47
+ * Support parsing EXIF date format (#112)
48
+ * Start using minitest for testing
49
+ * Ensure periods are interpreted as colons (#81).
50
+ * Support month/day and day/month parsing (#59).
51
+ * Support day(scalar)-month(name)-year(scalar) (#99).
52
+ * Handle text starting with 'a' or 'an' (#101, @steveburkett).
53
+ * Ensure post medium timestamps are correctly formatted (#89)
54
+
55
+ # 0.6.7 / 2012-01-31
56
+
57
+ * Handle day, month names with scalar day and year (Joe Fiorini)
58
+ * Ensure 31st parses correctly with day names (Joe Fiorini)
59
+
60
+ # 0.6.6 / 2011-11-23
61
+
62
+ * `Chronic.parse('thur')` no longer returns `nil` (@harold)
63
+
64
+ # 0.6.5 / 2011-11-04
65
+
66
+ * Fix bug when parsing ordinal repeaters (#73)
67
+ * Added handler support for day_name month_name (@imme5150)
68
+ * Fix bug when parsing strings prefixed with PM
69
+
70
+ # 0.6.4 / 2011-08-08
71
+
72
+ * Fixed bug where 'noon' was parsed as 00:00 rather than 12:00
73
+ with :ambiguous_time_range => :none (Vladimir Chernis)
74
+ * Add support for handling '2009 May 22nd'
75
+ * Add the ability to handle scalar-day/repeater-month-name as well as ordinals
76
+
1
77
  # 0.6.3 / 2011-08-01
2
78
 
3
79
  * Ensure 'thu' is parsed as Thursday for 1.8.7 generic timestamp
data/README.md CHANGED
@@ -1,56 +1,54 @@
1
1
  Chronic
2
2
  =======
3
3
 
4
- ## DESCRIPTION
5
-
6
4
  Chronic is a natural language date/time parser written in pure Ruby. See below
7
5
  for the wide variety of formats Chronic will parse.
8
6
 
7
+ ## Installation
9
8
 
10
- ## INSTALLATION
11
-
12
- ### RubyGems
13
-
14
- $ [sudo] gem install chronic
9
+ ```
10
+ $ gem install chronic
11
+ ```
15
12
 
16
- ### GitHub
13
+ ## Usage
17
14
 
18
- $ git clone git://github.com/mojombo/chronic.git
19
- $ cd chronic && gem build chronic.gemspec
20
- $ gem install chronic-<version>.gem
15
+ ```ruby
16
+ require 'chronic'
21
17
 
18
+ Time.now #=> Sun Aug 27 23:18:25 PDT 2006
22
19
 
23
- ## USAGE
20
+ Chronic.parse('tomorrow')
21
+ #=> Mon Aug 28 12:00:00 PDT 2006
24
22
 
25
- You can parse strings containing a natural language date using the
26
- `Chronic.parse` method.
23
+ Chronic.parse('monday', :context => :past)
24
+ #=> Mon Aug 21 12:00:00 PDT 2006
27
25
 
28
- require 'chronic'
26
+ Chronic.parse('this tuesday 5:00')
27
+ #=> Tue Aug 29 17:00:00 PDT 2006
29
28
 
30
- Time.now #=> Sun Aug 27 23:18:25 PDT 2006
29
+ Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
30
+ #=> Tue Aug 29 05:00:00 PDT 2006
31
31
 
32
- Chronic.parse('tomorrow')
33
- #=> Mon Aug 28 12:00:00 PDT 2006
32
+ Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
33
+ #=> Sat May 27 12:00:00 PDT 2000
34
34
 
35
- Chronic.parse('monday', :context => :past)
36
- #=> Mon Aug 21 12:00:00 PDT 2006
35
+ Chronic.parse('may 27th', :guess => false)
36
+ #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
37
37
 
38
- Chronic.parse('this tuesday 5:00')
39
- #=> Tue Aug 29 17:00:00 PDT 2006
38
+ Chronic.parse('6/4/2012', :endian_precedence => :little)
39
+ #=> Fri Apr 06 00:00:00 PDT 2012
40
40
 
41
- Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
42
- #=> Tue Aug 29 05:00:00 PDT 2006
41
+ Chronic.parse('INVALID DATE')
42
+ #=> nil
43
+ ```
43
44
 
44
- Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
45
- #=> Sat May 27 12:00:00 PDT 2000
46
-
47
- Chronic.parse('may 27th', :guess => false)
48
- #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
45
+ If the parser can find a date or time, either a Time or Chronic::Span
46
+ will be returned (depending on the value of `:guess`). If no
47
+ date or time can be found, `nil` will be returned.
49
48
 
50
49
  See `Chronic.parse` for detailed usage instructions.
51
50
 
52
-
53
- ## EXAMPLES
51
+ ## Examples
54
52
 
55
53
  Chronic can parse a huge variety of date and time formats. Following is a
56
54
  small sample of strings that will be properly parsed. Parsing is case
@@ -64,12 +62,17 @@ Simple
64
62
  * friday 13:00
65
63
  * mon 2:35
66
64
  * 4pm
65
+ * 10 to 8
66
+ * 10 past 2
67
+ * half past 2
67
68
  * 6 in the morning
68
69
  * friday 1pm
69
70
  * sat 7 in the evening
70
71
  * yesterday
71
72
  * today
72
73
  * tomorrow
74
+ * last week
75
+ * next week
73
76
  * this tuesday
74
77
  * next month
75
78
  * last winter
@@ -86,6 +89,7 @@ Simple
86
89
  Complex
87
90
 
88
91
  * 3 years ago
92
+ * a year ago
89
93
  * 5 months before now
90
94
  * 7 hours ago
91
95
  * 7 days from now
@@ -131,10 +135,11 @@ Specific Times (many of the above with an added time)
131
135
  * January 5 at 7pm
132
136
  * 22nd of june at 8am
133
137
  * 1979-05-27 05:00:00
138
+ * 03/01/2012 07:25:09.234567
134
139
  * etc
135
140
 
136
141
 
137
- ## TIME ZONES
142
+ ## Time Zones
138
143
 
139
144
  Chronic allows you to set which Time class to use when constructing times. By
140
145
  default, the built in Ruby time class creates times in your system's local
@@ -142,13 +147,14 @@ time zone. You can set this to something like ActiveSupport's
142
147
  [TimeZone](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html)
143
148
  class to get full time zone support.
144
149
 
145
- >> Time.zone = "UTC"
146
- >> Chronic.time_class = Time.zone
147
- >> Chronic.parse("June 15 2006 at 5:45 AM")
148
- => Thu, 15 Jun 2006 05:45:00 UTC +00:00
149
-
150
+ ```
151
+ >> Time.zone = "UTC"
152
+ >> Chronic.time_class = Time.zone
153
+ >> Chronic.parse("June 15 2006 at 5:45 AM")
154
+ => Thu, 15 Jun 2006 05:45:00 UTC +00:00
155
+ ```
150
156
 
151
- ## LIMITATIONS
157
+ ## Limitations
152
158
 
153
159
  Chronic uses Ruby's built in Time class for all time storage and computation.
154
160
  Because of this, only times that the Time class can handle will be properly
@@ -156,20 +162,19 @@ parsed. Parsing for times outside of this range will simply return nil.
156
162
  Support for a wider range of times is planned for a future release.
157
163
 
158
164
 
159
- ## CONTRIBUTE
165
+ ## Contribute
160
166
 
161
167
  If you'd like to hack on Chronic, start by forking the repo on GitHub:
162
168
 
163
169
  https://github.com/mojombo/chronic
164
170
 
165
- To get all of the dependencies, install the gem first. The best way to get
166
- your changes merged back into core is as follows:
171
+ The best way to get your changes merged back into core is as follows:
167
172
 
168
173
  1. Clone down your fork
169
174
  1. Create a thoughtfully named topic branch to contain your change
170
175
  1. Hack away
171
176
  1. Add tests and make sure everything still passes by running `rake`
172
- 1. Ensure your tests pass in multiple timezones
177
+ 1. Ensure your tests pass in multiple timezones. ie `TZ=utc rake` `TZ=BST rake`
173
178
  1. If you are adding new functionality, document it in the README
174
179
  1. Do not change the version number, we will do that on our end
175
180
  1. If necessary, rebase your commits into logical chunks, without errors
data/Rakefile CHANGED
@@ -5,19 +5,37 @@ def version
5
5
  contents[/VERSION = "([^"]+)"/, 1]
6
6
  end
7
7
 
8
- require 'rake/testtask'
9
- Rake::TestTask.new(:test) do |test|
10
- test.libs << 'lib' << 'test'
11
- test.pattern = 'test/**/test_*.rb'
12
- test.verbose = true
8
+ def do_test
9
+ $:.unshift './test'
10
+ Dir.glob('test/test_*.rb').each { |t| require File.basename(t) }
13
11
  end
14
12
 
15
- desc "Generate RCov test coverage and open in your browser"
13
+ def open_command
14
+ case RUBY_PLATFORM
15
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
16
+ 'start'
17
+ when /darwin|mac os/
18
+ 'open'
19
+ else
20
+ 'xdg-open'
21
+ end
22
+ end
23
+
24
+ task :test do
25
+ do_test
26
+ end
27
+
28
+ desc "Generate SimpleCov test coverage and open in your browser"
16
29
  task :coverage do
17
- require 'rcov'
18
- sh "rm -fr coverage"
19
- sh "rcov test/test_*.rb"
20
- sh "open coverage/index.html"
30
+ require 'simplecov'
31
+ FileUtils.rm_rf("./coverage")
32
+ SimpleCov.command_name 'Unit Tests'
33
+ SimpleCov.at_exit do
34
+ SimpleCov.result.format!
35
+ sh "#{open_command} #{SimpleCov.coverage_path}/index.html"
36
+ end
37
+ SimpleCov.start
38
+ do_test
21
39
  end
22
40
 
23
41
  desc "Open an irb session preloaded with this library"
@@ -40,9 +58,9 @@ end
40
58
 
41
59
  desc "Build a gem from the gemspec"
42
60
  task :build do
43
- sh "mkdir -p pkg"
61
+ FileUtils.mkdir_p "pkg"
44
62
  sh "gem build chronic.gemspec"
45
- sh "mv chronic-#{version}.gem pkg"
63
+ FileUtils.mv("./chronic-#{version}.gem", "pkg")
46
64
  end
47
65
 
48
66
  task :default => :test
data/chronic.gemspec CHANGED
@@ -8,10 +8,16 @@ Gem::Specification.new do |s|
8
8
  s.summary = 'Natural language date/time parsing.'
9
9
  s.description = 'Chronic is a natural language date/time parser written in pure Ruby.'
10
10
  s.authors = ['Tom Preston-Werner', 'Lee Jarvis']
11
- s.email = ['tom@mojombo.com', 'lee@jarvis.co']
11
+ s.email = ['tom@mojombo.com', 'ljjarvis@gmail.com']
12
12
  s.homepage = 'http://github.com/mojombo/chronic'
13
+ s.license = 'MIT'
13
14
  s.rdoc_options = ['--charset=UTF-8']
14
15
  s.extra_rdoc_files = %w[README.md HISTORY.md LICENSE]
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- test`.split("\n")
17
- end
16
+ s.files = `git ls-files`.split($/)
17
+ s.test_files = `git ls-files -- test`.split($/)
18
+
19
+ s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'simplecov'
21
+ s.add_development_dependency 'minitest', '~> 5.0'
22
+ s.add_development_dependency 'activesupport'
23
+ end
@@ -0,0 +1,82 @@
1
+ module Chronic
2
+ class Date
3
+ YEAR_MONTHS = 12
4
+ MONTH_DAYS = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
5
+ MONTH_DAYS_LEAP = [nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
6
+ YEAR_SECONDS = 31_536_000 # 365 * 24 * 60 * 60
7
+ SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
8
+ MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
9
+ FORTNIGHT_SECONDS = 1_209_600 # 14 * 24 * 60 * 60
10
+ WEEK_SECONDS = 604_800 # 7 * 24 * 60 * 60
11
+ WEEKEND_SECONDS = 172_800 # 2 * 24 * 60 * 60
12
+ DAY_SECONDS = 86_400 # 24 * 60 * 60
13
+ MONTHS = {
14
+ :january => 1,
15
+ :february => 2,
16
+ :march => 3,
17
+ :april => 4,
18
+ :may => 5,
19
+ :june => 6,
20
+ :july => 7,
21
+ :august => 8,
22
+ :september => 9,
23
+ :october => 10,
24
+ :november => 11,
25
+ :december => 12
26
+ }
27
+ DAYS = {
28
+ :sunday => 0,
29
+ :monday => 1,
30
+ :tuesday => 2,
31
+ :wednesday => 3,
32
+ :thursday => 4,
33
+ :friday => 5,
34
+ :saturday => 6
35
+ }
36
+
37
+ # Checks if given number could be day
38
+ def self.could_be_day?(day)
39
+ day >= 1 && day <= 31
40
+ end
41
+
42
+ # Checks if given number could be month
43
+ def self.could_be_month?(month)
44
+ month >= 1 && month <= 12
45
+ end
46
+
47
+ # Checks if given number could be year
48
+ def self.could_be_year?(year)
49
+ year >= 1 && year <= 9999
50
+ end
51
+
52
+ # Build a year from a 2 digit suffix.
53
+ #
54
+ # year - The two digit Integer year to build from.
55
+ # bias - The Integer amount of future years to bias.
56
+ #
57
+ # Examples:
58
+ #
59
+ # make_year(96, 50) #=> 1996
60
+ # make_year(79, 20) #=> 2079
61
+ # make_year(00, 50) #=> 2000
62
+ #
63
+ # Returns The Integer 4 digit year.
64
+ def self.make_year(year, bias)
65
+ return year if year.to_s.size > 2
66
+ start_year = Chronic.time_class.now.year - bias
67
+ century = (start_year / 100) * 100
68
+ full_year = century + year
69
+ full_year += 100 if full_year < start_year
70
+ full_year
71
+ end
72
+
73
+ def self.month_overflow?(year, month, day)
74
+ if ::Date.leap?(year)
75
+ day > Date::MONTH_DAYS_LEAP[month]
76
+ else
77
+ day > Date::MONTH_DAYS[month]
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -1,20 +1,22 @@
1
1
  module Chronic
2
2
  class Grabber < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Grabber tags to
5
- # each token
4
+ # Scan an Array of Tokens and apply any necessary Grabber tags to
5
+ # each token.
6
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
7
+ # tokens - An Array of Token objects to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of Token objects.
10
11
  def self.scan(tokens, options)
11
12
  tokens.each do |token|
12
13
  if t = scan_for_all(token) then token.tag(t); next end
13
14
  end
14
15
  end
15
16
 
16
- # @param [Token] token
17
- # @return [Grabber, nil]
17
+ # token - The Token object to scan.
18
+ #
19
+ # Returns a new Grabber object.
18
20
  def self.scan_for_all(token)
19
21
  scan_for token, self,
20
22
  {
@@ -1,77 +1,84 @@
1
1
  module Chronic
2
2
  class Handler
3
3
 
4
- # @return [Array] A list of patterns
5
4
  attr_reader :pattern
6
5
 
7
- # @return [Symbol] The method which handles this list of patterns.
8
- # This method should exist inside the {Handlers} module
9
6
  attr_reader :handler_method
10
7
 
11
- # @param [Array] pattern A list of patterns to match tokens against
12
- # @param [Symbol] handler_method The method to be invoked when patterns
13
- # are matched. This method should exist inside the {Handlers} module
8
+ # pattern - An Array of patterns to match tokens against.
9
+ # handler_method - A Symbol representing the method to be invoked
10
+ # when a pattern matches.
14
11
  def initialize(pattern, handler_method)
15
12
  @pattern = pattern
16
13
  @handler_method = handler_method
17
14
  end
18
15
 
19
- # @param [Array] tokens
20
- # @param [Hash] definitions
21
- # @return [Boolean]
22
- # @see Chronic.tokens_to_span
16
+ # tokens - An Array of tokens to process.
17
+ # definitions - A Hash of definitions to check against.
18
+ #
19
+ # Returns true if a match is found.
23
20
  def match(tokens, definitions)
24
21
  token_index = 0
22
+ @pattern.each do |elements|
23
+ was_optional = false
24
+ elements = [elements] unless elements.is_a?(Array)
25
+
26
+ elements.each_index do |i|
27
+ name = elements[i].to_s
28
+ optional = name[-1, 1] == '?'
29
+ name = name.chop if optional
30
+
31
+ case elements[i]
32
+ when Symbol
33
+ if tags_match?(name, tokens, token_index)
34
+ token_index += 1
35
+ break
36
+ else
37
+ if optional
38
+ was_optional = true
39
+ next
40
+ elsif i + 1 < elements.count
41
+ next
42
+ else
43
+ return false unless was_optional
44
+ end
45
+ end
25
46
 
26
- @pattern.each do |element|
27
- name = element.to_s
28
- optional = name[-1, 1] == '?'
29
- name = name.chop if optional
47
+ when String
48
+ return true if optional && token_index == tokens.size
30
49
 
31
- case element
32
- when Symbol
33
- if tags_match?(name, tokens, token_index)
34
- token_index += 1
35
- next
36
- else
37
- if optional
38
- next
50
+ if definitions.key?(name.to_sym)
51
+ sub_handlers = definitions[name.to_sym]
39
52
  else
40
- return false
53
+ raise "Invalid subset #{name} specified"
41
54
  end
42
- end
43
- when String
44
- return true if optional && token_index == tokens.size
45
55
 
46
- if definitions.key?(name.to_sym)
47
- sub_handlers = definitions[name.to_sym]
56
+ sub_handlers.each do |sub_handler|
57
+ return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
58
+ end
48
59
  else
49
- raise ChronicPain, "Invalid subset #{name} specified"
50
- end
51
-
52
- sub_handlers.each do |sub_handler|
53
- return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
60
+ raise "Invalid match type: #{elements[i].class}"
54
61
  end
55
- else
56
- raise ChronicPain, "Invalid match type: #{element.class}"
57
62
  end
63
+
58
64
  end
59
65
 
60
66
  return false if token_index != tokens.size
61
67
  return true
62
68
  end
63
69
 
64
- def invoke(type, tokens, options)
70
+ def invoke(type, tokens, parser, options)
65
71
  if Chronic.debug
66
72
  puts "-#{type}"
67
73
  puts "Handler: #{@handler_method}"
68
74
  end
69
75
 
70
- Handlers.send(@handler_method, tokens, options)
76
+ parser.send(@handler_method, tokens, options)
71
77
  end
72
78
 
73
- # @param [Handler] The handler to compare
74
- # @return [Boolean] True if these handlers match
79
+ # other - The other Handler object to compare.
80
+ #
81
+ # Returns true if these Handlers match.
75
82
  def ==(other)
76
83
  @pattern == other.pattern
77
84
  end
@@ -87,4 +94,4 @@ module Chronic
87
94
  end
88
95
 
89
96
  end
90
- end
97
+ end