tickle 0.0.1 → 0.0.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.
@@ -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