runt 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,48 @@
1
+ = Runt Changelog
2
+
3
+ == Version 0.2.0
4
+
5
+ * Fixed Schedule class
6
+ * Renamed Schedule#is_occurring? to Schedule#include?
7
+ * RAA deployment
8
+ * GEM deployment
9
+ * Renamed file dateprecisiontest.rb to dprecisiontest.rb
10
+ * Renamed several methods on PDate:
11
+ - second -> sec
12
+ - minute -> min
13
+ - hour_of_day -> hour
14
+ - day_of_month -> day
15
+
16
+ == Version 0.1.0
17
+
18
+ * Inspired by suggestions[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/95465] on c.l.r., did massive renaming and refactoring to make source more Ruby-esque:
19
+
20
+ - TemporalExpression class => TExpr
21
+ - DatePrecision module => DPrecision (file name changed also)
22
+ - TimePoint class => PDate (file name changed also)
23
+ - Added transparent use of bitwise set operators ( & , | ) for building composite expressions
24
+ - Added transparent use of - operator for diff expressions
25
+
26
+ * Updated TE Tutorial to reflect new and improved syntax usage
27
+ * Skipped several version numbers to celebrate
28
+
29
+ == Version 0.0.6
30
+
31
+ * TE Tutorial
32
+ * Website beautification
33
+ * Credits
34
+
35
+ == Version 0.0.4
36
+
37
+ * Improved Rake[http://rake.rubyforge.org] support
38
+ * Better documentation: README, TODO, CHANGES, etc.
39
+ * More Ruby-like source code layout and code organization inspired by Rake[http://rake.rubyforge.org] distribution
40
+ * Dropped the obviously superfluous 'alpha' from versioning
41
+
42
+ == Version 0.0.1
43
+
44
+ * Learned Ruby (or began trying, at any rate...)
45
+ * RubyForge project setup
46
+ * Basic implementation of the Java-based chronicJ[http://chronicJ.org] functionality in Ruby
47
+
48
+
data/LICENSE.txt ADDED
@@ -0,0 +1,44 @@
1
+ The Apache Software License, Version 1.1
2
+
3
+ Copyright (c) 2003-2004 Digital Clash LLC. All rights
4
+ reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions
8
+ are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright
14
+ notice, this list of conditions and the following disclaimer in
15
+ the documentation and/or other materials provided with the
16
+ distribution.
17
+
18
+ 3. The end-user documentation included with the redistribution,
19
+ if any, must include the following acknowledgment:
20
+ "This product includes software developed by the
21
+ Runt team (http://runt.rubyforge.org/)."
22
+ Alternately, this acknowledgment may appear in the software itself,
23
+ if and wherever such third-party acknowledgments normally appear.
24
+
25
+ 4. The names "Runt" and "Digital Clash" not be used to endorse or
26
+ promote products derived from this software without prior written
27
+ permission. For written permission, please contact
28
+ info@digitalclash.com.
29
+
30
+ 5. Products derived from this software may not be called "Runt",
31
+ "Digital Clash", nor may "Runt" or "Digital Clash" appear in
32
+ their name, without prior written permission of Digital Clash LLC.
33
+
34
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37
+ DISCLAIMED. IN NO EVENT SHALL DIGITAL CLASH LLC OR ITS CONTRIBUTORS
38
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
39
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
40
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
41
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
44
+ THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,88 @@
1
+ = RUNT -- Ruby Temporal Expressions
2
+
3
+ * Runt is a Ruby[http://www.ruby-lang.org/en/] implementation of select Martin Fowler patterns[http://www.martinfowler.com/articles].
4
+
5
+ * <em>TemporalExpression</em>s allow a developer to define patterns of date recurrence using set expressions.
6
+
7
+ ---
8
+
9
+ = INSTALL
10
+
11
+ * Unpack the Runt distribution.
12
+
13
+ $ tar -xzvf runt-<version>.tar.gz
14
+
15
+ * cd $UNPACK_DIR/runt/
16
+
17
+ * execute:
18
+
19
+ $ ruby setup.rb config
20
+ $ ruby setup.rb setup
21
+ $ ruby setup.rb install (may require root privilege)
22
+
23
+ <b>or</b>
24
+
25
+ $ ruby setup.rb --help
26
+
27
+ for more options.
28
+
29
+ ---
30
+
31
+ = QUICK START
32
+
33
+ * require 'runt'
34
+
35
+ * See http://runt.rubyforge.org/doc/index.html
36
+
37
+ * See $UNPACK_DIR/runt/test/*.rb for example usage.
38
+
39
+ * See this mini-TemporalExpression tutorial[http://runt.rubyforge.org/doc/files/doc/tutorial_te_rdoc.html]
40
+
41
+ * See http://www.chronicj.org/apidocs/index.html for better commented source (JavaDoc)
42
+
43
+ Get in touch if you have questions or if Runt causes your computer to burst into flames...
44
+
45
+ Matt[mailto:matt@digitalclash.com]
46
+
47
+ ---
48
+
49
+ == Credits
50
+
51
+ Rubyforge[http://rubyforge.org] for hosting this project.
52
+
53
+ M.Fowler[http://martinfowler.com], Matz[http://ruby-lang.org],
54
+ T.Funaba (Date/DateTime[http://www.funaba.org/en/ruby.html]),
55
+ J.Weirich (Rake[http://rake.rubyforge.org]),
56
+ PragmaticProgrammers[http://pragmaticprogrammer.com], and everyone on
57
+ ruby-talk[mailto:ruby-talk@ruby-lang.org] for their shameless public display
58
+ of smartness.
59
+
60
+ Hal Fulton, Mauricio Fernandez, and Mark Hubbart for the
61
+ thread[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/95465] on c.l.r.
62
+ that inspired a more Ruby-esque syntax for creating TE's. 2X Thanks to Hal, for
63
+ taking the time to exchange ideas off-line.
64
+
65
+ The ruby-nyc user's group for their suggestions and feedback.
66
+
67
+ BlueRobot[http://www.bluerobot.com/web/layouts/] for the CSS used to prettify the Runt website.
68
+
69
+ The number 12, and the letters E and J.
70
+
71
+ ---
72
+
73
+ = Etc...
74
+
75
+ Author:: Matthew Lipper <matt@digitalclash.com>
76
+ Requires:: Ruby 1.8.0 or later, Date/DateTime classes
77
+ License:: Copyright 2004 by Digital Clash LLC.
78
+ Released under the Apache Software license (see LICENSE.txt)
79
+ included in the distribution.
80
+
81
+ = Warranty
82
+
83
+ This software is provided "as is" and without any express or
84
+ implied warranties, including, without limitation, the implied
85
+ warranties of merchantibility and fitness for a particular
86
+ purpose.
87
+
88
+ link://../dcl-small.gif
data/Rakefile ADDED
@@ -0,0 +1,117 @@
1
+ # Rakefile for runt -*- ruby -*-
2
+
3
+ begin
4
+ require 'rubygems'
5
+ require 'rake/gempackagetask'
6
+ rescue Exception
7
+ nil
8
+ end
9
+ require 'rake'
10
+ require 'rake/clean'
11
+ require 'rake/testtask'
12
+ require 'rake/rdoctask'
13
+ #require 'rake/packagetask'
14
+
15
+ require 'rake/contrib/sshpublisher'
16
+ require 'rake/contrib/rubyforgepublisher'
17
+ require 'fileutils'
18
+
19
+ # Build Settings
20
+ PKG_VERSION = "0.2.0"
21
+ # 'gem_install_bin.rb',
22
+ # Files to be included in Runt distribution
23
+ PKG_FILES = FileList[
24
+ 'setup.rb',
25
+ '[A-Z]*',
26
+ 'lib/**/*.rb',
27
+ 'test/**/*.rb',
28
+ 'doc/**/*',
29
+ 'site/**/*'
30
+ ].exclude("*.ses")
31
+
32
+ # build directory
33
+ TARGET_DIR = "target"
34
+
35
+ # Targets
36
+ task :default => [:test]
37
+ task :clobber => [:clobber_build_dir]
38
+
39
+ # Make the build directory
40
+ directory TARGET_DIR
41
+
42
+ desc "Clobber the entire build directory."
43
+ task :clobber_build_dir do |t|
44
+ puts "It's clobberin' time! (hello from task #{t.name})"
45
+ CLOBBER.include(TARGET_DIR)
46
+ end
47
+
48
+ Rake::RDocTask.new do |rd|
49
+ rd.rdoc_dir="#{TARGET_DIR}/doc"
50
+ rd.options << "-S"
51
+ rd.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc','[A-Z]*')
52
+ rd.rdoc_files.exclude('test/*.rb','[A-Z]*.ses','Rakefile')
53
+ end
54
+
55
+ Rake::TestTask.new do |t|
56
+ t.libs << "test"
57
+ t.pattern = 'test/alltests.rb'
58
+ t.verbose = true
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)
66
+ end
67
+
68
+ desc "Copy html files for the Runt website to the build directory."
69
+ file "copy_site" => TARGET_DIR
70
+ file "copy_site" do
71
+ cp_r Dir["site/*.{html,gif,png,css}"], TARGET_DIR
72
+ end
73
+
74
+ desc "Publish the Documentation to RubyForge."
75
+ task :publish => [:rdoc,:copy_site,:clobber_package] do |t|
76
+ publisher = Rake::CompositePublisher.new
77
+ publisher.add Rake::SshDirPublisher.new("mlipper@rubyforge.org", "/var/www/gforge-projects/runt",TARGET_DIR)
78
+ publisher.upload
79
+ end
80
+
81
+ desc "Publish the Documentation to the build dir."
82
+ task :test_publish => [:rerdoc,:copy_site,:clobber_package] do |t|
83
+ puts "YAY! We've tested publish! YAY!"
84
+ end
85
+
86
+
87
+ if ! defined?(Gem)
88
+ puts "Package Target requires RubyGEMs"
89
+ else
90
+ spec = Gem::Specification.new do |s|
91
+ s.platform = Gem::Platform::RUBY
92
+ s.summary = "Ruby Temporal Expressions."
93
+ s.name = 'runt'
94
+ s.version = PKG_VERSION
95
+ s.requirements << 'none'
96
+ s.require_path = 'lib'
97
+ s.autorequire = 'runt'
98
+ s.files = PKG_FILES.to_a
99
+ s.author = 'Matthew Lipper'
100
+ s.email = 'matt@digitalclash.com'
101
+ s.homepage = 'http://runt.rubyforge.org'
102
+ s.has_rdoc = true
103
+ # s.rdoc_files = rd.rdoc_files
104
+ # s.rdoc_options = rd.option_list
105
+ s.rubyforge_project = 'runt'
106
+ s.description = <<EOF
107
+ Runt is a Ruby version of temporal patterns by
108
+ Martin Fowler. Runt provides an API for scheduling
109
+ recurring events using set-like semantics.
110
+ EOF
111
+ end
112
+
113
+ Rake::GemPackageTask.new(spec) do |pkg|
114
+ pkg.need_zip = true
115
+ pkg.need_tar = true
116
+ end
117
+ end
data/TODO ADDED
@@ -0,0 +1,19 @@
1
+ = Runt - Ruby Temporal Expressions -- To Do List
2
+
3
+ Send suggestions, questions, threats, etc. for this list to Matt[mailto:matt@digitalclash.com]
4
+
5
+ === To Do
6
+
7
+ * Fix REWeek so that ordinal day values where the end day < start day does not fail
8
+
9
+ * Make Schedule a Module instead of a class
10
+
11
+ * Schedule tutorial
12
+
13
+ * Better RDoc's which include usage examples
14
+
15
+ * Properly implement/test Range functionality in DateRange
16
+
17
+ * Address performance issues due to implementation (in particular, PDate#<=>)
18
+
19
+ * Use RDoc properly so Runt website and README don't cause DRY violations
@@ -0,0 +1,51 @@
1
+ = Schedule Tutorial
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].
5
+
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.
10
+
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
+
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:
35
+
36
+ expr1 = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
37
+
38
+ expr2 = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
39
+
40
+ ticket = expr1 | expr2
41
+
42
+
43
+
44
+ =.....FINNISH ME!......
45
+
46
+
47
+ <em>See Also:</em>
48
+
49
+ * Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
50
+
51
+ * Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]
@@ -0,0 +1,190 @@
1
+ = Temporal Expressions Tutorial
2
+
3
+ Based on a pattern[http://martinfowler.com/apsupp/recurring.pdf]
4
+ created by Martin Fowler, temporal expressions define points or ranges
5
+ in time using <em>set expressions</em>. This means, an application
6
+ developer can precisely describe recurring events without resorting to
7
+ hacking out a big-ol' nasty enumerated list of dates.
8
+
9
+ For example, say you wanted to schedule an event that occurred
10
+ annually on the last Thursday of every August. You might start out by
11
+ doing something like this:
12
+
13
+ require 'date'
14
+
15
+ some_dates = [Date.new(2002,8,29),Date.new(2003,8,28),Date.new(2004,8,26)]
16
+
17
+ ...etc.
18
+
19
+ This is fine for two or three years, but what about for thirty years?
20
+ What if you want to say every Monday, Tuesday and Friday, between 3
21
+ and 5pm for the next fifty years? *Ouch*.
22
+
23
+ As Fowler notes in his paper, TemporalExpressions(<tt>TE</tt>s for
24
+ short) provide a simple pattern language for defining a given set of
25
+ dates and/or times. They can be 'mixed-and- matched' as necessary,
26
+ providing an incremental, modular and expanding expressive power.
27
+
28
+ Alrighty, then...less talkin', more tutorin'!
29
+
30
+ === Example 1
31
+ <b>Define An Expression That Says: 'the last Thursday in August'</b>
32
+
33
+ 1 require 'runt'
34
+ 2 require 'date'
35
+ 3
36
+ 4 last_thursday = DIMonth.new(Last_of,Thursday)
37
+ 5
38
+ 6 august = REYear.new(8)
39
+ 7
40
+ 8 expr = last_thursday & august
41
+ 9
42
+ 10 expr.include?(Date.new(2002,8,29)) #Thurs 8/29/02 => true
43
+ 11 expr.include?(Date.new(2003,8,28)) #Thurs 8/28/03 => true
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
47
+ 15 expr.include?(Date.new(2004,8,27)) #Fri 8/27/04 => false
48
+
49
+ A couple things are worth noting before we move on to more complicated
50
+ expressions.
51
+
52
+ Clients use temporal expressions by creating specific instances
53
+ (DIMonth == day in month, REYear == range each year) and then,
54
+ optionally, combining them using various familiar operators
55
+ <tt>( & , | , - )</tt>.
56
+
57
+ Semantically, the '&' operator on line 8 behaves much like the
58
+ standard Ruby short-circuit operator '&&'. However, instead of
59
+ returning a boolean value, a new composite <tt>TE</tt> is instead
60
+ created and returned. This new expression is the logical
61
+ intersection of everything matched by <b>both</b> arguments '&'.
62
+
63
+ In the example above, line 4:
64
+
65
+
66
+ last_thursday = DIMonth.new(Last_of,Thursday)
67
+
68
+
69
+ will match the last Thursday of <b>any</b> month and line 6:
70
+
71
+
72
+ august = REYear.new(8)
73
+
74
+
75
+ will match <b>any</b> date or date range occurring within the month of
76
+ August. Thus, combining them, you have 'the last Thursday' <b>AND</b>
77
+ 'the month of August'.
78
+
79
+ By contrast:
80
+
81
+
82
+ expr = DIMonth.new(Last_of,Thursday) | REYear.new(8)
83
+
84
+
85
+ will all match dates and ranges occurring within 'the last Thursday'
86
+ <b>OR</b> 'the month of August'.
87
+
88
+
89
+ Now what? Beginning on line 11, you can see that calling the
90
+ <tt>#include?</tt> method will let you know whether the expression you've
91
+ defined includes a given date (or, in some cases, a range, or another
92
+ TE). This is much like the way you use the standard <tt>Range#include?</tt>.
93
+
94
+ === Example 2
95
+ <b>Define: 'Street Cleaning Rules/Alternate Side Parking in NYC'</b>
96
+
97
+ In his paper[http://martinfowler.com/apsupp/recurring.pdf], Fowler
98
+ uses Boston parking regulations to illustrate some examples. Since I'm
99
+ from New York City, and Boston-related examples might cause an
100
+ allergic reaction, I'll use NYC's street cleaning and parking
101
+ calendar[http://www.nyc.gov/html/dot/html/motorist/scrintro.html#street]
102
+ instead. Since I'm not <em>completely</em> insane, I'll only use a
103
+ small subset of the City's actual rules.
104
+
105
+ On my block, parking is prohibited on the north side of the street
106
+ Monday, Wednesday, and Friday between the hours of 8am to 11am, and on
107
+ Tuesday and Thursday from 11:30am to 2pm
108
+
109
+ Hmmm...let's start by selecting days in the week.
110
+
111
+ Monday <b>OR</b> Wednesday <b>OR</b> Friday:
112
+
113
+ mon_wed_fri = DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)
114
+
115
+ #Sanity check
116
+ mon_wed_fri.include?( DateTime.new(2004,3,10,19,15) ) # Wed => true
117
+ mon_wed_fri.include?( DateTime.new(2004,3,14,9,00) ) # Sun => false
118
+
119
+ 8am to 11am:
120
+
121
+ eight_to_eleven = REDay.new(8,00,11,00)
122
+
123
+ combine the two:
124
+
125
+ expr1 = mon_wed_fri & eight_to_eleven
126
+
127
+ and, logically speaking, we now have '(Mon <b>OR</b> Wed <b>OR</b> Fri)
128
+ <b>AND</b> (8am to 11am)'. We're halfway there.
129
+
130
+ Tuesdays and Thursdays:
131
+
132
+ tues_thurs = DIWeek.new(Tue) | DIWeek.new(Thu)
133
+
134
+ 11:30am to 2pm:
135
+ eleven_thirty_to_two = REDay.new(11,30,14,00)
136
+
137
+ #Sanity check
138
+ eleven_thirty_to_two.include?( DateTime.new(2004,3,8,12,00) ) # Noon => true
139
+ eleven_thirty_to_two.include?( DateTime.new(2004,3,11,00,00) ) # Midnite => false
140
+
141
+ expr2 = tues_thurs & eleven_thirty_to_two
142
+
143
+ <tt>expr2</tt> says '(Tues <b>OR</b> Thurs) <b>AND</b> (11:30am to 2pm)'.
144
+
145
+ and finally:
146
+
147
+ ticket = expr1 | expr2
148
+
149
+
150
+ Or, logically, ((Mon <b>OR</b> Wed <b>OR</b> Fri) <b>AND</b> (8am to
151
+ 11am)) <b>OR</b> ((Tues OR Thurs) <b>AND</b> (11:30am to 2pm))
152
+
153
+
154
+ Let's re-write this without all the noise:
155
+
156
+
157
+ expr1 = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
158
+
159
+ expr2 = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
160
+
161
+ ticket = expr1 | expr2
162
+
163
+
164
+ ticket.include?( DateTime.new(2004,3,11,12,15) ) # => true
165
+
166
+ ticket.include?( DateTime.new(2004,3,10,9,15) ) # => true
167
+
168
+ ticket.include?( DateTime.new(2004,3,10,8,00) ) # => true
169
+
170
+ ticket.include?( DateTime.new(2004,3,11,1,15) ) # => false
171
+
172
+
173
+ Sigh...now if I can only get my dad to remember this...
174
+
175
+
176
+ These are simple examples, but they demonstrate how temporal
177
+ expressions can be used instead of an enumerated list of date values
178
+ to define patterns of recurrence. There are many other temporal
179
+ expressions, and, more importantly, once you get the hang of it, it's
180
+ easy to write your own.
181
+
182
+ Fowler's paper[http://martinfowler.com/apsupp/recurring.pdf] also goes
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/schedule_te.html] for details.
185
+
186
+ <em>See Also:</em>
187
+
188
+ * Fowler's recurring event pattern[http://martinfowler.com/apsupp/recurring.pdf]
189
+
190
+ * Other temporal patterns[http://martinfowler.com/ap2/timeNarrative.html]