recurrence 0.1.1 → 0.1.2

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.
@@ -1,78 +1,9 @@
1
- class Recurrence::Event
2
- CARDINALS = %w(first second third fourth fifth)
3
- DAYS = %w(sunday monday tuesday wednesday thursday friday saturday)
4
-
5
- attr_accessor :start_date
6
-
7
- def initialize(options={})
8
- every, options = nil, every if every.is_a?(Hash)
9
-
10
- @options = options
11
- @date = options[:starts]
12
- @finished = false
13
-
14
- validate
15
- raise ArgumentError, 'interval should be greater than zero' if @options[:interval] <= 0
16
-
17
- prepare!
1
+ class Recurrence
2
+ module Event
3
+ autoload :Base, "recurrence/event/base"
4
+ autoload :Daily, "recurrence/event/daily"
5
+ autoload :Monthly, "recurrence/event/monthly"
6
+ autoload :Weekly, "recurrence/event/weekly"
7
+ autoload :Yearly, "recurrence/event/yearly"
18
8
  end
19
-
20
- def next!
21
- return nil if finished?
22
- return @date = @start_date if @start_date && @date.nil?
23
-
24
- @date = next_in_recurrence
25
-
26
- @finished, @date = true, nil if @date > @options[:until]
27
- @date
28
- end
29
-
30
- def next
31
- return nil if finished?
32
- @date || @start_date
33
- end
34
-
35
- def reset!
36
- @date = nil
37
- end
38
-
39
- def finished?
40
- @finished
41
- end
42
-
43
- private
44
-
45
- def initialized?
46
- !!@start_date
47
- end
48
-
49
- def prepare!
50
- @start_date = next!
51
- @date = nil
52
- end
53
-
54
- def validate
55
- # Inject custom validations
56
- end
57
-
58
- # Common validation for inherited classes.
59
- #
60
- def valid_month_day?(day) #:nodoc:
61
- raise ArgumentError, "invalid day #{day}" unless (1..31).include?(day)
62
- end
63
-
64
- # Check if the given key has a valid weekday (0 upto 6) or a valid weekday
65
- # name (defined in the DAYS constant). If a weekday name (String) is given,
66
- # convert it to a weekday (Integer).
67
- #
68
- def valid_weekday_or_weekday_name?(value)
69
- if value.kind_of?(Numeric)
70
- raise ArgumentError, "invalid day #{value}" unless (0..6).include?(value)
71
- value
72
- else
73
- raise ArgumentError, "invalid weekday #{value}" unless DAYS.include?(value.to_s)
74
- DAYS.index(value.to_s)
75
- end
76
- end
77
-
78
9
  end
@@ -0,0 +1,80 @@
1
+ class Recurrence
2
+ module Event
3
+ class Base
4
+ CARDINALS = %w(first second third fourth fifth)
5
+ DAYS = %w(sunday monday tuesday wednesday thursday friday saturday)
6
+
7
+ attr_accessor :start_date
8
+
9
+ def initialize(options={})
10
+ every, options = nil, every if every.is_a?(Hash)
11
+
12
+ @options = options
13
+ @date = options[:starts]
14
+ @finished = false
15
+
16
+ validate
17
+ raise ArgumentError, "interval should be greater than zero" if @options[:interval] <= 0
18
+
19
+ prepare!
20
+ end
21
+
22
+ def next!
23
+ return nil if finished?
24
+ return @date = @start_date if @start_date && @date.nil?
25
+
26
+ @date = next_in_recurrence
27
+
28
+ @finished, @date = true, nil if @date > @options[:until]
29
+ @date
30
+ end
31
+
32
+ def next
33
+ return nil if finished?
34
+ @date || @start_date
35
+ end
36
+
37
+ def reset!
38
+ @date = nil
39
+ end
40
+
41
+ def finished?
42
+ @finished
43
+ end
44
+
45
+ private
46
+ def initialized?
47
+ !!@start_date
48
+ end
49
+
50
+ def prepare!
51
+ @start_date = next!
52
+ @date = nil
53
+ end
54
+
55
+ def validate
56
+ # Inject custom validations
57
+ end
58
+
59
+ # Common validation for inherited classes.
60
+ #
61
+ def valid_month_day?(day) #:nodoc:
62
+ raise ArgumentError, "invalid day #{day}" unless (1..31).include?(day)
63
+ end
64
+
65
+ # Check if the given key has a valid weekday (0 upto 6) or a valid weekday
66
+ # name (defined in the DAYS constant). If a weekday name (String) is given,
67
+ # convert it to a weekday (Integer).
68
+ #
69
+ def valid_weekday_or_weekday_name?(value)
70
+ if value.kind_of?(Numeric)
71
+ raise ArgumentError, "invalid day #{value}" unless (0..6).include?(value)
72
+ value
73
+ else
74
+ raise ArgumentError, "invalid weekday #{value}" unless DAYS.include?(value.to_s)
75
+ DAYS.index(value.to_s)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,10 +1,12 @@
1
- class Recurrence::Event::Daily < Recurrence::Event
2
-
3
- protected
4
- def next_in_recurrence
5
- date = @date.to_date
6
- date += @options[:interval] if initialized?
7
- date
1
+ class Recurrence
2
+ module Event
3
+ class Daily < Base
4
+ protected
5
+ def next_in_recurrence
6
+ date = @date.to_date
7
+ date += @options[:interval] if initialized?
8
+ date
9
+ end
8
10
  end
