recurrence 0.1.2 → 0.1.3

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