recurrence 0.1.2 → 0.1.3

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/History.txt CHANGED
@@ -52,3 +52,7 @@
52
52
 
53
53
  * Refactor, refactor, refactor.
54
54
  * Using Recurrence as Rails plugin is now deprecated; install it as a gem instead.
55
+
56
+ == 0.1.3 2010-09-14
57
+
58
+ * Moved Recurrence class to SimpleIdeias namespace; see troubleshooting section for instructions.
data/README.markdown CHANGED
@@ -25,6 +25,7 @@ USAGE:
25
25
  # Daily
26
26
  r = Recurrence.new(:every => :day)
27
27
  r = Recurrence.new(:every => :day, :interval => 9)
28
+ r = Recurrence.daily(options = {})
28
29
 
29
30
  # Weekly
30
31
  r = Recurrence.new(:every => :week, :on => 5)
@@ -32,6 +33,7 @@ USAGE:
32
33
  r = Recurrence.new(:every => :week, :on => [:monday, :friday])
33
34
  r = Recurrence.new(:every => :week, :on => [:monday, :wednesday, :friday])
34
35
  r = Recurrence.new(:every => :week, :on => :friday, :interval => 2)
36
+ r = Recurrence.weekly(:on => :thursday)
35
37
 
36
38
  # Monthly by month day
37
39
  r = Recurrence.new(:every => :month, :on => 15)
@@ -39,6 +41,7 @@ USAGE:
39
41
  r = Recurrence.new(:every => :month, :on => 7, :interval => 2)
40
42
  r = Recurrence.new(:every => :month, :on => 7, :interval => :monthly)
41
43
  r = Recurrence.new(:every => :month, :on => 7, :interval => :bimonthly)
44
+ r = Recurrence.monthly(:on => 31)
42
45
 
43
46
  # Monthly by week day
44
47
  r = Recurrence.new(:every => :month, :on => :first, :weekday => :sunday)
@@ -53,6 +56,7 @@ USAGE:
53
56
  r = Recurrence.new(:every => :year, :on => [10, 31], :interval => 3)
54
57
  r = Recurrence.new(:every => :year, :on => [:jan, 31])
55
58
  r = Recurrence.new(:every => :year, :on => [:january, 31])
59
+ r = Recurrence.yearly(:on => [:january, 31])
56
60
 
57
61
  # Limit recurrence
58
62
  # :starts defaults to Date.today
@@ -80,6 +84,25 @@ USAGE:
80
84
  r.next # => Keep the original date object
81
85
  r.next! # => Change the internal date object to the next available date
82
86
 
87
+ TROUBLESHOOTING
88
+ ---------------
89
+
90
+ If you're having problems because already have a class/module called Recurrence that is conflicting with this gem, you can require the namespace and inherit from the `SimplesIdeias::Recurrence` class.
91
+
92
+ require "recurrence/namespace"
93
+
94
+ class RecurrentEvent < SimplesIdeias::Recurrence
95
+ end
96
+
97
+ r = RecurrentEvent.new(:every => :day)
98
+
99
+ If you're using Rails/Bundler or something like that, remember to override the `:require` option.
100
+
101
+ # Gemfile
102
+ source :rubygems
103
+
104
+ gem "recurrence", :require => "recurrence/namespace"
105
+
83
106
  MAINTAINER
84
107
  ----------
85
108
 
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/lib"
2
+
1
3
  require "rspec/core/rake_task"
2
- require "lib/recurrence/version"
4
+ require "recurrence"
3
5
 
4
6
  RSpec::Core::RakeTask.new
5
7
 
data/lib/recurrence.rb CHANGED
@@ -1,141 +1,4 @@
1
- require "date"
2
- require "active_support/all"
1
+ require "recurrence/namespace"
3
2
 
