runt 0.2.0 → 0.3.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.
- data/CHANGES +23 -0
- data/README +9 -3
- data/Rakefile +19 -16
- data/TODO +7 -6
- data/doc/tutorial_schedule.rdoc +1 -1
- data/doc/tutorial_te.rdoc +2 -2
- data/lib/runt.rb +21 -4
- data/lib/runt/daterange.rb +4 -4
- data/lib/runt/pdate.rb +0 -1
- data/lib/runt/temporalexpression.rb +143 -86
- data/site/index.html +2 -1
- data/test/alltests.rb +0 -2
- data/test/daterangetest.rb +7 -2
- data/test/dprecisiontest.rb +8 -9
- data/test/pdatetest.rb +1 -3
- data/test/scheduletest.rb +34 -2
- data/test/temporalexpressiontest.rb +126 -6
- metadata +13 -5
data/CHANGES
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
= Runt Changelog
|
2
2
|
|
3
|
+
== Version 0.3.0
|
4
|
+
|
5
|
+
* TExpr (finally!) becomes a Module instead of a superclass
|
6
|
+
|
7
|
+
* Added overlap? method for all temporal expressions and DateRange
|
8
|
+
|
9
|
+
* Added REMonth expression which matches a range of dates each month
|
10
|
+
|
11
|
+
* Contributed by Emmett Shear: TExpr#dates method which returns an array of dates occurring within the supplied DateRange
|
12
|
+
|
13
|
+
* Rakefile fixes:
|
14
|
+
- test path allow gem to be installed with -t switch
|
15
|
+
- usage of gzip which will break on Win32
|
16
|
+
|
17
|
+
* Removed and then subsequently restored SpecTE
|
18
|
+
|
19
|
+
* General clean-up, including:
|
20
|
+
- renamed several methods on PDate
|
21
|
+
- renamed 'dateprecisiontest.rb' to 'dprecisiontest.rb'
|
22
|
+
|
23
|
+
* Fixed Object.id deprecation warning
|
24
|
+
|
25
|
+
|
3
26
|
== Version 0.2.0
|
4
27
|
|
5
28
|
* Fixed Schedule class
|
data/README
CHANGED
@@ -8,6 +8,10 @@
|
|
8
8
|
|
9
9
|
= INSTALL
|
10
10
|
|
11
|
+
* gem install runt
|
12
|
+
|
13
|
+
<b>or</b>
|
14
|
+
|
11
15
|
* Unpack the Runt distribution.
|
12
16
|
|
13
17
|
$ tar -xzvf runt-<version>.tar.gz
|
@@ -32,7 +36,7 @@
|
|
32
36
|
|
33
37
|
* require 'runt'
|
34
38
|
|
35
|
-
* See http://runt.rubyforge.org
|
39
|
+
* See Runt website[http://runt.rubyforge.org].
|
36
40
|
|
37
41
|
* See $UNPACK_DIR/runt/test/*.rb for example usage.
|
38
42
|
|
@@ -66,7 +70,9 @@ The ruby-nyc user's group for their suggestions and feedback.
|
|
66
70
|
|
67
71
|
BlueRobot[http://www.bluerobot.com/web/layouts/] for the CSS used to prettify the Runt website.
|
68
72
|
|
69
|
-
|
73
|
+
Emmett Shear for contributed TExpr#dates code and several thought-provoking feature requests.
|
74
|
+
|
75
|
+
The number 12, and the letters E, J, and B.
|
70
76
|
|
71
77
|
---
|
72
78
|
|
@@ -85,4 +91,4 @@ implied warranties, including, without limitation, the implied
|
|
85
91
|
warranties of merchantibility and fitness for a particular
|
86
92
|
purpose.
|
87
93
|
|
88
|
-
link://../dcl-small.gif
|
94
|
+
link://../dcl-small.gif
|
data/Rakefile
CHANGED
@@ -10,15 +10,17 @@ require 'rake'
|
|
10
10
|
require 'rake/clean'
|
11
11
|
require 'rake/testtask'
|
12
12
|
require 'rake/rdoctask'
|
13
|
-
#require 'rake/packagetask'
|
14
|
-
|
15
13
|
require 'rake/contrib/sshpublisher'
|
16
14
|
require 'rake/contrib/rubyforgepublisher'
|
17
15
|
require 'fileutils'
|
18
16
|
|
17
|
+
#####################################################################
|
18
|
+
# Constants
|
19
|
+
#####################################################################
|
20
|
+
|
19
21
|
# Build Settings
|
20
|
-
PKG_VERSION = "0.
|
21
|
-
|
22
|
+
PKG_VERSION = "0.3.0"
|
23
|
+
|
22
24
|
# Files to be included in Runt distribution
|
23
25
|
PKG_FILES = FileList[
|
24
26
|
'setup.rb',
|
@@ -29,10 +31,19 @@ PKG_FILES = FileList[
|
|
29
31
|
'site/**/*'
|
30
32
|
].exclude("*.ses")
|
31
33
|
|
34
|
+
if(RUBY_PLATFORM =~ /win32/i)
|
35
|
+
PKG_EXEC_TAR = false
|
36
|
+
else
|
37
|
+
PKG_EXEC_TAR = true
|
38
|
+
end
|
39
|
+
|
32
40
|
# build directory
|
33
41
|
TARGET_DIR = "target"
|
34
42
|
|
43
|
+
#####################################################################
|
35
44
|
# Targets
|
45
|
+
#####################################################################
|
46
|
+
|
36
47
|
task :default => [:test]
|
37
48
|
task :clobber => [:clobber_build_dir]
|
38
49
|
|
@@ -55,14 +66,7 @@ end
|
|
55
66
|
Rake::TestTask.new do |t|
|
56
67
|
t.libs << "test"
|
57
68
|
t.pattern = 'test/alltests.rb'
|
58
|
-
t.verbose =
|
59
|
-
end
|
60
|
-
|
61
|
-
Rake::PackageTask.new("runt", PKG_VERSION) do |p|
|
62
|
-
p.package_dir="#{TARGET_DIR}/#{p.package_dir}"
|
63
|
-
p.need_tar = true
|
64
|
-
p.need_zip = true
|
65
|
-
p.package_files.include(PKG_FILES)
|
69
|
+
t.verbose = false
|
66
70
|
end
|
67
71
|
|
68
72
|
desc "Copy html files for the Runt website to the build directory."
|
@@ -79,7 +83,7 @@ task :publish => [:rdoc,:copy_site,:clobber_package] do |t|
|
|
79
83
|
end
|
80
84
|
|
81
85
|
desc "Publish the Documentation to the build dir."
|
82
|
-
task :test_publish => [:
|
86
|
+
task :test_publish => [:rdoc,:copy_site,:clobber_package] do |t|
|
83
87
|
puts "YAY! We've tested publish! YAY!"
|
84
88
|
end
|
85
89
|
|
@@ -100,8 +104,7 @@ else
|
|
100
104
|
s.email = 'matt@digitalclash.com'
|
101
105
|
s.homepage = 'http://runt.rubyforge.org'
|
102
106
|
s.has_rdoc = true
|
103
|
-
|
104
|
-
# s.rdoc_options = rd.option_list
|
107
|
+
s.test_files = Dir['test/*test.rb']
|
105
108
|
s.rubyforge_project = 'runt'
|
106
109
|
s.description = <<EOF
|
107
110
|
Runt is a Ruby version of temporal patterns by
|
@@ -112,6 +115,6 @@ EOF
|
|
112
115
|
|
113
116
|
Rake::GemPackageTask.new(spec) do |pkg|
|
114
117
|
pkg.need_zip = true
|
115
|
-
pkg.need_tar =
|
118
|
+
pkg.need_tar = PKG_EXEC_TAR
|
116
119
|
end
|
117
120
|
end
|
data/TODO
CHANGED
@@ -4,16 +4,17 @@ Send suggestions, questions, threats, etc. for this list to Matt[mailto:matt@dig
|
|
4
4
|
|
5
5
|
=== To Do
|
6
6
|
|
7
|
-
*
|
7
|
+
* Schedule tutorial
|
8
8
|
|
9
|
-
*
|
9
|
+
* Example usage with Rails
|
10
10
|
|
11
|
-
*
|
11
|
+
* Persistence strategy:
|
12
|
+
- Marshall
|
13
|
+
- database...create storable grammar, expression stack?
|
12
14
|
|
13
|
-
*
|
15
|
+
* Fix REWeek so that ordinal day values where the end day < start day does not fail
|
14
16
|
|
15
|
-
*
|
17
|
+
* Make Schedule a Module instead of a class
|
16
18
|
|
17
19
|
* Address performance issues due to implementation (in particular, PDate#<=>)
|
18
20
|
|
19
|
-
* Use RDoc properly so Runt website and README don't cause DRY violations
|
data/doc/tutorial_schedule.rdoc
CHANGED
@@ -48,4 +48,4 @@ We created a temporal expression to match this time period, like so:
|
|
48
48
|
|
49
49
|
* Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
|
50
50
|
|
51
|
-
* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
|
51
|
+
* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
|
data/doc/tutorial_te.rdoc
CHANGED
@@ -181,10 +181,10 @@ easy to write your own.
|
|
181
181
|
|
182
182
|
Fowler's paper[http://martinfowler.com/apsupp/recurring.pdf] also goes
|
183
183
|
on to describe another element of this pattern: the <tt>Schedule</tt>.
|
184
|
-
See the schedule tutorial[http://runt.rubyforge.org/doc/files/doc/
|
184
|
+
See the schedule tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_schedule_rdoc.html] for details.
|
185
185
|
|
186
186
|
<em>See Also:</em>
|
187
187
|
|
188
188
|
* Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
|
189
189
|
|
190
|
-
* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
|
190
|
+
* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
|
data/lib/runt.rb
CHANGED
@@ -84,10 +84,27 @@ module Runt
|
|
84
84
|
@negate.call(arg)
|
85
85
|
end
|
86
86
|
end
|
87
|
+
LastProc = ApplyLast.new
|
87
88
|
|
88
89
|
public
|
89
|
-
Last =
|
90
|
-
Last_of =
|
91
|
-
Second_to_last =
|
90
|
+
Last = LastProc[First]
|
91
|
+
Last_of = LastProc[First]
|
92
|
+
Second_to_last = LastProc[Second]
|
92
93
|
|
93
|
-
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Add precision +Runt::DPrecision+ to standard library classes Date and DateTime
|
98
|
+
# (which is a subclass of Date).
|
99
|
+
#
|
100
|
+
class Date
|
101
|
+
|
102
|
+
include Runt::DPrecision
|
103
|
+
|
104
|
+
attr_accessor :date_precision
|
105
|
+
|
106
|
+
def date_precision
|
107
|
+
return @date_precision unless @date_precision.nil?
|
108
|
+
return Runt::DPrecision::DEFAULT
|
109
|
+
end
|
110
|
+
end
|
data/lib/runt/daterange.rb
CHANGED
@@ -31,9 +31,9 @@ module Runt
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def overlap?(obj)
|
34
|
-
return true if( include?(obj.min) || include?(obj.max) )
|
35
|
-
|
36
|
-
|
34
|
+
return true if( member?(obj) || include?(obj.min) || include?(obj.max) )
|
35
|
+
return true if( obj.kind_of?(Range) && obj.include?(self) )
|
36
|
+
false
|
37
37
|
end
|
38
38
|
|
39
39
|
def empty?
|
@@ -71,4 +71,4 @@ module Runt
|
|
71
71
|
EMPTY = DateRange.new(PDate.day(2004,2,2),PDate.day(2004,2,1))
|
72
72
|
|
73
73
|
end
|
74
|
-
end
|
74
|
+
end
|
data/lib/runt/pdate.rb
CHANGED
@@ -2,19 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'date'
|
4
4
|
require 'runt/dprecision'
|
5
|
+
require 'runt/pdate'
|
5
6
|
|
6
7
|
#
|
7
8
|
# Author:: Matthew Lipper
|
8
9
|
|
9
10
|
module Runt
|
10
11
|
|
11
|
-
# FKA = 'Formally Known As'
|
12
|
-
|
13
|
-
|
14
|
-
# FKA: TemporalExpression
|
15
|
-
#
|
16
|
-
# Base class for all TExpr classes that has proved itself usefull enough to
|
17
|
-
# avoid O's Razor, but that may wind up as a module.
|
18
12
|
#
|
19
13
|
# 'TExpr' is short for 'TemporalExpression' and are inspired by the recurring event
|
20
14
|
# <tt>pattern</tt>[http://martinfowler.com/apsupp/recurring.pdf]
|
@@ -22,11 +16,12 @@ module Runt
|
|
22
16
|
# specifying recurring events using set expressions.
|
23
17
|
#
|
24
18
|
# See also [tutorial_te.rdoc]
|
25
|
-
|
19
|
+
module TExpr
|
26
20
|
|
27
21
|
# Returns true or false depending on whether this TExpr includes the supplied
|
28
22
|
# date expression.
|
29
23
|
def include?(date_expr); false end
|
24
|
+
|
30
25
|
def to_s; "TExpr" end
|
31
26
|
|
32
27
|
def or (arg)
|
@@ -65,44 +60,25 @@ class TExpr
|
|
65
60
|
self.minus(expr){|adjusted| adjusted }
|
66
61
|
end
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
def max_day_of_month(date)
|
78
|
-
result = 1
|
79
|
-
date.step( Date.new(date.year,date.mon+1,1), 1 ){ |d| result=d.day unless d.day < result }
|
80
|
-
result
|
81
|
-
end
|
82
|
-
|
83
|
-
def week_matches?(index,date)
|
84
|
-
if(index > 0)
|
85
|
-
return week_from_start_matches?(index,date)
|
86
|
-
else
|
87
|
-
return week_from_end_matches?(index,date)
|
63
|
+
# Contributed by Emmett Shear:
|
64
|
+
# Returns an Array of Date-like objects which occur within the supplied
|
65
|
+
# DateRange.
|
66
|
+
def dates(date_range)
|
67
|
+
result = []
|
68
|
+
date_range.each do |date|
|
69
|
+
result << date if self.include? date
|
88
70
|
end
|
89
|
-
|
90
|
-
|
91
|
-
def week_from_start_matches?(index,date)
|
92
|
-
week_in_month(date.day)==index
|
93
|
-
end
|
94
|
-
|
95
|
-
def week_from_end_matches?(index,date)
|
96
|
-
n = days_left_in_month(date) + 1
|
97
|
-
week_in_month(n)==index.abs
|
71
|
+
result
|
98
72
|
end
|
99
73
|
|
100
74
|
end
|
101
75
|
|
102
76
|
# Base class for TExpr classes that can be composed of other
|
103
77
|
# TExpr objects imlpemented using the <tt>Composite(GoF)</tt> pattern.
|
104
|
-
class Collection
|
105
|
-
|
78
|
+
class Collection
|
79
|
+
|
80
|
+
include TExpr
|
81
|
+
|
106
82
|
attr_reader :expressions
|
107
83
|
protected :expressions
|
108
84
|
|
@@ -115,6 +91,16 @@ class Collection < TExpr
|
|
115
91
|
self
|
116
92
|
end
|
117
93
|
|
94
|
+
# Will return true if the supplied object overlaps with the range used to
|
95
|
+
# create this instance
|
96
|
+
def overlap?(date_expr)
|
97
|
+
@expressions.each do | interval |
|
98
|
+
return true if date_expr.overlap?(interval)
|
99
|
+
end
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
|
118
104
|
def to_s; "Collection:" + @expressions.to_s end
|
119
105
|
end
|
120
106
|
|
@@ -137,7 +123,6 @@ end
|
|
137
123
|
class Intersect < Collection
|
138
124
|
|
139
125
|
def include?(aDate)
|
140
|
-
#Handle @expressions.size==0
|
141
126
|
result = false
|
142
127
|
@expressions.each do |expr|
|
143
128
|
return false unless (result = expr.include?(aDate))
|
@@ -149,8 +134,10 @@ class Intersect < Collection
|
|
149
134
|
end
|
150
135
|
|
151
136
|
# TExpr that will be true only if the first of
|
152
|
-
#
|
153
|
-
class Diff
|
137
|
+
# its two contained expressions is true and the second is false.
|
138
|
+
class Diff
|
139
|
+
|
140
|
+
include TExpr
|
154
141
|
|
155
142
|
def initialize(expr1, expr2)
|
156
143
|
@expr1 = expr1
|
@@ -166,8 +153,10 @@ class Diff < TExpr
|
|
166
153
|
end
|
167
154
|
|
168
155
|
# TExpr that provides for inclusion of an arbitrary date.
|
169
|
-
class Spec
|
170
|
-
|
156
|
+
class Spec
|
157
|
+
|
158
|
+
include TExpr
|
159
|
+
|
171
160
|
def initialize(date_expr)
|
172
161
|
@date_expr = date_expr
|
173
162
|
end
|
@@ -187,10 +176,11 @@ end
|
|
187
176
|
# facilitating inclusion of an arbitrary range in a temporal expression.
|
188
177
|
#
|
189
178
|
# See also: Range
|
190
|
-
class RSpec
|
179
|
+
class RSpec
|
180
|
+
|
181
|
+
include TExpr
|
191
182
|
|
192
183
|
def initialize(date_expr)
|
193
|
-
raise TypeError, 'expected range' unless date_expr.kind_of?(Range)
|
194
184
|
@date_expr = date_expr
|
195
185
|
end
|
196
186
|
|
@@ -199,10 +189,62 @@ class RSpec < TExpr
|
|
199
189
|
def include?(date_expr)
|
200
190
|
return @date_expr.include?(date_expr)
|
201
191
|
end
|
192
|
+
|
193
|
+
# Will return true if the supplied object overlaps with the range used to
|
194
|
+
# create this instance
|
195
|
+
def overlap?(date_expr)
|
196
|
+
@date_expr.each do | interval |
|
197
|
+
return true if date_expr.include?(interval)
|
198
|
+
end
|
199
|
+
false
|
200
|
+
end
|
202
201
|
|
203
202
|
def to_s; "RSpec" end
|
204
203
|
end
|
205
204
|
|
205
|
+
#######################################################################
|
206
|
+
# Utility methods common to some expressions
|
207
|
+
|
208
|
+
module TExprUtils
|
209
|
+
def week_in_month(day_in_month)
|
210
|
+
((day_in_month - 1) / 7) + 1
|
211
|
+
end
|
212
|
+
|
213
|
+
def days_left_in_month(date)
|
214
|
+
return max_day_of_month(date) - date.day
|
215
|
+
end
|
216
|
+
|
217
|
+
def max_day_of_month(date)
|
218
|
+
result = 1
|
219
|
+
next_month = nil
|
220
|
+
if(date.mon==12)
|
221
|
+
next_month = Date.new(date.year+1,1,1)
|
222
|
+
else
|
223
|
+
next_month = Date.new(date.year,date.mon+1,1)
|
224
|
+
end
|
225
|
+
date.step(next_month,1){ |d| result=d.day unless d.day < result }
|
226
|
+
result
|
227
|
+
end
|
228
|
+
|
229
|
+
def week_matches?(index,date)
|
230
|
+
if(index > 0)
|
231
|
+
return week_from_start_matches?(index,date)
|
232
|
+
else
|
233
|
+
return week_from_end_matches?(index,date)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def week_from_start_matches?(index,date)
|
238
|
+
week_in_month(date.day)==index
|
239
|
+
end
|
240
|
+
|
241
|
+
def week_from_end_matches?(index,date)
|
242
|
+
n = days_left_in_month(date) + 1
|
243
|
+
week_in_month(n)==index.abs
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
206
248
|
# TExpr that provides support for building a temporal
|
207
249
|
# expression using the form:
|
208
250
|
#
|
@@ -228,7 +270,10 @@ end
|
|
228
270
|
# DIMonth.new(Last,Saturday)
|
229
271
|
#
|
230
272
|
# See also: Date, Runt
|
231
|
-
class DIMonth
|
273
|
+
class DIMonth
|
274
|
+
|
275
|
+
include TExpr
|
276
|
+
include TExprUtils
|
232
277
|
|
233
278
|
def initialize(week_of_month_index,day_index)
|
234
279
|
@day_index = day_index
|
@@ -243,17 +288,6 @@ class DIMonth < TExpr
|
|
243
288
|
"DIMonth"
|
244
289
|
end
|
245
290
|
|
246
|
-
def print(date)
|
247
|
-
puts "DIMonth: #{date}"
|
248
|
-
puts "include? == #{include?(date)}"
|
249
|
-
puts "day_matches? == #{day_matches?(date)}"
|
250
|
-
puts "week_matches? == #{week_matches?(date)}"
|
251
|
-
puts "week_from_start_matches? == #{week_from_start_matches?(date)}"
|
252
|
-
puts "week_from_end_matches? == #{week_from_end_matches?(date)}"
|
253
|
-
puts "days_left_in_month == #{days_left_in_month(date)}"
|
254
|
-
puts "max_day_of_month == #{max_day_of_month(date)}"
|
255
|
-
end
|
256
|
-
|
257
291
|
private
|
258
292
|
def day_matches?(date)
|
259
293
|
@day_index == date.wday
|
@@ -275,7 +309,9 @@ end
|
|
275
309
|
# DIWeek.new(Sunday)
|
276
310
|
#
|
277
311
|
# See also: Date, Runt
|
278
|
-
class DIWeek
|
312
|
+
class DIWeek
|
313
|
+
|
314
|
+
include TExpr
|
279
315
|
|
280
316
|
VALID_RANGE = 0..6
|
281
317
|
|
@@ -298,20 +334,21 @@ end
|
|
298
334
|
# If start and end day are equal, the entire week will match true.
|
299
335
|
#
|
300
336
|
# See also: Date
|
301
|
-
class REWeek
|
337
|
+
class REWeek
|
338
|
+
|
339
|
+
include TExpr
|
302
340
|
|
303
341
|
VALID_RANGE = 0..6
|
304
342
|
|
305
|
-
|
343
|
+
# Creates a REWeek using the supplied start
|
306
344
|
# day(range = 0..6, where 0=>Sunday) and an optional end
|
307
345
|
# day. If an end day is not supplied, the maximum value
|
308
346
|
# (6 => Saturday) is assumed.
|
309
347
|
#
|
310
348
|
# If the start day is greater than the end day, an
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
validate(start_day,end_day)
|
349
|
+
# ArgumentError will be raised
|
350
|
+
def initialize(start_day,end_day=6)
|
351
|
+
validate(start_day,end_day)
|
315
352
|
@start_day = start_day
|
316
353
|
@end_day = end_day
|
317
354
|
end
|
@@ -325,7 +362,7 @@ class REWeek < TExpr
|
|
325
362
|
"REWeek"
|
326
363
|
end
|
327
364
|
|
328
|
-
|
365
|
+
private
|
329
366
|
def validate(start_day,end_day)
|
330
367
|
unless start_day<=end_day
|
331
368
|
raise ArgumentError, 'end day of week must be greater than start day'
|
@@ -336,10 +373,11 @@ class REWeek < TExpr
|
|
336
373
|
end
|
337
374
|
end
|
338
375
|
|
339
|
-
class REYear
|
376
|
+
class REYear
|
340
377
|
|
378
|
+
include TExpr
|
379
|
+
|
341
380
|
def initialize(start_month, start_day=0, end_month=start_month, end_day=0)
|
342
|
-
super()
|
343
381
|
@start_month = start_month
|
344
382
|
@start_day = start_day
|
345
383
|
@end_month = end_month
|
@@ -356,14 +394,6 @@ class REYear < TExpr
|
|
356
394
|
"REYear"
|
357
395
|
end
|
358
396
|
|
359
|
-
def print(date)
|
360
|
-
puts "DIMonth: #{date}"
|
361
|
-
puts "include? == #{include?(date)}"
|
362
|
-
puts "months_include? == #{months_include?(date)}"
|
363
|
-
puts "end_month_include? == #{end_month_include?(date)}"
|
364
|
-
puts "start_month_include? == #{start_month_include?(date)}"
|
365
|
-
end
|
366
|
-
|
367
397
|
private
|
368
398
|
def months_include?(date)
|
369
399
|
(date.mon > @start_month) && (date.mon < @end_month)
|
@@ -385,7 +415,9 @@ end
|
|
385
415
|
# is assumed to be on the following day.
|
386
416
|
#
|
387
417
|
# See also: Date
|
388
|
-
class REDay
|
418
|
+
class REDay
|
419
|
+
|
420
|
+
include TExpr
|
389
421
|
|
390
422
|
CURRENT=28
|
391
423
|
NEXT=29
|
@@ -406,8 +438,9 @@ class REDay < TExpr
|
|
406
438
|
end
|
407
439
|
|
408
440
|
def include?(date)
|
409
|
-
|
410
|
-
|
441
|
+
# If precision is day or greater, then the result is always true
|
442
|
+
return true if date.date_precision <= DPrecision::DAY
|
443
|
+
|
411
444
|
if(@spans_midnight&&date.hour<12) then
|
412
445
|
#Assume next day
|
413
446
|
return @range.include?(get_next(date.hour,date.min))
|
@@ -421,17 +454,11 @@ class REDay < TExpr
|
|
421
454
|
"REDay"
|
422
455
|
end
|
423
456
|
|
424
|
-
def print(date)
|
425
|
-
puts "DIMonth: #{date}"
|
426
|
-
puts "include? == #{include?(date)}"
|
427
|
-
end
|
428
|
-
|
429
457
|
private
|
430
458
|
def spans_midnight?(start_hour, end_hour)
|
431
459
|
return end_hour <= start_hour
|
432
460
|
end
|
433
461
|
|
434
|
-
private
|
435
462
|
def get_current(hour,minute)
|
436
463
|
PDate.min(ANY_DATE.year,ANY_DATE.month,CURRENT,hour,minute)
|
437
464
|
end
|
@@ -447,7 +474,10 @@ end
|
|
447
474
|
# WIMonth.new(1)
|
448
475
|
#
|
449
476
|
# See also: Date
|
450
|
-
class WIMonth
|
477
|
+
class WIMonth
|
478
|
+
|
479
|
+
include TExpr
|
480
|
+
include TExprUtils
|
451
481
|
|
452
482
|
VALID_RANGE = -2..5
|
453
483
|
|
@@ -464,4 +494,31 @@ class WIMonth < TExpr
|
|
464
494
|
|
465
495
|
end
|
466
496
|
|
497
|
+
# TExpr that matches a range of dates within a month. For example:
|
498
|
+
#
|
499
|
+
# REMonth.(12,28)
|
500
|
+
#
|
501
|
+
# matches from the 12th thru the 28th of any month. If end_day==0
|
502
|
+
# or is not given, start_day will define the range with that single day.
|
503
|
+
#
|
504
|
+
# See also: Date
|
505
|
+
class REMonth
|
506
|
+
|
507
|
+
include TExpr
|
508
|
+
|
509
|
+
def initialize(start_day, end_day=0)
|
510
|
+
end_day=start_day if end_day==0
|
511
|
+
@range = start_day..end_day
|
512
|
+
end
|
513
|
+
|
514
|
+
def include?(date)
|
515
|
+
@range.include? date.mday
|
516
|
+
end
|
517
|
+
|
518
|
+
def to_s
|
519
|
+
"REMonth"
|
520
|
+
end
|
521
|
+
|
522
|
+
end
|
523
|
+
|
467
524
|
end
|
data/site/index.html
CHANGED
@@ -49,6 +49,7 @@
|
|
49
49
|
ideas off-line.
|
50
50
|
</li>
|
51
51
|
<li><a href="http://www.bluerobot.com/web/layouts/">Blue Robot</a> for the CSS</li>
|
52
|
+
<li>Emmett Shear for contributed <a href="http://runt.rubyforge.org/doc/classes/Runt/TExpr.html">TExpr#dates</a> code and several thought-provoking feature requests.</li>
|
52
53
|
</ul>
|
53
54
|
</p>
|
54
55
|
</div>
|
@@ -89,4 +90,4 @@
|
|
89
90
|
<!-- BlueRobot was here. -->
|
90
91
|
|
91
92
|
</body>
|
92
|
-
</html>
|
93
|
+
</html>
|
data/test/alltests.rb
CHANGED
data/test/daterangetest.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$:<<'../lib'
|
4
|
-
|
5
3
|
require 'test/unit'
|
6
4
|
require 'runt'
|
7
5
|
require 'date'
|
@@ -57,6 +55,13 @@ class DateRangeTest < Test::Unit::TestCase
|
|
57
55
|
assert(o_range.overlap?(range))
|
58
56
|
assert(o_range.overlap?(DateRange.new(r_start,o_end)))
|
59
57
|
assert(o_range.overlap?(DateRange.new(o_start,r_end)))
|
58
|
+
|
59
|
+
# September 18th - 19th, 2005, 8am - 10am
|
60
|
+
expr1=DateRange.new(PDate.day(2005,9,18),PDate.day(2005,9,19))
|
61
|
+
# September 19th - 20th, 2005, 9am - 11am
|
62
|
+
expr2=DateRange.new(PDate.day(2005,9,19),PDate.day(2005,9,20))
|
63
|
+
|
64
|
+
assert(expr1.overlap?(expr2))
|
60
65
|
end
|
61
66
|
|
62
67
|
def test_empty
|
data/test/dprecisiontest.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$:<<'../lib'
|
4
|
-
|
5
3
|
require 'test/unit'
|
6
4
|
require 'runt'
|
7
5
|
require 'date'
|
@@ -9,6 +7,7 @@ require 'date'
|
|
9
7
|
# Unit tests for DPrecision class
|
10
8
|
#
|
11
9
|
# Author:: Matthew Lipper
|
10
|
+
|
12
11
|
class DPrecisionTest < Test::Unit::TestCase
|
13
12
|
|
14
13
|
include Runt
|
@@ -23,13 +22,13 @@ class DPrecisionTest < Test::Unit::TestCase
|
|
23
22
|
end
|
24
23
|
|
25
24
|
def test_pseudo_singleton_instance
|
26
|
-
assert(DPrecision::YEAR.
|
27
|
-
assert(DPrecision::MONTH.
|
28
|
-
assert(DPrecision::DAY.
|
29
|
-
assert(DPrecision::HOUR.
|
30
|
-
assert(DPrecision::MIN.
|
31
|
-
assert(DPrecision::SEC.
|
32
|
-
assert(DPrecision::MILLI.
|
25
|
+
assert(DPrecision::YEAR.object_id==DPrecision::YEAR.object_id, "Object Id's not equal.")
|
26
|
+
assert(DPrecision::MONTH.object_id==DPrecision::MONTH.object_id, "Object Id's not equal.")
|
27
|
+
assert(DPrecision::DAY.object_id==DPrecision::DAY.object_id, "Object Id's not equal.")
|
28
|
+
assert(DPrecision::HOUR.object_id==DPrecision::HOUR.object_id, "Object Id's not equal.")
|
29
|
+
assert(DPrecision::MIN.object_id==DPrecision::MIN.object_id, "Object Id's not equal.")
|
30
|
+
assert(DPrecision::SEC.object_id==DPrecision::SEC.object_id, "Object Id's not equal.")
|
31
|
+
assert(DPrecision::MILLI.object_id==DPrecision::MILLI.object_id, "Object Id's not equal.")
|
33
32
|
end
|
34
33
|
|
35
34
|
def test_to_precision
|
data/test/pdatetest.rb
CHANGED
data/test/scheduletest.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$:<<'../lib'
|
4
|
-
|
5
3
|
require 'test/unit'
|
6
4
|
require 'runt'
|
7
5
|
require 'date'
|
@@ -52,5 +50,39 @@ class ScheduleTest < Test::Unit::TestCase
|
|
52
50
|
assert_equal(expected,dates)
|
53
51
|
end
|
54
52
|
|
53
|
+
def test_using_a_schedule
|
54
|
+
|
55
|
+
# September 18th - 19th, 2005, 8am - 10am
|
56
|
+
expr1=RSpec.new(DateRange.new(PDate.day(2005,9,18),PDate.day(2005,9,19))) & REDay.new(8,0,10,0)
|
57
|
+
assert(expr1.include?(PDate.min(2005,9,18,8,15)))
|
58
|
+
# September 19th - 20th, 2005, 9am - 11am
|
59
|
+
expr2=RSpec.new(DateRange.new(PDate.day(2005,9,19),PDate.day(2005,9,20))) & REDay.new(9,0,11,0)
|
60
|
+
# Quick sanuty check
|
61
|
+
assert(expr1.overlap?(expr2))
|
62
|
+
# Setup a schedule w/first expression
|
63
|
+
sched = Schedule.new
|
64
|
+
sched.add(Event.new("Snafubar Opening"),expr1)
|
65
|
+
resource = Resource.new(sched)
|
66
|
+
# Add a another overlapping event
|
67
|
+
resource.add_event(Event.new("Yodeling Lesson"),expr2)
|
68
|
+
# Create a new resource using the same schedule
|
69
|
+
resource2 = Resource.new(sched)
|
70
|
+
# Add a another overlapping event and pass a block which should complain
|
71
|
+
#resource.add_event(Event.new("Yodeling Lesson"),expr2) \
|
72
|
+
#{|e,s| raise "Resource not available at requested time(s)." \
|
73
|
+
# if (@schedule.overlap?(s))}
|
74
|
+
end
|
55
75
|
end
|
56
76
|
|
77
|
+
class Resource
|
78
|
+
def initialize(schedule)
|
79
|
+
@schedule=schedule
|
80
|
+
end
|
81
|
+
def add_event(event,expr)
|
82
|
+
if(block_given?)
|
83
|
+
yield(event,expr)
|
84
|
+
else
|
85
|
+
@schedule.add(event,expr)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$:<<'../lib'
|
4
|
-
|
5
3
|
require 'test/unit'
|
6
4
|
require 'runt'
|
7
5
|
require 'date'
|
@@ -10,11 +8,13 @@ $DEBUG=false
|
|
10
8
|
|
11
9
|
# Unit tests for TExpr classes
|
12
10
|
# Author:: Matthew Lipper
|
11
|
+
|
13
12
|
class TExprTest < Test::Unit::TestCase
|
14
13
|
|
15
14
|
include Runt
|
16
15
|
include DPrecision
|
17
16
|
|
17
|
+
|
18
18
|
def test_collection_te
|
19
19
|
#base class that should always return false
|
20
20
|
expr = Collection.new
|
@@ -166,19 +166,28 @@ class TExprTest < Test::Unit::TestCase
|
|
166
166
|
#12:01 am (January 28th, 2004 - ignored)
|
167
167
|
assert(!expr2.include?(PDate.min(2004,1,28,0,01)))
|
168
168
|
end
|
169
|
-
def test_range_each_week_te
|
170
169
|
|
171
|
-
|
170
|
+
def test_range_each_day_te_again
|
171
|
+
dr = DateRange.new(PDate.day(2005,9,19),PDate.day(2005,9,20))
|
172
|
+
red = REDay.new(8,0,10,0)
|
173
|
+
result = false
|
174
|
+
dr.each do |interval|
|
175
|
+
result = red.include?(interval)
|
176
|
+
break if result
|
177
|
+
end
|
178
|
+
assert(result)
|
179
|
+
end
|
172
180
|
|
181
|
+
def test_range_each_week_te
|
182
|
+
assert_raises(ArgumentError){ expr = REWeek.new(10,4) }
|
173
183
|
expr1 = REWeek.new(Mon,Fri) & REDay.new(8,00,8,30)
|
174
184
|
assert(!expr1.include?(PDate.new(2004,5,1,8,06)))
|
175
|
-
|
176
|
-
|
177
185
|
#Sunday through Thursday
|
178
186
|
expr2 = REWeek.new(0,4)
|
179
187
|
assert(expr2.include?(PDate.min(2004,2,19,23,59,59)))
|
180
188
|
assert(!expr2.include?(PDate.min(2004,2,20,0,0,0)))
|
181
189
|
end
|
190
|
+
|
182
191
|
def test_combined_te
|
183
192
|
#This is a hack.....
|
184
193
|
#In the U.S., Memorial Day begins the last Monday of May
|
@@ -279,4 +288,115 @@ class TExprTest < Test::Unit::TestCase
|
|
279
288
|
assert(!ticket.include?(DateTime.new(2004,3,11,1,15)))
|
280
289
|
end
|
281
290
|
|
291
|
+
def test_re_month_te
|
292
|
+
# October 22nd, 2005
|
293
|
+
dt1 = Date.civil(2005,10,22)
|
294
|
+
# The 20th through the 29th of any month
|
295
|
+
expr1 = REMonth.new(20,29)
|
296
|
+
assert(expr1.include?(dt1))
|
297
|
+
assert(!expr1.include?(PDate.new(2010,12,12)))
|
298
|
+
# August 17th, 1975
|
299
|
+
dt2 = Date.civil(1975,8,17)
|
300
|
+
# The 17th of any month
|
301
|
+
expr2 = REMonth.new(17)
|
302
|
+
assert(expr2.include?(dt2))
|
303
|
+
assert(!expr2.include?(dt1))
|
304
|
+
end
|
305
|
+
|
306
|
+
###
|
307
|
+
# Dates functionality & tests contributed by Emmett Shear
|
308
|
+
###
|
309
|
+
def test_day_in_month_dates
|
310
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 12, 31)
|
311
|
+
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
312
|
+
expr = DIMonth.new(First, Tuesday)
|
313
|
+
dates = expr.dates(date_range)
|
314
|
+
assert dates.size == 12
|
315
|
+
dates.each do |d|
|
316
|
+
assert d.wday == 2 # tuesday
|
317
|
+
assert d.day < 8 # in the first week
|
318
|
+
end
|
319
|
+
expr2 = DIMonth.new(Last, Friday)
|
320
|
+
dates2 = expr2.dates(date_range)
|
321
|
+
assert dates2.size == 12
|
322
|
+
dates2.each do |d|
|
323
|
+
assert d.wday == 5 # friday
|
324
|
+
assert d.day > month_days[d.month-1] - 8 # last week
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_day_in_week_dates
|
329
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 1, 31)
|
330
|
+
expr = DIWeek.new(Sunday)
|
331
|
+
dates = expr.dates(date_range)
|
332
|
+
assert( dates.size == 5 )
|
333
|
+
assert( dates.include?( Date.civil(2005, 1, 16) ) )
|
334
|
+
assert( dates.include?( Date.civil(2005, 1, 30) ) )
|
335
|
+
end
|
336
|
+
|
337
|
+
def test_union_dates
|
338
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 12, 31)
|
339
|
+
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
340
|
+
expr = DIMonth.new(Last, Friday) | DIMonth.new(1, Tuesday)
|
341
|
+
dates = expr.dates(date_range)
|
342
|
+
assert dates.size == 24
|
343
|
+
dates.each do |d|
|
344
|
+
unless (d.wday == 2 and d.day < 8) or \
|
345
|
+
(d.wday == 5 and d.day > month_days[d.month-1] - 8)
|
346
|
+
assert false, d.to_s
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def test_intersection_dates
|
352
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 12, 31)
|
353
|
+
expr = DIWeek.new(Sunday) & DIMonth.new(Second, Sunday)
|
354
|
+
dates = expr.dates(date_range)
|
355
|
+
assert( dates.size == 12 )
|
356
|
+
other_dates = DIMonth.new(Second, Sunday).dates(date_range)
|
357
|
+
dates.each { |d| assert( other_dates.include?(d) ) }
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_range_each_week_dates
|
361
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 1, 31)
|
362
|
+
expr = REWeek.new(3, 5)
|
363
|
+
dates = expr.dates(date_range)
|
364
|
+
assert dates.size == 12
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_range_each_year_dates
|
368
|
+
date_range = Date.civil(2004, 5, 1)..Date.civil(2006, 5, 4)
|
369
|
+
expr = REYear.new(4, 28, 5, 6)
|
370
|
+
dates = expr.dates(date_range)
|
371
|
+
assert dates.size == 22, dates.size
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_week_in_month_dates
|
375
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 2, 28)
|
376
|
+
expr = WIMonth.new(2)
|
377
|
+
dates = expr.dates(date_range)
|
378
|
+
assert dates.size == 14, dates.size
|
379
|
+
assert dates.first.mday == 8
|
380
|
+
assert dates.last.mday == 14
|
381
|
+
expr_2 = WIMonth.new(Last)
|
382
|
+
dates_2 = expr_2.dates(date_range)
|
383
|
+
assert dates_2.size == 14, dates_2.size
|
384
|
+
assert dates_2.first.mday == 25
|
385
|
+
assert dates_2.last.mday == 28
|
386
|
+
end
|
387
|
+
|
388
|
+
def test_range_each_month_dates
|
389
|
+
date_range = Date.civil(2005, 1, 7)..Date.civil(2005, 1, 15)
|
390
|
+
expr = REMonth.new(5, 9)
|
391
|
+
dates = expr.dates(date_range)
|
392
|
+
assert dates.size == 3, dates.size
|
393
|
+
assert false if dates.include? Date.civil(2005, 1, 6)
|
394
|
+
end
|
395
|
+
|
396
|
+
def test_diff_dates
|
397
|
+
date_range = Date.civil(2005, 1, 1)..Date.civil(2005, 1, 31)
|
398
|
+
expr = REYear.new(1, 1, 1, 31) - REMonth.new(7, 15)
|
399
|
+
dates = expr.dates(date_range)
|
400
|
+
assert dates.size == 22, dates.size
|
401
|
+
end
|
282
402
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.11
|
3
3
|
specification_version: 1
|
4
4
|
name: runt
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2005-10-22 00:00:00 -04:00
|
8
8
|
summary: Ruby Temporal Expressions.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
author: Matthew Lipper
|
12
11
|
email: matt@digitalclash.com
|
13
12
|
homepage: http://runt.rubyforge.org
|
14
13
|
rubyforge_project: runt
|
@@ -25,6 +24,10 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
24
|
version: 0.0.0
|
26
25
|
version:
|
27
26
|
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
authors:
|
30
|
+
- Matthew Lipper
|
28
31
|
files:
|
29
32
|
- setup.rb
|
30
33
|
- CHANGES
|
@@ -52,7 +55,12 @@ files:
|
|
52
55
|
- site/logohover.png
|
53
56
|
- site/runt-logo.gif
|
54
57
|
- site/runt-logo.psd
|
55
|
-
test_files:
|
58
|
+
test_files:
|
59
|
+
- test/daterangetest.rb
|
60
|
+
- test/dprecisiontest.rb
|
61
|
+
- test/pdatetest.rb
|
62
|
+
- test/scheduletest.rb
|
63
|
+
- test/temporalexpressiontest.rb
|
56
64
|
rdoc_options: []
|
57
65
|
extra_rdoc_files: []
|
58
66
|
executables: []
|