runt 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,23 @@
1
1
  = Runt Changelog
2
2
 
3
+ == Version 0.6.0
4
+
5
+ * Refactored and improved temporal expression tests, moving them to separate files per class
6
+
7
+ * Fixed bug #5741: REYear incorrect handling of default args
8
+
9
+ * Fixed bug #5749: Fixed spans midnight for REDay (Revision 156), changed semantics when dealing with lower precision arguments
10
+
11
+ * Fixed bug #10640: incorrect tutorial section for tutorial_te.rdoc
12
+
13
+ * Fixed bug #10605: DateRange.empty? should be true for min == max
14
+
15
+ * Finished Schedule API tutorial
16
+
17
+ * Fixed bug #16143: Typo in Schedule RDoc
18
+
19
+ * Added README (and related files) in the generated rdoc
20
+
3
21
  == Version 0.5.0
4
22
 
5
23
  * Refactored Schedule implementation which greatly simplifies customization but does potentially break existing clients who relied on the ability to call add mulitple times (SEE BELOW)
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ require 'fileutils'
19
19
  #####################################################################
20
20
 
21
21
  # Build Settings
22
- PKG_VERSION = "0.5.0"
22
+ PKG_VERSION = "0.6.0"
23
23
 
24
24
  # Files to be included in Runt distribution
25
25
  PKG_FILES = FileList[
@@ -27,6 +27,7 @@ PKG_FILES = FileList[
27
27
  '[A-Z]*',
28
28
  'lib/**/*.rb',
29
29
  'test/**/*.rb',
30
+ 'examples/**/*.rb',
30
31
  'doc/**/*',
31
32
  'site/**/*'
32
33
  ].exclude("*.ses")
@@ -63,8 +64,8 @@ Rake::RDocTask.new do |rd|
63
64
  end
64
65
 
65
66
  Rake::TestTask.new do |t|
66
- t.libs << "test"
67
- t.pattern = 'test/*test.rb'
67
+ t.libs << "test" << "examples"
68
+ t.pattern = '**/*test.rb'
68
69
  t.verbose = false
69
70
  end
70
71
 
@@ -103,7 +104,9 @@ else
103
104
  s.email = 'mlipper@gmail.com'
104
105
  s.homepage = 'http://runt.rubyforge.org'
105
106
  s.has_rdoc = true
106
- s.test_files = Dir['test/*test.rb']
107
+ s.rdoc_options += %w{--main README --title Runt}
108
+ s.extra_rdoc_files = FileList["README","CHANGES","TODO","LICENSE.txt","doc/*.rdoc"]
109
+ s.test_files = Dir['**/*test.rb']
107
110
  s.rubyforge_project = 'runt'
108
111
  s.description = <<EOF
109
112
  Runt is a Ruby version of temporal patterns by
data/TODO CHANGED
@@ -4,8 +4,10 @@ Send suggestions, questions, threats, etc. for this list to Matt[mailto:mlipper@
4
4
 
5
5
  === To Do
6
6
 
7
- * Tutorials
7
+ * WIMonth#dates behaves unintuitively (see dates mixin tests)
8
8
 
9
- * Better examples
9
+ * DayIntervalTE matches date multiples prior to start date (see tests)
10
+
11
+ * Better docs, examples, tutorials
10
12
 
11
13
  * Laundry
@@ -1,80 +1,389 @@
1
1
  = Schedule Tutorial
2
2
 
3
- If you haven't done so already, took a look the temporal expression
4
- tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_te_rdoc.html].
3
+ <em> This tutorial assumes you are familiar with use of the Runt API to
4
+ create temporal expressions. If you're unfamiliar with how and why to write
5
+ temporal expressions, take a look at the temporal expression
6
+ tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_te_rdoc.html].</em>
5
7
 