4
- class Recurrence
5
- autoload :Event, "recurrence/event"
6
-
7
- FREQUENCY = %w(day week month year)
8
-
9
- attr_reader :event
10
-
11
- def self.default_until_date
12
- @default_until_date ||= Date.new(2037, 12, 31)
13
- end
14
-
15
- def self.default_until_date=(date)
16
- @default_until_date = if date.is_a?(String)
17
- Date.parse(date)
18
- else
19
- date
20
- end
21
- end
22
-
23
- def self.daily(options = {})
24
- options[:every] = :day
25
- new(options)
26
- end
27
-
28
- def self.weekly(options = {})
29
- options[:every] = :week
30
- new(options)
31
- end
32
-
33
- def self.monthly(options = {})
34
- options[:every] = :month
35
- new(options)
36
- end
37
-
38
- def self.yearly(options = {})
39
- options[:every] = :year
40
- new(options)
41
- end
42
-
43
- def initialize(options)
44
- raise ArgumentError, ":every option is required" unless options.key?(:every)
45
- raise ArgumentError, "invalid :every option" unless FREQUENCY.include?(options[:every].to_s)
46
-
47
- @options = initialize_dates(options)
48
- @options[:interval] ||= 1
49
-
50
- @event = case @options[:every].to_sym
51
- when :day
52
- Recurrence::Event::Daily.new(@options)
53
- when :week
54
- Recurrence::Event::Weekly.new(@options)
55
- when :month
56
- Recurrence::Event::Monthly.new(@options)
57
- when :year
58
- Recurrence::Event::Yearly.new(@options)
59
- end
60
- end
61
-
62
- def reset!
63
- @event.reset!
64
- @events = nil
65
- end
66
-
67
- def include?(required_date)
68
- required_date = Date.parse(required_date) if required_date.is_a?(String)
69
-
70
- if required_date < @options[:starts] || required_date > @options[:until]
71
- false
72
- else
73
- each do |date|
74
- return true if date == required_date
75
- end
76
- end
77
-
78
- return false
79
- end
80
-
81
- def next
82
- @event.next
83
- end
84
-
85
- def next!
86
- @event.next!
87
- end
88
-
89
- def events(options={})
90
- options[:starts] = Date.parse(options[:starts]) if options[:starts].is_a?(String)
91
- options[:until] = Date.parse(options[:until]) if options[:until].is_a?(String)
92
-
93
- reset! if options[:starts] || options[:until]
94
-
95
- @events ||= begin
96
- _events = []
97
-
98
- loop do
99
- date = @event.next!
100
-
101
- break if date.nil?
102
-
103
- valid_start = options[:starts].nil? || date >= options[:starts]
104
- valid_until = options[:until].nil? || date <= options[:until]
105
- _events << date if valid_start && valid_until
106
-
107
- break if options[:until] && options[:until] <= date
108
- end
109
-
110
- _events
111
- end
112
- end
113
-
114
- def events!(options={})
115
- reset!
116
- events(options)
117
- end
118
-
119
- def each!(&block)
120
- reset!
121
- each(&block)
122
- end
123
-
124
- def each(&block)
125
- events.each do |item|
126
- yield item
127
- end
128
- end
129
-
130
- private
131
- def initialize_dates(options) #:nodoc:
132
- [:starts, :until].each do |name|
133
- options[name] = Date.parse(options[name]) if options[name].is_a?(String)
134
- end
135
-
136
- options[:starts] ||= Date.today
137
- options[:until] ||= self.class.default_until_date
138
-
139
- options
140
- end
3
+ class Recurrence < SimplesIdeias::Recurrence
141
4
  end
@@ -1,9 +1,11 @@
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"
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ autoload :Base, "recurrence/event/base"
5
+ autoload :Daily, "recurrence/event/daily"
6
+ autoload :Monthly, "recurrence/event/monthly"
7
+ autoload :Weekly, "recurrence/event/weekly"
8
+ autoload :Yearly, "recurrence/event/yearly"
9
+ end
8
10
  end
9
11
  end
@@ -1,78 +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)
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ class Base
5
+ CARDINALS = %w(first second third fourth fifth)
6
+ DAYS = %w(sunday monday tuesday wednesday thursday friday saturday)
6
7
 
7
- attr_accessor :start_date
8
+ attr_accessor :start_date
8
9
 
9
- def initialize(options={})
10
- every, options = nil, every if every.is_a?(Hash)
10
+ def initialize(options={})
11
+ every, options = nil, every if every.is_a?(Hash)
11
12
 
