runt19 0.7.6

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