ice_cube_english 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,51 @@
1
+ == ice_cube_english - easy date expansion in English
2
+
3
+ gem install ice_cube_english
4
+
5
+ ice_cube_english provides english-language date and time parsing for ice_cube.
6
+
7
+ Imagine you want:
8
+
9
+ Every friday the 13th that falls in October
10
+
11
+ You would write:
12
+
13
+ schedule.add_recurrence_rule "Every friday the 13th that falls in October"
14
+
15
+ ---
16
+
17
+ ===Contributors
18
+
19
+ * Dwayne Litzenberger - dlitz@patientway.com
20
+
21
+ ---
22
+
23
+ ===Issues?
24
+
25
+ Lighthouse: https://github.com/dlitz/ice_cube_english/issues
26
+
27
+ ---
28
+
29
+ ===License
30
+
31
+ (The MIT License)
32
+
33
+ Copyright © 2011 Infonium Inc.
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
36
+ this software and associated documentation files (the ‘Software’), to deal in
37
+ the Software without restriction, including without limitation the rights to
38
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
39
+ of the Software, and to permit persons to whom the Software is furnished to do
40
+ so, subject to the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be included in all
43
+ copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
51
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ require 'ice_cube/english/rule_extension'
2
+ require 'ice_cube/english/schedule_extension'
@@ -0,0 +1,107 @@
1
+ module IceCube
2
+ module English
3
+ module GrammarHelper
4
+ class <<self
5
+ def deep_merge!(result, hash)
6
+ hash.each_pair do |k,v|
7
+ case v
8
+ when Hash
9
+ if result.include?(k)
10
+ raise ArgumentError("type mismatch: #{k.inspect}: #{v.class.name} -> #{result[k].class.name}") unless result[k].is_a?(Hash)
11
+ deep_merge!(result[k], v)
12
+ else
13
+ result[k] = {}
14
+ deep_merge!(result[k], v)
15
+ end
16
+ when Array
17
+ if result.include?(k)
18
+ raise ArgumentError("type mismatch: #{k.inspect}: #{v.class.name} -> #{result[k].class.name}") unless result[k].is_a?(Array)
19
+ result[k] += v
20
+ else
21
+ result[k] = []
22
+ result[k] += v
23
+ end
24
+ else
25
+ result[k] = v
26
+ end
27
+ end
28
+ result
29
+ end
30
+ end
31
+
32
+ RULE_TYPES = {
33
+ "secondly" => "IceCube::SecondlyRule",
34
+ "seconds" => "IceCube::SecondlyRule",
35
+ "second" => "IceCube::SecondlyRule",
36
+ "minutely" => "IceCube::MinutelyRule",
37
+ "minutes" => "IceCube::MinutelyRule",
38
+ "minute" => "IceCube::MinutelyRule",
39
+ "hourly" => "IceCube::HourlyRule",
40
+ "hours" => "IceCube::HourlyRule",
41
+ "hour" => "IceCube::HourlyRule",
42
+ "daily" => "IceCube::DailyRule",
43
+ "days" => "IceCube::DailyRule",
44
+ "day" => "IceCube::DailyRule",
45
+ "weekly" => "IceCube::WeeklyRule",
46
+ "weeks" => "IceCube::WeeklyRule",
47
+ "week" => "IceCube::WeeklyRule",
48
+ "monthly" => "IceCube::MonthlyRule",
49
+ "months" => "IceCube::MonthlyRule",
50
+ "month" => "IceCube::MonthlyRule",
51
+ "annually" => "IceCube::YearlyRule",
52
+ "yearly" => "IceCube::YearlyRule",
53
+ "years" => "IceCube::YearlyRule",
54
+ "year" => "IceCube::YearlyRule",
55
+ }
56
+
57
+ DAYS = {
58
+ "sundays" => 0,
59
+ "sunday" => 0,
60
+ "mondays" => 1,
61
+ "monday" => 1,
62
+ "tuesdays" => 2,
63
+ "tuesday" => 2,
64
+ "wednesdays" => 3,
65
+ "wednesday" => 3,
66
+ "thursdays" => 4,
67
+ "thursday" => 4,
68
+ "fridays" => 5,
69
+ "friday" => 5,
70
+ "saturdays" => 6,
71
+ "saturday" => 6,
72
+ }
73
+
74
+ DAYS_OF_WEEK = {
75
+ "sundays" => :sunday,
76
+ "sunday" => :sunday,
77
+ "mondays" => :monday,
78
+ "monday" => :monday,
79
+ "tuesdays" => :tuesday,
80
+ "tuesday" => :tuesday,
81
+ "wednesdays" => :wednesday,
82
+ "wednesday" => :wednesday,
83
+ "thursdays" => :thursday,
84
+ "thursday" => :thursday,
85
+ "fridays" => :friday,
86
+ "friday" => :friday,
87
+ "saturdays" => :saturday,
88
+ "saturday" => :saturday,
89
+ }
90
+
91
+ MONTHS_OF_YEAR = {
92
+ "january" => :january,
93
+ "february" => :february,
94
+ "march" => :march,
95
+ "april" => :april,
96
+ "may" => :may,
97
+ "june" => :june,
98
+ "july" => :july,
99
+ "august" => :august,
100
+ "september" => :september,
101
+ "october" => :october,
102
+ "november" => :november,
103
+ "december" => :december,
104
+ }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,36 @@
1
+ require 'treetop'
2
+ require 'ice_cube'
3
+ require 'ice_cube/english/schedule_grammar'
4
+ require 'ice_cube/english/rule_grammar'
5
+
6
+ module IceCube
7
+ module English
8
+ module RuleExtension
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+ module ClassMethods
13
+ # Return a new IceCube rule corresponding to the specified english string.
14
+ #
15
+ # If the :multiple option is set, return an Array of rules.
16
+ def from_english(string, options={})
17
+ if options[:multiple]
18
+ grammar = ::IceCube::English::ScheduleGrammarParser.new
19
+ parse_tree = grammar.parse(string.downcase)
20
+ raise ArgumentError.new(grammar.failure_reason) unless parse_tree
21
+ parse_tree.attribute_hashes.map{ |h| from_hash(h) }
22
+ else
23
+ grammar = ::IceCube::English::RuleGrammarParser.new
24
+ parse_tree = grammar.parse(string.downcase)
25
+ raise ArgumentError.new(grammar.failure_reason) unless parse_tree
26
+ from_hash(parse_tree.attributes)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ class ::IceCube::Rule # reopen
35
+ include ::IceCube::English::RuleExtension
36
+ end
@@ -0,0 +1,454 @@
1
+ require 'ice_cube/english/grammar_helper'
2
+
3
+ module IceCube
4
+ module English
5
+ grammar RuleGrammar
6
+ rule recurrence_rule
7
+ first_clause:recurrence_clause rest_clauses:( WS+ recurrence_clause )* {
8
+ def clauses
9
+ [first_clause] + rest_clauses
10
+ end
11
+
12
+ def rest_clauses
13
+ super.elements.map{|c| c.recurrence_clause}
14
+ end
15
+
16
+ def attributes
17
+ clause_attributes = []
18
+ clauses.each do |c|
19
+ clause_attributes << c.attributes
20
+ end
21
+
22
+ result = {:until => nil, :count => nil, :interval => 1, :validations => {}}
23
+ clause_attributes.each do |attributes|
24
+ GrammarHelper.deep_merge!(result, attributes)
25
+ end
26
+
27
+ # If :minute_of_hour is specified, make sure :second_of_minute is also specified.
28
+ if result[:validations][:minute_of_hour] and !result[:validations][:second_of_minute]
29
+ result[:validations][:second_of_minute] = [0]
30
+ end
31
+
32
+ # Make sure :rule_type is specified
33
+ unless result[:rule_type]
34
+ # XXX - Is this correct? Is it necessary to differentiate between these cases?
35
+ v = result[:validations]
36
+ result[:rule_type] = "IceCube::SecondlyRule"
37
+ result[:rule_type] = "IceCube::MinutelyRule" if (v[:second_of_minute] && v[:second_of_minute].length == 1) || v[:minute_of_hour]
38
+ result[:rule_type] = "IceCube::HourlyRule" if (v[:minute_of_hour] && v[:minute_of_hour].length == 1) || v[:hour_of_day]
39
+ result[:rule_type] = "IceCube::DailyRule" if v[:hour_of_day] && v[:hour_of_day].length == 1 || v[:day] || v[:day_of_week]
40
+ result[:rule_type] = "IceCube::WeeklyRule" if (v[:day] ? v[:day].length : 0) + (v[:day_of_week] ? v[:day_of_week].length : 0) == 1 || v[:day_of_month]
41
+ result[:rule_type] = "IceCube::MonthlyRule" if (v[:day_of_week] && v[:day_of_week].values.map{|vv| vv.length}.inject{|a,b| a+b} == 1) || (v[:day_of_month] && v[:day_of_month].length == 1)
42
+ end
43
+ result
44
+ end
45
+ }
46
+ end
47
+
48
+ rule recurrence_clause
49
+ frequency / for_n_occurrences / on_the_nth_x_of_the_y / the_nth / at_minutes_past_the_hour / at_time_of_day / of_month
50
+ end
51
+
52
+ # e.g. "daily", "every 2 days", "every 2nd day", "every monday", "every 2 mondays"
53
+ rule frequency
54
+ direct_frequency / every_frequency
55
+ end
56
+
57
+ rule direct_frequency
58
+ ( "secondly" / "minutely" / "hourly" / "daily" / "weekly" / "monthly" / "yearly" / "annually" ) {
59
+ def attributes
60
+ { :rule_type => GrammarHelper::RULE_TYPES[text_value] }
61
+ end
62
+ }
63
+ end
64
+
65
+ # e.g. "every day", "every 2nd day", "every 2nd monday", "every monday", "every 2 mondays"
66
+ rule every_frequency
67
+ "every" WS+
68
+ n:( m:positive_integer_or_ordinal WS+ )?
69
+ unit:( "seconds" / "minutes" / "hours" / "days" / "weeks" / "months" / "years" /
70
+ "second" / "minute" / "hour" / "day" / "week" / "month" / "year" /
71
+ days_of_week )
72
+ {
73
+ def attributes
74
+ result = {}
75
+ result[:interval] = n.m.number unless n.empty?
76
+ if unit.respond_to?(:day_validations) # days_of_week
77
+ result[:validations] = { :day => unit.day_validations }
78
+ else
79
+ result[:rule_type] = GrammarHelper::RULE_TYPES[unit.text_value]
80
+ end
81
+ result
82
+ end
83
+ }
84
+ end
85
+
86
+ rule days_of_week
87
+ first_day:day_of_week rest_days:( comma_joiner day_of_week )*
88
+ {
89
+ def days
90
+ [first_day] + rest_days
91
+ end
92
+
93
+ def rest_days
94
+ super.elements.map{|d| d.day_of_week}
95
+ end
96
+
97
+ def day_validations
98
+ days.map{|day_of_week| day_of_week.day_validation}
99
+ end
100
+ }
101
+ end
102
+
103
+ rule day_of_week
104
+ ( "sundays" / "mondays" / "tuesdays" / "wednesdays" / "thursdays" / "fridays" / "saturdays" /
105
+ "sunday" / "monday" / "tuesday" / "wednesday" / "thursday" / "friday" / "saturday" )
106
+ {
107
+ def day_validation
108
+ GrammarHelper::DAYS[text_value]
109
+ end
110
+ }
111
+ end
112
+
113
+ # e.g. "every second", "every 1 second", "every 2 seconds"
114
+ rule number_frequency
115
+ n:( positive_integer WS+ )? time_unit {
116
+ def attributes
117
+ { :rule_type => GrammarHelper::RULE_TYPES[time_unit.text_value], :interval => n.text_value.to_i }
118
+ end
119
+ }
120
+ end
121
+
122
+ rule for_n_occurrences
123
+ "for" WS+ positive_integer WS+ ( "occurrences" / "occurrence" ) {
124
+ def count
125
+ positive_integer.number
126
+ end
127
+
128
+ def attributes
129
+ { :count => count }
130
+ end
131
+ }
132
+ end
133
+
134
+ rule on_the_nth_x_of_the_y
135
+ "on" WS+ "the" WS+ ordinals WS+ ("seconds" / "second") WS+ "of" WS+ "the" WS+ "minute" {
136
+ def attributes
137
+ { :validations => {:second_of_minute => ordinals.numbers} }
138
+ end
139
+ }
140
+ /
141
+ "on" WS+ "the" WS+ ordinals WS+ ("minutes" / "minute") WS+ "of" WS+ "the" WS+ "hour" {
142
+ def attributes
143
+ { :validations => {:minute_of_hour => ordinals.numbers} }
144
+ end
145
+ }
146
+ /
147
+ "on" WS+ "the" WS+ lordinals WS+ ("days" / "day") WS+ "of" WS+ "the" WS+ "month" {
148
+ def attributes
149
+ { :validations => {:day_of_month => lordinals.numbers} }
150
+ end
151
+ }
152
+ /
153
+ "on" WS+ "the" WS+ lordinals WS+ days_of_week ( WS+ "of" WS+ "the" WS+ "month" )? {
154
+ def attributes
155
+ result = {}
156
+ days_of_week.days.each do |day_of_week|
157
+ GrammarHelper.deep_merge! result, { :validations => { :day_of_week => { GrammarHelper::DAYS_OF_WEEK[day_of_week.text_value] => lordinals.numbers } } }
158
+ end
159
+ result
160
+ end
161
+ }
162
+ end
163
+
164
+ # e.g. "the 13th"
165
+ rule the_nth
166
+ "the" WS+ ordinal {
167
+ def attributes
168
+ { :validations => {:day_of_month => [ordinal.number]} }
169
+ end
170
+ }
171
+ end
172
+
173
+ # e.g. "that falls in october", "of october", "in October"
174
+ rule of_month
175
+ ( "of" / "in" / "that" WS+ "falls" WS+ "in" ) WS+ month_names {
176
+ def attributes
177
+ { :validations => {:month_of_year => month_names.months_of_year} }
178
+ end
179
+ }
180
+ end
181
+
182
+ rule month_names
183
+ first_month_name:month_name rest_month_names:(comma_joiner month_name)* {
184
+ def month_names
185
+ [first_month_name] + rest_month_names
186
+ end
187
+
188
+ def rest_month_names
189
+ super.elements.map{|e| e.month_name}
190
+ end
191
+
192
+ def months_of_year
193
+ month_names.map{|m| GrammarHelper::MONTHS_OF_YEAR[m.text_value]}
194
+ end
195
+ }
196
+ end
197
+
198
+ rule month_name
199
+ ( "january" / "february" / "march" / "april" / "may" / "june" /
200
+ "july" / "august" / "september" / "october" / "november" / "december" )
201
+ end
202
+
203
+ # e.g. "at 15 minutes past the hour"
204
+ rule at_minutes_past_the_hour
205
+ "at" WS+ int60s WS+ "minutes" WS+ "past" WS+ "the" WS+ "hour" {
206
+ def attributes
207
+ { :validations => {:minute_of_hour => int60s.numbers} }
208
+ end
209
+ }
210
+ end
211
+
212
+ rule time_unit
213
+ "seconds" / "minutes" / "hours" / "days" / "weeks" / "months" / "years" /
214
+ "second" / "minute" / "hour" / "day" / "week" / "month" / "year"
215
+ end
216
+
217
+ rule ordinal
218
+ ( "first" / positive_integer ( "st" / "nd" / "rd" / "th" ) ) {
219
+ def number
220
+ if text_value == "first"
221
+ 1
222
+ else
223
+ positive_integer.text_value.to_i
224
+ end
225
+ end
226
+ }
227
+ end
228
+
229
+ rule ordinals
230
+ first_ordinal:ordinal rest_ordinals:(comma_joiner ordinal)* {
231
+ def ordinals
232
+ [first_ordinal] + rest_ordinals
233
+ end
234
+
235
+ def rest_ordinals
236
+ super.elements.map{|e| e.ordinal}
237
+ end
238
+
239
+ def numbers
240
+ ordinals.map{|o| o.number}
241
+ end
242
+ }
243
+ end
244
+
245
+ # ordinal, including "last", "2nd last", etc.
246
+ rule lordinal
247
+ ( "first" / "last" / positive_integer ( "st" / "nd" / "rd" / "th" ) l:(WS+ "last")? ) {
248
+ def number
249
+ if text_value == "first"
250
+ 1
251
+ elsif text_value == "last"
252
+ -1
253
+ elsif l.empty?
254
+ positive_integer.text_value.to_i
255
+ else
256
+ -(positive_integer.text_value.to_i)
257
+ end
258
+ end
259
+ }
260
+ end
261
+
262
+ rule lordinals
263
+ first_lordinal:lordinal rest_lordinals:(comma_joiner lordinal)* {
264
+ def lordinals
265
+ [first_lordinal] + rest_lordinals
266
+ end
267
+
268
+ def rest_lordinals
269
+ super.elements.map{|e| e.lordinal}
270
+ end
271
+
272
+ def numbers
273
+ lordinals.map{|o| o.number}
274
+ end
275
+ }
276
+ end
277
+
278
+ rule positive_integer_or_ordinal
279
+ ( "first" / positive_integer ( "st" / "nd" / "rd" / "th" )? ) {
280
+ def number
281
+ if text_value == "first"
282
+ 1
283
+ else
284
+ positive_integer.text_value.to_i
285
+ end
286
+ end
287
+ }
288
+ end
289
+
290
+ rule nonnegative_integers
291
+ first_nonnegative_integer:nonnegative_integer rest_nonnegative_integers:(comma_joiner nonnegative_integer)* {
292
+ def nonnegative_integers
293
+ [first_nonnegative_integer] + rest_nonnegative_integers
294
+ end
295
+
296
+ def rest_nonnegative_integers
297
+ super.elements.map{|e| e.nonnegative_integer}
298
+ end
299
+
300
+ def numbers
301
+ nonnegative_integers.map{|n| n.number}
302
+ end
303
+ }
304
+ end
305
+
306
+ rule nonnegative_integer
307
+ "0" / positive_integer
308
+ end
309
+
310
+ rule positive_integer
311
+ [1-9] [0-9]* {
312
+ def number
313
+ text_value.to_i
314
+ end
315
+ }
316
+ end
317
+
318
+ rule at_time_of_day
319
+ "at" WS+ t:( twelve_noon_or_midnight / time12 / time24 ) {
320
+ def attributes
321
+ { :validations => { :hour_of_day => [t.hour], :minute_of_hour => [t.minute], :second_of_minute => [t.second] } }
322
+ end
323
+ }
324
+ end
325
+
326
+ # Time of day (24-hour clock)
327
+ rule time24
328
+ h:hour24 a:( ":" m:int60 b:( ":" s:int60 )? )?
329
+ {
330
+ def hour
331
+ h.text_value.to_i
332
+ end
333
+
334
+ def minute
335
+ return 0 if a.empty?
336
+ a.m.number
337
+ end
338
+
339
+ def second
340
+ return 0 if a.empty? or a.b.empty?
341
+ a.b.s.number
342
+ end
343
+ }
344
+ end
345
+
346
+ # Time of day (12-hour clock)
347
+ rule time12
348
+ h:hour12 a:( ":" m:int60 b:( ":" s:int60 )? )? WS* c:( "am" / "a.m." / "pm" / "p.m." ) {
349
+ def hour
350
+ hr = h.text_value.to_i
351
+ if c.text_value =~ /\Ap/
352
+ # PM
353
+ if hr == 12
354
+ 12
355
+ else
356
+ hr + 12
357
+ end
358
+ else
359
+ # AM
360
+ if hr == 12
361
+ 0
362
+ else
363
+ hr
364
+ end
365
+ end
366
+ end
367
+
368
+ def minute
369
+ return 0 if a.empty?
370
+ a.m.text_value.to_i
371
+ end
372
+
373
+ def second
374
+ return 0 if a.empty? or a.b.empty?
375
+ a.b.s.text_value.to_i
376
+ end
377
+ }
378
+ end
379
+
380
+ # Noon or midnight
381
+ rule twelve_noon_or_midnight
382
+ ( "12" ( ":" "00" ( ":" "00" )? )? WS+ )? a:( "noon" / "midnight") {
383
+ def hour
384
+ if a.text_value == "noon"
385
+ 12
386
+ else
387
+ 0
388
+ end
389
+ end
390
+
391
+ def minute
392
+ 0
393
+ end
394
+
395
+ def second
396
+ 0
397
+ end
398
+ }
399
+ end
400
+
401
+ # The numbers 1 though 12 (possibly padded to 2 digits)
402
+ rule hour12
403
+ "0" [1-9] / "10" / "11" / "12" / [1-9]
404
+ end
405
+
406
+ # The numbers 0 through 24 (possibly padded to 2 digits)
407
+ rule hour24
408
+ "0" [0-9] / "1" [0-9] / "2" [0-3] / [0-9]
409
+ end
410
+
411
+ # The numbers 0 through 59, always padded to 2 digits.
412
+ # Used for minutes or seconds.
413
+ rule int60s
414
+ first_int60:int60 rest_int60s:(comma_joiner int60)* {
415
+ def int60s
416
+ [first_int60] + rest_int60s
417
+ end
418
+
419
+ def rest_int60s
420
+ super.elements.map{|e| e.int60}
421
+ end
422
+
423
+ def numbers
424
+ int60s.map{ |n| n.number }
425
+ end
426
+ }
427
+ end
428
+
429
+ # The numbers 0 through 59, always padded to 2 digits.
430
+ # Used for minutes or seconds.
431
+ rule int60
432
+ [0-5] [0-9] {
433
+ def number
434
+ text_value.to_i
435
+ end
436
+ }
437
+ end
438
+
439
+ rule comma_joiner
440
+ (WS* "," WS* ("and" WS+)? / WS+ "and" WS+)
441
+ end
442
+
443
+ rule comma_or_semicolon_joiner
444
+ (WS* [,;] WS* ("and" WS+)? / WS+ "and" WS+)
445
+ end
446
+
447
+ # whitespace
448
+ rule WS
449
+ [ \t]
450
+ end
451
+ end
452
+ end
453
+ end
454
+
@@ -0,0 +1,44 @@
1
+ require 'treetop'
2
+ require 'ice_cube'
3
+ require 'ice_cube/english/schedule_grammar'
4
+
5
+ module IceCube
6
+ module English
7
+ module ScheduleExtension
8
+ # Add the specified rule to the Schedule as a recurrence rule.
9
+ # The rule may be a Rule object, or it may be an english String.
10
+ def add_recurrence_rule_with_english(rule_or_english)
11
+ return add_recurrence_rule_without_english(rule_or_english) unless rule_or_english.is_a?(String)
12
+ rules = ::IceCube::Rule.from_english(rule_or_english, :multiple=>true)
13
+ rules.each do |rule|
14
+ add_recurrence_rule(rule)
15
+ end
16
+ self
17
+ end
18
+
19
+ # Add the specified rule to the Schedule as a exception rule.
20
+ # The rule may be a Rule object, or it may be an english String.
21
+ def add_exception_rule_with_english(rule_or_english)
22
+ return add_exception_rule_without_english(rule_or_english) unless rule_or_english.is_a?(String)
23
+ rules = ::IceCube::Rule.from_english(rule_or_english, :multiple=>true)
24
+ rules.each do |rule|
25
+ add_exception_rule(rule)
26
+ end
27
+ self
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ class IceCube::Schedule # reopen
34
+ include ::IceCube::English::ScheduleExtension
35
+
36
+ alias add_recurrence_rule_without_english add_recurrence_rule
37
+ alias add_recurrence_rule add_recurrence_rule_with_english
38
+
39
+ alias add_exception_rule_without_english add_exception_rule
40
+ alias add_exception_rule add_exception_rule_with_english
41
+
42
+ alias rrule add_recurrence_rule
43
+ alias exrule add_exception_rule
44
+ end
@@ -0,0 +1,26 @@
1
+ require 'ice_cube/english/rule_grammar'
2
+
3
+ module IceCube
4
+ module English
5
+ grammar ScheduleGrammar
6
+ include IceCube::English::RuleGrammar
7
+
8
+ # Multiple recurrence rules
9
+ rule recurrence_rules
10
+ first_rule:recurrence_rule rest_rules:( comma_or_semicolon_joiner recurrence_rule )* "."? {
11
+ def rules
12
+ [first_rule] + rest_rules
13
+ end
14
+
15
+ def rest_rules
16
+ super.elements.map{|r| r.recurrence_rule}
17
+ end
18
+
19
+ def attribute_hashes
20
+ rules.map{ |r| r.attributes }
21
+ end
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ module IceCube
2
+ module English
3
+ VERSION = '0.1'
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'ice_cube/english'
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ $LOAD_PATH.unshift File.expand_path "../../lib", __FILE__
3
+ require 'ice_cube_english'
4
+ require 'test/unit'
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ice_cube_english
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Dwayne Litzenberger
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-03-30 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: treetop
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 21
29
+ segments:
30
+ - 1
31
+ - 4
32
+ - 9
33
+ version: 1.4.9
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: ice_cube
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 11
45
+ segments:
46
+ - 0
47
+ - 6
48
+ - 6
49
+ version: 0.6.6
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: ice_cube_english provides english time and date parsing for ice_cube.
53
+ email: dlitz@patientway.com
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files:
59
+ - README.rdoc
60
+ files:
61
+ - lib/ice_cube/english/grammar_helper.rb
62
+ - lib/ice_cube/english/rule_extension.rb
63
+ - lib/ice_cube/english/schedule_extension.rb
64
+ - lib/ice_cube/english/version.rb
65
+ - lib/ice_cube/english.rb
66
+ - lib/ice_cube_english.rb
67
+ - lib/ice_cube/english/rule_grammar.treetop
68
+ - lib/ice_cube/english/schedule_grammar.treetop
69
+ - README.rdoc
70
+ - test/test_helper.rb
71
+ has_rdoc: true
72
+ homepage: https://github.com/dlitz/ice_cube_english
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 23
95
+ segments:
96
+ - 1
97
+ - 3
98
+ - 6
99
+ version: 1.3.6
100
+ requirements: []
101
+
102
+ rubyforge_project: ice-cube-english
103
+ rubygems_version: 1.3.7
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: English time and date parsing for ice_cube
107
+ test_files:
108
+ - test/test_helper.rb