12
- @options = options
13
- @date = options[:starts]
14
- @finished = false
13
+ @options = options
14
+ @date = options[:starts]
15
+ @finished = false
15
16
 
16
- validate
17
- raise ArgumentError, "interval should be greater than zero" if @options[:interval] <= 0
17
+ validate
18
+ raise ArgumentError, "interval should be greater than zero" if @options[:interval] <= 0
18
19
 
19
- prepare!
20
- end
20
+ prepare!
21
+ end
21
22
 
22
- def next!
23
- return nil if finished?
24
- return @date = @start_date if @start_date && @date.nil?
23
+ def next!
24
+ return nil if finished?
25
+ return @date = @start_date if @start_date && @date.nil?
25
26
 
26
- @date = next_in_recurrence
27
+ @date = next_in_recurrence
27
28
 
28
- @finished, @date = true, nil if @date > @options[:until]
29
- @date
30
- end
29
+ @finished, @date = true, nil if @date > @options[:until]
30
+ @date
31
+ end
31
32
 
32
- def next
33
- return nil if finished?
34
- @date || @start_date
35
- end
33
+ def next
34
+ return nil if finished?
35
+ @date || @start_date
36
+ end
36
37
 
37
- def reset!
38
- @date = nil
39
- end
38
+ def reset!
39
+ @date = nil
40
+ end
40
41
 
41
- def finished?
42
- @finished
43
- end
42
+ def finished?
43
+ @finished
44
+ end
44
45
 
45
- private
46
- def initialized?
47
- !!@start_date
48
- end
46
+ private
47
+ def initialized?
48
+ !!@start_date
49
+ end
49
50
 
50
- def prepare!
51
- @start_date = next!
52
- @date = nil
53
- end
51
+ def prepare!
52
+ @start_date = next!
53
+ @date = nil
54
+ end
54
55
 
55
- def validate
56
- # Inject custom validations
57
- end
56
+ def validate
57
+ # Inject custom validations
58
+ end
58
59
 
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
60
+ # Common validation for inherited classes.
61
+ #
62
+ def valid_month_day?(day) #:nodoc:
63
+ raise ArgumentError, "invalid day #{day}" unless (1..31).include?(day)
64
+ end
64
65
 
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)
66
+ # Check if the given key has a valid weekday (0 upto 6) or a valid weekday
67
+ # name (defined in the DAYS constant). If a weekday name (String) is given,
68
+ # convert it to a weekday (Integer).
69
+ #
70
+ def valid_weekday_or_weekday_name?(value)
71
+ if value.kind_of?(Numeric)
72
+ raise ArgumentError, "invalid day #{value}" unless (0..6).include?(value)
73
+ value
74
+ else
75
+ raise ArgumentError, "invalid weekday #{value}" unless DAYS.include?(value.to_s)
76
+ DAYS.index(value.to_s)
77
+ end
76
78
  end
77
79
  end
78
80
  end
@@ -1,11 +1,13 @@
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
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ class Daily < Base
5
+ protected
6
+ def next_in_recurrence
7
+ date = @date.to_date
8
+ date += @options[:interval] if initialized?
9
+ date
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,102 +1,104 @@
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])
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ class Monthly < Base
5
+ INTERVALS = {
6
+ :monthly => 1,
7
+ :bimonthly => 2,
8
+ :quarterly => 3,
9
+ :semesterly => 6
10
+ }
11
+
12
+ protected
13
+ def validate
14
+ if @options.key?(:weekday)
15
+
16
+ # Allow :on => :last, :weekday => :thursday contruction.
17
+ if @options[:on].to_s == "last"
18
+ @options[:on] = 5
19
+ elsif @options[:on].kind_of?(Numeric)
20
+ valid_week?(@options[:on])
21
+ else
22
+ valid_cardinal?(@options[:on])
23
+ @options[:on] = CARDINALS.index(@options[:on].to_s) + 1
24
+ end
25
+
26
+ @options[:weekday] = valid_weekday_or_weekday_name?(@options[:weekday])
20
27
  else
