runt 0.6.0 → 0.7.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.
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'runt'
4
+
5
+ class Report
6
+
7
+ attr_reader :schedule
8
+
9
+ def initialize(schedule)
10
+ @schedule = schedule
11
+ end
12
+ def list(range)
13
+ result = {}
14
+ range.each do |dt|
15
+ events = @schedule.events(dt)
16
+ result[dt]=events unless events.empty?
17
+ end
18
+ result
19
+ end
20
+ end
21
+
22
+ class Payment < Runt::Event
23
+ attr_accessor :amount
24
+ def initialize(id, amount)
25
+ super(id)
26
+ @amount = amount
27
+ end
28
+ end
29
+
30
+
31
+ if __FILE__ == $0
32
+
33
+ include Runt
34
+
35
+ schedule = Schedule.new
36
+
37
+ # Gas payment on the first Wednesday of every month
38
+ gas_payment = Payment.new("Gas", 234)
39
+ gas_expr = DIMonth.new(First, Wednesday)
40
+ schedule.add(gas_payment, gas_expr)
41
+
42
+ # Insurance payment every year on January 7th
43
+ insurance_payment = Payment.new("Insurance", 345)
44
+ insurance_expr = REYear.new(1, 7, 1, 7)
45
+ schedule.add(insurance_payment, insurance_expr)
46
+
47
+ # Run a report
48
+ report = Report.new(schedule)
49
+ result = report.list(PDate.day(2008, 1, 1)..PDate.day(2008,1,31))
50
+ result.keys.sort.each do |dt|
51
+ unless result[dt].empty? then
52
+ print "#{dt.ctime} - "
53
+ result[dt].each do |event|
54
+ puts "#{event.id}, $#{event.amount}"
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'test/unit'
4
+ require 'runt'
5
+ require 'payment_report'
6
+
7
+ class ReportTest < Test::Unit::TestCase
8
+
9
+ include Runt
10
+
11
+ def setup
12
+ @schedule = Schedule.new
13
+
14
+ # Gas payment on the first Wednesday of every month
15
+ @gas_payment = Payment.new("Gas", 234)
16
+ @gas_expr = DIMonth.new(First, Wednesday)
17
+ @schedule.add(@gas_payment, @gas_expr)
18
+
19
+ # Insurance payment every year on January 7th
20
+ @insurance_payment = Payment.new("Insurance", 345)
21
+ @insurance_expr = REYear.new(1, 7, 1, 7)
22
+ @schedule.add(@insurance_payment, @insurance_expr)
23
+ @report = Report.new(@schedule)
24
+ end
25
+ def test_initialize
26
+ assert_equal @schedule, @report.schedule
27
+ end
28
+ def test_list
29
+ range = PDate.day(2008, 1, 1)..PDate.day(2008,1,31)
30
+ result = @report.list(range)
31
+ assert_equal(2, result.size)
32
+ assert_equal(@gas_payment, result[PDate.day(2008, 1, 2)][0])
33
+ assert_equal(@insurance_payment, result[PDate.day(2008, 1, 7)][0])
34
+ end
35
+ end
36
+
37
+ class PaymentTest < Test::Unit::TestCase
38
+
39
+ include Runt
40
+
41
+ def test_initialize
42
+ p = Payment.new "Foo", 12
43
+ assert_equal "Foo", p.id
44
+ assert_equal 12, p.amount
45
+ end
46
+
47
+ end
48
+
49
+
@@ -1,63 +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}
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}
@@ -1,219 +1,237 @@
1
- #!/usr/bin/env ruby
2
-
3
- # :title:Runt -- Ruby Temporal Expressions
4
- #
5
- # == Runt -- Ruby Temporal Expressions
6
- #
7
- # The usage and design patterns expressed in this library are mostly...*uhm*..
8
- # <em>entirely</em>..*cough*...based on a series of
9
- # <tt>articles</tt>[http://www.martinfowler.com] by Martin Fowler.
10
- #
11
- # It highly recommended that anyone using Runt (or writing
12
- # object-oriented software :) take a moment to peruse the wealth of useful info
13
- # that Fowler has made publicly available:
14
- #
15
- # * An excellent introductory summation of temporal <tt>patterns</tt>[http://martinfowler.com/ap2/timeNarrative.html]
16
- # * Recurring event <tt>pattern</tt>[http://martinfowler.com/apsupp/recurring.pdf]
17
- #
18
- # Also, for those of you (like me, for example) still chained in your cubicle and forced
19
- # to write <tt>Java</tt>[http://java.sun.com] code, check out the original version of
20
- # project called <tt>ChronicJ</tt>[http://chronicj.org].
21
- #
22
- # ---
23
- # Author:: Matthew Lipper (mailto:mlipper@gmail.com)
24
- # Copyright:: Copyright (c) 2004 Digital Clash, LLC
25
- # License:: See LICENSE.txt
26
- #
27
- # = Warranty
28
- #
29
- # This software is provided "as is" and without any express or
30
- # implied warranties, including, without limitation, the implied
31
- # warranties of merchantibility and fitness for a particular
32
- # purpose.
33
-
34
- require 'time'
35
- require 'date'
36
- require 'date/format'
37
- require "runt/dprecision"
38
- require "runt/pdate"
39
- require "runt/temporalexpression"
40
- require "runt/schedule"
41
- require "runt/daterange"
42
-
43
- #
44
- # The Runt module is the main namespace for all Runt modules and classes. Using
45
- # require statements, it makes the entire Runt library available.It also
46
- # defines some new constants and exposes some already defined in the standard
47
- # library classes <tt>Date</tt> and <tt>DateTime</tt>.
48
- #
49
- # <b>See also</b> date.rb
50
- #
51
- module Runt
52
-
53
- class << self
54
-
55
- def day_name(number)
56
- Date::DAYNAMES[number]
57
- end
58
-
59
- def month_name(number)
60
- Date::MONTHNAMES[number]
61
- end
62
-
63
- def format_time(date)
64
- date.strftime('%I:%M%p')
65
- end
66
-
67
- def format_date(date)
68
- date.ctime
69
- end
70
-
71
- #
72
- # Cut and pasted from activesupport-1.2.5/lib/inflector.rb
73
- #
74
- def ordinalize(number)
75
- if (number.to_i==-1)
76
- 'last'
77
- elsif (number.to_i==-2)
78
- 'second to last'
79
- elsif (11..13).include?(number.to_i % 100)
80
- "#{number}th"
81
- else
82
- case number.to_i % 10
83
- when 1: "#{number}st"
84
- when 2: "#{number}nd"
85
- when 3: "#{number}rd"
86
- else "#{number}th"
87
- end
88
- end
89
- end
90
-
91
- end
92
-
93
- #Yes it's true, I'm a big idiot!
94
- Sunday = Date::DAYNAMES.index("Sunday")
95
- Monday = Date::DAYNAMES.index("Monday")
96
- Tuesday = Date::DAYNAMES.index("Tuesday")
97
- Wednesday = Date::DAYNAMES.index("Wednesday")
98
- Thursday = Date::DAYNAMES.index("Thursday")
99
- Friday = Date::DAYNAMES.index("Friday")
100
- Saturday = Date::DAYNAMES.index("Saturday")
101
- Sun = Date::ABBR_DAYNAMES.index("Sun")
102
- Mon = Date::ABBR_DAYNAMES.index("Mon")
103
- Tue = Date::ABBR_DAYNAMES.index("Tue")
104
- Wed = Date::ABBR_DAYNAMES.index("Wed")
105
- Thu = Date::ABBR_DAYNAMES.index("Thu")
106
- Fri = Date::ABBR_DAYNAMES.index("Fri")
107
- Sat = Date::ABBR_DAYNAMES.index("Sat")
108
- First = 1
109
- Second = 2
110
- Third = 3
111
- Fourth = 4
112
- Fifth = 5
113
- Sixth = 6
114
- Seventh = 7
115
- Eigth = 8
116
- Ninth = 9
117
- Tenth = 10
118
-
119
- private
120
- class ApplyLast #:nodoc:
121
- def initialize
122
- @negate=Proc.new{|n| n*-1}
123
- end
124
- def [](arg)
125
- @negate.call(arg)
126
- end
127
- end
128
- LastProc = ApplyLast.new
129
-
130
- public
131
- Last = LastProc[First]
132
- Last_of = LastProc[First]
133
- Second_to_last = LastProc[Second]
134
-
135
- end
136
-
137
- #
138
- # Add precision +Runt::DPrecision+ to standard library classes Date and DateTime
139
- # (which is a subclass of Date). Also, add an inlcude? method for interoperability
140
- # with +Runt::TExpr+ classes
141
- #
142
- class Date
143
-
144
- include Runt
145
-
146
- attr_accessor :date_precision
147
-
148
- def include?(expr)
149
- eql?(expr)
150
- end
151
-
152
- def date_precision
153
- return @date_precision unless @date_precision.nil?
154
- return Runt::DPrecision::DEFAULT
155
- end
156
- end
157
-
158
- #
159
- # Add the ability to use Time class
160
- #
161
- # Contributed by Paul Wright
162
- #
163
- class Time
164
-
165
- include Runt
166
-
167
- attr_accessor :date_precision
168
- alias_method :old_initialize, :initialize
169
- def initialize(*args)
170
- if(args[0].instance_of?(Runt::DPrecision::Precision))
171
- @precision=args.shift
172
- else
173
- @precision=Runt::DPrecision::DEFAULT
174
- end
175
- old_initialize(*args)
176
- end
177
-
178
- class << self
179
- alias_method :old_parse, :parse
180
- def parse(*args)
181
- precision=Runt::DPrecision::DEFAULT
182
- if(args[0].instance_of?(Runt::DPrecision::Precision))
183
- precision=args.shift
184
- end
185
- _parse=old_parse(*args)
186
- _parse.date_precision=precision
187
- _parse
188
- end
189
- end
190
-
191
- def date_precision
192
- return @date_precision unless @date_precision.nil?
193
- return Runt::DPrecision::DEFAULT
194
- end
195
- end
196
-
197
- #
198
- # Useful shortcuts!
199
- #
200
- # Contributed by Ara T. Howard who is pretty sure he got the idea from
201
- # somewhere else. :-)
202
- #
203
- class Numeric #:nodoc:
204
- def microseconds() Float(self * (10 ** -6)) end
205
- def milliseconds() Float(self * (10 ** -3)) end
206
- def seconds() self end
207
- def minutes() 60 * seconds end
208
- def hours() 60 * minutes end
209
- def days() 24 * hours end
210
- def weeks() 7 * days end
211
- def months() 30 * days end
212
- def years() 365 * days end
213
- def decades() 10 * years end
214
- # This causes RDoc to hurl:
215
- %w[
216
- microseconds milliseconds seconds minutes hours days weeks months years decades
217
- ].each{|m| alias_method m.chop, m}
218
- end
219
-
1
+ #!/usr/bin/env ruby
2
+
3
+ # :title:Runt -- Ruby Temporal Expressions
4
+ #
5
+ # == Runt -- Ruby Temporal Expressions
6
+ #
7
+ # The usage and design patterns expressed in this library are mostly...*uhm*..
8
+ # <em>entirely</em>..*cough*...based on a series of
9
+ # <tt>articles</tt>[http://www.martinfowler.com] by Martin Fowler.
10
+ #
11
+ # It highly recommended that anyone using Runt (or writing
12
+ # object-oriented software :) take a moment to peruse the wealth of useful info
13
+ # that Fowler has made publicly available:
14
+ #
15
+ # * An excellent introductory summation of temporal <tt>patterns</tt>[http://martinfowler.com/ap2/timeNarrative.html]
16
+ # * Recurring event <tt>pattern</tt>[http://martinfowler.com/apsupp/recurring.pdf]
17
+ #
18
+ # Also, for those of you (like me, for example) still chained in your cubicle and forced
19
+ # to write <tt>Java</tt>[http://java.sun.com] code, check out the original version of
20
+ # project called <tt>ChronicJ</tt>[http://chronicj.org].
21
+ #
22
+ # ---
23
+ # Author:: Matthew Lipper (mailto:mlipper@gmail.com)
24
+ # Copyright:: Copyright (c) 2004 Digital Clash, LLC
25
+ # License:: See LICENSE.txt
26
+ #
27
+ # = Warranty
28
+ #
29
+ # This software is provided "as is" and without any express or
30
+ # implied warranties, including, without limitation, the implied
31
+ # warranties of merchantibility and fitness for a particular
32
+ # purpose.
33
+
34
+ require 'time'
35
+ require 'date'
36
+ require 'date/format'
37
+ require "runt/dprecision"
38
+ require "runt/pdate"
39
+ require "runt/temporalexpression"
40
+ require "runt/schedule"
41
+ require "runt/daterange"
42
+ require "runt/sugar"
43
+ require "runt/expressionbuilder"
44
+
45
+ #
46
+ # The Runt module is the main namespace for all Runt modules and classes. Using
47
+ # require statements, it makes the entire Runt library available.It also
48
+ # defines some new constants and exposes some already defined in the standard
49
+ # library classes <tt>Date</tt> and <tt>DateTime</tt>.
50
+ #
51
+ # <b>See also</b> runt/sugar_rb which re-opens this module and adds
52
+ # some additional functionality
53
+ #
54
+ # <b>See also</b> date.rb
55
+ #
56
+ module Runt
57
+
58
+ class << self
59
+
60
+ def day_name(number)
61
+ Date::DAYNAMES[number]
62
+ end
63
+
64
+ def month_name(number)
65
+ Date::MONTHNAMES[number]
66
+ end
67
+
68
+ def format_time(date)
69
+ date.strftime('%I:%M%p')
70
+ end
71
+
72
+ def format_date(date)
73
+ date.ctime
74
+ end
75
+
76
+ #
77
+ # Cut and pasted from activesupport-1.2.5/lib/inflector.rb
78
+ #
79
+ def ordinalize(number)
80
+ if (number.to_i==-1)
81
+ 'last'
82
+ elsif (number.to_i==-2)
83
+ 'second to last'
84
+ elsif (11..13).include?(number.to_i % 100)
85
+ "#{number}th"
86
+ else
87
+ case number.to_i % 10
88
+ when 1: "#{number}st"
89
+ when 2: "#{number}nd"
90
+ when 3: "#{number}rd"
91
+ else "#{number}th"
92
+ end
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ #Yes it's true, I'm a big idiot!
99
+ Sunday = Date::DAYNAMES.index("Sunday")
100
+ Monday = Date::DAYNAMES.index("Monday")
101
+ Tuesday = Date::DAYNAMES.index("Tuesday")
102
+ Wednesday = Date::DAYNAMES.index("Wednesday")
103
+ Thursday = Date::DAYNAMES.index("Thursday")
104
+ Friday = Date::DAYNAMES.index("Friday")
105
+ Saturday = Date::DAYNAMES.index("Saturday")
106
+ Sun = Date::ABBR_DAYNAMES.index("Sun")
107
+ Mon = Date::ABBR_DAYNAMES.index("Mon")
108
+ Tue = Date::ABBR_DAYNAMES.index("Tue")
109
+ Wed = Date::ABBR_DAYNAMES.index("Wed")
110
+ Thu = Date::ABBR_DAYNAMES.index("Thu")
111
+ Fri = Date::ABBR_DAYNAMES.index("Fri")
112
+ Sat = Date::ABBR_DAYNAMES.index("Sat")
113
+ January = Date::MONTHNAMES.index("January")
114
+ February = Date::MONTHNAMES.index("February")
115
+ March = Date::MONTHNAMES.index("March")
116
+ April = Date::MONTHNAMES.index("April")
117
+ May = Date::MONTHNAMES.index("May")
118
+ June = Date::MONTHNAMES.index("June")
119
+ July = Date::MONTHNAMES.index("July")
120
+ August = Date::MONTHNAMES.index("August")
121
+ September = Date::MONTHNAMES.index("September")
122
+ October = Date::MONTHNAMES.index("October")
123
+ November = Date::MONTHNAMES.index("November")
124
+ December = Date::MONTHNAMES.index("December")
125
+ First = 1
126
+ Second = 2
127
+ Third = 3
128
+ Fourth = 4
129
+ Fifth = 5
130
+ Sixth = 6
131
+ Seventh = 7
132
+ Eighth = 8
133
+ Eigth = 8 # Will be removed in v0.9.0
134
+ Ninth = 9
135
+ Tenth = 10
136
+
137
+ private
138
+ class ApplyLast #:nodoc:
139
+ def initialize
140
+ @negate=Proc.new{|n| n*-1}
141
+ end
142
+ def [](arg)
143
+ @negate.call(arg)
144
+ end
145
+ end
146
+ LastProc = ApplyLast.new
147
+
148
+ public
149
+ Last = LastProc[First]
150
+ Last_of = LastProc[First]
151
+ Second_to_last = LastProc[Second]
152
+
153
+ end
154
+
155
+ #
156
+ # Add precision +Runt::DPrecision+ to standard library classes Date and DateTime
157
+ # (which is a subclass of Date). Also, add an inlcude? method for interoperability
158
+ # with +Runt::TExpr+ classes
159
+ #
160
+ class Date
161
+
162
+ include Runt
163
+
164
+ attr_accessor :date_precision
165
+
166
+ def include?(expr)
167
+ eql?(expr)
168
+ end
169
+
170
+ def date_precision
171
+ return @date_precision unless @date_precision.nil?
172
+ return Runt::DPrecision::DEFAULT
173
+ end
174
+ end
175
+
176
+ #
177
+ # Add the ability to use Time class
178
+ #
179
+ # Contributed by Paul Wright
180
+ #
181
+ class Time
182
+
183
+ include Runt
184
+
185
+ attr_accessor :date_precision
186
+ alias_method :old_initialize, :initialize
187
+ def initialize(*args)
188
+ if(args[0].instance_of?(Runt::DPrecision::Precision))
189
+ @precision=args.shift
190
+ else
191
+ @precision=Runt::DPrecision::DEFAULT
192
+ end
193
+ old_initialize(*args)
194
+ end
195
+
196
+ class << self
197
+ alias_method :old_parse, :parse
198
+ def parse(*args)
199
+ precision=Runt::DPrecision::DEFAULT
200
+ if(args[0].instance_of?(Runt::DPrecision::Precision))
201
+ precision=args.shift
202
+ end
203
+ _parse=old_parse(*args)
204
+ _parse.date_precision=precision
205
+ _parse
206
+ end
207
+ end
208
+
209
+ def date_precision
210
+ return @date_precision unless @date_precision.nil?
211
+ return Runt::DPrecision::DEFAULT
212
+ end
213
+ end
214
+
215
+ #
216
+ # Useful shortcuts!
217
+ #
218
+ # Contributed by Ara T. Howard who is pretty sure he got the idea from
219
+ # somewhere else. :-)
220
+ #
221
+ class Numeric #:nodoc:
222
+ def microseconds() Float(self * (10 ** -6)) end
223
+ def milliseconds() Float(self * (10 ** -3)) end
224
+ def seconds() self end
225
+ def minutes() 60 * seconds end
226
+ def hours() 60 * minutes end
227
+ def days() 24 * hours end
228
+ def weeks() 7 * days end
229
+ def months() 30 * days end
230
+ def years() 365 * days end
231
+ def decades() 10 * years end
232
+ # This causes RDoc to hurl:
233
+ %w[
234
+ microseconds milliseconds seconds minutes hours days weeks months years decades
235
+ ].each{|m| alias_method m.chop, m}
236
+ end
237
+