9
-
11
+ end
10
12
  end
@@ -1,102 +1,103 @@
1
- class Recurrence::Event::Monthly < Recurrence::Event
2
- INTERVALS = {
3
- :monthly => 1,
4
- :bimonthly => 2,
5
- :quarterly => 3,
6
- :semesterly => 6
7
- }
8
-
9
- protected
10
-
11
- def validate
12
- if @options.key?(:weekday)
13
-
14
- # Allow :on => :last, :weekday => :thursday contruction.
15
- if @options[:on].to_s == 'last'
16
- @options[:on] = 5
17
- elsif @options[:on].kind_of?(Numeric)
18
- valid_week?(@options[:on])
1
+ class Recurrence
2
+ module Event
3
+ class Monthly < Base
4
+ INTERVALS = {
5
+ :monthly => 1,
6
+ :bimonthly => 2,
7
+ :quarterly => 3,
8
+ :semesterly => 6
9
+ }
10
+
11
+ protected
12
+ def validate
13
+ if @options.key?(:weekday)
14
+
15
+ # Allow :on => :last, :weekday => :thursday contruction.
16
+ if @options[:on].to_s == "last"
17
+ @options[:on] = 5
18
+ elsif @options[:on].kind_of?(Numeric)
19
+ valid_week?(@options[:on])
20
+ else
21
+ valid_cardinal?(@options[:on])
22
+ @options[:on] = CARDINALS.index(@options[:on].to_s) + 1
23
+ end
24
+
25
+ @options[:weekday] = valid_weekday_or_weekday_name?(@options[:weekday])
19
26
  else
20
- valid_cardinal?(@options[:on])
21
- @options[:on] = CARDINALS.index(@options[:on].to_s) + 1
27
+ valid_month_day?(@options[:on])
22
28
  end
23
29
 
24
- @options[:weekday] = valid_weekday_or_weekday_name?(@options[:weekday])
25
- else
26
- valid_month_day?(@options[:on])
27
- end
28
-
29
- if @options[:interval].is_a?(Symbol)
30
- valid_interval?(@options[:interval])
31
- @options[:interval] = INTERVALS[@options[:interval]]
30
+ if @options[:interval].is_a?(Symbol)
31
+ valid_interval?(@options[:interval])
32
+ @options[:interval] = INTERVALS[@options[:interval]]
33
+ end
32
34
  end
33
- end
34
35
 
35
- def next_in_recurrence
36
- return next_month if self.respond_to?(:next_month)
37
- type = @options.key?(:weekday) ? :weekday : :monthday
38
-
39
- class_eval <<-METHOD
40
- def next_month
41
- if initialized?
42
- advance_to_month_by_#{type}(@date)
43
- else
44
- new_date = advance_to_month_by_#{type}(@date, 0)
45
- new_date = advance_to_month_by_#{type}(new_date) if @date > new_date
46
- new_date
36
+ def next_in_recurrence
37
+ return next_month if self.respond_to?(:next_month)
38
+ type = @options.key?(:weekday) ? :weekday : :monthday
39
+
40
+ class_eval <<-METHOD
41
+ def next_month
42
+ if initialized?
43
+ advance_to_month_by_#{type}(@date)
44
+ else
45
+ new_date = advance_to_month_by_#{type}(@date, 0)
46
+ new_date = advance_to_month_by_#{type}(new_date) if @date > new_date
47
+ new_date
48
+ end
47
49
  end
48
- end
49
- METHOD
50
+ METHOD
50
51
 
51
- next_month
52
- end
52
+ next_month
53
+ end
53
54
 
54
- def advance_to_month_by_monthday(date, interval=@options[:interval])
55
- # Have a raw month from 0 to 11 interval
56
- raw_month = date.month + interval - 1
55
+ def advance_to_month_by_monthday(date, interval=@options[:interval])
56
+ # Have a raw month from 0 to 11 interval
57
+ raw_month = date.month + interval - 1
57
58
 