21
- valid_cardinal?(@options[:on])
22
- @options[:on] = CARDINALS.index(@options[:on].to_s) + 1
28
+ valid_month_day?(@options[:on])
23
29
  end
24
30
 
25
- @options[:weekday] = valid_weekday_or_weekday_name?(@options[:weekday])
26
- else
27
- valid_month_day?(@options[:on])
28
- end
29
-
30
- if @options[:interval].is_a?(Symbol)
31
- valid_interval?(@options[:interval])
32
- @options[:interval] = INTERVALS[@options[:interval]]
31
+ if @options[:interval].is_a?(Symbol)
32
+ valid_interval?(@options[:interval])
33
+ @options[:interval] = INTERVALS[@options[:interval]]
34
+ end
33
35
  end
34
- end
35
-
36
- def next_in_recurrence
37
- return next_month if self.respond_to?(:next_month)
38
- type = @options.key?(:weekday) ? :weekday : :monthday
39
36
 
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
37
+ def next_in_recurrence
38
+ return next_month if self.respond_to?(:next_month)
39
+ type = @options.key?(:weekday) ? :weekday : :monthday
40
+
41
+ class_eval <<-METHOD
42
+ def next_month
43
+ if initialized?
44
+ advance_to_month_by_#{type}(@date)
45
+ else
46
+ new_date = advance_to_month_by_#{type}(@date, 0)
47
+ new_date = advance_to_month_by_#{type}(new_date) if @date > new_date
48
+ new_date
49
+ end
48
50
  end
49
- end
50
- METHOD
51
+ METHOD
51
52
 
52
- next_month
53
- end
53
+ next_month
54
+ end
54
55
 
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
56
+ def advance_to_month_by_monthday(date, interval=@options[:interval])
57
+ # Have a raw month from 0 to 11 interval
58
+ raw_month = date.month + interval - 1
58
59
 
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
60
+ next_year = date.year + raw_month / 12
61
+ next_month = (raw_month % 12) + 1 # change back to ruby interval
62
+ next_day = [ @options[:on], Time.days_in_month(next_month, next_year) ].min
62
63
 
63
- Date.new(next_year, next_month, next_day)
64
- end
64
+ Date.new(next_year, next_month, next_day)
65
+ end
65
66
 
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)
67
+ def advance_to_month_by_weekday(date, interval=@options[:interval])
68
+ raw_month = date.month + interval - 1
69
+ next_year = date.year + raw_month / 12
70
+ next_month = (raw_month % 12) + 1 # change back to ruby interval
71
+ date = Date.new(next_year, next_month, 1)
71
72
 
72
- weekday, month = @options[:weekday], date.month
73
+ weekday, month = @options[:weekday], date.month
73
74
 
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
75
+ # Adjust week day
76
+ to_add = weekday - date.wday
77
+ to_add += 7 if to_add < 0
78
+ to_add += (@options[:on] - 1) * 7
79
+ date += to_add
79
80
 
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
81
+ # Go to the previous month if we lost it
82
+ if date.month != month
83
+ weeks = (date.day - 1) / 7 + 1
84
+ date -= weeks * 7
85
+ end
85
86
 
86
- date
87
- end
87
+ date
88
+ end
88
89
 
89
- private
90
- def valid_cardinal?(cardinal) #:nodoc:
91
- raise ArgumentError, "invalid cardinal #{cardinal}" unless CARDINALS.include?(cardinal.to_s)
92
- end
90
+ private
91
+ def valid_cardinal?(cardinal) #:nodoc:
92
+ raise ArgumentError, "invalid cardinal #{cardinal}" unless CARDINALS.include?(cardinal.to_s)
93
+ end
93
94
 
94
- def valid_interval?(interval) #:nodoc:
95
- raise ArgumentError, "invalid cardinal #{interval}" unless INTERVALS.key?(interval)
96
- end
95
+ def valid_interval?(interval) #:nodoc:
96
+ raise ArgumentError, "invalid cardinal #{interval}" unless INTERVALS.key?(interval)
97
+ end
97
98
 