6
- So, you've defined some temporal expressions, now what? In his
7
- paper[http://martinfowler.com/apsupp/recurring.pdf] about recurring events,
8
- Martin Fowler also discusses a simple schedule API which is used, surprisingly
9
- enough, to build a schedule.
8
+ In his paper[http://martinfowler.com/apsupp/recurring.pdf] about recurring
9
+ events, Martin Fowler also discusses a simple schedule API which is used,
10
+ surprisingly enough, to build a schedule. We're not going to cover the pattern
11
+ itself in this tutorial as Fowler already does a nice job. Because it is such
12
+ a simple pattern (once you invent it!), you'll be able understand it even if
13
+ you decide not to read his paper[http://martinfowler.com/apsupp/recurring.pdf].
10
14
 
11
- We're not going to cover the pattern itself in this tutorial as Fowler already
12
- does a nice job. Luckily, because it is such a simple pattern (once you invent
13
- it!), you'll be able understand it even if you decide not to skim the
14
- aforementioned paper[http://martinfowler.com/apsupp/recurring.pdf].
15
+ So, let's pretend that I own a car. Since I don't want to get a ticket, I
16
+ decide to create an application which will tell me where and when I can park
17
+ it on my street. (Since this is all make believe anyway, my car is a late 60's
18
+ model black Ford Mustang with flame detailing (and on the back seat is one
19
+ million dollars)).
15
20
 
16
- In the last tutorial we learned about the exciting world of NYC street cleaning
17
- regulations. For the uptown side of my block, the sign says:
18
-
19
- #############################
20
- # #
21
- # NO PARKING #
22
- # #
23
- # Mon, Wed, Fri 8am-11am #
24
- # #
25
- # T,Th 11:30am-2:00pm #
26
- # #
27
- # Violators will be towed! #
28
- # #
29
- #############################
30
- # #
31
- # #
32
- # #
33
-
34
- We created a temporal expression to match this time period, like so:
21
+ We'll build a Runt Schedule that models the parking regulations. Our app
22
+ will check this Schedule at regular intervals and send us reminders to
23
+ move our car so we don't get a ticket. YAY!
24
+
25
+ First, let's visit the exciting world of NYC street cleaning regulations.
26
+ Let's pretend the following rules are in place for our block:
27
+
28
+ * For the north side of the street, there is no parking Monday, Wednesday, or Friday, from 8am thru 11am
35
29
 
36
- expr1=(DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
30
+ * For the south side of the street, there is no parking Tuesday or Thursday between 11:30am and 2pm
31
+
32
+ Thus...
37
33
 
38
- expr2=(DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
34
+ ############################# #############################
35
+ # # # #
36
+ # NO PARKING # # NO PARKING #
37
+ # # # #
38
+ # Mon, Wed, Fri 8am-11am # # Tu, Th 11:30am-2:00pm #
39
+ # # # #
40
+ # # # #
41
+ # Violators will be towed! # # Violaters will be towed! #
42
+ # # # #
43
+ ############################# #############################
44
+ # # # #
45
+ # # # #
46
+ # # # #
47
+
48
+ North side of the street South side of the street
49
+
50
+
51
+ We'll start by creating temporal expressions which describe the verboten
52
+ parking times:
53
+
54
+
55
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
39
56
 
40
- ticket=expr1 | expr2
57
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
41
58
 
42
- What we need at this point is a way to associate this expression with the rest
43
- of our domain model. For this purpose, we can use a Schedule and an associated
44
- Event both of which are supplied by Runt.
59
+
60
+ What we need at this point is a way to write queries against these expressions
61
+ to determine whether we need to send a reminder. For this purpose, we can use
62
+ a Schedule and an associated Event, both of which are supplied by Runt.
63
+
64
+ schedule = Schedule.new
45
65
 
46
66
  A Schedule holds zero or more Event/TemporalExpression pairs, allowing clients
47
67
  to easily query and update TemporalExpressions as well perform certain range
48
- operations as we will see in a moment.
68
+ operations as we will see in a moment. We'll create two events, one for each
69
+ side of the street:
70
+
71
+ north_event = Event.new("north side")
72
+
73
+ south_event = Event.new("south side")
74
+
75
+ Now we add each event and its associated occurrence to our Schedule:
76
+
77
+ schedule.add(north_event, north_expr)
78
+
79
+ schedule.add(south_event, south_expr)
49
80
 
50
81
  An Event is simply a container for domain data. Although Runt uses Events
51
82
  by default, Schedules will happily house any kind of Object. Internally, a
52
83
  Schedule is really just a Hash where the keys are whatever it is you are
53
84
  scheduling and the values are the TemporalExpressions you create.
54
85
 
86
+ class Schedule
87
+ ...
55
88
 
56
89
  def add(obj, expression)
57
90
  @elems[obj]=expression
58
91
  end
92
+ ...
93
+
94
+ Now that we have a Schedule configured, we need something to check it and
95
+ then let us know if we need to move the car. For this, we'll create a simple
96
+ class called Reminder which will function as the "main-able" part of
97
+ our app.
98
+
99
+ We'll start by creating an easily testable constructor which will be passed
100
+ a Schedule instance (like the one we just created) and an SMTP server.
101
+
102
+
103
+ class Reminder
104
+
105
+ attr_reader :schedule, :mail_server
106
+
107
+ def initialize(schedule,mail_server)
108
+ @schedule = schedule
109
+ @mail_server = mail_server
110
+ end
111
+ ...
59
112
 
113
+ Being devoted Agilists, we'll of course also create a unit test to
114
+ help flesh out the specifics of our new Reminder class. We'll create
115
+ test fixtures using the Runt Objects described above.
60
116
 
61
- For the remainder of this tutorial I will refer to Events specifically, but
62
- just remember that anything reasonably Hash-keyable will do.
63
117
 
118
+ class ReminderTest < Test::Unit::TestCase
119
+
120
+ include Runt
121
+
122
+ def setup
123
+ @schedule = Schedule.new
124
+ @north_event = Event.new("north side of the street will be ticketed")
125
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
126
+ @schedule.add(@north_event, north_expr)
127
+ @south_event = Event.new("south side of the street will be ticketed")
128
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
129
+ @schedule.add(@south_event, south_expr)
130
+ @mail_server = MailServer.new
131
+ @reminder = Reminder.new(@schedule, @mail_server)
132
+ @saturday_at_10 = PDate.min(2007,11,24,10,0,0)
133
+ @monday_at_10 = PDate.min(2007,11,26,10,0,0)
134
+ @tuesday_at_noon = PDate.min(2007,11,27,12,0,0)
135
+ end
136
+
137
+ def test_initalize
138
+ assert_same @schedule, @reminder.schedule, "Expected #{@schedule} instead was #{@reminder.schedule}"
139
+ assert_same @mail_server, @reminder.mail_server, "Expected #{@mail_server} instead was #{@reminder.mail_server}"
140
+ end
141
+ ...
142
+
143
+ For the purposes of this tutorial, the mail server will simply be a stub to
144
+ illustrate how a real one might be used.
145
+
146
+ class MailServer
147
+
148
+ Struct.new("Email",:to,:from,:subject,:text)
149
+
150
+ def send(to, from, subject, text)
151
+ Struct::Email.new(to, from, subject, text)
152
+ # etc...
153
+ end
154
+
155
+ end
156
+
157
+ Next, let's add a method to our Reminder class which actually checks our
158
+ schedule using a date which is passed in as a parameter.
159
+
160
+ class Reminder
161
+ ...
162
+ def check(date)
163
+ return @schedule.events(date)
164
+ end
165
+ ...
166
+
167
+ The Schedule#events method will return an Array of Event Objects for any
168
+ events which occur at the date and time given by the method's argument. Usage
169
+ is easily demonstrated by a test case which makes use of the fixtures created
170
+ by the TestCase#setup method defined above.
171
+
172
+ class ReminderTest < Test::Unit::TestCase
173
+ ...
174
+ def test_check
175
+ assert_equal 1, @reminder.check(@monday_at_10).size, "Unexpected size #{@reminder.check(@monday_at_10).size} returned"
176
+ assert_same @north_event, @reminder.check(@monday_at_10)[0], "Expected Event #{@north_event}. Got #{@reminder.check(@monday_at_10)[0]}."
177
+ assert_equal 1, @reminder.check(@tuesday_at_noon).size, "Unexpected size #{@reminder.check(@tuesday_at_noon).size} returned"
178
+ assert_same @south_event, @reminder.check(@tuesday_at_noon)[0], "Expected Event #{@south_event}. Got #{@reminder.check(@tuesday_at_noon)[0]}."
179
+ assert @reminder.check(@saturday_at_10).empty?, "Expected empty Array. Got #{@reminder.check(@saturday_at_10)}"
180
+ end
181
+ ...
64
182
 
65
- So, let's pretend that I own a car. Since I don't want to get a ticket, I
66
- decide to create a little cron-like utility daemon to remind myself whenever
67
- it's time for me to move it.
68
183
 
69
- class Ron
184
+ There are other methods in the Schedule API which allow a client to query for
185
+ information. Although we don't need them for this tutorial, I'll mention two
186
+ briefly because they are generally useful. The first is Schedule#dates
187
+ which will return an Array of PDate Objects which occur during the DateRange
188
+ supplied as a parameter. The second is Schedule#include? which returns a
189
+ boolean value indicating whether the Event occurs on the date which are both
190
+ supplied as arguments.
70
191
 
192
+ Next, let's make use of the mail server argument given to the Reminder class
193
+ in it's constructor. This is the method that will be called when a call to the
194
+ Reminder#check method produces results.
195
+
196
+ class Reminder
197
+ ...
198
+ def send(date)
199
+ text = "Warning: " + events.join(', ')
200
+ return @mail_server.send(TO, FROM, SUBJECT, text)
201
+ end
202
+ ...
203
+
204
+
205
+ Testing this is simple thanks to our MailServer stub which simply regurgitates
206
+ the text argument it's passed as a result.
207
+
208
+ class ReminderTest < Test::Unit::TestCase
209
+ ...
210
+ def test_send
211
+ params = [@north_event, @south_event]
212
+ result = @reminder.send(params)
213
+ assert_email result, Reminder::TEXT + params.join(', ')
214
+ end
215
+
216
+ def assert_email(result, text)
217
+ assert_equal Reminder::TO, result.to, "Unexpected value for 'to' field of Email Struct: #{result.to}"
218
+ assert_equal Reminder::FROM, result.from, "Unexpected value for 'from' field of Email Struct: #{result.from}"
219
+ assert_equal Reminder::SUBJECT, result.subject, "Unexpected value for 'subject' field of Email Struct: #{result.subject}"
220
+ assert_equal text, result.text, "Unexpected value for 'text' field of Email Struct: #{result.text}"
221
+ end
222
+ ...
223
+
224
+ Note the ReminderTest#assert_email method we've added to make assertions
225
+ common to multiple test cases.
226
+
227
+
228
+ Now, let's tie the whole thing together with a method which which checks for
229
+ occuring Events and (upon finding some) sends a reminder. This method is
230
+ really the only one in the Reminder class that needs to be public.
231
+
232
+ class Reminder
233
+ ...
234
+ def run(date)
235
+ result = self.check(date)
236
+ self.send(result) if !result.empty?
237
+ end
238
+ ...
239
+
240
+ class ReminderTest < Test::Unit::TestCase
241
+ ...
242
+ def test_send
243
+ params = [@north_event, @south_event]
244
+ result = @reminder.send(params)
245
+ assert_email result, Reminder::TEXT + params.join(', ')
246
+ end
247
+ ...
248
+
249
+ Finally, we'll cheat a bit and stitch every thing together so it can be run
250
+ from a command line.
71
251
 
72
- def run
73
- loop
74
252
 
75
- etc...
253
+ include Runt
254
+
255
+ schedule = Schedule.new
256
+ north_event = Event.new("north side")
257
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
258
+ schedule.add(north_event, north_expr)
259
+ south_event = Event.new("south side")
260
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
261
+ schedule.add(south_event, south_expr)
262
+ reminder = Reminder.new(schedule, MailServer.new)
263
+ while true
264
+ sleep 15.minutes
265
+ reminder.run Time.now
266
+ end
267
+
268
+ So, here's all the code for this tutorial (it's in the Runt distribution under
269
+ the examples folder):
270
+
271
+ ### schedule_tutorial.rb ###
272
+
273
+ #!/usr/bin/ruby
274
+
275
+ require 'runt'
276
+
277
+ class Reminder
278
+
279
+ TO = "me@myselfandi.com"
280
+ FROM = "reminder@daemon.net"
281
+ SUBJECT = "Move your car!"
282
+ TEXT = "Warning: "
283
+
284
+ attr_reader :schedule, :mail_server
285
+
286
+ def initialize(schedule,mail_server)
287
+ @schedule = schedule
288
+ @mail_server = mail_server
289
+ end
290
+ def run(date)
291
+ result = self.check(date)
292
+ self.send(result) if !result.empty?
293
+ end
294
+ def check(date)
295
+ puts "Checking the schedule..." if $DEBUG
296
+ return @schedule.events(date)
297
+ end
298
+ def send(events)
299
+ text = TEXT + events.join(', ')
300
+ return @mail_server.send(TO, FROM, SUBJECT, text)
301
+ end
302
+ end
303
+
304
+ class MailServer
305
+ Struct.new("Email",:to,:from,:subject,:text)
306
+ def send(to, from, subject, text)
307
+ puts "Sending message TO: #{to} FROM: #{from} RE: #{subject}..." if $DEBUG
308
+ Struct::Email.new(to, from, subject, text)
309
+ # etc...
310
+ end
311
+ end
312
+
313
+
314
+ if __FILE__ == $0
315
+
316
+ include Runt
317
+
318
+ schedule = Schedule.new
319
+ north_event = Event.new("north side")
320
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
321
+ schedule.add(north_event, north_expr)
322
+ south_event = Event.new("south side")
323
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
324
+ schedule.add(south_event, south_expr)
325
+ reminder = Reminder.new(schedule, MailServer.new)
326
+ while true
327
+ sleep 15.minutes
328
+ reminder.run Time.now
329
+ end
330
+
331
+ end
332
+
333
+
334
+ ### schedule_tutorialtest.rb ###
335
+
336
+ #!/usr/bin/ruby
337
+
338
+ require 'test/unit'
339
+ require 'runt'
340
+ require 'schedule_tutorial'
341
+
342
+ class ReminderTest < Test::Unit::TestCase
343
+
344
+ include Runt
76
345
 
77
- =.....FINISH ME!......
346
+ def setup
347
+ @schedule = Schedule.new
348
+ @north_event = Event.new("north side of the street will be ticketed")
349
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
350
+ @schedule.add(@north_event, north_expr)
351
+ @south_event = Event.new("south side of the street will be ticketed")
352
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
353
+ @schedule.add(@south_event, south_expr)
354
+ @mail_server = MailServer.new
355
+ @reminder = Reminder.new(@schedule, @mail_server)
356
+ @saturday_at_10 = PDate.min(2007,11,24,10,0,0)
357
+ @monday_at_10 = PDate.min(2007,11,26,10,0,0)
358
+ @tuesday_at_noon = PDate.min(2007,11,27,12,0,0)
359
+ end
360
+ def test_initalize
361
+ assert_same @schedule, @reminder.schedule, "Expected #{@schedule} instead was #{@reminder.schedule}"
362
+ assert_same @mail_server, @reminder.mail_server, "Expected #{@mail_server} instead was #{@reminder.mail_server}"
363
+ end
364
+ def test_send
365
+ params = [@north_event, @south_event]
366
+ result = @reminder.send(params)
367
+ assert_email result, Reminder::TEXT + params.join(', ')
368
+ end
369
+ def test_check
370
+ assert_equal 1, @reminder.check(@monday_at_10).size, "Unexpected size #{@reminder.check(@monday_at_10).size} returned"
371
+ assert_same @north_event, @reminder.check(@monday_at_10)[0], "Expected Event #{@north_event}. Got #{@reminder.check(@monday_at_10)[0]}."
372
+ assert_equal 1, @reminder.check(@tuesday_at_noon).size, "Unexpected size #{@reminder.check(@tuesday_at_noon).size} returned"
373
+ assert_same @south_event, @reminder.check(@tuesday_at_noon)[0], "Expected Event #{@south_event}. Got #{@reminder.check(@tuesday_at_noon)[0]}."
374
+ assert @reminder.check(@saturday_at_10).empty?, "Expected empty Array. Got #{@reminder.check(@saturday_at_10)}"
375
+ end
376
+ def test_run
377
+ result = @reminder.run(@monday_at_10)
378
+ assert_email result, Reminder::TEXT + @north_event.to_s
379
+ end
380
+ def assert_email(result, text)
381
+ assert_equal Reminder::TO, result.to, "Unexpected value for 'to' field of Email Struct: #{result.to}"
382
+ assert_equal Reminder::FROM, result.from, "Unexpected value for 'from' field of Email Struct: #{result.from}"
383
+ assert_equal Reminder::SUBJECT, result.subject, "Unexpected value for 'subject' field of Email Struct: #{result.subject}"
384
+ assert_equal text, result.text, "Unexpected value for 'text' field of Email Struct: #{result.text}"
385
+ end
386
+ end
78
387
 
79
388
 
80
389
  <em>See Also:</em>
data/doc/tutorial_te.rdoc CHANGED
@@ -42,8 +42,8 @@ Alrighty, then...less talkin', more tutorin'!
42
42
  10 expr.include?(Date.new(2002,8,29)) #Thurs 8/29/02 => true
43
43
  11 expr.include?(Date.new(2003,8,28)) #Thurs 8/28/03 => true
44
44
  12 expr.include?(Date.new(2004,8,26)) #Thurs 8/26/04 => true
45
- 13 expr.include?(Date.new(2004,3,18)) #Thurs 3/18/04 => true
46
- 14
45
+ 13
46
+ 14 expr.include?(Date.new(2004,3,18)) #Thurs 3/18/04 => false
47
47
  15 expr.include?(Date.new(2004,8,27)) #Fri 8/27/04 => false
48
48
 
49
49
  A couple things are worth noting before we move on to more complicated
@@ -86,7 +86,7 @@ will all match dates and ranges occurring within 'the last Thursday'
86
86
  <b>OR</b> 'the month of August'.
87
87
 
88
88
 
89
- Now what? Beginning on line 11, you can see that calling the
89
+ Now what? Beginning on line 10, you can see that calling the
90
90
  <tt>#include?</tt> method will let you know whether the expression you've
91
91
  defined includes a given date (or, in some cases, a range, or another
92
92
  TE). This is much like the way you use the standard <tt>Range#include?</tt>.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # NOTE this is slightly broken; it is in the process of being fixed
4
+ base = File.basename(Dir.pwd)
5
+ if base == "examples" || base =~ /runt/
6
+ Dir.chdir("..") if base == "examples"
7
+ $LOAD_PATH.unshift(Dir.pwd + '/lib')
8
+ Dir.chdir("examples") if base =~ /runt/
9
+ end
10
+
11
+
12
+
13
+
14
+ require 'runt'
15
+
16
+ class Reminder
17
+ include Runt
18
+
19
+ def initialize(schedule)
20
+ @schedule=schedule
21
+ end
22
+
23
+ def next_times(event,end_point,now=Time.now)
24
+ @schedule.dates(event,DateRange.new(now,end_point))
25
+ end
26
+ end
27
+
28
+ # start of range whose occurrences we want to list
29
+ # TODO fix Runt so this can be done with Time instead
30
+ # e.g., now=Time.now
31
+ #now=Time.parse("13:00")
32
+ #now.date_precision=Runt::DPrecision::MIN
33
+ now=Runt::PDate.min(2006,12,8,13,00)
34
+
35
+ # end of range
36
+ soon=(now + 10.minutes)
37
+
38
+ # Sanity check
39
+ print "start: #{now.to_s} (#{now.date_precision}) end: #{soon.to_s} (#{soon.date_precision})\n"
40
+
41
+ #
42
+ # Schedule used to house TemporalExpression describing the recurrence from
43
+ # which we'd list to generate a list of dates. In this example, some Event
44
+ # occuring every 5 minutes.
45
+ #
46
+ schedule=Runt::Schedule.new
47
+
48
+ # Some event whose schedule we're interested in
49
+ event=Runt::Event.new("whatever")
50
+
51
+ # Add the event to the schedule (
52
+ # NOTE: any Object that is a sensible Hash key can be used
53
+ schedule.add(event,Runt::EveryTE.new(now,5.minutes))
54
+
55
+ # Example domain Object using Runt
56
+ reminder=Reminder.new(schedule)
57
+
58
+ # Call our domain Object with the start and end times and the event
59
+ # in which we're interested
60
+ #puts "times (inclusive) = #{reminder.next_times(event,soon,now).join('\n')}"
61
+
62
+ puts "times (inclusive):"
63
+ reminder.next_times(event,soon,now).each{|t| puts t}
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'runt'
4
+
5
+ class Reminder
6
+
7
+ TO = "me@myselfandi.com"
8
+ FROM = "reminder@daemon.net"
9
+ SUBJECT = "Move your car!"
10
+ TEXT = "Warning: "
11
+
12
+ attr_reader :schedule, :mail_server
13
+
14
+ def initialize(schedule,mail_server)
15
+ @schedule = schedule
16
+ @mail_server = mail_server
17
+ end
18
+ def run(date)
19
+ result = self.check(date)
20
+ self.send(result) if !result.empty?
21
+ end
22
+ def check(date)
23
+ puts "Checking the schedule..." if $DEBUG
24
+ return @schedule.events(date)
25
+ end
26
+ def send(events)
27
+ text = TEXT + events.join(', ')
28
+ return @mail_server.send(TO, FROM, SUBJECT, text)
29
+ end
30
+ end
31
+
32
+ class MailServer
33
+ Struct.new("Email",:to,:from,:subject,:text)
34
+ def send(to, from, subject, text)
35
+ puts "Sending message TO: #{to} FROM: #{from} RE: #{subject}..." if $DEBUG
36
+ Struct::Email.new(to, from, subject, text)
37
+ # etc...
38
+ end
39
+ end
40
+
41
+
42
+ if __FILE__ == $0
43
+
44
+ include Runt
45
+
46
+ schedule = Schedule.new
47
+ north_event = Event.new("north side")
48
+ north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
49
+ schedule.add(north_event, north_expr)
50
+ south_event = Event.new("south side")
51
+ south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
52
+ schedule.add(south_event, south_expr)
53
+ reminder = Reminder.new(schedule, MailServer.new)
54
+ while true
55
+ sleep 15.minutes
56
+ reminder.run Time.now
57
+ end
58
+
59
+ end