runt19 0.7.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|