tickle 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,34 +20,140 @@ $ gem install tickle
20
20
 
21
21
  Everything's at Github - http://github.com/noctivityinc/tickle
22
22
 
23
+ -- DEPENDENCIES
24
+
25
+ chronic gem (gem install chronic)
26
+ thoughtbot's shoulda (gem install shoulda)
27
+
23
28
  == USAGE
24
29
 
25
30
  You can parse strings containing a natural language interval using the Tickle.parse method.
26
31
 
32
+ Tickle.parse returns an array of first occurrence, next occurrence, interval between occurrences.
33
+
34
+ You can also pass a start date with the word "starting" (e.g. Tickle.parse('every 3 days starting next friday'))
35
+
36
+ Tickle HEAVILY uses chronic for parsing both the event and the start date.
37
+
38
+ -- EXAMPLES
39
+
27
40
  require 'rubygems'
28
41
  require 'tickle'
29
42
 
30
- Time.now #=> 2010-04-21 14:32:02 -0400
31
-
32
- Tickle.parse('every 2 days')
33
- #=> 2010-04-23, 2
34
-
35
- Tickle.parse('every Sunday') #=> note, this upcoming Sunday is 4/25/2010
36
- #=> 2010-04-25, 7
37
-
38
- Tickle.parse('every 3 weeks')
39
- #=> 2010-05-12, 21
40
-
41
- Tickle.parse('2 days')
42
- #=> 2010-04-23, 2
43
+ Time.now
44
+ 2010-04-22 16:38:12 -0400
45
+
46
+ Tickle.parse('each day')
47
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 1]
48
+
49
+ Tickle.parse('every day')
50
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 1]
51
+
52
+ Tickle.parse('every week')
53
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 7]
54
+
55
+ Tickle.parse('every Month')
56
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 30]
57
+
58
+ Tickle.parse('every year')
59
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 365]
60
+
61
+ Tickle.parse('daily')
62
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 1]
63
+
64
+ Tickle.parse('weekly')
65
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 7]
66
+
67
+ Tickle.parse('monthly')
68
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 30]
69
+
70
+ Tickle.parse('yearly')
71
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 365]
72
+
73
+ Tickle.parse('every 3 days')
74
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 3]
75
+
76
+ Tickle.parse('every 3 weeks')
77
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 21]
78
+
79
+ Tickle.parse('every 3 months')
80
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 90]
81
+
82
+ Tickle.parse('every 3 years')
83
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 1095]
84
+
85
+ Tickle.parse('every other day')
86
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 2]
87
+
88
+ Tickle.parse('every other week')
89
+ #=> [2010-04-22 16:38:12 -0400, 2010-04-23 16:38:12 -0400, 14]
90
+
91
+ Tickle.parse('every other month')
92
+ #=> [2010-04-22 16:38:12 -0400, 2010-06-22 16:38:12 -0400, 60]
93
+
94
+ Tickle.parse('every other year')
95
+ #=> [2010-04-22 16:38:12 -0400, 2012-04-22 16:38:12 -0400, 730]
96
+
97
+ Tickle.parse('every other day starting May 1st')
98
+ #=> [2010-05-01 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 2]
99
+
100
+ Tickle.parse('every other week starting this Sunday')
101
+ #=> [2010-04-25 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 14]
102
+
103
+ Tickle.parse('every Monday')
104
+ #=> [2010-04-26 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
105
+
106
+ Tickle.parse('every Wednesday')
107
+ #=> [2010-04-28 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
108
+
109
+ Tickle.parse('every Friday')
110
+ #=> [2010-04-23 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
111
+
112
+ Tickle.parse('every May')
113
+ #=> [2010-05-01 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 30]
114
+
115
+ Tickle.parse('every june')
116
+ #=> [2010-06-01 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 30]
117
+
118
+ Tickle.parse('beginning of the week')
119
+ #=> [2010-04-25 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
120
+
121
+ Tickle.parse('middle of the week')
122
+ #=> [2010-04-28 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
123
+
124
+ Tickle.parse('end of the week')
125
+ #=> [2010-04-24 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 7]
126
+
127
+ Tickle.parse('beginning of the month')
128
+ #=> [2010-05-01 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 30]
129
+
130
+ Tickle.parse('middle of the month')
131
+ #=> [2010-05-15 12:00:00 -0400, 2012-04-22 16:38:12 -0400, 30]
132
+
133
+ Tickle.parse('end of the month')
134
+ #=> [2010-04-30 00:00:00 -0400, 2012-04-22 16:38:12 -0400, 30]
135
+
136
+ Tickle.parse('beginning of the year')
137
+ #=> [2011-01-01 12:00:00 -0500, 2012-04-22 16:38:12 -0400, 365]
138
+
139
+ Tickle.parse('middle of the year')
140
+ #=> [2010-06-15 00:00:00 -0400, 2012-04-22 16:38:12 -0400, 365]
141
+
142
+ Tickle.parse('end of the year')
143
+ #=> [2010-12-31 00:00:00 -0500, 2012-04-22 16:38:12 -0400, 365]
43
144
 