98
- def valid_week?(week) #:nodoc:
99
- raise ArgumentError, "invalid week #{week}" unless (1..5).include?(week)
99
+ def valid_week?(week) #:nodoc:
100
+ raise ArgumentError, "invalid week #{week}" unless (1..5).include?(week)
101
+ end
100
102
  end
101
103
  end
102
104
  end
@@ -1,27 +1,29 @@
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)
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ class Weekly < Base
5
+ protected
6
+ def validate
7
+ @options[:on] = Array.wrap(@options[:on]).inject([]) do |days, value|
8
+ days << valid_weekday_or_weekday_name?(value)
9
+ end
10
+
11
+ @options[:on].sort!
8
12
  end
9
13
 
10
- @options[:on].sort!
11
- end
14
+ def next_in_recurrence
15
+ return @date if !initialized? && @options[:on].include?(@date.wday)
12
16
 
13
- def next_in_recurrence
14
- return @date if !initialized? && @options[:on].include?(@date.wday)
17
+ if next_day = @options[:on].find { |day| day > @date.wday }
18
+ to_add = next_day - @date.wday
19
+ else
20
+ to_add = (7 - @date.wday) # Move to next week
21
+ to_add += (@options[:interval] - 1) * 7 # Add extra intervals
22
+ to_add += @options[:on].first # Go to first required day
23
+ end
15
24
 
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
25
+ @date.to_date + to_add
22
26
  end
23
-
24
- @date.to_date + to_add
25
27
  end
26
28
  end
27
29
  end
@@ -1,58 +1,60 @@
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
- }
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Event
4
+ class Yearly < Base
5
+ MONTHS = {
6
+ "jan" => 1, "january" => 1,
7
+ "feb" => 2, "february" => 2,
8
+ "mar" => 3, "march" => 3,
9
+ "apr" => 4, "april" => 4,
10
+ "may" => 5,
11
+ "jun" => 6, "june" => 6,
12
+ "jul" => 7, "july" => 7,
13
+ "aug" => 8, "august" => 8,
14
+ "sep" => 9, "september" => 9,
15
+ "oct" => 10, "october" => 10,
16
+ "nov" => 11, "november" => 11,
17
+ "dec" => 12, "december" => 12
18
+ }
18
19
 
19
- protected
20
- def validate
21
- valid_month_day?(@options[:on].last)
20
+ protected
21
+ def validate
22
+ valid_month_day?(@options[:on].last)
22
23
 
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 ]
24
+ if @options[:on].first.kind_of?(Numeric)
25
+ valid_month?(@options[:on].first)
26
+ else
27
+ valid_month_name?(@options[:on].first)
28
+ @options[:on] = [ MONTHS[@options[:on].first.to_s], @options.last ]
29
+ end
28
30
  end
29
- end
30
31
 
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
32
+ def next_in_recurrence
33
+ if initialized?
34
+ advance_to_year(@date)
35
+ else
36
+ new_date = advance_to_year(@date, 0)
37
+ new_date = advance_to_year(new_date) if @date > new_date
38
+ new_date
39
+ end
38
40
  end
39
- end
40
41
 
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
42
+ def advance_to_year(date, interval=@options[:interval])
43
+ next_year = date.year + interval
44
+ next_month = @options[:on].first
45
+ next_day = [ @options[:on].last, Time.days_in_month(next_month, next_year) ].min
45
46
 
46
- Date.new(next_year, next_month, next_day)
47
- end
47
+ Date.new(next_year, next_month, next_day)
48
+ end
48
49
 
49
- private
50
- def valid_month?(month) #:nodoc:
51
- raise ArgumentError, "invalid month #{month}" unless (1..12).include?(month)
52
- end
50
+ private
51
+ def valid_month?(month) #:nodoc:
52
+ raise ArgumentError, "invalid month #{month}" unless (1..12).include?(month)
53
+ end
53
54
 
54
- def valid_month_name?(month) #:nodoc:
55
- raise ArgumentError, "invalid month #{month}" unless MONTHS.keys.include?(month.to_s)
55
+ def valid_month_name?(month) #:nodoc:
56
+ raise ArgumentError, "invalid month #{month}" unless MONTHS.keys.include?(month.to_s)
57
+ end
56
58
  end
57
59
  end
58
60
  end
