Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

This will probably work better without an empty manifest.

  • Loading branch information...
commit e15d3e865cd6706c8488f6e8c7c0babdbeafa8ec 1 parent 2c35d8e
@texel authored
View
112 Manifest.txt
@@ -0,0 +1,112 @@
+History.txt
+LICENSE.txt
+Manifest.txt
+README.txt
+Rakefile
+TODO
+examples/payment_report.rb
+examples/payment_reporttest.rb
+examples/reminder.rb
+examples/schedule_tutorial.rb
+examples/schedule_tutorialtest.rb
+lib/runt.rb
+lib/runt/daterange.rb
+lib/runt/dprecision.rb
+lib/runt/expressionbuilder.rb
+lib/runt/pdate.rb
+lib/runt/schedule.rb
+lib/runt/sugar.rb
+lib/runt/temporalexpression.rb
+runt.gemspec
+setup.rb
+site/blue-robot3.css
+site/dcl-small.gif
+site/index.html
+site/logohover.png
+site/runt-logo.gif
+site/runt-logo.psd
+target/blue-robot3.css
+target/dcl-small.gif
+target/doc/classes/Date.html
+target/doc/classes/ExpressionBuilder.html
+target/doc/classes/Runt.html
+target/doc/classes/Runt/AfterTE.html
+target/doc/classes/Runt/BeforeTE.html
+target/doc/classes/Runt/Collection.html
+target/doc/classes/Runt/DIMonth.html
+target/doc/classes/Runt/DIWeek.html
+target/doc/classes/Runt/DPrecision.html
+target/doc/classes/Runt/DPrecision/Precision.html
+target/doc/classes/Runt/DateRange.html
+target/doc/classes/Runt/DayIntervalTE.html
+target/doc/classes/Runt/Diff.html
+target/doc/classes/Runt/Event.html
+target/doc/classes/Runt/EveryTE.html
+target/doc/classes/Runt/Intersect.html
+target/doc/classes/Runt/PDate.html
+target/doc/classes/Runt/REDay.html
+target/doc/classes/Runt/REMonth.html
+target/doc/classes/Runt/REWeek.html
+target/doc/classes/Runt/REYear.html
+target/doc/classes/Runt/RSpec.html
+target/doc/classes/Runt/Schedule.html
+target/doc/classes/Runt/Spec.html
+target/doc/classes/Runt/TExpr.html
+target/doc/classes/Runt/TExprUtils.html
+target/doc/classes/Runt/Union.html
+target/doc/classes/Runt/WIMonth.html
+target/doc/classes/Runt/YearTE.html
+target/doc/classes/Time.html
+target/doc/created.rid
+target/doc/files/CHANGES.html
+target/doc/files/LICENSE_txt.html
+target/doc/files/README.html
+target/doc/files/TODO.html
+target/doc/files/doc/tutorial_schedule_rdoc.html
+target/doc/files/doc/tutorial_sugar_rdoc.html
+target/doc/files/doc/tutorial_te_rdoc.html
+target/doc/files/lib/runt/daterange_rb.html
+target/doc/files/lib/runt/dprecision_rb.html
+target/doc/files/lib/runt/expressionbuilder_rb.html
+target/doc/files/lib/runt/pdate_rb.html
+target/doc/files/lib/runt/schedule_rb.html
+target/doc/files/lib/runt/sugar_rb.html
+target/doc/files/lib/runt/temporalexpression_rb.html
+target/doc/files/lib/runt_rb.html
+target/doc/fr_class_index.html
+target/doc/fr_file_index.html
+target/doc/fr_method_index.html
+target/doc/index.html
+target/doc/rdoc-style.css
+target/index.html
+target/logohover.png
+target/runt-logo.gif
+test/aftertetest.rb
+test/baseexpressiontest.rb
+test/beforetetest.rb
+test/collectiontest.rb
+test/combinedexpressionstest.rb
+test/daterangetest.rb
+test/dayintervaltetest.rb
+test/difftest.rb
+test/dimonthtest.rb
+test/diweektest.rb
+test/dprecisiontest.rb
+test/everytetest.rb
+test/expressionbuildertest.rb
+test/icalendartest.rb
+test/intersecttest.rb
+test/pdatetest.rb
+test/redaytest.rb
+test/remonthtest.rb
+test/reweektest.rb
+test/reyeartest.rb
+test/rspectest.rb
+test/runttest.rb
+test/scheduletest.rb
+test/spectest.rb
+test/sugartest.rb
+test/temporalexpressiontest.rb
+test/uniontest.rb
+test/wimonthtest.rb
+test/yeartetest.rb
View
393 doc/tutorial_schedule.rdoc
@@ -1,393 +0,0 @@
-= Schedule Tutorial
-
-<em> This tutorial assumes you are familiar with use of the Runt API to
-create temporal expressions. If you're unfamiliar with how and why to write
-temporal expressions, take a look at the temporal expression
-tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_te_rdoc.html].</em>
-
-In his paper[http://martinfowler.com/apsupp/recurring.pdf] about recurring
-events, Martin Fowler also discusses a simple schedule API which is used,
-surprisingly enough, to build a schedule. We're not going to cover the pattern
-itself in this tutorial as Fowler already does a nice job. Because it is such
-a simple pattern (once you invent it!), you'll be able understand it even if
-you decide not to read his paper[http://martinfowler.com/apsupp/recurring.pdf].
-
-So, let's pretend that I own a car. Since I don't want to get a ticket, I
-decide to create an application which will tell me where and when I can park
-it on my street. (Since this is all make believe anyway, my car is a late 60's
-model black Ford Mustang with flame detailing (and on the back seat is one
-million dollars)).
-
-We'll build a Runt Schedule that models the parking regulations. Our app
-will check this Schedule at regular intervals and send us reminders to
-move our car so we don't get a ticket. YAY!
-
-First, let's visit the exciting world of NYC street cleaning regulations.
-Let's pretend the following rules are in place for our block:
-
-* For the north side of the street, there is no parking Monday, Wednesday, or Friday, from 8am thru 11am
-
-* For the south side of the street, there is no parking Tuesday or Thursday between 11:30am and 2pm
-
-Thus...
-
- ############################# #############################
- # # # #
- # NO PARKING # # NO PARKING #
- # # # #
- # Mon, Wed, Fri 8am-11am # # Tu, Th 11:30am-2:00pm #
- # # # #
- # # # #
- # Violators will be towed! # # Violaters will be towed! #
- # # # #
- ############################# #############################
- # # # #
- # # # #
- # # # #
-
- North side of the street South side of the street
-
-
-We'll start by creating temporal expressions which describe the verboten
-parking times:
-
-
- north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
-
- south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
-
-
-What we need at this point is a way to write queries against these expressions
-to determine whether we need to send a reminder. For this purpose, we can use
-a Schedule and an associated Event, both of which are supplied by Runt.
-
- schedule = Schedule.new
-
-A Schedule holds zero or more Event/TemporalExpression pairs, allowing clients
-to easily query and update TemporalExpressions as well perform certain range
-operations as we will see in a moment. We'll create two events, one for each
-side of the street:
-
- north_event = Event.new("north side")
-
- south_event = Event.new("south side")
-
-Now we add each event and its associated occurrence to our Schedule:
-
- schedule.add(north_event, north_expr)
-
- schedule.add(south_event, south_expr)
-
-An Event is simply a container for domain data. Although Runt uses Events
-by default, Schedules will happily house any kind of Object. Internally, a
-Schedule is really just a Hash where the keys are whatever it is you are
-scheduling and the values are the TemporalExpressions you create.
-
- class Schedule
- ...
-
- def add(obj, expression)
- @elems[obj]=expression
- end
- ...
-
-Now that we have a Schedule configured, we need something to check it and
-then let us know if we need to move the car. For this, we'll create a simple
-class called Reminder which will function as the "main-able" part of
-our app.
-
-We'll start by creating an easily testable constructor which will be passed
-a Schedule instance (like the one we just created) and an SMTP server.
-
-
- class Reminder
-
- attr_reader :schedule, :mail_server
-
- def initialize(schedule,mail_server)
- @schedule = schedule
- @mail_server = mail_server
- end
- ...
-
-Being devoted Agilists, we'll of course also create a unit test to
-help flesh out the specifics of our new Reminder class. We'll create
-test fixtures using the Runt Objects described above.
-
-
- class ReminderTest < Test::Unit::TestCase
-
- include Runt
-
- def setup
- @schedule = Schedule.new
- @north_event = Event.new("north side of the street will be ticketed")
- north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
- @schedule.add(@north_event, north_expr)
- @south_event = Event.new("south side of the street will be ticketed")
- south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
- @schedule.add(@south_event, south_expr)
- @mail_server = MailServer.new
- @reminder = Reminder.new(@schedule, @mail_server)
- @saturday_at_10 = PDate.min(2007,11,24,10,0,0)
- @monday_at_10 = PDate.min(2007,11,26,10,0,0)
- @tuesday_at_noon = PDate.min(2007,11,27,12,0,0)
- end
-
- def test_initalize
- assert_same @schedule, @reminder.schedule, "Expected #{@schedule} instead was #{@reminder.schedule}"
- assert_same @mail_server, @reminder.mail_server, "Expected #{@mail_server} instead was #{@reminder.mail_server}"
- end
- ...
-
-For the purposes of this tutorial, the mail server will simply be a stub to
-illustrate how a real one might be used.
-
- class MailServer
-
- Struct.new("Email",:to,:from,:subject,:text)
-
- def send(to, from, subject, text)
- Struct::Email.new(to, from, subject, text)
- # etc...
- end
-
- end
-
-Next, let's add a method to our Reminder class which actually checks our
-schedule using a date which is passed in as a parameter.
-
- class Reminder
- ...
- def check(date)
- return @schedule.events(date)
- end
- ...
-
-The Schedule#events method will return an Array of Event Objects for any
-events which occur at the date and time given by the method's argument. Usage
-is easily demonstrated by a test case which makes use of the fixtures created
-by the TestCase#setup method defined above.
-
- class ReminderTest < Test::Unit::TestCase
- ...
- def test_check
- assert_equal 1, @reminder.check(@monday_at_10).size, "Unexpected size #{@reminder.check(@monday_at_10).size} returned"
- assert_same @north_event, @reminder.check(@monday_at_10)[0], "Expected Event #{@north_event}. Got #{@reminder.check(@monday_at_10)[0]}."
- assert_equal 1, @reminder.check(@tuesday_at_noon).size, "Unexpected size #{@reminder.check(@tuesday_at_noon).size} returned"
- assert_same @south_event, @reminder.check(@tuesday_at_noon)[0], "Expected Event #{@south_event}. Got #{@reminder.check(@tuesday_at_noon)[0]}."
- assert @reminder.check(@saturday_at_10).empty?, "Expected empty Array. Got #{@reminder.check(@saturday_at_10)}"
- end
- ...
-
-
-There are other methods in the Schedule API which allow a client to query for
-information. Although we don't need them for this tutorial, I'll mention two
-briefly because they are generally useful. The first is Schedule#dates
-which will return an Array of PDate Objects which occur during the DateRange
-supplied as a parameter. The second is Schedule#include? which returns a
-boolean value indicating whether the Event occurs on the date which are both
-supplied as arguments.
-
-Next, let's make use of the mail server argument given to the Reminder class
-in it's constructor. This is the method that will be called when a call to the
-Reminder#check method produces results.
-
- class Reminder
- ...
- def send(date)
- text = "Warning: " + events.join(', ')
- return @mail_server.send(TO, FROM, SUBJECT, text)
- end
- ...
-
-
-Testing this is simple thanks to our MailServer stub which simply regurgitates
-the text argument it's passed as a result.
-
- class ReminderTest < Test::Unit::TestCase
- ...
- def test_send
- params = [@north_event, @south_event]
- result = @reminder.send(params)
- assert_email result, Reminder::TEXT + params.join(', ')
- end
-
- def assert_email(result, text)
- assert_equal Reminder::TO, result.to, "Unexpected value for 'to' field of Email Struct: #{result.to}"
- assert_equal Reminder::FROM, result.from, "Unexpected value for 'from' field of Email Struct: #{result.from}"
- assert_equal Reminder::SUBJECT, result.subject, "Unexpected value for 'subject' field of Email Struct: #{result.subject}"
- assert_equal text, result.text, "Unexpected value for 'text' field of Email Struct: #{result.text}"
- end
- ...
-
-Note the ReminderTest#assert_email method we've added to make assertions
-common to multiple test cases.
-
-
-Now, let's tie the whole thing together with a method which which checks for
-occuring Events and (upon finding some) sends a reminder. This method is
-really the only one in the Reminder class that needs to be public.
-
- class Reminder
- ...
- def run(date)
- result = self.check(date)
- self.send(result) if !result.empty?
- end
- ...
-
- class ReminderTest < Test::Unit::TestCase
- ...
- def test_send
- params = [@north_event, @south_event]
- result = @reminder.send(params)
- assert_email result, Reminder::TEXT + params.join(', ')
- end
- ...
-
-Finally, we'll cheat a bit and stitch every thing together so it can be run
-from a command line.
-
-
- include Runt
-
- schedule = Schedule.new
- north_event = Event.new("north side")
- north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
- schedule.add(north_event, north_expr)
- south_event = Event.new("south side")
- south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
- schedule.add(south_event, south_expr)
- reminder = Reminder.new(schedule, MailServer.new)
- while true
- sleep 15.minutes
- reminder.run Time.now
- end
-
-So, here's all the code for this tutorial (it's in the Runt distribution under
-the examples folder):
-
-### schedule_tutorial.rb ###
-
-#!/usr/bin/ruby
-
-require 'runt'
-
-class Reminder
-
- TO = "me@myselfandi.com"
- FROM = "reminder@daemon.net"
- SUBJECT = "Move your car!"
- TEXT = "Warning: "
-
- attr_reader :schedule, :mail_server
-
- def initialize(schedule,mail_server)
- @schedule = schedule
- @mail_server = mail_server
- end
- def run(date)
- result = self.check(date)
- self.send(result) if !result.empty?
- end
- def check(date)
- puts "Checking the schedule..." if $DEBUG
- return @schedule.events(date)
- end
- def send(events)
- text = TEXT + events.join(', ')
- return @mail_server.send(TO, FROM, SUBJECT, text)
- end
-end
-
-class MailServer
- Struct.new("Email",:to,:from,:subject,:text)
- def send(to, from, subject, text)
- puts "Sending message TO: #{to} FROM: #{from} RE: #{subject}..." if $DEBUG
- Struct::Email.new(to, from, subject, text)
- # etc...
- end
-end
-
-
-if __FILE__ == $0
-
- include Runt
-
- schedule = Schedule.new
- north_event = Event.new("north side")
- north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
- schedule.add(north_event, north_expr)
- south_event = Event.new("south side")
- south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
- schedule.add(south_event, south_expr)
- reminder = Reminder.new(schedule, MailServer.new)
- while true
- sleep 15.minutes
- reminder.run Time.now
- end
-
-end
-
-
-### schedule_tutorialtest.rb ###
-
-#!/usr/bin/ruby
-
-require 'test/unit'
-require 'runt'
-require 'schedule_tutorial'
-
-class ReminderTest < Test::Unit::TestCase
-
- include Runt
-
- def setup
- @schedule = Schedule.new
- @north_event = Event.new("north side of the street will be ticketed")
- north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
- @schedule.add(@north_event, north_expr)
- @south_event = Event.new("south side of the street will be ticketed")
- south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
- @schedule.add(@south_event, south_expr)
- @mail_server = MailServer.new
- @reminder = Reminder.new(@schedule, @mail_server)
- @saturday_at_10 = PDate.min(2007,11,24,10,0,0)
- @monday_at_10 = PDate.min(2007,11,26,10,0,0)
- @tuesday_at_noon = PDate.min(2007,11,27,12,0,0)
- end
- def test_initalize
- assert_same @schedule, @reminder.schedule, "Expected #{@schedule} instead was #{@reminder.schedule}"
- assert_same @mail_server, @reminder.mail_server, "Expected #{@mail_server} instead was #{@reminder.mail_server}"
- end
- def test_send
- params = [@north_event, @south_event]
- result = @reminder.send(params)
- assert_email result, Reminder::TEXT + params.join(', ')
- end
- def test_check
- assert_equal 1, @reminder.check(@monday_at_10).size, "Unexpected size #{@reminder.check(@monday_at_10).size} returned"
- assert_same @north_event, @reminder.check(@monday_at_10)[0], "Expected Event #{@north_event}. Got #{@reminder.check(@monday_at_10)[0]}."
- assert_equal 1, @reminder.check(@tuesday_at_noon).size, "Unexpected size #{@reminder.check(@tuesday_at_noon).size} returned"
- assert_same @south_event, @reminder.check(@tuesday_at_noon)[0], "Expected Event #{@south_event}. Got #{@reminder.check(@tuesday_at_noon)[0]}."
- assert @reminder.check(@saturday_at_10).empty?, "Expected empty Array. Got #{@reminder.check(@saturday_at_10)}"
- end
- def test_run
- result = @reminder.run(@monday_at_10)
- assert_email result, Reminder::TEXT + @north_event.to_s
- end
- def assert_email(result, text)
- assert_equal Reminder::TO, result.to, "Unexpected value for 'to' field of Email Struct: #{result.to}"
- assert_equal Reminder::FROM, result.from, "Unexpected value for 'from' field of Email Struct: #{result.from}"
- assert_equal Reminder::SUBJECT, result.subject, "Unexpected value for 'subject' field of Email Struct: #{result.subject}"
- assert_equal text, result.text, "Unexpected value for 'text' field of Email Struct: #{result.text}"
- end
-end
-
-
-<em>See Also:</em>
-
-* Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
-
-* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
View
143 doc/tutorial_sugar.rdoc
@@ -1,143 +0,0 @@
-= Sugar Tutorial
-
-<em> This tutorial assumes you are familiar with use of the Runt API to
-create temporal expressions. If you're unfamiliar with how and why to write
-temporal expressions, take a look at the temporal expression
-tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_te_rdoc.html].</em>
-
-Starting with version 0.7.0, Runt provides some syntactic sugar for creating
-temporal expressions. Runt also provides a builder class for which can be
-used to create expressions in a more readable way than simply using :new.
-
-First, let's look at some of the new shorcuts for creating individual
-expressions. If you look at the lib/runt/sugar.rb file you find that the
-Runt module has been re-opened and some nutty stuff happens when
-:method_missing is called.
-
-For example, if you've included the Runt module, you can now create a
-DIWeek expression by calling a method whose name matches the following
-pattern:
-
- /^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/
-
-So
-
- tuesday
-
-is equivalent to
-
- DIWeek.new(Tuesday)
-
-Here's a quick summary of patterns and the expressions they create.
-
-=== REDay
-
-<b>regex</b>:: /^daily_(\d{1,2})_(\d{2})([ap]m)_to_(\d{1,2})_(\d{2})([ap]m)$/
-
-<b>example</b>:: daily_8_30am_to_10_00pm
-
-<b>action</b>:: REDay.new(8,30,22,00)
-
-=== REWeek
-
-<b>regex</b>:: /^weekly_(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\_to\_(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/
-
-<b>example</b>:: weekly_wednesday_to_friday
-
-<b>action</b>:: REWeek.new(Wednesday, Friday)
-
-=== REMonth
-
-<b>regex</b>:: /^monthly_(\d{1,2})(?:st|nd|rd|th)\_to\_(\d{1,2})(?:st|nd|rd|th)$/
-
-<b>example</b>:: monthly_2nd_to_24th
-
-<b>action</b>:: REMonth.new(2,24)
-
-=== REYear
-
-<b>regex</b>:: /^yearly_(january|february|march|april|may|june|july|august|september|october|november|december)_(\d{1,2})\_to\_(january|february|march|april|may|june|july|august|september|october|november|december)_(\d{1,2})
-
-<b>example</b>:: yearly_may_31_to_september_1
-
-<b>action</b>:: REYear.new(May,31,September,1)
-
-=== DIWeek
-
-<b>regex</b>:: /^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/
-
-<b>example</b>:: friday
-
-<b>action</b>:: DIWeek.new(Friday)
-
-=== DIMonth
-
-<b>regex</b>:: /^(first|second|third|fourth|last|second_to_last)_(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/
-
-<b>example</b>:: last_friday
-
-<b>action</b>:: DIMonth.new(Last,Friday)
-
-Now let's look at the new ExpressionBuilder class. This class uses some simple methods and instance_eval to allow one to create composite tempooral expressions in a more fluid style than :new and friends. The idea is that you define a block where method calls add to a composite expression using either "and", "or", or "not".
-
- # Create a new builder
- b = ExpressionBuilder.new
-
- # Call define with a block
- expression = d.define do
- on REDay.new(8,45,9,30)
- on DIWeek.new(Friday) # "And"
- possibly DIWeek.new(Saturday) # "Or"
- except DIMonth.new(Last, Friday) # "Not"
- end
-
- # expression = "Daily 8:45am to 9:30 and Fridays or Saturday except not the last Friday of the month"
-
-Hmmm, this is not really an improvement over
-
-
- REDay.new(8,45,9,30) & DIWeek.new(Friday) | DIWeek.new(Saturday) - DIMonth.new(Last, Friday)
-
-
-I know, let's try the new constructor aliases defined above!
-
-
- expression = d.define do
- on daily_8_45am_to_9_30am
- on friday
- possibly saturday
- except last_friday
- end
-
-Much better, except "on daily..." seems a little awkward. We can use :occurs which is aliased to :on for just such a scenario.
-
-
- expression = d.define do
- occurs daily_8_45am_to_9_30am
- on friday
- possibly saturday
- except last_friday
- end
-
-
-ExpressionBuilder creates expressions by evaluating a block passed to the
-:define method. From inside the block, methods :occurs, :on, :every, :possibly,
-and :maybe can be called with a temporal expression which will be added to
-a composite expression as follows:
-
-* <b>:on</b>:: creates an "and" (&)
-* <b>:possibly</b>:: creates an "or" (|)
-* <b>:except</b>:: creates a "not" (-)
-* <b>:every</b>:: alias for :on method
-* <b>:occurs</b>:: alias for :on method
-* <b>:maybe</b>:: alias for :possibly method
-
-Of course it's easy to open the builder class and add you own aliases if the ones provided don't work for you:
-
- class ExpressionBuilder
- alias_method :potentially, :possibly
- etc....
- end
-
-If there are shortcuts or macros that you think others would find useful, send in a feature request or patch.
-
View
190 doc/tutorial_te.rdoc
@@ -1,190 +0,0 @@
-= Temporal Expressions Tutorial
-
-Based on a pattern[http://martinfowler.com/apsupp/recurring.pdf]
-created by Martin Fowler, temporal expressions define points or ranges
-in time using <em>set expressions</em>. This means, an application
-developer can precisely describe recurring events without resorting to
-hacking out a big-ol' nasty enumerated list of dates.
-
-For example, say you wanted to schedule an event that occurred
-annually on the last Thursday of every August. You might start out by
-doing something like this:
-
- require 'date'
-
- some_dates = [Date.new(2002,8,29),Date.new(2003,8,28),Date.new(2004,8,26)]
-
- ...etc.
-
-This is fine for two or three years, but what about for thirty years?
-What if you want to say every Monday, Tuesday and Friday, between 3
-and 5pm for the next fifty years? *Ouch*.
-
-As Fowler notes in his paper, TemporalExpressions(<tt>TE</tt>s for
-short) provide a simple pattern language for defining a given set of
-dates and/or times. They can be 'mixed-and- matched' as necessary,
-providing an incremental, modular and expanding expressive power.
-
-Alrighty, then...less talkin', more tutorin'!
-
-=== Example 1
-<b>Define An Expression That Says: 'the last Thursday in August'</b>
-
- 1 require 'runt'
- 2 require 'date'
- 3
- 4 last_thursday = DIMonth.new(Last_of,Thursday)
- 5
- 6 august = REYear.new(8)
- 7
- 8 expr = last_thursday & august
- 9
- 10 expr.include?(Date.new(2002,8,29)) #Thurs 8/29/02 => true
- 11 expr.include?(Date.new(2003,8,28)) #Thurs 8/28/03 => true
- 12 expr.include?(Date.new(2004,8,26)) #Thurs 8/26/04 => true
- 13
- 14 expr.include?(Date.new(2004,3,18)) #Thurs 3/18/04 => false
- 15 expr.include?(Date.new(2004,8,27)) #Fri 8/27/04 => false
-
-A couple things are worth noting before we move on to more complicated
-expressions.
-
-Clients use temporal expressions by creating specific instances
-(DIMonth == day in month, REYear == range each year) and then,
-optionally, combining them using various familiar operators
-<tt>( & , | , - )</tt>.
-
-Semantically, the '&' operator on line 8 behaves much like the
-standard Ruby short-circuit operator '&&'. However, instead of
-returning a boolean value, a new composite <tt>TE</tt> is instead
-created and returned. This new expression is the logical
-intersection of everything matched by <b>both</b> arguments '&'.
-
-In the example above, line 4:
-
-
- last_thursday = DIMonth.new(Last_of,Thursday)
-
-
-will match the last Thursday of <b>any</b> month and line 6:
-
-
- august = REYear.new(8)
-
-
-will match <b>any</b> date or date range occurring within the month of
-August. Thus, combining them, you have 'the last Thursday' <b>AND</b>
-'the month of August'.
-
-By contrast:
-
-
- expr = DIMonth.new(Last_of,Thursday) | REYear.new(8)
-
-
-will all match dates and ranges occurring within 'the last Thursday'
-<b>OR</b> 'the month of August'.
-
-
-Now what? Beginning on line 10, you can see that calling the
-<tt>#include?</tt> method will let you know whether the expression you've
-defined includes a given date (or, in some cases, a range, or another
-TE). This is much like the way you use the standard <tt>Range#include?</tt>.
-
-=== Example 2
-<b>Define: 'Street Cleaning Rules/Alternate Side Parking in NYC'</b>
-
-In his paper[http://martinfowler.com/apsupp/recurring.pdf], Fowler
-uses Boston parking regulations to illustrate some examples. Since I'm
-from New York City, and Boston-related examples might cause an
-allergic reaction, I'll use NYC's street cleaning and parking
-calendar[http://www.nyc.gov/html/dot/html/motorist/scrintro.html#street]
-instead. Since I'm not <em>completely</em> insane, I'll only use a
-small subset of the City's actual rules.
-
-On my block, parking is prohibited on the north side of the street
-Monday, Wednesday, and Friday between the hours of 8am to 11am, and on
-Tuesday and Thursday from 11:30am to 2pm
-
-Hmmm...let's start by selecting days in the week.
-
-Monday <b>OR</b> Wednesday <b>OR</b> Friday:
-
- mon_wed_fri = DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)
-
- #Sanity check
- mon_wed_fri.include?( DateTime.new(2004,3,10,19,15) ) # Wed => true
- mon_wed_fri.include?( DateTime.new(2004,3,14,9,00) ) # Sun => false
-
-8am to 11am:
-
- eight_to_eleven = REDay.new(8,00,11,00)
-
-combine the two:
-
- expr1 = mon_wed_fri & eight_to_eleven
-
-and, logically speaking, we now have '(Mon <b>OR</b> Wed <b>OR</b> Fri)
-<b>AND</b> (8am to 11am)'. We're halfway there.
-
-Tuesdays and Thursdays:
-
- tues_thurs = DIWeek.new(Tue) | DIWeek.new(Thu)
-
-11:30am to 2pm:
- eleven_thirty_to_two = REDay.new(11,30,14,00)
-
- #Sanity check
- eleven_thirty_to_two.include?( DateTime.new(2004,3,8,12,00) ) # Noon => true
- eleven_thirty_to_two.include?( DateTime.new(2004,3,11,00,00) ) # Midnite => false
-
- expr2 = tues_thurs & eleven_thirty_to_two
-
-<tt>expr2</tt> says '(Tues <b>OR</b> Thurs) <b>AND</b> (11:30am to 2pm)'.
-
-and finally:
-
- ticket = expr1 | expr2
-
-
-Or, logically, ((Mon <b>OR</b> Wed <b>OR</b> Fri) <b>AND</b> (8am to
-11am)) <b>OR</b> ((Tues OR Thurs) <b>AND</b> (11:30am to 2pm))
-
-
-Let's re-write this without all the noise:
-
-
- expr1 = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
-
- expr2 = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
-
- ticket = expr1 | expr2
-
-
- ticket.include?( DateTime.new(2004,3,11,12,15) ) # => true
-
- ticket.include?( DateTime.new(2004,3,10,9,15) ) # => true
-
- ticket.include?( DateTime.new(2004,3,10,8,00) ) # => true
-
- ticket.include?( DateTime.new(2004,3,11,1,15) ) # => false
-
-
-Sigh...now if I can only get my dad to remember this...
-
-
-These are simple examples, but they demonstrate how temporal
-expressions can be used instead of an enumerated list of date values
-to define patterns of recurrence. There are many other temporal
-expressions, and, more importantly, once you get the hang of it, it's
-easy to write your own.
-
-Fowler's paper[http://martinfowler.com/apsupp/recurring.pdf] also goes
-on to describe another element of this pattern: the <tt>Schedule</tt>.
-See the schedule tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_schedule_rdoc.html] for details.
-
-<em>See Also:</em>
-
-* Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
-
-* Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
View
2  runt.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = %q{runt}
- s.version = "0.7.1"
+ s.version = "0.7.2"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Matthew Lipper"]
Please sign in to comment.
Something went wrong with that request. Please try again.