44
145
 
45
- You can either pass a string prefixed with the word "every" or simply the time frame.
146
+ You can either pass a string prefixed with the word "every, each or 'on the'" or simply the time frame.
46
147
 
47
148
  -- LIMITATIONS
48
149
 
49
- Currently, Tickle only works for day intervals but feel free to fork and add time-based interval support. Also, numbers must be entered in numeric and not string form (i.e. 3 not three).
150
+ Currently, Tickle only works for day intervals but feel free to fork and add time-based interval support or send me a note if you really want me to add it.
151
+
152
+ == CREDIT
153
+
154
+ HUGE shout-out to both the creator of Chronic, Tom Preston-Werner (http://chronic.rubyforge.org/) as well as Brian Brownling who maintains a github version at http://github.com/mojombo/chronic.
50
155
 
156
+ Without their work and code structure I'd be lost.
51
157
 
52
158
 
53
159
  == Note on Patches/Pull Requests
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ begin
11
11
  gem.homepage = "http://github.com/noctivityinc/tickle"
12
12
  gem.authors = ["Joshua Lippiner"]
13
13
  gem.add_dependency('chronic', '>= 0.2.3')
14
- gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_development_dependency "shoulda", ">= 2.10.3"
15
15
 
16
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
17
  end
@@ -0,0 +1,12 @@
1
+ every 2 weeks => word 'week' found plus a number so the interval = number * 7 and start_date = today
2
+ every second tuesday => => day of week found WITH number (ordinal converted to 2). interval = 30 with start_date = Chronic.parse(Ordinal Number Day Of Week 'in' Start Date Month)
3
+ every sunday => day of week found without number. interval = 7, start_date = next day of week occurrence
4
+ every other day => word 'day' found with word 'other.' interval = 2, start_date = today
5
+ every fourth thursday => day of week found WITH number (ordinal converted to 4). interval = 30 with start_date = next occurrence of 'event' as parsed by chronic
6
+ on the 15th of each month => 'each month' becomes interval = 30, number found + start month through chronic equals start date
7
+ on the 15th of November => month found with number. interval = 365, start_date = Chronic.parse(month + number)
8
+ on the second monday in April => month, day and number found. interval = 365, start_date = Chronic.parse(ordinal number form of number, day of week, month)
9
+ every November 15th => month found with number. interval = 365, start_date = Chronic.parse(month + number)
10
+ every day => word 'day' found without a number. interval = 1. start_day = today
11
+ every week => word 'week' found without a number. interval = 7. start_day = today
12
+ every month => word 'month' found without a number. interval = 30. start_day = today
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -8,13 +8,34 @@
8
8
 
9
9
  $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
10
10
 
11
+ require 'date'
12
+ require 'time'
11
13
  require 'chronic'
12
- require 'numerizer/numerizer'
13
14
 
14
15
  require 'tickle/tickle'
16
+ require 'tickle/handler'
17
+ require 'tickle/repeater'
15
18
 
16
19
  module Tickle
17
- VERSION = "0.0.1"
20
+ VERSION = "0.0.2"
18
21
 
19
22
  def self.debug; false; end
23
+
24
+ def self.dwrite(msg)
25
+ puts msg if Tickle.debug
26
+ end
27
+ end
28
+
29
+ class Date
30
+ def days_in_month
31
+ d,m,y = mday,month,year
32
+ d += 1 while Date.valid_civil?(y,m,d)
33
+ d - 1
34
+ end
35
+ end
36
+
37
+ class Array
38
+ def same?(y)
39
+ self.sort == y.sort
40
+ end
20
41
  end
@@ -0,0 +1,98 @@
1
+ module Tickle
2
+ class << self
3
+
4
+ def guess()
5
+ interval = guess_unit_types
6
+ interval ||= guess_weekday
7
+ interval ||= guess_weekday
8
+ interval ||= guess_month_names
9
+ interval ||= guess_number_and_unit
10
+ interval ||= guess_special
11
+
12
+ # defines the next occurrence of this tickle if not set in a guess routine
13
+ @next ||= @start + (interval * 60 * 60 * 24) if interval
14
+ return [@start.to_time, @next.to_time, interval] if interval
15
+ end
16
+
17
+ def guess_unit_types
18
+ interval = 1 if token_types.same?([:day])
19
+ interval = 7 if token_types.same?([:week])
20
+ interval = 30 if token_types.same?([:month])
21
+ interval = 365 if token_types.same?([:year])
22
+ interval
23
+ end
24
+
25
+ def guess_weekday
26
+ if token_types.same?([:weekday]) then
27
+ @start = Chronic.parse(token_of_type(:weekday).start.to_s)
28
+ interval = 7
29
+ end
30
+ interval
31
+ end
32
+
33
+ def guess_month_names
34
+ if token_types.same?([:month_name]) then
35
+ @start = Chronic.parse("#{token_of_type(:month_name).start.to_s} 1")
36
+ interval = 30
37
+ end
38
+ interval
39
+ end
40
+
41
+ def guess_number_and_unit
42
+ interval = token_of_type(:number).interval if token_types.same?([:number, :day])
43
+ interval = (token_of_type(:number).interval * 7) if token_types.same?([:number, :week])
44
+ interval = (token_of_type(:number).interval * 30) if token_types.same?([:number, :month])
45
+ interval = (token_of_type(:number).interval * 365) if token_types.same?([:number, :year])
46
+ interval
47
+ end
48
+
49
+ def guess_special
50
+ interval = guess_special_other
51
+ interval ||= guess_special_beginning
52
+ interval ||= guess_special_middle
53
+ interval ||= guess_special_end
54
+ end
55
+
56
+ private
57
+
58
+ def guess_special_other
59
+ interval = 2 if token_types.same?([:special, :day]) && token_of_type(:special).start == :other
60
+ interval = 14 if token_types.same?([:special, :week]) && token_of_type(:special).start == :other
61
+ if token_types.same?([:special, :month]) && token_of_type(:special).start == :other then interval = 60; @next = Chronic.parse('2 months from now'); end
62
+ if token_types.same?([:special, :year]) && token_of_type(:special).start == :other then interval = 730; @next = Chronic.parse('2 years from now'); end
63
+ interval
64
+ end
65
+
66
+ def guess_special_beginning
67
+ if token_types.same?([:special, :week]) && token_of_type(:special).start == :beginning then interval = 7; @start = Chronic.parse('Sunday'); end
68
+ if token_types.same?([:special, :month]) && token_of_type(:special).start == :beginning then interval = 30; @start = Chronic.parse('1st day next month'); end
69
+ if token_types.same?([:special, :year]) && token_of_type(:special).start == :beginning then interval = 365; @start = Chronic.parse('1st day next year'); end
70
+ interval
71
+ end
72
+
73
+ def guess_special_end
74
+ if token_types.same?([:special, :week]) && token_of_type(:special).start == :end then interval = 7; @start = Chronic.parse('Saturday'); end
75
+ if token_types.same?([:special, :month]) && token_of_type(:special).start == :end then interval = 30; @start = Date.new(Date.today.year, Date.today.month, Date.today.days_in_month); end
76
+ if token_types.same?([:special, :year]) && token_of_type(:special).start == :end then interval = 365; @start = Date.new(Date.today.year, 12, 31); end
77
+ interval
78
+ end
79
+
80
+ def guess_special_middle
81
+ if token_types.same?([:special, :week]) && token_of_type(:special).start == :middle then interval = 7; @start = Chronic.parse('Wednesday'); end
82
+ if token_types.same?([:special, :month]) && token_of_type(:special).start == :middle then
83
+ interval = 30;
84
+ @start = (Date.today.day >= 15 ? Chronic.parse('15th day of next month') : Date.new(Date.today.year, Date.today.month, 15))
85
+ end
86
+ if token_types.same?([:special, :year]) && token_of_type(:special).start == :middle then
87
+ interval = 365;
88
+ @start = (Date.today.day >= 15 && Date.today.month >= 6 ? Date.new(Date.today.year+1, 6, 15) : Date.new(Date.today.year, 6, 15))
89
+ end
90
+ interval
91
+ end
92
+
93
+ def token_of_type(type)
94
+ @tokens.detect {|token| token.type == type}
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,85 @@
1
+ class Tickle::Repeater < Chronic::Tag #:nodoc:
2
+ #
3
+ def self.scan(tokens)
4
+ # for each token
5
+ tokens.each do |token|
6
+ token = self.scan_for_numbers(token)
7
+ token = self.scan_for_month_names(token)
8
+ token = self.scan_for_day_names(token)
9
+ token = self.scan_for_special_text(token)
10
+ token = self.scan_for_units(token)
11
+ end
12
+ tokens
13
+ end
14
+
15
+ def self.scan_for_numbers(token)
16
+ num = Float(token.word) rescue nil
17
+ token.update(:number, nil, num.to_i) if num
18
+ token
19
+ end
20
+
21
+ def self.scan_for_month_names(token)
22
+ scanner = {/^jan\.?(uary)?$/ => :january,
23
+ /^feb\.?(ruary)?$/ => :february,
24
+ /^mar\.?(ch)?$/ => :march,
25
+ /^apr\.?(il)?$/ => :april,
26
+ /^may$/ => :may,
27
+ /^jun\.?e?$/ => :june,
28
+ /^jul\.?y?$/ => :july,
29
+ /^aug\.?(ust)?$/ => :august,
30
+ /^sep\.?(t\.?|tember)?$/ => :september,
31
+ /^oct\.?(ober)?$/ => :october,
32
+ /^nov\.?(ember)?$/ => :november,
33
+ /^dec\.?(ember)?$/ => :december}
34
+ scanner.keys.each do |scanner_item|
35
+ token.update(:month_name, scanner[scanner_item], 30) if scanner_item =~ token.word
36
+ end
37
+ token
38
+ end
39
+
40
+ def self.scan_for_day_names(token)
41
+ scanner = {/^m[ou]n(day)?$/ => :monday,
42
+ /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
43
+ /^tue$/ => :tuesday,
44
+ /^we(dnes|nds|nns)day$/ => :wednesday,
45
+ /^wed$/ => :wednesday,
46
+ /^th(urs|ers)day$/ => :thursday,
47
+ /^thu$/ => :thursday,
48
+ /^fr[iy](day)?$/ => :friday,
49
+ /^sat(t?[ue]rday)?$/ => :saturday,
50
+ /^su[nm](day)?$/ => :sunday}
51
+ scanner.keys.each do |scanner_item|
52
+ token.update(:weekday, scanner[scanner_item], 7) if scanner_item =~ token.word
53
+ end
54
+ token
55
+ end
56
+
57
+ def self.scan_for_special_text(token)
58
+ scanner = {/^other$/ => :other,
59
+ /^begin(ing|ning)?$/ => :beginning,
60
+ /^start$/ => :beginning,
61
+ /^end$/ => :end,
62
+ /^mid(d)?le$/ => :middle}
63
+ scanner.keys.each do |scanner_item|
64
+ token.update(:special, scanner[scanner_item], 7) if scanner_item =~ token.word
65
+ end
66
+ token
67
+ end
68
+
69
+ def self.scan_for_units(token)
70
+ scanner = {/^year(ly)?s?$/ => {:type => :year, :interval => 365, :start => :today},
71
+ /^month(ly)?s?$/ => {:type => :month, :interval => 30, :start => :today},
72
+ /^fortnights?$/ => {:type => :fortnight, :interval => 365, :start => :today},
73
+ /^week(ly)?s?$/ => {:type => :week, :interval => 7, :start => :today},
74
+ /^weekends?$/ => {:type => :weekend, :interval => 7, :start => :saturday},
75
+ /^days?$/ => {:type => :day, :interval => 1, :start => :today},
76
+ /^daily?$/ => {:type => :day, :interval => 1, :start => :today}}
77
+ scanner.keys.each do |scanner_item|
78
+ if scanner_item =~ token.word
79
+ token.update(scanner[scanner_item][:type], scanner[scanner_item][:start], scanner[scanner_item][:interval]) if scanner_item =~ token.word
80
+ end
81
+ end
82
+ token
83
+ end
84
+
85
+ end
@@ -1,25 +1,86 @@
1
1
  module Tickle
2
2
  class << self
3
-
3
+
4
4
  def parse(text, specified_options = {})
5
5
  # get options and set defaults if necessary
6
- default_options = {:now => Time.now}
6
+ default_options = {:start => Time.now}
7
7
  options = default_options.merge specified_options
8
-
8
+
9
9
  # ensure the specified options are valid
10
10
  specified_options.keys.each do |key|
11
11
  default_options.keys.include?(key) || raise(InvalidArgumentException, "#{key} is not a valid option key.")
12
12
  end
13
- Chronic.parse(options[:now]) || rails(InvalidArgumentException, ':now specified is not a valid datetime.')
14
-
15
- # store now for later =)
16
- @now = options[:now]
17
-
13
+ Chronic.parse(specified_options[:start]) || raise(InvalidArgumentException, ':start specified is not a valid datetime.') if specified_options[:start]
14
+
15
+ # remove every is specified
16
+ text = text.gsub(/^every\s\b/, '')
17
+
18
18
  # put the text into a normal format to ease scanning using Chronic
19
+ text = pre_normalize(text)
19
20
  text = Chronic.pre_normalize(text)
20
-
21
- return text
21
+ text = numericize_ordinals(text)
22
+
23
+ # check to see if this event starts some other time and reset now
24
+ event, starting = text.split('starting')
25
+ @start = (Chronic.parse(starting) || options[:start])
26
+
27
+ # split into tokens
28
+ @tokens = base_tokenize(event)
29
+
30
+ # scan the tokens with each token scanner
31
+ @tokens = Repeater.scan(@tokens)
32
+
33
+ # remove all tokens without a type
34
+ @tokens.reject! {|token| token.type.nil? }
35
+
36
+ # dwrite @tokens.inspect
37
+
38
+ return guess
22
39
  end
23
-
40
+
41
+ # Normalize natural string removing prefix language
42
+ def pre_normalize(text)
43
+ normalized_text = text.gsub(/^every\s\b/, '')
44
+ normalized_text = text.gsub(/^each\s\b/, '')
45
+ normalized_text = text.gsub(/^on the\s\b/, '')
46
+ normalized_text
47
+ end
48
+
49
+ # Split the text on spaces and convert each word into
50
+ # a Token
51
+ def base_tokenize(text) #:nodoc:
52
+ text.split(' ').map { |word| Token.new(word) }
53
+ end
54
+
55
+ # Convert ordinal words to numeric ordinals (third => 3rd)
56
+ def numericize_ordinals(text) #:nodoc:
57
+ text = text.gsub(/\b(\d*)(st|nd|rd|th)\b/, '\1')
58
+ end
59
+
60
+ # Returns an array of types for all tokens
61
+ def token_types
62
+ @tokens.map(&:type)
63
+ end
64
+ end
65
+
66
+ class Token #:nodoc:
67
+ attr_accessor :word, :type, :interval, :start
68
+
69
+ def initialize(word)
70
+ @word = word
71
+ @type = @interval = @start = nil
72
+ end
73
+
74
+ def update(type, start=nil, interval=nil)
75
+ @start = start
76
+ @type = type
77
+ @interval = interval
78
+ end
79
+ end
80
+
81
+ # This exception is raised if an invalid argument is provided to
82
+ # any of Tickle's methods
83
+ class InvalidArgumentException < Exception
84
+
24
85
  end
25
- end
86
+ end
@@ -4,7 +4,8 @@ require 'shoulda'
4
4
 
5
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
- require 'tickle'
7
+
8
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'tickle')
8
9
 