58
- next_year = date.year + raw_month / 12
59
- next_month = (raw_month % 12) + 1 # change back to ruby interval
60
- next_day = [ @options[:on], Time.days_in_month(next_month, next_year) ].min
59
+ next_year = date.year + raw_month / 12
60
+ next_month = (raw_month % 12) + 1 # change back to ruby interval
61
+ next_day = [ @options[:on], Time.days_in_month(next_month, next_year) ].min
61
62
 
62
- Date.new(next_year, next_month, next_day)
63
- end
64
-
65
- def advance_to_month_by_weekday(date, interval=@options[:interval])
66
- raw_month = date.month + interval - 1
67
- next_year = date.year + raw_month / 12
68
- next_month = (raw_month % 12) + 1 # change back to ruby interval
69
- date = Date.new(next_year, next_month, 1)
63
+ Date.new(next_year, next_month, next_day)
64
+ end
70
65
 
71
- weekday, month = @options[:weekday], date.month
66
+ def advance_to_month_by_weekday(date, interval=@options[:interval])
67
+ raw_month = date.month + interval - 1
68
+ next_year = date.year + raw_month / 12
69
+ next_month = (raw_month % 12) + 1 # change back to ruby interval
70
+ date = Date.new(next_year, next_month, 1)
72
71
 
73
- # Adjust week day
74
- to_add = weekday - date.wday
75
- to_add += 7 if to_add < 0
76
- to_add += (@options[:on] - 1) * 7
77
- date += to_add
72
+ weekday, month = @options[:weekday], date.month
78
73
 
79
- # Go to the previous month if we lost it
80
- if date.month != month
81
- weeks = (date.day - 1) / 7 + 1
82
- date -= weeks * 7
83
- end
74
+ # Adjust week day
75
+ to_add = weekday - date.wday
76
+ to_add += 7 if to_add < 0
77
+ to_add += (@options[:on] - 1) * 7
78
+ date += to_add
84
79
 
85
- date
86
- end
80
+ # Go to the previous month if we lost it
81
+ if date.month != month
82
+ weeks = (date.day - 1) / 7 + 1
83
+ date -= weeks * 7
84
+ end
87
85
 
88
- private
86
+ date
87
+ end
89
88
 
90
- def valid_cardinal?(cardinal) #:nodoc:
91
- raise ArgumentError, "invalid cardinal #{cardinal}" unless CARDINALS.include?(cardinal.to_s)
92
- end
89
+ private
90
+ def valid_cardinal?(cardinal) #:nodoc:
91
+ raise ArgumentError, "invalid cardinal #{cardinal}" unless CARDINALS.include?(cardinal.to_s)
92
+ end
93
93
 
94
- def valid_interval?(interval) #:nodoc:
95
- raise ArgumentError, "invalid cardinal #{interval}" unless INTERVALS.key?(interval)
96
- end
94
+ def valid_interval?(interval) #:nodoc:
95
+ raise ArgumentError, "invalid cardinal #{interval}" unless INTERVALS.key?(interval)
96
+ end
97
97
 
98
- def valid_week?(week) #:nodoc:
99
- raise ArgumentError, "invalid week #{week}" unless (1..5).include?(week)
98
+ def valid_week?(week) #:nodoc:
99
+ raise ArgumentError, "invalid week #{week}" unless (1..5).include?(week)
100
+ end
100
101
  end
101
-
102
+ end
102
103
  end
@@ -1,27 +1,28 @@
1
- class Recurrence::Event::Weekly < Recurrence::Event
1
+ class Recurrence
2
+ module Event
3
+ class Weekly < Base
4
+ protected
5
+ def validate
6
+ @options[:on] = Array.wrap(@options[:on]).inject([]) do |days, value|
7
+ days << valid_weekday_or_weekday_name?(value)
8
+ end
2
9
 
3
- protected
4
-
5
- def validate
6
- @options[:on] = Array.wrap(@options[:on]).inject([]) do |days, value|
7
- days << valid_weekday_or_weekday_name?(value)
10
+ @options[:on].sort!
8
11
  end
9
12
 
10
- @options[:on].sort!
11
- end
13
+ def next_in_recurrence
14
+ return @date if !initialized? && @options[:on].include?(@date.wday)
12
15
 
13
- def next_in_recurrence
14
- return @date if !initialized? && @options[:on].include?(@date.wday)
16
+ if next_day = @options[:on].find { |day| day > @date.wday }
17
+ to_add = next_day - @date.wday
18
+ else
19
+ to_add = (7 - @date.wday) # Move to next week
20
+ to_add += (@options[:interval] - 1) * 7 # Add extra intervals
21
+ to_add += @options[:on].first # Go to first required day
22
+ end
15
23
 
