chronic 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
11
  tokens.each_index do |i|
5
12
  if t = scan_for_all(tokens[i]) then tokens[i].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,5 +1,12 @@
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
11
  tokens.each_index do |i|
5
12
  if t = scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
@@ -11,6 +18,8 @@ module Chronic
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,
@@ -2,6 +2,8 @@ module Chronic
2
2
  class RepeaterMonth < Repeater #:nodoc:
3
3
  MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
4
  YEAR_MONTHS = 12
5
+ MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
6
+ MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
5
7
 
6
8
  def initialize(type)
7
9
  super
@@ -54,7 +56,11 @@ module Chronic
54
56
  new_year += 1
55
57
  new_month -= YEAR_MONTHS
56
58
  end
57
- Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
59
+
60
+ days = month_days(new_year, new_month)
61
+ new_day = time.day > days ? days : time.day
62
+
63
+ Time.construct(new_year, new_month, new_day, time.hour, time.min, time.sec)
58
64
  end
59
65
 
60
66
  def width
@@ -64,5 +70,11 @@ module Chronic
64
70
  def to_s
65
71
  super << '-month'
66
72
  end
73
+
74
+ private
75
+
76
+ def month_days(year, month)
77
+ Date.leap?(year) ? MONTH_DAYS_LEAP[month - 1] : MONTH_DAYS[month - 1]
78
+ end
67
79
  end
68
80
  end
@@ -1,7 +1,13 @@
1
1
  module Chronic
2
- class Scalar < Tag #:nodoc:
2
+ class Scalar < Tag
3
3
  DAY_PORTIONS = %w( am pm morning afternoon evening night )
4
4
 
5
+ # Scan an Array of {Token}s and apply any necessary Scalar tags to
6
+ # each token
7
+ #
8
+ # @param [Array<Token>] tokens Array of tokens to scan
9
+ # @param [Hash] options Options specified in {Chronic.parse}
10
+ # @return [Array] list of tokens
5
11
  def self.scan(tokens, options)
6
12
  tokens.each_index do |i|
7
13
  if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
@@ -11,6 +17,9 @@ module Chronic
11
17
  end
12
18
  end
13
19
 
20
+ # @param [Token] token
21
+ # @param [Token] post_token
22
+ # @return [Scalar, nil]
14
23
  def self.scan_for_scalars(token, post_token)
15
24
  if token.word =~ /^\d*$/
16
25
  unless post_token && DAY_PORTIONS.include?(post_token.word)
@@ -19,6 +28,9 @@ module Chronic
19
28
  end
20
29
  end
21
30
 
31
+ # @param [Token] token
32
+ # @param [Token] post_token
33
+ # @return [ScalarDay, nil]
22
34
  def self.scan_for_days(token, post_token)
23
35
  if token.word =~ /^\d\d?$/
24
36
  toi = token.word.to_i
@@ -28,6 +40,9 @@ module Chronic
28
40
  end
29
41
  end
30
42
 
43
+ # @param [Token] token
44
+ # @param [Token] post_token
45
+ # @return [ScalarMonth, nil]
31
46
  def self.scan_for_months(token, post_token)
32
47
  if token.word =~ /^\d\d?$/
33
48
  toi = token.word.to_i
@@ -37,6 +52,10 @@ module Chronic
37
52
  end
38
53
  end
39
54
 
55
+ # @param [Token] token
56
+ # @param [Token] post_token
57
+ # @param [Hash] options Options specified in {Chronic.parse}
58
+ # @return [ScalarYear, nil]
40
59
  def self.scan_for_years(token, post_token, options)
41
60
  if token.word =~ /^([1-9]\d)?\d\d?$/
42
61
  unless post_token && DAY_PORTIONS.include?(post_token.word)
@@ -47,6 +66,15 @@ module Chronic
47
66
  end
48
67
 
49
68
  # Build a year from a 2 digit suffix
69
+ #
70
+ # @example
71
+ # make_year(96, 50) #=> 1996
72
+ # make_year(79, 20) #=> 2079
73
+ # make_year(00, 50) #=> 2000
74
+ #
75
+ # @param [Integer] year The two digit year to build from
76
+ # @param [Integer] bias The amount of future years to bias
77
+ # @return [Integer] The 4 digit year
50
78
  def self.make_year(year, bias)
51
79
  return year if year.to_s.size > 2
52
80
  start_year = Chronic.time_class.now.year - bias
@@ -1,5 +1,12 @@
1
1
  module Chronic
2
- class Separator < Tag #:nodoc:
2
+ class Separator < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Separator 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
11
  tokens.each_index do |i|
5
12
  if t = scan_for_commas(tokens[i]) then tokens[i].tag(t); next end
@@ -10,10 +17,14 @@ module Chronic
10
17
  end
11
18
  end
12
19
 
20
+ # @param [Token] token
21
+ # @return [SeparatorComma, nil]
13
22
  def self.scan_for_commas(token)
14
23
  scan_for token, SeparatorComma, { /^,$/ => :comma }
15
24
  end
16
25
 
26
+ # @param [Token] token
27
+ # @return [SeparatorSlashOrDash, nil]
17
28
  def self.scan_for_slash_or_dash(token)
18
29
  scan_for token, SeparatorSlashOrDash,
19
30
  {
@@ -22,14 +33,20 @@ module Chronic
22
33
  }
23
34
  end
24
35
 
36
+ # @param [Token] token
37
+ # @return [SeparatorAt, nil]
25
38
  def self.scan_for_at(token)
26
39
  scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
27
40
  end
28
41
 
42
+ # @param [Token] token
43
+ # @return [SeparatorIn, nil]
29
44
  def self.scan_for_in(token)