9
10
  class Test::Unit::TestCase
10
11
  end
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+ require 'time'
3
+ require 'test/unit'
4
+
5
+ class TestParsing < Test::Unit::TestCase
6
+
7
+ def setup
8
+
9
+ end
10
+
11
+ def test_parse_best_guess
12
+ puts "Time.now"
13
+ p Time.now
14
+
15
+ parse_now('each day')
16
+
17
+ parse_now('every day')
18
+ parse_now('every week')
19
+ parse_now('every Month')
20
+ parse_now('every year')
21
+
22
+ parse_now('daily')
23
+ parse_now('weekly')
24
+ parse_now('monthly')
25
+ parse_now('yearly')
26
+
27
+ parse_now('every 3 days')
28
+ parse_now('every 3 weeks')
29
+ parse_now('every 3 months')
30
+ parse_now('every 3 years')
31
+
32
+ parse_now('every other day')
33
+ parse_now('every other week')
34
+ parse_now('every other month')
35
+ parse_now('every other year')
36
+ parse_now('every other day starting May 1st')
37
+ parse_now('every other week starting this Sunday')
38
+
39
+ parse_now('every Monday')
40
+ parse_now('every Wednesday')
41
+ parse_now('every Friday')
42
+
43
+ parse_now('every May')
44
+ parse_now('every june')
45
+
46
+ parse_now('beginning of the week')
47
+ parse_now('middle of the week')
48
+ parse_now('end of the week')
49
+
50
+ parse_now('beginning of the month')
51
+ parse_now('middle of the month')
52
+ parse_now('end of the month')
53
+
54
+ parse_now('beginning of the year')
55
+ parse_now('middle of the year')
56
+ parse_now('end of the year')
57
+ end
58
+
59
+ def test_argument_validation
60
+ assert_raise(Tickle::InvalidArgumentException) do
61
+ time = Tickle.parse("may 27", :today => 'something odd')
62
+ end
63
+
64
+ assert_raise(Tickle::InvalidArgumentException) do
65
+ time = Tickle.parse("may 27", :foo => :bar)
66
+ end
67
+ end
68
+
69
+ private
70
+ def parse_now(string, options={})
71
+ puts ("attempting to parse '#{string}'")
72
+ out = Tickle.parse(string, {}.merge(options))
73
+ p ("output: #{out}")
74
+ out
75
+ end
76
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{tickle}
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Joshua Lippiner"]
12
+ s.date = %q{2010-04-22}
13
+ s.description = %q{Tickle is a date/time helper gem to help parse natural language into a recurring pattern. Tickle is designed to be a compliment of Chronic and can interpret things such as "every 2 days, every Sunday, Sundays, Weekly, etc.}
14
+ s.email = %q{jlippiner@noctivity.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".rvmrc",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "SCENARIOS.rdoc",
27
+ "VERSION",
28
+ "lib/numerizer/numerizer.rb",
29
+ "lib/tickle.rb",
30
+ "lib/tickle/handler.rb",
31
+ "lib/tickle/repeater.rb",
32
+ "lib/tickle/tickle.rb",
33
+ "test/helper.rb",
34
+ "test/test_parsing.rb",
35
+ "tickle.gemspec"
36
+ ]
37
+ s.homepage = %q{http://github.com/noctivityinc/tickle}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.6}
41
+ s.summary = %q{natural language parser for recurring events}
42
+ s.test_files = [
43
+ "test/helper.rb",
44
+ "test/test_parsing.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<chronic>, [">= 0.2.3"])
53
+ s.add_development_dependency(%q<shoulda>, [">= 2.10.3"])
54
+ else
55
+ s.add_dependency(%q<chronic>, [">= 0.2.3"])
56
+ s.add_dependency(%q<shoulda>, [">= 2.10.3"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<chronic>, [">= 0.2.3"])
60
+ s.add_dependency(%q<shoulda>, [">= 2.10.3"])
61
+ end
62
+ end
63
+
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Joshua Lippiner
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-21 00:00:00 -04:00
17
+ date: 2010-04-22 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -32,15 +32,17 @@ dependencies:
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
35
- name: thoughtbot-shoulda
35
+ name: shoulda
36
36
  prerelease: false
37
37
  requirement: &id002 !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  segments:
42
- - 0
43
- version: "0"
42
+ - 2
43
+ - 10
44
+ - 3
45
+ version: 2.10.3
44
46
  type: :development
45
47
  version_requirements: *id002
46
48
  description: Tickle is a date/time helper gem to help parse natural language into a recurring pattern. Tickle is designed to be a compliment of Chronic and can interpret things such as "every 2 days, every Sunday, Sundays, Weekly, etc.
@@ -59,12 +61,16 @@ files:
59
61
  - LICENSE
60
62
  - README.rdoc
61
63
  - Rakefile
64
+ - SCENARIOS.rdoc
62
65
  - VERSION
63
66
  - lib/numerizer/numerizer.rb
64
67
  - lib/tickle.rb
68
+ - lib/tickle/handler.rb
69
+ - lib/tickle/repeater.rb
65
70
  - lib/tickle/tickle.rb
66
71
  - test/helper.rb
67
- - test/test_tickle.rb
72
+ - test/test_parsing.rb
73
+ - tickle.gemspec
68
74
  has_rdoc: true
69
75
  homepage: http://github.com/noctivityinc/tickle
70
76
  licenses: []
@@ -97,4 +103,4 @@ specification_version: 3
97
103
  summary: natural language parser for recurring events
98
104
  test_files:
99
105
  - test/helper.rb
100
- - test/test_tickle.rb
106
+ - test/test_parsing.rb
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- class TestTickle < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
6
- end
7
- end