16
- if next_day = @options[:on].find { |day| day > @date.wday }
17
- to_add = next_day - @date.wday
18
- else
19
- to_add = (7 - @date.wday) # Move to next week
20
- to_add += (@options[:interval] - 1) * 7 # Add extra intervals
21
- to_add += @options[:on].first # Go to first required day
24
+ @date.to_date + to_add
22
25
  end
23
-
24
- @date.to_date + to_add
25
26
  end
26
-
27
+ end
27
28
  end
@@ -1,58 +1,59 @@
1
- class Recurrence::Event::Yearly < Recurrence::Event
2
- MONTHS = {
3
- "jan" => 1, "january" => 1,
4
- "feb" => 2, "february" => 2,
5
- "mar" => 3, "march" => 3,
6
- "apr" => 4, "april" => 4,
7
- "may" => 5,
8
- "jun" => 6, "june" => 6,
9
- "jul" => 7, "july" => 7,
10
- "aug" => 8, "august" => 8,
11
- "sep" => 9, "september" => 9,
12
- "oct" => 10, "october" => 10,
13
- "nov" => 11, "november" => 11,
14
- "dec" => 12, "december" => 12
15
- }
16
-
17
- protected
18
-
19
- def validate
20
- valid_month_day?(@options[:on].last)
21
-
22
- if @options[:on].first.kind_of?(Numeric)
23
- valid_month?(@options[:on].first)
24
- else
25
- valid_month_name?(@options[:on].first)
26
- @options[:on] = [ MONTHS[@options[:on].first.to_s], @options.last ]
1
+ class Recurrence
2
+ module Event
3
+ class Yearly < Base
4
+ MONTHS = {
5
+ "jan" => 1, "january" => 1,
6
+ "feb" => 2, "february" => 2,
7
+ "mar" => 3, "march" => 3,
8
+ "apr" => 4, "april" => 4,
9
+ "may" => 5,
10
+ "jun" => 6, "june" => 6,
11
+ "jul" => 7, "july" => 7,
12
+ "aug" => 8, "august" => 8,
13
+ "sep" => 9, "september" => 9,
14
+ "oct" => 10, "october" => 10,
15
+ "nov" => 11, "november" => 11,
16
+ "dec" => 12, "december" => 12
17
+ }
18
+
19
+ protected
20
+ def validate
21
+ valid_month_day?(@options[:on].last)
22
+
23
+ if @options[:on].first.kind_of?(Numeric)
24
+ valid_month?(@options[:on].first)
25
+ else
26
+ valid_month_name?(@options[:on].first)
27
+ @options[:on] = [ MONTHS[@options[:on].first.to_s], @options.last ]
28
+ end
27
29
  end
28
- end
29
30
 
30
- def next_in_recurrence
31
- if initialized?
32
- advance_to_year(@date)
33
- else
34
- new_date = advance_to_year(@date, 0)
35
- new_date = advance_to_year(new_date) if @date > new_date
36
- new_date
31
+ def next_in_recurrence
32
+ if initialized?
33
+ advance_to_year(@date)
34
+ else
35
+ new_date = advance_to_year(@date, 0)
36
+ new_date = advance_to_year(new_date) if @date > new_date
37
+ new_date
38
+ end
37
39
  end
38
- end
39
-
40
- def advance_to_year(date, interval=@options[:interval])
41
- next_year = date.year + interval
42
- next_month = @options[:on].first
43
- next_day = [ @options[:on].last, Time.days_in_month(next_month, next_year) ].min
44
40
 
45
- Date.new(next_year, next_month, next_day)
46
- end
41
+ def advance_to_year(date, interval=@options[:interval])
42
+ next_year = date.year + interval
43
+ next_month = @options[:on].first
44
+ next_day = [ @options[:on].last, Time.days_in_month(next_month, next_year) ].min
47
45
 
48
- private
46
+ Date.new(next_year, next_month, next_day)
47
+ end
49
48
 
50
- def valid_month?(month) #:nodoc:
51
- raise ArgumentError, "invalid month #{month}" unless (1..12).include?(month)
52
- end
49
+ private
50
+ def valid_month?(month) #:nodoc:
51
+ raise ArgumentError, "invalid month #{month}" unless (1..12).include?(month)
52
+ end
53
53
 
54
- def valid_month_name?(month) #:nodoc:
55
- raise ArgumentError, "invalid month #{month}" unless MONTHS.keys.include?(month.to_s)
54
+ def valid_month_name?(month) #:nodoc:
55
+ raise ArgumentError, "invalid month #{month}" unless MONTHS.keys.include?(month.to_s)
56
+ end
56
57
  end
57
-
58
+ end
58
59
  end