30
45
  scan_for token, SeparatorIn, { /^in$/ => :in }
31
46
  end
32
47
 
48
+ # @param [Token] token
49
+ # @return [SeparatorOn, nil]
33
50
  def self.scan_for_on(token)
34
51
  scan_for token, SeparatorOn, { /^on$/ => :on }
35
52
  end
@@ -1,11 +1,20 @@
1
1
  module Chronic
2
- class TimeZone < Tag #:nodoc:
2
+ class TimeZone < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary TimeZone 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
11
  tokens.each_index do |i|
5
12
  if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
6
13
  end
7
14
  end
8
15
 
16
+ # @param [Token] token
17
+ # @return [TimeZone, nil]
9
18
  def self.scan_for_all(token)
10
19
  scan_for token, self,
11
20
  {
@@ -1,6 +1,11 @@
1
1
  module Chronic
2
- class Token #:nodoc:
3
- attr_accessor :word, :tags
2
+ class Token
3
+
4
+ # @return [String] The word this Token represents
5
+ attr_accessor :word
6
+
7
+ # @return [Array] A list of tag associated with this Token
8
+ attr_accessor :tags
4
9
 
5
10
  def initialize(word)
6
11
  @word = word
@@ -8,21 +13,26 @@ module Chronic
8
13
  end
9
14
 
10
15
  # Tag this token with the specified tag
16
+ #
17
+ # @param [Tag] new_tag A instance of {Tag} or one of its subclasses
11
18
  def tag(new_tag)
12
19
  @tags << new_tag
13
20
  end
14
21
 
15
22
  # Remove all tags of the given class
23
+ #
24
+ # @param [Class] The tag class to remove
16
25
  def untag(tag_class)
17
26
  @tags.delete_if { |m| m.kind_of? tag_class }
18
27
  end
19
28
 
20
- # Return true if this token has any tags
29
+ # @return [Boolean] true if this token has any tags
21
30
  def tagged?
22
31
  @tags.size > 0
23
32
  end
24
33
 
25
- # Return the Tag that matches the given class
34
+ # @param [Class] tag_class The tag class to search for
35
+ # @return [Tag] The first Tag that matches the given class
26
36
  def get_tag(tag_class)
27
37
  @tags.find { |m| m.kind_of? tag_class }
28
38
  end
@@ -21,7 +21,7 @@ class TestChronic < Test::Unit::TestCase
21
21
 
22
22
  assert_equal :morning, tokens[1].tags[0].type
23
23
 
24
- tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
24
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
25
25
 
26
26
  assert_equal :am, tokens[1].tags[0].type
27
27
  assert_equal 2, tokens.size
@@ -34,7 +34,7 @@ class TestChronic < Test::Unit::TestCase
34
34
 
35
35
  assert_equal :morning, tokens[1].tags[0].type
36
36
 
37
- tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
37
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
38
38
 
39
39
  assert_equal :morning, tokens[1].tags[0].type
40
40
  assert_equal 2, tokens.size
@@ -51,4 +51,54 @@ class TestChronic < Test::Unit::TestCase
51
51
  assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
52
52
  end
53
53
 
54
+ def test_now
55
+ Chronic.parse('now', :now => Time.local(2006, 01))
56
+ assert_equal Time.local(2006, 01), Chronic.now
57
+
58
+ Chronic.parse('now', :now => Time.local(2007, 01))
59
+ assert_equal Time.local(2007, 01), Chronic.now
60
+ end
61
+
62
+ def test_endian_definitions
63
+ # middle, little
64
+ endians = [
65
+ Chronic::Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
66
+ Chronic::Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
67
+ ]
68
+
69
+ assert_equal endians, Chronic.definitions[:endian]
70
+
71
+ defs = Chronic.definitions(:endian_precedence => :little)
72
+ assert_equal endians.reverse, defs[:endian]
73
+
74
+ defs = Chronic.definitions(:endian_precedence => [:little, :middle])
75
+ assert_equal endians.reverse, defs[:endian]
76
+
77
+ assert_raises(Chronic::InvalidArgumentException) do
78
+ Chronic.definitions(:endian_precedence => :invalid)
79
+ end
80
+ end
81
+
82
+ def test_passing_options
83
+ assert_raises(Chronic::InvalidArgumentException) do
84
+ Chronic.parse('now', :invalid => :option)
85
+ end
86
+
87
+ assert_raises(Chronic::InvalidArgumentException) do
88
+ Chronic.parse('now', :context => :invalid_context)
89
+ end
90
+ end
91
+
92
+ def test_debug
93
+ require 'stringio'
94
+ $stdout = StringIO.new
95
+ Chronic.debug = true
96
+
97
+ Chronic.parse 'now'
98
+ assert $stdout.string.include?('this(grabber-this)')
99
+ ensure
100
+ $stdout = STDOUT
101
+ Chronic.debug = false
102
+ end
103
+
54
104
  end
@@ -23,6 +23,10 @@ class TestRepeaterMonth < Test::Unit::TestCase
23
23
 
24
24
  time = Chronic::RepeaterMonth.new(:month).offset_by(@now, 10, :past)
25
25
  assert_equal Time.local(2005, 10, 16, 14), time
26
+
27
+ time = Chronic::RepeaterMonth.new(:month).offset_by(Time.local(2010, 3, 29), 1, :past)
28
+ assert_equal 2, time.month
29
+ assert_equal 28, time.day
26
30
  end
27
31
 
28
32
  def test_offset
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: chronic
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.1
5
+ version: 0.4.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tom Preston-Werner
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-06-05 00:00:00 Z
14
+ date: 2011-06-07 00:00:00 Z
15
15
  dependencies: []
16
16
 
17
17
  description: Chronic is a natural language date/time parser written in pure Ruby.