@@ -0,0 +1,144 @@
1
+ require "date"
2
+ require "active_support/all"
3
+
4
+ module SimplesIdeias
5
+ class Recurrence
6
+ autoload :Event, "recurrence/event"
7
+ autoload :Version, "recurrence/version"
8
+
9
+ FREQUENCY = %w(day week month year)
10
+
11
+ attr_reader :event
12
+
13
+ def self.default_until_date
14
+ @default_until_date ||= Date.new(2037, 12, 31)
15
+ end
16
+
17
+ def self.default_until_date=(date)
18
+ @default_until_date = if date.is_a?(String)
19
+ Date.parse(date)
20
+ else
21
+ date
22
+ end
23
+ end
24
+
25
+ def self.daily(options = {})
26
+ options[:every] = :day
27
+ new(options)
28
+ end
29
+
30
+ def self.weekly(options = {})
31
+ options[:every] = :week
32
+ new(options)
33
+ end
34
+
35
+ def self.monthly(options = {})
36
+ options[:every] = :month
37
+ new(options)
38
+ end
39
+
40
+ def self.yearly(options = {})
41
+ options[:every] = :year
42
+ new(options)
43
+ end
44
+
45
+ def initialize(options)
46
+ raise ArgumentError, ":every option is required" unless options.key?(:every)
47
+ raise ArgumentError, "invalid :every option" unless FREQUENCY.include?(options[:every].to_s)
48
+
49
+ @options = initialize_dates(options)
50
+ @options[:interval] ||= 1
51
+
52
+ @event = case @options[:every].to_sym
53
+ when :day
54
+ Recurrence::Event::Daily.new(@options)
55
+ when :week
56
+ Recurrence::Event::Weekly.new(@options)
57
+ when :month
58
+ Recurrence::Event::Monthly.new(@options)
59
+ when :year
60
+ Recurrence::Event::Yearly.new(@options)
61
+ end
62
+ end
63
+
64
+ def reset!
65
+ @event.reset!
66
+ @events = nil
67
+ end
68
+
69
+ def include?(required_date)
70
+ required_date = Date.parse(required_date) if required_date.is_a?(String)
71
+
72
+ if required_date < @options[:starts] || required_date > @options[:until]
73
+ false
74
+ else
75
+ each do |date|
76
+ return true if date == required_date
77
+ end
78
+ end
79
+
80
+ return false
81
+ end
82
+
83
+ def next
84
+ @event.next
85
+ end
86
+
87
+ def next!
88
+ @event.next!
89
+ end
90
+
91
+ def events(options={})
92
+ options[:starts] = Date.parse(options[:starts]) if options[:starts].is_a?(String)
93
+ options[:until] = Date.parse(options[:until]) if options[:until].is_a?(String)
94
+
95
+ reset! if options[:starts] || options[:until]
96
+
97
+ @events ||= begin
98
+ _events = []
99
+
100
+ loop do
101
+ date = @event.next!
102
+
103
+ break if date.nil?
104
+
105
+ valid_start = options[:starts].nil? || date >= options[:starts]
106
+ valid_until = options[:until].nil? || date <= options[:until]
107
+ _events << date if valid_start && valid_until
108
+
109
+ break if options[:until] && options[:until] <= date
110
+ end
111
+
112
+ _events
113
+ end
114
+ end
115
+
116
+ def events!(options={})
117
+ reset!
118
+ events(options)
119
+ end
120
+
121
+ def each!(&block)
122
+ reset!
123
+ each(&block)
124
+ end
125
+
126
+ def each(&block)
127
+ events.each do |item|
128
+ yield item
129
+ end
130
+ end
131
+
132
+ private
133
+ def initialize_dates(options) #:nodoc:
134
+ [:starts, :until].each do |name|
135
+ options[name] = Date.parse(options[name]) if options[name].is_a?(String)
136
+ end
137
+
138
+ options[:starts] ||= Date.today
139
+ options[:until] ||= self.class.default_until_date
140
+
141
+ options
142
+ end
143
+ end
144
+ end
@@ -1,8 +1,10 @@
1
- class Recurrence
2
- module Version
3
- MAJOR = 0
4
- MINOR = 1
5
- PATCH = 2
6
- STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
1
+ module SimplesIdeias
2
+ class Recurrence
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 3
7
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
+ end
7
9
  end
