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.
Files changed (63) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/History.txt +153 -0
  4. data/LICENSE +22 -0
  5. data/LICENSE.txt +44 -0
  6. data/Manifest.txt +112 -0
  7. data/README.md +29 -0
  8. data/README.txt +106 -0
  9. data/Rakefile +2 -0
  10. data/TODO +13 -0
  11. data/examples/payment_report.rb +59 -0
  12. data/examples/payment_reporttest.rb +49 -0
  13. data/examples/reminder.rb +63 -0
  14. data/examples/schedule_tutorial.rb +59 -0
  15. data/examples/schedule_tutorialtest.rb +52 -0
  16. data/lib/runt.rb +249 -0
  17. data/lib/runt/daterange.rb +74 -0
  18. data/lib/runt/dprecision.rb +150 -0
  19. data/lib/runt/expressionbuilder.rb +65 -0
  20. data/lib/runt/pdate.rb +165 -0
  21. data/lib/runt/schedule.rb +88 -0
  22. data/lib/runt/sugar.rb +171 -0
  23. data/lib/runt/temporalexpression.rb +795 -0
  24. data/lib/runt/version.rb +3 -0
  25. data/lib/runt19.rb +1 -0
  26. data/runt19.gemspec +17 -0
  27. data/setup.rb +1331 -0
  28. data/site/blue-robot3.css +132 -0
  29. data/site/dcl-small.gif +0 -0
  30. data/site/index.html +72 -0
  31. data/site/logohover.png +0 -0
  32. data/site/runt-logo.gif +0 -0
  33. data/site/runt-logo.psd +0 -0
  34. data/test/aftertetest.rb +31 -0
  35. data/test/baseexpressiontest.rb +110 -0
  36. data/test/beforetetest.rb +31 -0
  37. data/test/collectiontest.rb +63 -0
  38. data/test/combinedexpressionstest.rb +158 -0
  39. data/test/daterangetest.rb +89 -0
  40. data/test/dayintervaltetest.rb +37 -0
  41. data/test/difftest.rb +37 -0
  42. data/test/dimonthtest.rb +59 -0
  43. data/test/diweektest.rb +32 -0
  44. data/test/dprecisiontest.rb +58 -0
  45. data/test/everytetest.rb +36 -0
  46. data/test/expressionbuildertest.rb +64 -0
  47. data/test/icalendartest.rb +1104 -0
  48. data/test/intersecttest.rb +34 -0
  49. data/test/pdatetest.rb +147 -0
  50. data/test/redaytest.rb +40 -0
  51. data/test/remonthtest.rb +37 -0
  52. data/test/reweektest.rb +51 -0
  53. data/test/reyeartest.rb +99 -0
  54. data/test/rspectest.rb +25 -0
  55. data/test/runttest.rb +98 -0
  56. data/test/scheduletest.rb +148 -0
  57. data/test/spectest.rb +36 -0
  58. data/test/sugartest.rb +104 -0
  59. data/test/temporalexpressiontest.rb +76 -0
  60. data/test/uniontest.rb +36 -0
  61. data/test/wimonthtest.rb +54 -0
  62. data/test/yeartetest.rb +22 -0
  63. metadata +137 -0
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'date'
4
+ require 'runt'
5
+
6
+
7
+ module Runt
8
+ # :title:DateRange
9
+ # == DateRange
10
+ #
11
+ #
12
+ # Based the <tt>range</tt>[http://martinfowler.com/ap2/range.html] pattern by Martin Fowler.
13
+ #
14
+ #
15
+ #
16
+ # Author:: Matthew Lipper
17
+ class DateRange < Range
18
+
19
+ include DPrecision
20
+
21
+ attr_reader :start_expr, :end_expr
22
+
23
+ def initialize(start_expr, end_expr,exclusive=false)
24
+ super(start_expr, end_expr,exclusive)
25
+ @start_expr, @end_expr = start_expr, end_expr
26
+ end
27
+
28
+ def include?(obj)
29
+ return super(obj.min) && super(obj.max) if obj.kind_of? Range
30
+ return super(obj)
31
+ end
32
+
33
+ def overlap?(obj)
34
+ return true if( member?(obj) || include?(obj.min) || include?(obj.max) )
35
+ return true if( obj.kind_of?(Range) && obj.include?(self) )
36
+ false
37
+ end
38
+
39
+ def empty?
40
+ return @start_expr >= @end_expr
41
+ end
42
+
43
+ def gap(obj)
44
+
45
+ return EMPTY if self.overlap? obj
46
+
47
+ lower=nil
48
+ higher=nil
49
+
50
+ if((self<=>obj)<0)
51
+ lower=self
52
+ higher=obj
53
+ else
54
+ lower=obj
55
+ higher=self
56
+ end
57
+
58
+ return DateRange.new((lower.end_expr+1),(higher.start_expr-1))
59
+ end
60
+
61
+ def <=>(other)
62
+ return @start_expr <=> other.start_expr if(@start_expr != other.start_expr)
63
+ return @end_expr <=> other.end_expr
64
+ end
65
+
66
+ def min; @start_expr end
67
+ def max; @end_expr end
68
+ def to_s; @start_expr.to_s + " " + @end_expr.to_s end
69
+
70
+
71
+ EMPTY = DateRange.new(PDate.day(2004,2,2),PDate.day(2004,2,1))
72
+
73
+ end
74
+ end
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'runt'
4
+ require 'date'
5
+
6
+ module Runt
7
+
8
+ # :title:DPrecision
9
+ # == DPrecision
10
+ # Module providing automatic precisioning of Date, DateTime, and PDate classes.
11
+ #
12
+ # Inspired by a <tt>pattern</tt>[http://martinfowler.com/ap2/timePoint.html] by Martin Fowler.
13
+ #
14
+ #
15
+ # Author:: Matthew Lipper
16
+ module DPrecision
17
+
18
+ def DPrecision.to_p(date,prec=DEFAULT)
19
+
20
+ case prec
21
+ when MIN then PDate.min(*DPrecision.explode(date,prec))
22
+ when DAY then PDate.day(*DPrecision.explode(date,prec))
23
+ when HOUR then PDate.hour(*DPrecision.explode(date,prec))
24
+ when WEEK then PDate.week(*DPrecision.explode(date,prec))
25
+ when MONTH then PDate.month(*DPrecision.explode(date,prec))
26
+ when YEAR then PDate.year(*DPrecision.explode(date,prec))
27
+ when SEC then PDate.sec(*DPrecision.explode(date,prec))
28
+ when MILLI then date #raise "Not implemented."
29
+ else PDate.default(*DPrecision.explode(date,prec))
30
+ end
31
+ end
32
+
33
+ def DPrecision.explode(date,prec)
34
+ result = [date.year,date.month,date.day]
35
+ if(date.respond_to?("hour"))
36
+ result << date.hour << date.min << date.sec
37
+ else
38
+ result << 0 << 0 << 0
39
+ end
40
+ result
41
+ end
42
+
43
+ #Simple value class for keeping track of precisioned dates
44
+ class Precision
45
+ include Comparable
46
+
47
+ attr_reader :precision
48
+ private_class_method :new
49
+
50
+ #Some constants w/arbitrary integer values used internally for comparisions
51
+ YEAR_PREC = 0
52
+ MONTH_PREC = 1
53
+ WEEK_PREC = 2
54
+ DAY_PREC = 3
55
+ HOUR_PREC = 4
56
+ MIN_PREC = 5
57
+ SEC_PREC = 6
58
+ MILLI_PREC = 7
59
+
60
+ #String values for display
61
+ LABEL = { YEAR_PREC => "YEAR",
62
+ MONTH_PREC => "MONTH",
63
+ WEEK_PREC => "WEEK",
64
+ DAY_PREC => "DAY",
65
+ HOUR_PREC => "HOUR",
66
+ MIN_PREC => "MINUTE",
67
+ SEC_PREC => "SECOND",
68
+ MILLI_PREC => "MILLISECOND"}
69
+
70
+ #Minimun values that precisioned fields get set to
71
+ FIELD_MIN = { YEAR_PREC => 1,
72
+ MONTH_PREC => 1,
73
+ WEEK_PREC => 1,
74
+ DAY_PREC => 1,
75
+ HOUR_PREC => 0,
76
+ MIN_PREC => 0,
77
+ SEC_PREC => 0,
78
+ MILLI_PREC => 0}
79
+
80
+ def Precision.year
81
+ new(YEAR_PREC)
82
+ end
83
+
84
+ def Precision.month
85
+ new(MONTH_PREC)
86
+ end
87
+
88
+ def Precision.week
89
+ new(WEEK_PREC)
90
+ end
91
+
92
+ def Precision.day
93
+ new(DAY_PREC)
94
+ end
95
+
96
+ def Precision.hour
97
+ new(HOUR_PREC)
98
+ end
99
+
100
+ def Precision.min
101
+ new(MIN_PREC)
102
+ end
103
+
104
+ def Precision.sec
105
+ new(SEC_PREC)
106
+ end
107
+
108
+ def Precision.millisec
109
+ new(MILLI_PREC)
110
+ end
111
+
112
+ def min_value()
113
+ FIELD_MIN[@precision]
114
+ end
115
+
116
+ def initialize(prec)
117
+ @precision = prec
118
+ end
119
+
120
+ def <=>(other)
121
+ self.precision <=> other.precision
122
+ end
123
+
124
+ def ===(other)
125
+ self.precision == other.precision
126
+ end
127
+
128
+ def to_s
129
+ "DPrecision::#{self.label}"
130
+ end
131
+
132
+ def label
133
+ LABEL[@precision]
134
+ end
135
+ end
136
+
137
+ #Pseudo Singletons:
138
+ YEAR = Precision.year
139
+ MONTH = Precision.month
140
+ WEEK = Precision.week
141
+ DAY = Precision.day
142
+ HOUR = Precision.hour
143
+ MIN = Precision.min
144
+ SEC = Precision.sec
145
+ MILLI = Precision.millisec
146
+ DEFAULT=MIN
147
+
148
+ end
149
+
150
+ end
@@ -0,0 +1,65 @@
1
+ require 'runt'
2
+
3
+ # Convenience class for building temporal expressions in a more
4
+ # human-friendly way. Used in conjunction with shortcuts defined in the
5
+ # sugar.rb file, this allows one to create expressions like the following:
6
+ #
7
+ # b = ExpressionBuilder.new
8
+ # expr = b.define do
9
+ # occurs daily_8_30am_to_9_45am
10
+ # on tuesday
11
+ # possibly wednesday
12
+ # end
13
+ #
14
+ # This equivalent to:
15
+ #
16
+ # expr = REDay.new(8,30,9,45) & DIWeek.new(Tuesday) | DIWeek.new(Wednesday)
17
+ #
18
+ # ExpressionBuilder creates expressions by evaluating a block passed to the
19
+ # :define method. From inside the block, methods :occurs, :on, :every, :possibly,
20
+ # and :maybe can be called with a temporal expression which will be added to
21
+ # a composite expression as follows:
22
+ #
23
+ # * <b>:on</b> - creates an "and" (&)
24
+ # * <b>:possibly</b> - creates an "or" (|)
25
+ # * <b>:except</b> - creates a "not" (-)
26
+ # * <b>:every</b> - alias for :on method
27
+ # * <b>:occurs</b> - alias for :on method
28
+ # * <b>:maybe</b> - alias for :possibly method
29
+ #
30
+ class ExpressionBuilder
31
+
32
+ include Runt
33
+
34
+ attr_accessor :ctx
35
+
36
+ def initialize
37
+ @ctx = nil
38
+ end
39
+
40
+ def define(&block)
41
+ instance_eval(&block)
42
+ end
43
+
44
+ def on(expr)
45
+ add(expr, :&)
46
+ end
47
+
48
+ def add(expr, op)
49
+ @ctx ||= expr
50
+ @ctx = @ctx.send(op, expr) unless @ctx == expr
51
+ @ctx # explicit return, previous line may not execute
52
+ end
53
+
54
+ def except(expr)
55
+ add(expr, :-)
56
+ end
57
+
58
+ def possibly(expr)
59
+ add(expr, :|)
60
+ end
61
+
62
+ alias_method :every, :on
63
+ alias_method :occurs, :on
64
+ alias_method :maybe, :possibly
65
+ end
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'date'
4
+ require 'runt'
5
+
6
+
7
+ module Runt
8
+
9
+
10
+ # :title:PDate
11
+ # == PDate
12
+ # Date and DateTime with explicit precision.
13
+ #
14
+ # Based the <tt>pattern</tt>[http://martinfowler.com/ap2/timePoint.html] by Martin Fowler.
15
+ #
16
+ #
17
+ # Author:: Matthew Lipper
18
+ class PDate < DateTime
19
+ include DPrecision
20
+
21
+ attr_accessor :date_precision
22
+
23
+ class << self
24
+ alias_method :old_civil, :civil
25
+
26
+ def civil(*args)
27
+ precision=nil
28
+ if(args[0].instance_of?(DPrecision::Precision))
29
+ precision = args.shift
30
+ else
31
+ return PDate::sec(*args)
32
+ end
33
+ _civil = old_civil(*args)
34
+ _civil.date_precision = precision
35
+ _civil
36
+ end
37
+ end
38
+
39
+ class << self; alias_method :new, :civil end
40
+
41
+ def include?(expr)
42
+ eql?(expr)
43
+ end
44
+
45
+ def + (n)
46
+ raise TypeError, 'expected numeric' unless n.kind_of?(Numeric)
47
+ case @date_precision
48
+ when YEAR then
49
+ return DPrecision::to_p(PDate::civil(year+n,month,day),@date_precision)
50
+ when MONTH then
51
+ current_date = self.class.to_date(self)
52
+ return DPrecision::to_p((current_date>>n),@date_precision)
53
+ when WEEK then
54
+ return new_self_plus(n*7)
55
+ when DAY then
56
+ return new_self_plus(n)
57
+ when HOUR then
58
+ return new_self_plus(n){ |n| n = (n*(1.to_r/24) ) }
59
+ when MIN then
60
+ return new_self_plus(n){ |n| n = (n*(1.to_r/1440) ) }
61
+ when SEC then
62
+ return new_self_plus(n){ |n| n = (n*(1.to_r/86400) ) }
63
+ when MILLI then
64
+ return self
65
+ end
66
+ end
67
+
68
+ def - (x)
69
+ case x
70
+ when Numeric then
71
+ return self+(-x)
72
+ #FIXME!!
73
+ when Date; return @ajd - x.ajd
74
+ end
75
+ raise TypeError, 'expected numeric or date'
76
+ end
77
+
78
+ def <=> (other)
79
+ result = nil
80
+ if(other.respond_to?("date_precision") && other.date_precision>@date_precision)
81
+ result = super(DPrecision::to_p(other,@date_precision))
82
+ else
83
+ result = super(other)
84
+ end
85
+ #puts "#{self.to_s}<=>#{other.to_s} => #{result}" if $DEBUG
86
+ result
87
+ end
88
+
89
+ def new_self_plus(n)
90
+ if(block_given?)
91
+ n=yield(n)
92
+ end
93
+ return DPrecision::to_p(self.class.new!(@ajd + n, @of, @sg),@date_precision)
94
+ end
95
+
96
+ def PDate.to_date(pdate)
97
+ if( pdate.date_precision > DPrecision::DAY) then
98
+ DateTime.new(pdate.year,pdate.month,pdate.day,pdate.hour,pdate.min,pdate.sec)
99
+ end
100
+ return Date.new(pdate.year,pdate.month,pdate.day)
101
+ end
102
+
103
+ def PDate.year(yr,*ignored)
104
+ PDate.civil(YEAR, yr, MONTH.min_value, DAY.min_value )
105
+ end
106
+
107
+ def PDate.month( yr,mon,*ignored )
108
+ PDate.civil(MONTH, yr, mon, DAY.min_value )
109
+ end
110
+
111
+ def PDate.week( yr,mon,day,*ignored )
112
+ #LJK: need to calculate which week this day implies,
113
+ #and then move the day back to the *first* day in that week;
114
+ #note that since rfc2445 defaults to weekstart=monday, I'm
115
+ #going to use commercial day-of-week
116
+ raw = PDate.day(yr, mon, day)
117
+ cooked = PDate.commercial(raw.cwyear, raw.cweek, 1)
118
+ PDate.civil(WEEK, cooked.year, cooked.month, cooked.day)
119
+ end
120
+
121
+ def PDate.day( yr,mon,day,*ignored )
122
+ PDate.civil(DAY, yr, mon, day )
123
+ end
124
+
125
+ def PDate.hour( yr,mon,day,hr=HOUR.min_value,*ignored )
126
+ PDate.civil(HOUR, yr, mon, day,hr,MIN.min_value, SEC.min_value)
127
+ end
128
+
129
+ def PDate.min( yr,mon,day,hr=HOUR.min_value,min=MIN.min_value,*ignored )
130
+ PDate.civil(MIN, yr, mon, day,hr,min, SEC.min_value)
131
+ end
132
+
133
+ def PDate.sec( yr,mon,day,hr=HOUR.min_value,min=MIN.min_value,sec=SEC.min_value,*ignored )
134
+ PDate.civil(SEC, yr, mon, day,hr,min, sec)
135
+ end
136
+
137
+ def PDate.millisecond( yr,mon,day,hr,min,sec,ms,*ignored )
138
+ PDate.civil(SEC, yr, mon, day,hr,min, sec, ms, *ignored)
139
+ #raise "Not implemented yet."
140
+ end
141
+
142
+ def PDate.default(*args)
143
+ PDate.civil(DEFAULT, *args)
144
+ end
145
+
146
+ #
147
+ # Custom dump which preserves DatePrecision
148
+ #
149
+ # Author:: Jodi Showers
150
+ #
151
+ def marshal_dump
152
+ [date_precision, ajd, start, offset]
153
+ end
154
+
155
+ #
156
+ # Custom load which preserves DatePrecision
157
+ #
158
+ # Author:: Jodi Showers
159
+ #
160
+ def marshal_load(dumped_obj)
161
+ @date_precision, @ajd, @sg, @of=dumped_obj
162
+ end
163
+
164
+ end
165
+ end