tickle 1.2.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,115 @@
1
+ module Tickle
2
+
3
+ # A place to keep all the regular expressions.
4
+ module Patterns
5
+
6
+ PLURAL_OR_PRESENT_PARTICIPLE = /
7
+ s
8
+ |
9
+ ing
10
+ /x
11
+
12
+ ON_THE = /
13
+ \bon\b
14
+ (?:
15
+ \s+
16
+ the
17
+ )?
18
+ /x
19
+
20
+ END_OR_UNTIL = /
21
+ (?:
22
+ (?:\band\b\s+)?
23
+ \bend
24
+ (?: #{PLURAL_OR_PRESENT_PARTICIPLE} )?
25
+ (?:
26
+ \s+
27
+ #{ON_THE}
28
+ )?
29
+ )
30
+ |
31
+ (:?
32
+ until
33
+ (?:
34
+ \s+
35
+ \bthe\b
36
+ )?
37
+ )
38
+ /x
39
+
40
+ SET_IDENTIFIER = /
41
+ every
42
+ |
43
+ each
44
+ |
45
+ (?: #{ON_THE} )
46
+ /x
47
+
48
+ # This is here so we can check for repetition
49
+ # and set 'until' more easily. If so desired.
50
+ REPETITION = /
51
+ (?<repeat>
52
+ repeat
53
+ )
54
+ /x
55
+
56
+ START = /
57
+ start
58
+ (?: #{PLURAL_OR_PRESENT_PARTICIPLE} )?
59
+ /x
60
+
61
+ START_EVERY_REGEX = /^
62
+ (?:
63
+ #{START}
64
+ )
65
+ \s+
66
+ (?<start>.*?)
67
+ (?:
68
+ \s+
69
+ #{REPETITION}
70
+ )?
71
+ \s+
72
+ #{SET_IDENTIFIER}
73
+ \s+
74
+ (?<event>.*)
75
+ /ix
76
+
77
+
78
+ EVERY_START_REGEX = /^
79
+ (?: #{SET_IDENTIFIER} )
80
+ \s+
81
+ (?<event>.*)
82
+ (?:
83
+ \s+
84
+ #{START}
85
+ (?:
86
+ \s+
87
+ #{ON_THE}
88
+ )?
89
+ )
90
+ \s+
91
+ (?<start>.*)
92
+ /ix
93
+
94
+ START_ENDING_REGEX = /^
95
+ #{START}
96
+ \s+
97
+ (?<start>.*?)
98
+ (?:
99
+ \s+
100
+ #{END_OR_UNTIL}
101
+ )
102
+ \s+
103
+ (?<finish>.*)
104
+ /ix
105
+
106
+ PROCESS_FOR_ENDING = /^
107
+ (?<target>.*)
108
+ \s+
109
+ (?: #{END_OR_UNTIL})
110
+ \s+
111
+ (?<ending>.*)
112
+ /ix
113
+
114
+ end
115
+ end
@@ -1,136 +1,252 @@
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_ordinal_names(token) unless token.type
8
- token = self.scan_for_ordinals(token) unless token.type
9
- token = self.scan_for_month_names(token) unless token.type
10
- token = self.scan_for_day_names(token) unless token.type
11
- token = self.scan_for_year_name(token) unless token.type
12
- token = self.scan_for_special_text(token) unless token.type
13
- token = self.scan_for_units(token) unless token.type
1
+ module Tickle
2
+ require_relative "../ext/string.rb"
3
+ class Repeater
4
+ require_relative "token.rb"
5
+
6
+ attr_reader :tokens
7
+
8
+ def initialize( tokens )
9
+ @tokens = tokens.map(&:clone)
14
10
  end
15
- tokens
16
- end
17
11
 
18
- def self.scan_for_numbers(token)
19
- regex = /\b(\d\d?)\b/
20
- token.update(:number, token.word.gsub(regex,'\1').to_i, token.word.gsub(regex,'\1').to_i) if token.word =~ regex
21
- token
22
- end
12
+
13
+ SCANNING_METHODS = [
14
+ :scan_for_numbers,
15
+ :scan_for_ordinal_names,
16
+ :scan_for_ordinals,
17
+ :scan_for_month_names,
18
+ :scan_for_day_names,
19
+ :scan_for_year_name,
20
+ :scan_for_special_text,
21
+ :scan_for_units,
22
+ ]
23
+
23
24
 
24
- def self.scan_for_ordinal_names(token)
25
- scanner = {/first/ => '1st',
26
- /second/ => '2nd',
27
- /third/ => '3rd',
28
- /fourth/ => '4th',
29
- /fifth/ => '5th',
30
- /sixth/ => '6th',
31
- /seventh/ => '7th',
32
- /eighth/ => '8th',
33
- /ninth/ => '9th',
34
- /tenth/ => '10th',
35
- /eleventh/ => '11th',
36
- /twelfth/ => '12th',
37
- /thirteenth/ => '13th',
38
- /fourteenth/ => '14th',
39
- /fifteenth/ => '15th',
40
- /sixteenth/ => '16th',
41
- /seventeenth/ => '17th',
42
- /eighteenth/ => '18th',
43
- /nineteenth/ => '19th',
44
- /twentieth/ => '20th',
45
- /thirtieth/ => '30th',
25
+ #
26
+ def scan!
27
+ # for each token
28
+ @tokens.each do |token|
29
+ new_details = catch(:token_found) {
30
+ SCANNING_METHODS.each{|meth|
31
+ send meth, token
32
+ }
33
+ nil # if nothing matched, set to nil
34
+ }
35
+ token.update! new_details if new_details
36
+ end
37
+ self
38
+ end
39
+
40
+
41
+ def detection(token, scanner, &block )
42
+ scanner = [scanner] unless scanner.respond_to? :keys
43
+ scanner.each do |key,value|
44
+ if (md = key.match token.downcase) or (md = key.match token.word)
45
+ throw :token_found, block.call(md,key,value)
46
+ end
47
+ end
48
+ nil # if it reaches here nothing was found so return nil
49
+ end
50
+
51
+
52
+ SCAN_FOR_NUMBERS = /
53
+ \b
54
+ (?<number>\d\d?)
55
+ \b
56
+ /x
57
+
58
+ def scan_for_numbers(token)
59
+ detection token, SCAN_FOR_NUMBERS do |md,key,value|
60
+ n = md[:number].to_i
61
+ {type: :number, start: n, interval: n }
62
+ end
63
+ end
64
+
65
+
66
+ SCAN_FOR_ORDINAL_NAMES = {
67
+ /first/ => Ordinal.new( '1st' ),
68
+ /second\b/ => Ordinal.new( '2nd' ),
69
+ /third/ => Ordinal.new( '3rd' ),
70
+ /fourth/ => Ordinal.new( '4th' ),
71
+ /fifth/ => Ordinal.new( '5th' ),
72
+ /sixth/ => Ordinal.new( '6th' ),
73
+ /seventh/ => Ordinal.new( '7th' ),
74
+ /eighth/ => Ordinal.new( '8th' ),
75
+ /ninth/ => Ordinal.new( '9th' ),
76
+ /tenth/ => Ordinal.new( '10th' ),
77
+ /eleventh/ => Ordinal.new( '11th' ),
78
+ /twelfth/ => Ordinal.new( '12th' ),
79
+ /thirteenth/ => Ordinal.new( '13th' ),
80
+ /fourteenth/ => Ordinal.new( '14th' ),
81
+ /fifteenth/ => Ordinal.new( '15th' ),
82
+ /sixteenth/ => Ordinal.new( '16th' ),
83
+ /seventeenth/ => Ordinal.new( '17th' ),
84
+ /eighteenth/ => Ordinal.new( '18th' ),
85
+ /nineteenth/ => Ordinal.new( '19th' ),
86
+ /twentieth/ => Ordinal.new( '20th' ),
87
+ /thirtieth/ => Ordinal.new( '30th' ),
46
88
  }
47
- scanner.keys.each do |scanner_item|
48
- if scanner_item =~ token.original
49
- token.word = scanner[scanner_item]
50
- token.update(:ordinal, scanner[scanner_item].ordinal_as_number, Tickle.days_in_month(Tickle.get_next_month(scanner[scanner_item].ordinal_as_number)))
89
+
90
+
91
+ def scan_for_ordinal_names(token)
92
+ detection token, SCAN_FOR_ORDINAL_NAMES do |md,key,value|
93
+ { :type => :ordinal,
94
+ :start => value.ordinal_as_number,
95
+ :interval => Tickle::Helpers.days_in_month( Tickle::Helpers.get_next_month( value.ordinal_as_number )),
96
+ }
51
97
  end
52
98
  end
53
- token
54
- end
55
99
 
56
- def self.scan_for_ordinals(token)
57
- regex = /\b(\d*)(st|nd|rd|th)\b/
58
- if token.original =~ regex
59
- token.word = token.original
60
- token.update(:ordinal, token.word.ordinal_as_number, Tickle.days_in_month(Tickle.get_next_month(token.word)))
100
+
101
+ SCAN_FOR_ORDINALS = /
102
+ \b
103
+ (?<number>\d+)
104
+ (?:
105
+ st
106
+ |
107
+ nd
108
+ |
109
+ rd
110
+ |th
111
+ )
112
+ \b
113
+ /x
114
+
115
+ def scan_for_ordinals(token)
116
+ detection token, SCAN_FOR_ORDINALS do |md,key,value|
117
+ number = Ordinal.new(md[:number])
118
+ { :type => :ordinal,
119
+ :start => number.ordinal_as_number,
120
+ :interval => Tickle::Helpers.days_in_month(Tickle::Helpers.get_next_month number )
121
+ }
122
+ end
61
123
  end
62
- token
63
- end
64
124
 
65
- def self.scan_for_month_names(token)
66
- scanner = {/^jan\.?(uary)?$/ => 1,
67
- /^feb\.?(ruary)?$/ => 2,
68
- /^mar\.?(ch)?$/ => 3,
69
- /^apr\.?(il)?$/ => 4,
70
- /^may$/ => 5,
71
- /^jun\.?e?$/ => 6,
72
- /^jul\.?y?$/ => 7,
73
- /^aug\.?(ust)?$/ => 8,
74
- /^sep\.?(t\.?|tember)?$/ => 9,
75
- /^oct\.?(ober)?$/ => 10,
76
- /^nov\.?(ember)?$/ => 11,
77
- /^dec\.?(ember)?$/ => 12}
78
- scanner.keys.each do |scanner_item|
79
- token.update(:month_name, scanner[scanner_item], 30) if scanner_item =~ token.word
125
+
126
+ def scan_for_month_names(token)
127
+ scanner = {/^jan\.?(uary)?$/ => 1,
128
+ /^feb\.?(ruary)?$/ => 2,
129
+ /^mar\.?(ch)?$/ => 3,
130
+ /^apr\.?(il)?$/ => 4,
131
+ /^may$/ => 5,
132
+ /^jun\.?e?$/ => 6,
133
+ /^jul\.?y?$/ => 7,
134
+ /^aug\.?(ust)?$/ => 8,
135
+ /^sep\.?(t\.?|tember)?$/ => 9,
136
+ /^oct\.?(ober)?$/ => 10,
137
+ /^nov\.?(ember)?$/ => 11,
138
+ /^dec\.?(ember)?$/ => 12}
139
+ detection token, scanner do |md,key,value|
140
+ {
141
+ :type => :month_name,
142
+ :start => value,
143
+ :interval => 30,
144
+ }
145
+ end
80
146
  end
81
- token
82
- end
83
147
 
84
- def self.scan_for_day_names(token)
85
- scanner = {/^m[ou]n(day)?$/ => :monday,
86
- /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
87
- /^tue$/ => :tuesday,
88
- /^we(dnes|nds|nns)day$/ => :wednesday,
89
- /^wed$/ => :wednesday,
90
- /^th(urs|ers)day$/ => :thursday,
91
- /^thu$/ => :thursday,
92
- /^fr[iy](day)?$/ => :friday,
93
- /^sat(t?[ue]rday)?$/ => :saturday,
94
- /^su[nm](day)?$/ => :sunday}
95
- scanner.keys.each do |scanner_item|
96
- token.update(:weekday, scanner[scanner_item], 7) if scanner_item =~ token.word
148
+
149
+ def scan_for_day_names(token)
150
+ scanner = {
151
+ /^m[ou]n(day)?$/ => :monday,
152
+ /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
153
+ /^tue$/ => :tuesday,
154
+ /^we(dnes|nds|nns)day$/ => :wednesday,
155
+ /^wed$/ => :wednesday,
156
+ /^th(urs|ers)day$/ => :thursday,
157
+ /^thu$/ => :thursday,
158
+ /^fr[iy](day)?$/ => :friday,
159
+ /^sat(t?[ue]rday)?$/ => :saturday,
160
+ /^su[nm](day)?$/ => :sunday
161
+ }
162
+ detection token, scanner do |md,key,value|
163
+ {
164
+ :type => :weekday,
165
+ :start => value,
166
+ :interval => 7,
167
+ }
168
+ end
97
169
  end
98
- token
99
- end
100
170
 
101
- def self.scan_for_year_name(token)
102
- regex = /\b\d{4}\b/
103
- token.update(:specific_year, token.original.gsub(regex,'\1'), 365) if token.original =~ regex
104
- token
105
- end
106
171
 
107
- def self.scan_for_special_text(token)
108
- scanner = {/^other$/ => :other,
109
- /^begin(ing|ning)?$/ => :beginning,
110
- /^start$/ => :beginning,
111
- /^end$/ => :end,
112
- /^mid(d)?le$/ => :middle}
113
- scanner.keys.each do |scanner_item|
114
- token.update(:special, scanner[scanner_item], 7) if scanner_item =~ token.word
172
+ def scan_for_year_name(token)
173
+ detection token, /\b(?<year>\d{4})\b/ do |md,key,value|
174
+ {
175
+ :type => :specific_year,
176
+ :start => md[:year],
177
+ :interval => 365,
178
+ }
179
+ end
115
180
  end
116
- token
117
- end
118
181
 
119
- def self.scan_for_units(token)
120
- scanner = {/^year(ly)?s?$/ => {:type => :year, :interval => 365, :start => :today},
121
- /^month(ly)?s?$/ => {:type => :month, :interval => 30, :start => :today},
122
- /^fortnights?$/ => {:type => :fortnight, :interval => 365, :start => :today},
123
- /^week(ly)?s?$/ => {:type => :week, :interval => 7, :start => :today},
124
- /^weekends?$/ => {:type => :weekend, :interval => 7, :start => :saturday},
125
- /^days?$/ => {:type => :day, :interval => 0, :start => :today},
126
- /^daily?$/ => {:type => :day, :interval => 0, :start => :today}}
127
- scanner.keys.each do |scanner_item|
128
- if scanner_item =~ token.word
129
- token.update(scanner[scanner_item][:type], scanner[scanner_item][:start], scanner[scanner_item][:interval]) if scanner_item =~ token.word
182
+
183
+ def scan_for_special_text(token)
184
+ scanner = {
185
+ /^other$/ => :other,
186
+ /^begin(ing|ning)?$/ => :beginning,
187
+ /^start$/ => :beginning,
188
+ /^end$/ => :end,
189
+ /^mid(d)?le$/ => :middle
190
+ }
191
+ detection token, scanner do |md,key,value|
192
+ {
193
+ :type => :special,
194
+ :start => value,
195
+ :interval => 7,
196
+ }
130
197
  end
131
198
  end
132
- token
133
- end
134
199
 
135
200
 
136
- end
201
+ def scan_for_units(token)
202
+ scanner = {
203
+ /^year(?:ly)?s?$/ => {
204
+ :type => :year,
205
+ :interval => 365,
206
+ :start => :today
207
+ },
208
+ /^month(?:ly|s)?$/ => {
209
+ :type => :month,
210
+ :interval => 30,
211
+ :start => :today
212
+ },
213
+ /^fortnights?$/ => {
214
+ :type => :fortnight,
215
+ :interval => 14,
216
+ :start => :today
217
+ },
218
+ /^week(?:ly|s)?$/ => {
219
+ :type => :week,
220
+ :interval => 7,
221
+ :start => :today
222
+ },
223
+ /^weekends?$/ => {
224
+ :type => :weekend,
225
+ :interval => 7,
226
+ :start => :saturday
227
+ },
228
+ /^days?$/ => {
229
+ :type => :day,
230
+ :interval => 1,
231
+ :start => :today
232
+ },
233
+ /^daily$/ => {
234
+ :type => :day,
235
+ :interval => 1,
236
+ :start => :today
237
+ },
238
+ /^sec(?:onds)?$/ => {
239
+ :type => :sec,
240
+ :interval => 1,
241
+ :start => :today
242
+ },
243
+ }
244
+
245
+ detection token, scanner do |md,key,value|
246
+ value
247
+ end
248
+ end
249
+
250
+
251
+ end
252
+ end