runt19 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/History.txt +153 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +44 -0
- data/Manifest.txt +112 -0
- data/README.md +29 -0
- data/README.txt +106 -0
- data/Rakefile +2 -0
- data/TODO +13 -0
- data/examples/payment_report.rb +59 -0
- data/examples/payment_reporttest.rb +49 -0
- data/examples/reminder.rb +63 -0
- data/examples/schedule_tutorial.rb +59 -0
- data/examples/schedule_tutorialtest.rb +52 -0
- data/lib/runt.rb +249 -0
- data/lib/runt/daterange.rb +74 -0
- data/lib/runt/dprecision.rb +150 -0
- data/lib/runt/expressionbuilder.rb +65 -0
- data/lib/runt/pdate.rb +165 -0
- data/lib/runt/schedule.rb +88 -0
- data/lib/runt/sugar.rb +171 -0
- data/lib/runt/temporalexpression.rb +795 -0
- data/lib/runt/version.rb +3 -0
- data/lib/runt19.rb +1 -0
- data/runt19.gemspec +17 -0
- data/setup.rb +1331 -0
- data/site/blue-robot3.css +132 -0
- data/site/dcl-small.gif +0 -0
- data/site/index.html +72 -0
- data/site/logohover.png +0 -0
- data/site/runt-logo.gif +0 -0
- data/site/runt-logo.psd +0 -0
- data/test/aftertetest.rb +31 -0
- data/test/baseexpressiontest.rb +110 -0
- data/test/beforetetest.rb +31 -0
- data/test/collectiontest.rb +63 -0
- data/test/combinedexpressionstest.rb +158 -0
- data/test/daterangetest.rb +89 -0
- data/test/dayintervaltetest.rb +37 -0
- data/test/difftest.rb +37 -0
- data/test/dimonthtest.rb +59 -0
- data/test/diweektest.rb +32 -0
- data/test/dprecisiontest.rb +58 -0
- data/test/everytetest.rb +36 -0
- data/test/expressionbuildertest.rb +64 -0
- data/test/icalendartest.rb +1104 -0
- data/test/intersecttest.rb +34 -0
- data/test/pdatetest.rb +147 -0
- data/test/redaytest.rb +40 -0
- data/test/remonthtest.rb +37 -0
- data/test/reweektest.rb +51 -0
- data/test/reyeartest.rb +99 -0
- data/test/rspectest.rb +25 -0
- data/test/runttest.rb +98 -0
- data/test/scheduletest.rb +148 -0
- data/test/spectest.rb +36 -0
- data/test/sugartest.rb +104 -0
- data/test/temporalexpressiontest.rb +76 -0
- data/test/uniontest.rb +36 -0
- data/test/wimonthtest.rb +54 -0
- data/test/yeartetest.rb +22 -0
- metadata +137 -0
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= Runt - Ruby Temporal Expressions -- To Do List
|
2
|
+
|
3
|
+
Send suggestions, questions, threats, etc. for this list to Matt[mailto:mlipper@gmail.com]
|
4
|
+
|
5
|
+
=== To Do
|
6
|
+
|
7
|
+
* WIMonth#dates behaves unintuitively (see dates mixin tests)
|
8
|
+
|
9
|
+
* DayIntervalTE matches date multiples prior to start date (see tests)
|
10
|
+
|
11
|
+
* Better docs, examples, tutorials
|
12
|
+
|
13
|
+
* Laundry
|
@@ -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
|
+
|
@@ -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
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'runt'
|
5
|
+
require 'schedule_tutorial'
|
6
|
+
|
7
|
+
class ReminderTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
include Runt
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@schedule = Schedule.new
|
13
|
+
@north_event = Event.new("north side of the street will be ticketed")
|
14
|
+
north_expr = (DIWeek.new(Mon) | DIWeek.new(Wed) | DIWeek.new(Fri)) & REDay.new(8,00,11,00)
|
15
|
+
@schedule.add(@north_event, north_expr)
|
16
|
+
@south_event = Event.new("south side of the street will be ticketed")
|
17
|
+
south_expr = (DIWeek.new(Tue) | DIWeek.new(Thu)) & REDay.new(11,30,14,00)
|
18
|
+
@schedule.add(@south_event, south_expr)
|
19
|
+
@mail_server = MailServer.new
|
20
|
+
@reminder = Reminder.new(@schedule, @mail_server)
|
21
|
+
@saturday_at_10 = PDate.min(2007,11,24,10,0,0)
|
22
|
+
@monday_at_10 = PDate.min(2007,11,26,10,0,0)
|
23
|
+
@tuesday_at_noon = PDate.min(2007,11,27,12,0,0)
|
24
|
+
end
|
25
|
+
def test_initalize
|
26
|
+
assert_same @schedule, @reminder.schedule, "Expected #{@schedule} instead was #{@reminder.schedule}"
|
27
|
+
assert_same @mail_server, @reminder.mail_server, "Expected #{@mail_server} instead was #{@reminder.mail_server}"
|
28
|
+
end
|
29
|
+
def test_send
|
30
|
+
params = [@north_event, @south_event]
|
31
|
+
result = @reminder.send(params)
|
32
|
+
assert_email result, Reminder::TEXT + params.join(', ')
|
33
|
+
end
|
34
|
+
def test_check
|
35
|
+
assert_equal 1, @reminder.check(@monday_at_10).size, "Unexpected size #{@reminder.check(@monday_at_10).size} returned"
|
36
|
+
assert_same @north_event, @reminder.check(@monday_at_10)[0], "Expected Event #{@north_event}. Got #{@reminder.check(@monday_at_10)[0]}."
|
37
|
+
assert_equal 1, @reminder.check(@tuesday_at_noon).size, "Unexpected size #{@reminder.check(@tuesday_at_noon).size} returned"
|
38
|
+
assert_same @south_event, @reminder.check(@tuesday_at_noon)[0], "Expected Event #{@south_event}. Got #{@reminder.check(@tuesday_at_noon)[0]}."
|
39
|
+
assert @reminder.check(@saturday_at_10).empty?, "Expected empty Array. Got #{@reminder.check(@saturday_at_10)}"
|
40
|
+
end
|
41
|
+
def test_run
|
42
|
+
result = @reminder.run(@monday_at_10)
|
43
|
+
assert_email result, Reminder::TEXT + @north_event.to_s
|
44
|
+
end
|
45
|
+
def assert_email(result, text)
|
46
|
+
assert_equal Reminder::TO, result.to, "Unexpected value for 'to' field of Email Struct: #{result.to}"
|
47
|
+
assert_equal Reminder::FROM, result.from, "Unexpected value for 'from' field of Email Struct: #{result.from}"
|
48
|
+
assert_equal Reminder::SUBJECT, result.subject, "Unexpected value for 'subject' field of Email Struct: #{result.subject}"
|
49
|
+
assert_equal text, result.text, "Unexpected value for 'text' field of Email Struct: #{result.text}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
data/lib/runt.rb
ADDED
@@ -0,0 +1,249 @@
|
|
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 'yaml'
|
35
|
+
require 'time'
|
36
|
+
require 'date'
|
37
|
+
require 'date/format'
|
38
|
+
require "runt/version"
|
39
|
+
require "runt/dprecision"
|
40
|
+
require "runt/pdate"
|
41
|
+
require "runt/temporalexpression"
|
42
|
+
require "runt/schedule"
|
43
|
+
require "runt/daterange"
|
44
|
+
require "runt/sugar"
|
45
|
+
require "runt/expressionbuilder"
|
46
|
+
|
47
|
+
#
|
48
|
+
# The Runt module is the main namespace for all Runt modules and classes. Using
|
49
|
+
# require statements, it makes the entire Runt library available.It also
|
50
|
+
# defines some new constants and exposes some already defined in the standard
|
51
|
+
# library classes <tt>Date</tt> and <tt>DateTime</tt>.
|
52
|
+
#
|
53
|
+
# <b>See also</b> runt/sugar_rb which re-opens this module and adds
|
54
|
+
# some additional functionality
|
55
|
+
#
|
56
|
+
# <b>See also</b> date.rb
|
57
|
+
#
|
58
|
+
module Runt
|
59
|
+
class << self
|
60
|
+
|
61
|
+
def day_name(number)
|
62
|
+
Date::DAYNAMES[number]
|
63
|
+
end
|
64
|
+
|
65
|
+
def month_name(number)
|
66
|
+
Date::MONTHNAMES[number]
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_time(date)
|
70
|
+
date.strftime('%I:%M%p')
|
71
|
+
end
|
72
|
+
|
73
|
+
def format_date(date)
|
74
|
+
date.ctime
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Cut and pasted from activesupport-1.2.5/lib/inflector.rb
|
79
|
+
#
|
80
|
+
def ordinalize(number)
|
81
|
+
if (number.to_i==-1)
|
82
|
+
'last'
|
83
|
+
elsif (number.to_i==-2)
|
84
|
+
'second to last'
|
85
|
+
elsif (11..13).include?(number.to_i % 100)
|
86
|
+
"#{number}th"
|
87
|
+
else
|
88
|
+
case number.to_i%10
|
89
|
+
when 1 then "#{number}st"
|
90
|
+
when 2 then "#{number}nd"
|
91
|
+
when 3 then "#{number}rd"
|
92
|
+
else "#{number}th"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
#Yes it's true, I'm a big idiot!
|
100
|
+
Sunday = Date::DAYNAMES.index("Sunday")
|
101
|
+
Monday = Date::DAYNAMES.index("Monday")
|
102
|
+
Tuesday = Date::DAYNAMES.index("Tuesday")
|
103
|
+
Wednesday = Date::DAYNAMES.index("Wednesday")
|
104
|
+
Thursday = Date::DAYNAMES.index("Thursday")
|
105
|
+
Friday = Date::DAYNAMES.index("Friday")
|
106
|
+
Saturday = Date::DAYNAMES.index("Saturday")
|
107
|
+
Sun = Date::ABBR_DAYNAMES.index("Sun")
|
108
|
+
Mon = Date::ABBR_DAYNAMES.index("Mon")
|
109
|
+
Tue = Date::ABBR_DAYNAMES.index("Tue")
|
110
|
+
Wed = Date::ABBR_DAYNAMES.index("Wed")
|
111
|
+
Thu = Date::ABBR_DAYNAMES.index("Thu")
|
112
|
+
Fri = Date::ABBR_DAYNAMES.index("Fri")
|
113
|
+
Sat = Date::ABBR_DAYNAMES.index("Sat")
|
114
|
+
January = Date::MONTHNAMES.index("January")
|
115
|
+
February = Date::MONTHNAMES.index("February")
|
116
|
+
March = Date::MONTHNAMES.index("March")
|
117
|
+
April = Date::MONTHNAMES.index("April")
|
118
|
+
May = Date::MONTHNAMES.index("May")
|
119
|
+
June = Date::MONTHNAMES.index("June")
|
120
|
+
July = Date::MONTHNAMES.index("July")
|
121
|
+
August = Date::MONTHNAMES.index("August")
|
122
|
+
September = Date::MONTHNAMES.index("September")
|
123
|
+
October = Date::MONTHNAMES.index("October")
|
124
|
+
November = Date::MONTHNAMES.index("November")
|
125
|
+
December = Date::MONTHNAMES.index("December")
|
126
|
+
First = 1
|
127
|
+
Second = 2
|
128
|
+
Third = 3
|
129
|
+
Fourth = 4
|
130
|
+
Fifth = 5
|
131
|
+
Sixth = 6
|
132
|
+
Seventh = 7
|
133
|
+
Eighth = 8
|
134
|
+
Eigth = 8 # Will be removed in v0.9.0
|
135
|
+
Ninth = 9
|
136
|
+
Tenth = 10
|
137
|
+
|
138
|
+
private
|
139
|
+
class ApplyLast #:nodoc:
|
140
|
+
def initialize
|
141
|
+
@negate=Proc.new{|n| n*-1}
|
142
|
+
end
|
143
|
+
def [](arg)
|
144
|
+
@negate.call(arg)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
LastProc = ApplyLast.new
|
148
|
+
|
149
|
+
public
|
150
|
+
Last = LastProc[First]
|
151
|
+
Last_of = LastProc[First]
|
152
|
+
Second_to_last = LastProc[Second]
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Add precision +Runt::DPrecision+ to standard library classes Date and DateTime
|
158
|
+
# (which is a subclass of Date). Also, add an inlcude? method for interoperability
|
159
|
+
# with +Runt::TExpr+ classes
|
160
|
+
#
|
161
|
+
class Date
|
162
|
+
|
163
|
+
include Runt
|
164
|
+
|
165
|
+
attr_accessor :date_precision
|
166
|
+
alias_method :precision, :date_precision
|
167
|
+
def include?(expr)
|
168
|
+
eql?(expr)
|
169
|
+
end
|
170
|
+
|
171
|
+
def date_precision
|
172
|
+
return @date_precision unless @date_precision.nil?
|
173
|
+
return Runt::DPrecision::DEFAULT
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
# Add the ability to use Time class
|
179
|
+
#
|
180
|
+
# Contributed by Paul Wright
|
181
|
+
#
|
182
|
+
class Time
|
183
|
+
|
184
|
+
include Runt
|
185
|
+
|
186
|
+
attr_accessor :date_precision
|
187
|
+
alias_method :old_initialize, :initialize
|
188
|
+
alias_method :precision, :date_precision
|
189
|
+
|
190
|
+
def initialize(*args)
|
191
|
+
if(args[0].instance_of?(Runt::DPrecision::Precision))
|
192
|
+
@precision=args.shift
|
193
|
+
else
|
194
|
+
@precision=Runt::DPrecision::DEFAULT
|
195
|
+
end
|
196
|
+
old_initialize(*args)
|
197
|
+
end
|
198
|
+
|
199
|
+
alias :old_to_yaml :to_yaml
|
200
|
+
def to_yaml(options)
|
201
|
+
if self.instance_variables.empty?
|
202
|
+
self.old_to_yaml(options)
|
203
|
+
else
|
204
|
+
Time.old_parse(self.to_s).old_to_yaml(options)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class << self
|
209
|
+
alias_method :old_parse, :parse
|
210
|
+
def parse(*args)
|
211
|
+
precision=Runt::DPrecision::DEFAULT
|
212
|
+
if(args[0].instance_of?(Runt::DPrecision::Precision))
|
213
|
+
precision=args.shift
|
214
|
+
end
|
215
|
+
_parse=old_parse(*args)
|
216
|
+
_parse.date_precision=precision
|
217
|
+
_parse
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def date_precision
|
222
|
+
return @date_precision unless @date_precision.nil?
|
223
|
+
return Runt::DPrecision::DEFAULT
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Useful shortcuts!
|
229
|
+
#
|
230
|
+
# Contributed by Ara T. Howard who is pretty sure he got the idea from
|
231
|
+
# somewhere else. :-)
|
232
|
+
#
|
233
|
+
class Numeric #:nodoc:
|
234
|
+
def microseconds() Float(self * (10 ** -6)) end unless self.instance_methods.include?('microseconds')
|
235
|
+
def milliseconds() Float(self * (10 ** -3)) end unless self.instance_methods.include?('milliseconds')
|
236
|
+
def seconds() self end unless self.instance_methods.include?('seconds')
|
237
|
+
def minutes() 60 * seconds end unless self.instance_methods.include?('minutes')
|
238
|
+
def hours() 60 * minutes end unless self.instance_methods.include?('hours')
|
239
|
+
def days() 24 * hours end unless self.instance_methods.include?('days')
|
240
|
+
def weeks() 7 * days end unless self.instance_methods.include?('weeks')
|
241
|
+
def months() 30 * days end unless self.instance_methods.include?('months')
|
242
|
+
def years() 365 * days end unless self.instance_methods.include?('years')
|
243
|
+
def decades() 10 * years end unless self.instance_methods.include?('decades')
|
244
|
+
# This causes RDoc to hurl:
|
245
|
+
%w[
|
246
|
+
microseconds milliseconds seconds minutes hours days weeks months years decades
|
247
|
+
].each{|m| alias_method m.chop, m}
|
248
|
+
end
|
249
|
+
|