8
10
  end
data/recurrence.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{recurrence}
8
- s.version = "0.1.2"
8
+ s.version = "0.1.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nando Vieira"]
12
- s.date = %q{2010-09-09}
12
+ s.date = %q{2010-09-14}
13
13
  s.description = %q{}
14
14
  s.email = %q{fnando.vieira@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -28,9 +28,11 @@ Gem::Specification.new do |s|
28
28
  "lib/recurrence/event/monthly.rb",
29
29
  "lib/recurrence/event/weekly.rb",
30
30
  "lib/recurrence/event/yearly.rb",
31
+ "lib/recurrence/namespace.rb",
31
32
  "lib/recurrence/version.rb",
32
33
  "recurrence.gemspec",
33
- "spec/recurrence_spec.rb"
34
+ "spec/recurrence_spec.rb",
35
+ "spec/spec_helper.rb"
34
36
  ]
35
37
  s.homepage = %q{http://github.com/fnando/recurrence}
36
38
  s.rdoc_options = ["--charset=UTF-8"]
@@ -38,7 +40,8 @@ Gem::Specification.new do |s|
38
40
  s.rubygems_version = %q{1.3.7}
39
41
  s.summary = %q{A simple library to handle recurring events}
40
42
  s.test_files = [
41
- "spec/recurrence_spec.rb"
43
+ "spec/recurrence_spec.rb",
44
+ "spec/spec_helper.rb"
42
45
  ]
43
46
 
44
47
  if s.respond_to? :specification_version then
@@ -1,7 +1,4 @@
1
- require "recurrence"
2
-
3
- Date::DATE_FORMATS[:date] = "%d/%m/%Y"
4
- Time::DATE_FORMATS[:date] = "%d/%m/%Y"
1
+ require "spec_helper"
5
2
 
6
3
  describe "recurrence" do
7
4
  it "should require :every option" do
@@ -0,0 +1,5 @@
1
+ require "rspec"
2
+ require "recurrence"
3
+
4
+ Date::DATE_FORMATS[:date] = "%d/%m/%Y"
5
+ Time::DATE_FORMATS[:date] = "%d/%m/%Y"
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recurrence
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 1
9
- - 2
10
- version: 0.1.2
8
+ - 3
9
+ version: 0.1.3
11
10
  platform: ruby
12
11
  authors:
13
12
  - Nando Vieira
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-09-09 00:00:00 -03:00
17
+ date: 2010-09-14 00:00:00 -03:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 15
30
28
  segments:
31
29
  - 2
32
30
  - 0
@@ -42,7 +40,6 @@ dependencies:
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 3
46
43
  segments:
47
44
  - 0
48
45
  version: "0"
@@ -69,9 +66,11 @@ files:
69
66
  - lib/recurrence/event/monthly.rb
70
67
  - lib/recurrence/event/weekly.rb
71
68
  - lib/recurrence/event/yearly.rb
69
+ - lib/recurrence/namespace.rb
72
70
  - lib/recurrence/version.rb
73
71
  - recurrence.gemspec
74
72
  - spec/recurrence_spec.rb
73
+ - spec/spec_helper.rb
75
74
  has_rdoc: true
76
75
  homepage: http://github.com/fnando/recurrence
77
76
  licenses: []
@@ -86,7 +85,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
85
  requirements:
87
86
  - - ">="
88
87
  - !ruby/object:Gem::Version
89
- hash: 3
90
88
  segments:
91
89
  - 0
92
90
  version: "0"
@@ -95,7 +93,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
93
  requirements:
96
94
  - - ">="
97
95
  - !ruby/object:Gem::Version
98
- hash: 3
99
96
  segments:
100
97
  - 0
101
98
  version: "0"
@@ -108,3 +105,4 @@ specification_version: 3
108
105
  summary: A simple library to handle recurring events
109
106
  test_files:
110
107
  - spec/recurrence_spec.rb
108
+ - spec/spec_helper.rb