nickel 0.0.6 → 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +8 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +3 -0
- data/License.txt +3 -1
- data/README.md +110 -0
- data/Rakefile +10 -18
- data/bin/run_specs.sh +14 -0
- data/lib/nickel.rb +4 -23
- data/lib/nickel/construct.rb +25 -28
- data/lib/nickel/construct_finder.rb +251 -244
- data/lib/nickel/construct_interpreter.rb +68 -69
- data/lib/nickel/nlp.rb +67 -31
- data/lib/nickel/{query.rb → nlp_query.rb} +160 -348
- data/lib/nickel/{query_constants.rb → nlp_query_constants.rb} +2 -6
- data/lib/nickel/occurrence.rb +48 -67
- data/lib/nickel/version.rb +3 -0
- data/lib/nickel/zdate.rb +244 -162
- data/lib/nickel/ztime.rb +152 -72
- data/nickel.gemspec +31 -38
- data/spec/lib/nickel/nlp_spec.rb +14 -0
- data/spec/lib/nickel/occurrence_spec.rb +40 -0
- data/spec/lib/nickel/zdate_spec.rb +94 -0
- data/spec/lib/nickel/ztime_spec.rb +331 -0
- data/spec/lib/nickel_spec.rb +1859 -0
- data/spec/spec_helper.rb +22 -0
- metadata +124 -35
- data/History.txt +0 -11
- data/README.rdoc +0 -69
- data/lib/nickel/instance_from_hash.rb +0 -13
- data/lib/nickel/ruby_ext/calling_method.rb +0 -10
- data/lib/nickel/ruby_ext/to_s2.rb +0 -12
- data/spec/nickel_spec.rb +0 -165
- data/test/compare.rb +0 -109
- data/test/nlp_test.rb +0 -813
- data/test/nlp_tests_helper.rb +0 -55
- data/test/zdate_test.rb +0 -44
- data/test/ztime_test.rb +0 -429
data/lib/nickel/ztime.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
|
2
|
-
# Copyright (c) 2008-2011 Lou Zell, lzell11@gmail.com, http://hazelmade.com
|
3
|
-
# MIT License [http://www.opensource.org/licenses/mit-license.php]
|
1
|
+
require 'time'
|
4
2
|
|
5
3
|
module Nickel
|
6
|
-
|
4
|
+
|
7
5
|
class ZTime
|
8
|
-
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# \@firm will be used to indicate user provided am/pm
|
9
9
|
attr_accessor :firm
|
10
10
|
|
11
|
-
#
|
12
|
-
# we will convert this to 24 hour clock and set
|
11
|
+
# \@time is always stored on 24 hour clock, but we could initialize a Time object with ZTime.new("1020", :pm)
|
12
|
+
# we will convert this to 24 hour clock and set \@firm = true
|
13
13
|
def initialize(hhmmss = nil, am_pm = nil)
|
14
14
|
t = hhmmss ? hhmmss : ::Time.new.strftime("%H%M%S")
|
15
15
|
t.gsub!(/:/,'') # remove any hyphens, so a user can initialize with something like "2008-10-23"
|
@@ -20,7 +20,7 @@ module Nickel
|
|
20
20
|
def time
|
21
21
|
@time
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def time=(hhmmss)
|
25
25
|
@time = lazy(hhmmss)
|
26
26
|
@firm = false
|
@@ -30,44 +30,68 @@ module Nickel
|
|
30
30
|
def hour_str
|
31
31
|
@time[0..1]
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
|
+
# @deprecated Please use {#min_str} instead
|
34
35
|
def minute_str
|
36
|
+
warn "[DEPRECATION] `minute_str` is deprecated. Please use `min_str` instead."
|
37
|
+
min_str
|
38
|
+
end
|
39
|
+
|
40
|
+
def min_str
|
35
41
|
@time[2..3]
|
36
42
|
end
|
37
|
-
|
43
|
+
|
44
|
+
# @deprecated Please use {#sec_str} instead
|
38
45
|
def second_str
|
46
|
+
warn "[DEPRECATION] `second_str` is deprecated. Please use `sec_str` instead."
|
47
|
+
sec_str
|
48
|
+
end
|
49
|
+
|
50
|
+
def sec_str
|
39
51
|
@time[4..5]
|
40
52
|
end
|
41
|
-
|
53
|
+
|
42
54
|
def hour
|
43
55
|
hour_str.to_i
|
44
56
|
end
|
45
|
-
|
57
|
+
|
58
|
+
# @deprecated Please use {#min} instead
|
46
59
|
def minute
|
47
|
-
|
60
|
+
warn "[DEPRECATION] `minute` is deprecated. Please use `min` instead."
|
61
|
+
min
|
48
62
|
end
|
49
|
-
|
63
|
+
|
64
|
+
def min
|
65
|
+
min_str.to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
# @deprecated Please use {#sec} instead
|
50
69
|
def second
|
51
|
-
|
70
|
+
warn "[DEPRECATION] `second` is deprecated. Please use `sec` instead."
|
71
|
+
sec
|
72
|
+
end
|
73
|
+
|
74
|
+
def sec
|
75
|
+
sec_str.to_i
|
52
76
|
end
|
53
77
|
|
54
|
-
#
|
55
|
-
#
|
78
|
+
# add\_ methods return new ZTime object
|
79
|
+
# add\_ methods take an optional block, the block will be passed the number of days that have passed;
|
56
80
|
# i.e. adding 48 hours will pass a 2 to the block, this is handy for something like this:
|
57
81
|
# time.add_hours(15) {|x| date.add_days(x)}
|
58
82
|
def add_minutes(number, &block)
|
59
83
|
# new minute is going to be (current minute + number) % 60
|
60
84
|
# number of hours to add is (current minute + number) / 60
|
61
|
-
hours_to_add = (self.
|
85
|
+
hours_to_add = (self.min + number) / 60
|
62
86
|
# note add_hours returns a new time object
|
63
87
|
if block_given?
|
64
88
|
o = self.add_hours(hours_to_add, &block)
|
65
89
|
else
|
66
90
|
o = self.add_hours(hours_to_add)
|
67
91
|
end
|
68
|
-
o.change_minute_to((o.
|
92
|
+
o.change_minute_to((o.min + number) % 60) # modifies self
|
69
93
|
end
|
70
|
-
|
94
|
+
|
71
95
|
def add_hours(number, &block)
|
72
96
|
o = self.dup
|
73
97
|
if block_given?
|
@@ -79,73 +103,73 @@ module Nickel
|
|
79
103
|
|
80
104
|
# NOTE: change_ methods modify self.
|
81
105
|
def change_hour_to(h)
|
82
|
-
self.time = h
|
106
|
+
self.time = ZTime.format_time(h, min_str, sec_str)
|
83
107
|
self
|
84
108
|
end
|
85
|
-
|
109
|
+
|
86
110
|
def change_minute_to(m)
|
87
|
-
self.time = hour_str
|
111
|
+
self.time = ZTime.format_time(hour_str, m, sec_str)
|
88
112
|
self
|
89
113
|
end
|
90
|
-
|
114
|
+
|
91
115
|
def change_second_to(s)
|
92
|
-
self.time = hour_str
|
116
|
+
self.time = ZTime.format_time(hour_str, min_str, s)
|
93
117
|
self
|
94
118
|
end
|
95
119
|
|
96
120
|
def readable
|
97
121
|
@time[0..1] + ":" + @time[2..3] + ":" + @time[4..5]
|
98
122
|
end
|
99
|
-
|
123
|
+
|
100
124
|
def readable_12hr
|
101
|
-
hour_on_12hr_clock
|
125
|
+
hour_on_12hr_clock + ":" + @time[2..3] + " #{am_pm}"
|
102
126
|
end
|
103
|
-
|
127
|
+
|
104
128
|
def hour_on_12hr_clock
|
105
|
-
h = hour % 12
|
129
|
+
h = hour % 12
|
106
130
|
h += 12 if h == 0
|
107
131
|
h
|
108
132
|
end
|
109
|
-
|
133
|
+
|
110
134
|
def is_am?
|
111
135
|
hour < 12 # 0 through 11 on 24hr clock
|
112
136
|
end
|
113
|
-
|
137
|
+
|
114
138
|
def am_pm
|
115
139
|
is_am? ? "am" : "pm"
|
116
140
|
end
|
117
|
-
|
118
141
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
(self.hour < t2.hour) || (self.hour == t2.hour && (self.minute < t2.minute || (self.minute == t2.minute && self.second <= t2.second)))
|
125
|
-
end
|
126
|
-
|
127
|
-
def >(t2)
|
128
|
-
(self.hour > t2.hour) || (self.hour == t2.hour && (self.minute > t2.minute || (self.minute == t2.minute && self.second > t2.second)))
|
129
|
-
end
|
130
|
-
|
131
|
-
def >=(t2)
|
132
|
-
(self.hour > t2.hour) || (self.hour == t2.hour && (self.minute > t2.minute || (self.minute == t2.minute && self.second >= t2.second)))
|
142
|
+
|
143
|
+
def before(t2)
|
144
|
+
(t2.respond_to? :hour) && (hour < t2.hour) ||
|
145
|
+
(t2.respond_to? :min) && (hour == t2.hour && (min < t2.min ||
|
146
|
+
(t2.respond_to? :sec) && (min == t2.min && sec < t2.sec)))
|
133
147
|
end
|
134
|
-
|
135
|
-
def
|
136
|
-
|
148
|
+
|
149
|
+
def after(t2)
|
150
|
+
(t2.respond_to? :hour) && (hour > t2.hour) ||
|
151
|
+
(t2.respond_to? :min) && (hour == t2.hour && (min > t2.min ||
|
152
|
+
(t2.respond_to? :sec) && (min == t2.min && sec > t2.sec)))
|
137
153
|
end
|
138
|
-
|
154
|
+
|
139
155
|
def <=>(t2)
|
140
|
-
if
|
156
|
+
if before(t2)
|
141
157
|
-1
|
142
|
-
elsif
|
158
|
+
elsif after(t2)
|
143
159
|
1
|
144
160
|
else
|
145
161
|
0
|
146
162
|
end
|
147
163
|
end
|
148
|
-
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
time
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_time
|
170
|
+
Time.parse("#{hour}:#{min}:#{sec}")
|
171
|
+
end
|
172
|
+
|
149
173
|
class << self
|
150
174
|
|
151
175
|
# send an array of ZTime objects, this will make a guess at whether they should be am/pm if the user did not specify
|
@@ -154,7 +178,7 @@ module Nickel
|
|
154
178
|
# find firm time indices
|
155
179
|
firm_time_indices = []
|
156
180
|
time_array.each_with_index {|t,i| firm_time_indices << i if t.firm}
|
157
|
-
|
181
|
+
|
158
182
|
if firm_time_indices.empty?
|
159
183
|
# pure guess
|
160
184
|
# DO WE REALLY WANT TO DO THIS?
|
@@ -170,7 +194,7 @@ module Nickel
|
|
170
194
|
(min_boundary...max_boundary).to_a.reverse.each do |i| # this says, iterate backwards starting from max_boundary, but not including it, until the min boundary
|
171
195
|
time_array[i].modify_such_that_is_before(time_array[i+1])
|
172
196
|
end
|
173
|
-
|
197
|
+
|
174
198
|
firm_time_indices.each_index do |j|
|
175
199
|
# now handle all times after first firm time until the next firm time
|
176
200
|
min_boundary = firm_time_indices[j]
|
@@ -186,10 +210,66 @@ module Nickel
|
|
186
210
|
# note 12am is 00
|
187
211
|
h % 12
|
188
212
|
end
|
189
|
-
|
213
|
+
|
190
214
|
def pm_to_24hr(h)
|
191
215
|
h == 12 ? 12 : h + 12
|
192
216
|
end
|
217
|
+
|
218
|
+
def format_hour(h)
|
219
|
+
h.to_s.rjust(2, '0')
|
220
|
+
end
|
221
|
+
|
222
|
+
def format_minute(m)
|
223
|
+
m.to_s.rjust(2, '0')
|
224
|
+
end
|
225
|
+
|
226
|
+
def format_second(s)
|
227
|
+
s.to_s.rjust(2, '0')
|
228
|
+
end
|
229
|
+
|
230
|
+
# formats the hours, minutes and seconds into the format expected by the ZTime constructor
|
231
|
+
def format_time(hours, minutes=0, seconds=0)
|
232
|
+
format_hour(hours) + format_minute(minutes) + format_second(seconds)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Interpret Time is an important one, set some goals:
|
236
|
+
# match all of the following
|
237
|
+
# a.) 5, 12, 530, 1230, 2000
|
238
|
+
# b.) 5pm, 12pm, 530am, 1230am,
|
239
|
+
# c.) 5:30, 12:30, 20:00
|
240
|
+
# d.) 5:3, 12:3, 20:3 ... that's not needed but we supported it in version 1, this would be 5:30 and 12:30
|
241
|
+
# e.) 5:30am, 12:30am
|
242
|
+
# 20:00am, 20:00pm ... ZTime will flag these as invalid, so it is ok if we match them here
|
243
|
+
def interpret(str)
|
244
|
+
a_b = /^(\d{1,4})(am|pm)?$/ # handles cases (a) and (b)
|
245
|
+
c_d_e = /^(\d{1,2}):(\d{1,2})(am|pm)?$/ # handles cases (c), (d), and (e)
|
246
|
+
if mdata = str.match(a_b)
|
247
|
+
am_pm = mdata[2]
|
248
|
+
# this may look a bit confusing, but all we are doing is interpreting
|
249
|
+
# what the user meant based on the number of digits they provided
|
250
|
+
if mdata[1].length <= 2
|
251
|
+
# e.g. "11" means 11:00
|
252
|
+
hstr = mdata[1]
|
253
|
+
mstr = "0"
|
254
|
+
elsif mdata[1].length == 3
|
255
|
+
# e.g. "530" means 5:30
|
256
|
+
hstr = mdata[1][0..0]
|
257
|
+
mstr = mdata[1][1..2]
|
258
|
+
elsif mdata[1].length == 4
|
259
|
+
# e.g. "1215" means 12:15
|
260
|
+
hstr = mdata[1][0..1]
|
261
|
+
mstr = mdata[1][2..3]
|
262
|
+
end
|
263
|
+
elsif mdata = str.match(c_d_e)
|
264
|
+
am_pm = mdata[3]
|
265
|
+
hstr = mdata[1]
|
266
|
+
mstr = mdata[2]
|
267
|
+
else
|
268
|
+
return nil
|
269
|
+
end
|
270
|
+
# in this case we do not care if time fails validation, if it does, it just means we haven't found a valid time, return nil
|
271
|
+
begin ZTime.new(ZTime.format_time(hstr, mstr), am_pm) rescue return nil end
|
272
|
+
end
|
193
273
|
end
|
194
274
|
|
195
275
|
# this can very easily be cleaned up
|
@@ -198,7 +278,7 @@ module Nickel
|
|
198
278
|
raise "ZTime#modify_such_that_is_before says: time2 does not have @firm set" if !time2.firm
|
199
279
|
# self cannot have @firm set, so all hours will be between 1 and 12
|
200
280
|
# time2 is an end time, self could be its current setting, or off by 12 hours
|
201
|
-
|
281
|
+
|
202
282
|
# self to time2 --> self to time2
|
203
283
|
# 12 to 2am --> 1200 to 0200
|
204
284
|
# 12 to 12am --> 1200 to 0000
|
@@ -211,7 +291,7 @@ module Nickel
|
|
211
291
|
self.hour == 12 ? change_hour_to(0) : change_hour_to(self.hour + 12)
|
212
292
|
end
|
213
293
|
elsif self < time2
|
214
|
-
if time2.hour >= 12 && ZTime.new((time2.hour - 12
|
294
|
+
if time2.hour >= 12 && ZTime.new(ZTime.format_time(time2.hour - 12, time2.min_str, time2.sec_str)) > self
|
215
295
|
# 4 to 5pm or 0400 to 1700
|
216
296
|
change_hour_to(self.hour + 12)
|
217
297
|
else
|
@@ -225,7 +305,7 @@ module Nickel
|
|
225
305
|
self.firm = true
|
226
306
|
self
|
227
307
|
end
|
228
|
-
|
308
|
+
|
229
309
|
def modify_such_that_is_after(time1)
|
230
310
|
raise "ZTime#modify_such_that_is_after says: trying to modify time that has @firm set" if @firm
|
231
311
|
raise "ZTime#modify_such_that_is_after says: time1 does not have @firm set" if !time1.firm
|
@@ -237,14 +317,14 @@ module Nickel
|
|
237
317
|
# 930am to 5 ---> 0930 to 0500
|
238
318
|
# 930pm to 5 ---> 2130 to 0500
|
239
319
|
if self < time1
|
240
|
-
unless time1.hour >= 12 && ZTime.new((time1.hour - 12
|
320
|
+
unless time1.hour >= 12 && ZTime.new(ZTime.format_time(time1.hour - 12, time1.min_str, time1.sec_str)) >= self
|
241
321
|
self.hour == 12 ? change_hour_to(0) : change_hour_to(self.hour + 12)
|
242
322
|
end
|
243
323
|
elsif self > time1
|
244
324
|
# # time1 to self --> time1 to self
|
245
325
|
# # 10am to 11 --> 1000 to 1100
|
246
|
-
# #
|
247
|
-
# if time1.hour >= 12 && ZTime.new((time1.hour - 12
|
326
|
+
# #
|
327
|
+
# if time1.hour >= 12 && ZTime.new(ZTime.format_time(time1.hour - 12, time1.min_str, time1.sec_str)) > self
|
248
328
|
# change_hour_to(self.hour + 12)
|
249
329
|
# else
|
250
330
|
# # do nothing
|
@@ -269,9 +349,9 @@ module Nickel
|
|
269
349
|
self.hour == 12 ? change_hour_to(0) : change_hour_to(self.hour + 12)
|
270
350
|
end
|
271
351
|
end
|
272
|
-
|
352
|
+
|
273
353
|
private
|
274
|
-
|
354
|
+
|
275
355
|
def adjust_for(am_pm)
|
276
356
|
# how does validation work? Well, we already know that @time is valid, and once we modify we call time= which will
|
277
357
|
# perform validation on the new time. That won't catch something like this though: ZTime.new("2215", :am)
|
@@ -287,27 +367,27 @@ module Nickel
|
|
287
367
|
end
|
288
368
|
@firm = true
|
289
369
|
end
|
290
|
-
|
370
|
+
|
291
371
|
def validate
|
292
372
|
raise "ZTime#validate says: invalid time" unless valid
|
293
373
|
end
|
294
|
-
|
374
|
+
|
295
375
|
def valid
|
296
376
|
@time.length == 6 && @time !~ /\D/ && valid_hour && valid_minute && valid_second
|
297
377
|
end
|
298
|
-
|
378
|
+
|
299
379
|
def valid_hour
|
300
380
|
hour >= 0 and hour < 24
|
301
381
|
end
|
302
|
-
|
382
|
+
|
303
383
|
def valid_minute
|
304
|
-
|
384
|
+
min >= 0 and min < 60
|
305
385
|
end
|
306
|
-
|
386
|
+
|
307
387
|
def valid_second
|
308
|
-
|
388
|
+
sec >= 0 and sec < 60
|
309
389
|
end
|
310
|
-
|
390
|
+
|
311
391
|
def lazy(s)
|
312
392
|
# someone isn't following directions, but we will let it slide
|
313
393
|
s.length == 1 && s = "0#{s}0000" # only provided h
|
data/nickel.gemspec
CHANGED
@@ -1,40 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'nickel/version'
|
5
|
+
|
1
6
|
Gem::Specification.new do |s|
|
2
|
-
s.name
|
3
|
-
s.version
|
4
|
-
s.summary
|
5
|
-
s.
|
6
|
-
s.
|
7
|
-
|
8
|
-
s.
|
9
|
-
s.authors
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"lib/nickel/ruby_ext/calling_method.rb",
|
29
|
-
"lib/nickel/ruby_ext/to_s2.rb",
|
30
|
-
"test/compare.rb",
|
31
|
-
"test/nlp_test.rb",
|
32
|
-
"test/nlp_tests_helper.rb",
|
33
|
-
"test/zdate_test.rb",
|
34
|
-
"test/ztime_test.rb",
|
35
|
-
"spec/nickel_spec.rb"
|
36
|
-
]
|
37
|
-
s.require_paths = ["lib"]
|
38
|
-
s.rdoc_options = ["--main", "README.rdoc", "--title", "Nickel"]
|
39
|
-
s.extra_rdoc_files = ["README.rdoc"]
|
7
|
+
s.name = "nickel"
|
8
|
+
s.version = Nickel::VERSION
|
9
|
+
s.summary = "Natural language date, time, and message parsing."
|
10
|
+
s.homepage = "http://github.com/iainbeeston/nickel"
|
11
|
+
s.description = "Extracts date, time, and message information from naturally worded text."
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.license = "MIT"
|
14
|
+
s.authors = ["Lou Zell", "Iain Beeston"]
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec)/})
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
if RUBY_ENGINE == 'rbx'
|
21
|
+
s.add_dependency 'rubysl-date'
|
22
|
+
s.add_dependency 'rubysl-time'
|
23
|
+
s.add_development_dependency 'rubysl-rake'
|
24
|
+
s.add_development_dependency 'rubysl-bundler'
|
25
|
+
end
|
26
|
+
|
27
|
+
s.add_development_dependency "bundler"
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
s.add_development_dependency "rspec", "3.0.0.beta2"
|
30
|
+
s.add_development_dependency "coveralls"
|
31
|
+
s.add_development_dependency "yard"
|
32
|
+
s.add_development_dependency "kramdown"
|
40
33
|
end
|