From df5a60790b6fb6192dd65a1fb7c28e90a05a72df Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 10:53:07 -0700 Subject: [PATCH 1/6] Set ruby 3.3 as the minimum supported version. --- .rubocop.yml | 2 +- lib/recurrence/event/monthly.rb | 4 +- lib/recurrence/event/yearly.rb | 4 +- lib/recurrence/namespace.rb | 18 +++--- lib/recurrence/version.rb | 2 +- recurrence.gemspec | 5 +- test/recurrence/daily_recurring_test.rb | 17 +++++- test/recurrence/date_shift_test.rb | 5 ++ test/recurrence/default_starts_date_test.rb | 2 + test/recurrence/events_test.rb | 4 ++ test/recurrence/except_test.rb | 12 ++-- test/recurrence/include_test.rb | 56 ++++++++++++------- test/recurrence/monthly_recurring/day_test.rb | 16 +++++- .../monthly_recurring/interval_test.rb | 5 ++ .../monthly_recurring/weekday_test.rb | 9 +++ test/recurrence/recurrence_test.rb | 3 +- test/recurrence/weekly_recurring_test.rb | 17 +++++- test/recurrence/yearly_recurring_test.rb | 16 +++++- 18 files changed, 141 insertions(+), 56 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 64a7741..d1d9d9b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,7 +3,7 @@ inherit_gem: rubocop-fnando: .rubocop.yml AllCops: - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.3 NewCops: enable Exclude: - bin/**/* diff --git a/lib/recurrence/event/monthly.rb b/lib/recurrence/event/monthly.rb index 63695f5..82bd3f2 100644 --- a/lib/recurrence/event/monthly.rb +++ b/lib/recurrence/event/monthly.rb @@ -109,13 +109,13 @@ class Monthly < Base # :nodoc: all end private def valid_ordinal?(ordinal) - return if ORDINALS.include?(ordinal.to_s) + return false if ORDINALS.include?(ordinal.to_s) raise ArgumentError, "invalid ordinal #{ordinal}" end private def valid_interval?(interval) - return if INTERVALS.key?(interval) + return false if INTERVALS.key?(interval) raise ArgumentError, "invalid ordinal #{interval}" end diff --git a/lib/recurrence/event/yearly.rb b/lib/recurrence/event/yearly.rb index eab5ba0..c87759d 100644 --- a/lib/recurrence/event/yearly.rb +++ b/lib/recurrence/event/yearly.rb @@ -51,13 +51,13 @@ class Yearly < Base # :nodoc: all end private def valid_month?(month) - return if (1..12).cover?(month) + return false if (1..12).cover?(month) raise ArgumentError, "invalid month #{month}" end private def valid_month_name?(month) - return if MONTHS.key?(month.to_s) + return false if MONTHS.key?(month.to_s) raise ArgumentError, "invalid month #{month}" end diff --git a/lib/recurrence/namespace.rb b/lib/recurrence/namespace.rb index 463d81e..abfa51c 100644 --- a/lib/recurrence/namespace.rb +++ b/lib/recurrence/namespace.rb @@ -179,11 +179,11 @@ def include?(required_date) required_date = as_date(required_date) if required_date < @_options[:starts] || required_date > @_options[:until] - false - else - each do |date| - return true if date == required_date - end + return false + end + + each do |date| + return true if date == required_date end false @@ -280,14 +280,14 @@ def events!(options = {}) # r.each # #=> # # - def each(&block) - events.each(&block) + def each(&) + events.each(&) end # Works like Recurrence::Namespace#each, but removes the cache first. - def each!(&block) + def each!(&) reset! - each(&block) + each(&) end private def validate_initialize_options(options) diff --git a/lib/recurrence/version.rb b/lib/recurrence/version.rb index 530b34d..8566561 100644 --- a/lib/recurrence/version.rb +++ b/lib/recurrence/version.rb @@ -5,6 +5,6 @@ module Version MAJOR = 1 MINOR = 3 PATCH = 0 - STRING = "#{MAJOR}.#{MINOR}.#{PATCH}" + STRING = "#{MAJOR}.#{MINOR}.#{PATCH}".freeze end end diff --git a/recurrence.gemspec b/recurrence.gemspec index 69890b8..7c58903 100644 --- a/recurrence.gemspec +++ b/recurrence.gemspec @@ -3,7 +3,7 @@ require "./lib/recurrence/version" Gem::Specification.new do |s| - s.required_ruby_version = ">= 2.5" + s.required_ruby_version = ">= 3.3" s.name = "recurrence" s.version = Recurrence_::Version::STRING s.platform = Gem::Platform::RUBY @@ -12,16 +12,15 @@ Gem::Specification.new do |s| s.homepage = "http://rubygems.org/gems/recurrence" s.summary = "A simple library to handle recurring events" s.description = s.summary + s.metadata["rubygems_mfa_required"] = "true" s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map do |f| File.basename(f) end s.require_paths = ["lib"] s.add_dependency "activesupport" - s.add_dependency "i18n" s.add_development_dependency "minitest-utils" s.add_development_dependency "pry-meta" s.add_development_dependency "rake" diff --git a/test/recurrence/daily_recurring_test.rb b/test/recurrence/daily_recurring_test.rb index dbee189..0d2e108 100644 --- a/test/recurrence/daily_recurring_test.rb +++ b/test/recurrence/daily_recurring_test.rb @@ -5,24 +5,28 @@ class DailyRecurringTest < Minitest::Test test "recurs until limit date" do r = Recurrence.daily + assert_equal Date.parse("2037-12-31"), r.events[-1] end test "repeats until 1 month from now" do date = 1.month.from_now r = recurrence(every: :day, until: date.to_date) + assert_equal date.to_date, r.events[-1] end test "recurs through 1 month from now" do date = 1.month.from_now r = recurrence(every: :day, through: date.to_date) + assert_equal date.to_date, r.events[-1] end - test "starts 2 months ago (#{2.months.ago.to_s(:date)})" do + test "starts 2 months ago (#{2.months.ago.to_date})" do date = 2.months.ago r = recurrence(every: :day, starts: date.to_date) + assert_equal date.to_date, r.events[0] assert_equal (date + 1.day).to_date, r.events[1].to_date assert_equal (date + 2.day).to_date, r.events[2].to_date @@ -31,6 +35,7 @@ class DailyRecurringTest < Minitest::Test test "starts at 2008-03-19 and repeat until 2008-04-24" do r = recurrence(every: :day, starts: "2008-03-19", until: "2008-04-24") + assert_equal "2008-03-19", r.events[0].to_s assert_equal "2008-03-20", r.events[1].to_s assert_equal "2008-04-24", r.events[-1].to_s @@ -39,6 +44,7 @@ class DailyRecurringTest < Minitest::Test test "starts at 2008-03-19 and repeat through 2008-04-24" do r = recurrence(every: :day, starts: "2008-03-19", through: "2008-04-24") + assert_equal "2008-03-19", r.events[0].to_s assert_equal "2008-03-20", r.events[1].to_s assert_equal "2008-04-24", r.events[-1].to_s @@ -46,6 +52,7 @@ class DailyRecurringTest < Minitest::Test test "uses interval" do r = recurrence(every: :day, interval: 2, starts: "2008-09-21") + assert_equal "2008-09-21", r.events[0].to_s assert_equal "2008-09-23", r.events[1].to_s assert_equal "2008-09-25", r.events[2].to_s @@ -53,6 +60,7 @@ class DailyRecurringTest < Minitest::Test test "uses repeat" do r = recurrence(every: :day, starts: "2008-09-21", repeat: 10) + assert_equal 10, r.events.size end @@ -63,6 +71,7 @@ class DailyRecurringTest < Minitest::Test until: "2008-04-25", interval: 2 ) + assert_equal "2008-04-24", r.events[-1].to_s end @@ -73,12 +82,14 @@ class DailyRecurringTest < Minitest::Test through: "2008-04-25", interval: 2 ) + assert_equal "2008-04-26", r.events[-1].to_s end test "uses except" do r = Recurrence.daily(except: Date.tomorrow) - refute r.events.include?(Date.tomorrow) - assert r.events.include?(Date.tomorrow + 1.day) + + refute_includes r.events, Date.tomorrow + assert_includes r.events, Date.tomorrow + 1.day end end diff --git a/test/recurrence/date_shift_test.rb b/test/recurrence/date_shift_test.rb index b77a3ec..00908c6 100644 --- a/test/recurrence/date_shift_test.rb +++ b/test/recurrence/date_shift_test.rb @@ -6,6 +6,7 @@ class DateShiftTest < Minitest::Test test "shifts yearly recurrences around February 29" do r = recurrence(every: :year, starts: "2012-02-29", on: [2, 29], shift: true) + assert_equal Date.new(2012, 2, 29), r.events[0] assert_equal Date.new(2013, 2, 28), r.events[1] assert_equal Date.new(2014, 2, 28), r.events[2] @@ -16,6 +17,7 @@ class DateShiftTest < Minitest::Test test "shifts monthly recurrences around the 31st" do r = recurrence(every: :month, starts: "2011-01-31", on: 31, shift: true) + assert_equal Date.new(2011, 1, 31), r.events[0] assert_equal Date.new(2011, 2, 28), r.events[1] assert_equal Date.new(2011, 3, 28), r.events[2] @@ -24,6 +26,7 @@ class DateShiftTest < Minitest::Test test "shifts monthly recurrences around the 30th" do r = recurrence(every: :month, starts: "2011-01-30", on: 30, shift: true) + assert_equal Date.new(2011, 1, 30), r.events[0] assert_equal Date.new(2011, 2, 28), r.events[1] assert_equal Date.new(2011, 3, 28), r.events[2] @@ -32,12 +35,14 @@ class DateShiftTest < Minitest::Test test "shifts monthly recurrences around the 29th" do r = recurrence(every: :month, starts: "2011-01-29", on: 29, shift: true) + assert_equal Date.new(2011, 1, 29), r.events[0] assert_equal Date.new(2011, 2, 28), r.events[1] assert_equal Date.new(2011, 3, 28), r.events[2] r = recurrence(every: :month, starts: "2012-01-29", on: 29, shift: true) + assert_equal Date.new(2012, 1, 29), r.events[0] assert_equal Date.new(2012, 2, 29), r.events[1] assert_equal Date.new(2012, 3, 29), r.events[2] diff --git a/test/recurrence/default_starts_date_test.rb b/test/recurrence/default_starts_date_test.rb index b22d116..1355d91 100644 --- a/test/recurrence/default_starts_date_test.rb +++ b/test/recurrence/default_starts_date_test.rb @@ -15,9 +15,11 @@ class DefaultStartsDateTest < Minitest::Test test "applies assigned callable" do Recurrence.default_starts_date = -> { Date.tomorrow } + assert_equal Date.tomorrow, Recurrence.default_starts_date r = Recurrence.new(every: :day, until: 3.days.from_now.to_date) + assert_equal Date.tomorrow, r.events.first end end diff --git a/test/recurrence/events_test.rb b/test/recurrence/events_test.rb index 6d1fcfe..fb71531 100644 --- a/test/recurrence/events_test.rb +++ b/test/recurrence/events_test.rb @@ -14,23 +14,27 @@ class EventsTest < Minitest::Test test "returns only events greater than starting date" do events = r.events(starts: "2009-01-10") + assert_equal "2009-01-10", events[0].to_s end test "returns only events smaller than until date" do events = r.events(until: "2009-01-10") + assert_equal "2009-01-06", events[0].to_s assert_equal "2009-01-10", events[-1].to_s end test "returns only events between starting and until date" do events = r.events(starts: "2009-01-12", until: "2009-01-14") + assert_equal "2009-01-12", events[0].to_s assert_equal "2009-01-14", events[-1].to_s end test "doesn't iterate all dates when using until" do events = r.events(starts: "2009-01-06", until: "2009-01-08") + assert_equal 3, r.events.size assert_equal 3, events.size assert_equal "2009-01-08", events[-1].to_s diff --git a/test/recurrence/except_test.rb b/test/recurrence/except_test.rb index ee58678..1d1fedc 100644 --- a/test/recurrence/except_test.rb +++ b/test/recurrence/except_test.rb @@ -11,16 +11,16 @@ class ExceptTest < Minitest::Test test "skips day specified in except" do r = recurrence(every: :day, except: Date.tomorrow) - assert r.include?(Date.current) - refute r.include?(Date.tomorrow) - assert r.include?(Date.tomorrow + 1.day) + assert_includes r, Date.current + refute_includes r, Date.tomorrow + assert_includes r, Date.tomorrow + 1.day end test "skips multiple days specified in except" do r = recurrence(every: :day, except: [Date.tomorrow, "2012-02-29"]) - assert r.include?(Date.current) - refute r.include?(Date.tomorrow) - refute r.include?("2012-02-29") + assert_includes r, Date.current + refute_includes r, Date.tomorrow + refute_includes r, "2012-02-29" end end diff --git a/test/recurrence/include_test.rb b/test/recurrence/include_test.rb index eccb3c9..c02c4ed 100644 --- a/test/recurrence/include_test.rb +++ b/test/recurrence/include_test.rb @@ -5,72 +5,86 @@ class IncludeTest < Minitest::Test test "includes date (day)" do r = recurrence(every: :day, starts: "2008-09-30") - assert r.include?("2008-09-30") - assert r.include?("2008-10-01") + + assert_includes r, "2008-09-30" + assert_includes r, "2008-10-01" end test "includes date (week)" do r = recurrence(every: :week, on: :thursday, starts: "2008-09-30") - refute r.include?("2008-09-30") - assert r.include?("2008-10-02") + + refute_includes r, "2008-09-30" + assert_includes r, "2008-10-02" r = recurrence(every: :week, on: :monday, starts: "2008-09-29") - assert r.include?("2008-09-29") - assert r.include?("2008-10-06") + + assert_includes r, "2008-09-29" + assert_includes r, "2008-10-06" end test "includes date (month)" do r = recurrence(every: :month, on: 10, starts: "2008-09-30") - refute r.include?("2008-09-30") - assert r.include?("2008-10-10") + + refute_includes r, "2008-09-30" + assert_includes r, "2008-10-10" r = recurrence(every: :month, on: 10, starts: "2008-09-10") - assert r.include?("2008-09-10") - assert r.include?("2008-10-10") + + assert_includes r, "2008-09-10" + assert_includes r, "2008-10-10" end test "includes date (year)" do r = recurrence(every: :year, on: [6, 28], starts: "2008-09-30") - refute r.include?("2009-09-30") - assert r.include?("2009-06-28") + + refute_includes r, "2009-09-30" + assert_includes r, "2009-06-28" r = recurrence(every: :year, on: [6, 28], starts: "2008-06-28") - assert r.include?("2009-06-28") - assert r.include?("2009-06-28") + + assert_includes r, "2009-06-28" + assert_includes r, "2009-06-28" end test "doesn't include date when is smaller than starting date (day)" do r = recurrence(every: :day, starts: "2008-09-30") - refute r.include?("2008-09-29") + + refute_includes r, "2008-09-29" end test "doesn't include date when is smaller than starting date (week)" do r = recurrence(every: :week, on: :friday, starts: "2008-09-30") - refute r.include?("2008-09-24") + + refute_includes r, "2008-09-24" end test "doesn't include date when is smaller than starting date (month)" do r = recurrence(every: :month, on: 10, starts: "2008-09-30") - refute r.include?("2008-09-10") + + refute_includes r, "2008-09-10" end test "doesn't include date when is smaller than starting date (year)" do r = recurrence(every: :year, on: [6, 28], starts: "2008-09-30") - refute r.include?("2008-06-28") + + refute_includes r, "2008-06-28" end test "doesn't include date when is greater than ending date (day)" do r = recurrence(every: :day, until: "2008-09-30") - refute r.include?("2008-10-01") + + refute_includes r, "2008-10-01" end test "doesn't include date when is greater than ending date (week)" do r = recurrence(every: :week, on: :friday, until: "2008-09-30") - refute r.include?("2008-10-03") + + refute_includes r, "2008-10-03" end test "doesn't include date when is greater than ending date (year)" do r = recurrence(every: :year, on: [6, 28], until: "2008-09-30") - refute r.include?("2009-06-28") + + refute_includes r, "2009-06-28" end end diff --git a/test/recurrence/monthly_recurring/day_test.rb b/test/recurrence/monthly_recurring/day_test.rb index c941dc3..25e064a 100644 --- a/test/recurrence/monthly_recurring/day_test.rb +++ b/test/recurrence/monthly_recurring/day_test.rb @@ -5,18 +5,21 @@ class MonthlyRecurringDayTest < Minitest::Test test "recurs until limit date" do r = Recurrence.monthly(on: 31) + assert_equal Date.parse("2037-12-31"), r.events[-1] end test "repeats until 8 months from now" do date = 8.months.from_now r = recurrence(every: :month, on: date.day, until: date.to_date) + assert_equal date.to_date, r.events[-1] end test "repeats through 8 months from now" do date = 8.months.from_now r = recurrence(every: :month, on: date.day, through: date.to_date) + assert_equal date.to_date, r.events[-1] end @@ -24,6 +27,7 @@ class MonthlyRecurringDayTest < Minitest::Test date = 9.months.ago r = recurrence(every: :month, on: date.day, starts: date.to_date) + assert_equal date.to_date, r.events[0] end @@ -37,6 +41,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: ends ) + assert_equal "2008-06-07", r.events[0].to_s assert_equal "2008-11-07", r.events[-1].to_s end @@ -51,6 +56,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, through: ends ) + assert_equal "2008-06-07", r.events[0].to_s assert_equal "2008-11-07", r.events[-1].to_s end @@ -64,6 +70,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-10-27", r.events[0].to_s end @@ -76,6 +83,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, through: "2009-01-01" ) + assert_equal "2009-01-27", r.events[-1].to_s end @@ -88,6 +96,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: "2010-01-01" ) + assert_equal "2008-01-31", r.events[0].to_s assert_equal "2008-03-31", r.events[1].to_s assert_equal "2008-05-31", r.events[2].to_s @@ -104,6 +113,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: "2010-01-01" ) + assert_equal "2008-04-29", r.events[0].to_s assert_equal "2008-07-29", r.events[1].to_s assert_equal "2008-10-29", r.events[2].to_s @@ -119,6 +129,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: "2010-01-01" ) + assert_equal "2008-02-29", r.events[0].to_s assert_equal "2008-06-30", r.events[1].to_s assert_equal "2008-10-31", r.events[2].to_s @@ -134,6 +145,7 @@ class MonthlyRecurringDayTest < Minitest::Test until: "2010-01-01", repeat: 5 ) + assert_equal 5, r.events.size end @@ -141,7 +153,7 @@ class MonthlyRecurringDayTest < Minitest::Test r = recurrence(every: :month, on: Date.current.day, except: 8.months.from_now.to_date) - assert r.events.include?(7.months.from_now.to_date) - refute r.events.include?(8.months.from_now.to_date) + assert_includes r.events, 7.months.from_now.to_date + refute_includes r.events, 8.months.from_now.to_date end end diff --git a/test/recurrence/monthly_recurring/interval_test.rb b/test/recurrence/monthly_recurring/interval_test.rb index e949779..08462b0 100644 --- a/test/recurrence/monthly_recurring/interval_test.rb +++ b/test/recurrence/monthly_recurring/interval_test.rb @@ -8,6 +8,7 @@ class MonthlyRecurringIntervalTest < Minitest::Test test "uses numeric interval" do r = recurrence(every: :month, on: 21, interval: 2, starts: starts) + assert_equal "2008-09-21", r.events[0].to_s assert_equal "2008-11-21", r.events[1].to_s assert_equal "2009-01-21", r.events[2].to_s @@ -21,6 +22,7 @@ class MonthlyRecurringIntervalTest < Minitest::Test starts: starts, interval: :monthly ) + assert_equal "2008-09-10", r.events[0].to_s assert_equal "2008-10-10", r.events[1].to_s end @@ -32,6 +34,7 @@ class MonthlyRecurringIntervalTest < Minitest::Test starts: starts, interval: :bimonthly ) + assert_equal "2008-09-10", r.events[0].to_s assert_equal "2008-11-10", r.events[1].to_s end @@ -43,6 +46,7 @@ class MonthlyRecurringIntervalTest < Minitest::Test starts: starts, interval: :quarterly ) + assert_equal "2008-09-10", r.events[0].to_s assert_equal "2008-12-10", r.events[1].to_s end @@ -50,6 +54,7 @@ class MonthlyRecurringIntervalTest < Minitest::Test test "accepts semesterly symbol" do r = recurrence(every: :month, on: 10, starts: starts, interval: :semesterly) + assert_equal "2008-09-10", r.events[0].to_s assert_equal "2009-03-10", r.events[1].to_s end diff --git a/test/recurrence/monthly_recurring/weekday_test.rb b/test/recurrence/monthly_recurring/weekday_test.rb index 775d5bd..03aa3ec 100644 --- a/test/recurrence/monthly_recurring/weekday_test.rb +++ b/test/recurrence/monthly_recurring/weekday_test.rb @@ -5,11 +5,13 @@ class MonthlyRecurringWeekdayTest < Minitest::Test test "recurs until limit date" do r = Recurrence.daily(on: 5, weekday: :thursday) + assert_equal Date.parse("2037-12-31"), r.events[-1] end test "uses weekday shortcut" do r = Recurrence.daily(on: 5, weekday: :thu) + assert_equal Date.parse("2037-12-31"), r.events[-1] end @@ -22,6 +24,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, until: date.to_date ) + assert_equal date.to_date, r.events[-1] end @@ -34,6 +37,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, through: date.to_date ) + assert_equal date.to_date, r.events[-1] end @@ -46,6 +50,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, starts: date.to_date ) + assert_equal date.to_date, r.events[0] end @@ -60,6 +65,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test starts: starts, until: ends ) + assert_equal "2008-06-07", r.events[0].to_s assert_equal "2008-11-01", r.events[-1].to_s end @@ -75,6 +81,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test starts: starts, until: ends ) + assert_equal "2008-06-29", r.events[0].to_s assert_equal "2008-11-30", r.events[-1].to_s end @@ -89,6 +96,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test starts: starts, until: "2010-02-01" ) + assert_equal "2009-01-18", r.events[0].to_s assert_equal "2009-03-15", r.events[1].to_s assert_equal "2009-05-17", r.events[2].to_s @@ -108,6 +116,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test until: "2011-02-01", repeat: 5 ) + assert_equal 5, r.events.size end end diff --git a/test/recurrence/recurrence_test.rb b/test/recurrence/recurrence_test.rb index ad3c74a..a33e3b2 100644 --- a/test/recurrence/recurrence_test.rb +++ b/test/recurrence/recurrence_test.rb @@ -27,6 +27,7 @@ class RecurrenceTest < Minitest::Test test "returns passed-in options" do r = recurrence(every: :day) options = {every: :day} + assert_equal options, r.options end @@ -76,7 +77,7 @@ class RecurrenceTest < Minitest::Test end end - Recurrence::Event::Yearly::MONTHS.each do |month_name, _month_number| + Recurrence::Event::Yearly::MONTHS.each_key do |month_name| test "accepts month as symbol for yearly recurrence (#{month_name})" do recurrence(every: :year, on: [month_name, 10]) end diff --git a/test/recurrence/weekly_recurring_test.rb b/test/recurrence/weekly_recurring_test.rb index 0331698..8716b09 100644 --- a/test/recurrence/weekly_recurring_test.rb +++ b/test/recurrence/weekly_recurring_test.rb @@ -5,25 +5,29 @@ class WeeklyRecurringTest < Minitest::Test test "recurs until limit date" do r = Recurrence.weekly(on: :thursday) + assert_equal Date.parse("2037-12-31"), r.events[-1] end test "repeats 6 weeks from now" do date = 6.weeks.from_now r = recurrence(every: :week, on: date.wday, until: date.to_date) + assert_equal date.to_date, r.events[-1] end test "repeats through 6 weeks from now" do date = 6.weeks.from_now r = recurrence(every: :week, on: date.wday, through: date.to_date) + assert_equal date.to_date, r.events[-1] end - test "starts 3 months ago (#{3.months.ago.to_s(:date)})" do + test "starts 3 months ago (#{3.months.ago.to_date})" do date = 3.months.ago r = recurrence(every: :week, on: date.wday, starts: date.to_date) + assert_equal date.to_date, r.events[0] assert_equal (date + 1.week).to_date, r.events[1] assert_equal (date + 2.weeks).to_date, r.events[2] @@ -43,6 +47,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: ends.to_date ) + assert_equal "2008-02-29", r.events[0].to_s assert_equal "2008-03-07", r.events[1].to_s assert_equal ends.to_s, r.events[-1].to_s @@ -58,6 +63,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, through: ends.to_date ) + assert_equal "2008-02-29", r.events[0].to_s assert_equal "2008-03-07", r.events[1].to_s assert_equal ends.to_s, r.events[-1].to_s @@ -72,6 +78,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-09-21", r.events[0].to_s assert_equal "2008-10-05", r.events[1].to_s assert_equal "2008-10-19", r.events[2].to_s @@ -90,6 +97,7 @@ class WeeklyRecurringTest < Minitest::Test until: "2011-01-01", repeat: 5 ) + assert_equal 5, r.events.size end @@ -102,6 +110,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-09-21", r.events[0].to_s assert_equal "2008-09-27", r.events[1].to_s assert_equal "2008-10-05", r.events[2].to_s @@ -118,6 +127,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-09-22", r.events[0].to_s assert_equal "2008-09-24", r.events[1].to_s assert_equal "2008-09-26", r.events[2].to_s @@ -134,6 +144,7 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-09-27", r.events[0].to_s end @@ -142,7 +153,7 @@ class WeeklyRecurringTest < Minitest::Test r = recurrence(every: :week, on: date.wday, except: 2.weeks.from_now.to_date) - assert r.events.include?(1.week.from_now.to_date) - refute r.events.include?(2.weeks.from_now.to_date) + assert_includes r.events, 1.week.from_now.to_date + refute_includes r.events, 2.weeks.from_now.to_date end end diff --git a/test/recurrence/yearly_recurring_test.rb b/test/recurrence/yearly_recurring_test.rb index 4a979ac..b2b4179 100644 --- a/test/recurrence/yearly_recurring_test.rb +++ b/test/recurrence/yearly_recurring_test.rb @@ -5,6 +5,7 @@ class YearlyRecurringTest < Minitest::Test test "recurs until limit date" do r = Recurrence.yearly(on: [12, 31]) + assert_equal Date.parse("2037-12-31"), r.events[-1] end @@ -15,6 +16,7 @@ class YearlyRecurringTest < Minitest::Test on: [date.month, date.day], until: date.to_date ) + assert_equal date.to_date, r.events[-1] end @@ -25,6 +27,7 @@ class YearlyRecurringTest < Minitest::Test on: [date.month, date.day], through: date.to_date ) + assert_equal date.to_date, r.events[-1] end @@ -35,6 +38,7 @@ class YearlyRecurringTest < Minitest::Test on: [date.month, date.day], starts: date.to_date ) + assert_equal date.to_date, r.events[0] end @@ -48,6 +52,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, until: ends ) + assert_equal "2003-06-07", r.events[0].to_s assert_equal "2018-06-07", r.events[-1].to_s end @@ -62,6 +67,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, through: ends ) + assert_equal "2003-06-07", r.events[0].to_s assert_equal "2018-06-07", r.events[-1].to_s end @@ -75,6 +81,7 @@ class YearlyRecurringTest < Minitest::Test interval: 2, starts: starts ) + assert_equal "2008-09-21", r.events[0].to_s assert_equal "2010-09-21", r.events[1].to_s assert_equal "2012-09-21", r.events[2].to_s @@ -90,6 +97,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, repeat: 5 ) + assert_equal 5, r.events.size end @@ -103,6 +111,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, through: ends ) + assert_equal "2019-06-07", r.events[-1].to_s end @@ -111,6 +120,7 @@ class YearlyRecurringTest < Minitest::Test starts = Date.parse("2008-09-03") r = recurrence(every: :year, on: [10, 27], starts: starts) + assert_equal "2008-10-27", r.events[0].to_s end @@ -118,10 +128,12 @@ class YearlyRecurringTest < Minitest::Test "start date" do starts = Date.parse("2008-09-03") r = recurrence(every: :year, on: [7, 1], starts: starts) + assert_equal "2009-07-01", r.events[0].to_s starts = Date.parse("2008-09-03") r = recurrence(every: :year, on: [9, 1], starts: starts) + assert_equal "2009-09-01", r.events[0].to_s end @@ -129,7 +141,7 @@ class YearlyRecurringTest < Minitest::Test r = Recurrence.yearly(on: [12, 31], except: "#{Time.now.year + 3}-12-31") - assert r.events.include?("#{Time.now.year + 2}-12-31".to_date) - refute r.events.include?("#{Time.now.year + 3}-12-31".to_date) + assert_includes r.events, "#{Time.now.year + 2}-12-31".to_date + refute_includes r.events, "#{Time.now.year + 3}-12-31".to_date end end From 64e8f4dcc529eae5e6bf607e5b403ffe17c0f14f Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 10:53:21 -0700 Subject: [PATCH 2/6] Update author email. --- recurrence.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recurrence.gemspec b/recurrence.gemspec index 7c58903..3a1b49c 100644 --- a/recurrence.gemspec +++ b/recurrence.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.version = Recurrence_::Version::STRING s.platform = Gem::Platform::RUBY s.authors = ["Nando Vieira"] - s.email = ["fnando.vieira@gmail.com"] + s.email = ["me@fnando.com"] s.homepage = "http://rubygems.org/gems/recurrence" s.summary = "A simple library to handle recurring events" s.description = s.summary From 80dbcea81d5039f1510692e84a5c822ee0f8456a Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 12:32:37 -0700 Subject: [PATCH 3/6] Remove active support dependency. --- lib/recurrence.rb | 2 +- lib/recurrence/event.rb | 11 ------- lib/recurrence/event/base.rb | 3 ++ lib/recurrence/event/monthly.rb | 2 +- lib/recurrence/event/weekly.rb | 2 +- lib/recurrence/handler.rb | 7 ---- lib/recurrence/handler/fall_back.rb | 2 ++ lib/recurrence/namespace.rb | 31 +++++++++++------- lib/recurrence/refinements/date.rb | 15 +++++++++ lib/recurrence/refinements/time.rb | 19 +++++++++++ recurrence.gemspec | 1 - test/recurrence/daily_recurring_test.rb | 18 ++++++----- test/recurrence/default_starts_date_test.rb | 6 ++-- test/recurrence/except_test.rb | 10 +++--- test/recurrence/monthly_recurring/day_test.rb | 22 ++++++++----- .../monthly_recurring/weekday_test.rb | 8 +++-- test/recurrence/recurrence_test.rb | 14 ++++---- test/recurrence/weekly_recurring_test.rb | 32 ++++++++++--------- test/recurrence/yearly_recurring_test.rb | 12 ++++--- test/test_helper.rb | 29 +++++++++++++++-- 20 files changed, 159 insertions(+), 87 deletions(-) delete mode 100644 lib/recurrence/event.rb delete mode 100644 lib/recurrence/handler.rb create mode 100644 lib/recurrence/refinements/date.rb create mode 100644 lib/recurrence/refinements/time.rb diff --git a/lib/recurrence.rb b/lib/recurrence.rb index c6dfeef..95e9292 100644 --- a/lib/recurrence.rb +++ b/lib/recurrence.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "recurrence/namespace" +require_relative "recurrence/namespace" # The default namespace. If you already have Recurrence constant set on your # codebase, you can inherit from `Recurrence_` and have your own namespace. diff --git a/lib/recurrence/event.rb b/lib/recurrence/event.rb deleted file mode 100644 index b5c831e..0000000 --- a/lib/recurrence/event.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class Recurrence_ - module Event # :nodoc: all - require "recurrence/event/base" - require "recurrence/event/daily" - require "recurrence/event/monthly" - require "recurrence/event/weekly" - require "recurrence/event/yearly" - end -end diff --git a/lib/recurrence/event/base.rb b/lib/recurrence/event/base.rb index 8eda081..b04c782 100644 --- a/lib/recurrence/event/base.rb +++ b/lib/recurrence/event/base.rb @@ -41,11 +41,14 @@ def next! @date = next_in_recurrence @finished = true if @options[:through] && @date >= @options[:through] + if @date > @options[:until] @finished = true @date = nil end + shift_to @date if @date && @options[:shift] + @date end diff --git a/lib/recurrence/event/monthly.rb b/lib/recurrence/event/monthly.rb index 82bd3f2..1f9060d 100644 --- a/lib/recurrence/event/monthly.rb +++ b/lib/recurrence/event/monthly.rb @@ -40,7 +40,7 @@ class Monthly < Base # :nodoc: all type = @options.key?(:weekday) ? :weekday : :monthday - class_eval <<-METHOD, __FILE__, __LINE__ + 1 + singleton_class.class_eval <<-METHOD, __FILE__, __LINE__ + 1 # private def next_month # if initialized? # advance_to_month_by_weekday(@date) diff --git a/lib/recurrence/event/weekly.rb b/lib/recurrence/event/weekly.rb index 2d877a9..cb3d232 100644 --- a/lib/recurrence/event/weekly.rb +++ b/lib/recurrence/event/weekly.rb @@ -4,7 +4,7 @@ class Recurrence_ module Event class Weekly < Base # :nodoc: all private def validate - @options[:on] = Array.wrap(@options[:on]).inject([]) do |days, value| + @options[:on] = Array(@options[:on]).inject([]) do |days, value| days << valid_weekday_or_weekday_name?(value) end diff --git a/lib/recurrence/handler.rb b/lib/recurrence/handler.rb deleted file mode 100644 index b3051c2..0000000 --- a/lib/recurrence/handler.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class Recurrence_ - module Handler # :nodoc: all - require "recurrence/handler/fall_back" - end -end diff --git a/lib/recurrence/handler/fall_back.rb b/lib/recurrence/handler/fall_back.rb index a3113a4..59e8dc2 100644 --- a/lib/recurrence/handler/fall_back.rb +++ b/lib/recurrence/handler/fall_back.rb @@ -15,6 +15,8 @@ module Handler # :nodoc: all # # => February 28, 2011 # module FallBack + using Refinements + def self.call(day, month, year) Date.new(year, month, [day, Time.days_in_month(month, year)].min) end diff --git a/lib/recurrence/namespace.rb b/lib/recurrence/namespace.rb index abfa51c..6ede6a7 100644 --- a/lib/recurrence/namespace.rb +++ b/lib/recurrence/namespace.rb @@ -1,13 +1,18 @@ # frozen_string_literal: true -require "active_support" -require "active_support/core_ext" require "date" +require "time" class Recurrence_ - require "recurrence/event" - require "recurrence/handler" - require "recurrence/version" + require_relative "refinements/time" + require_relative "refinements/date" + require_relative "handler/fall_back" + require_relative "event/base" + require_relative "event/daily" + require_relative "event/monthly" + require_relative "event/weekly" + require_relative "event/yearly" + require_relative "version" include Enumerable @@ -20,10 +25,10 @@ def self.extended(target) FREQUENCY = %w[day week month year].freeze # This is the default callable that is used as the current date. - # If `Date.current` is available, use it. Otherwise, fall back to - # `Date.current`. + # If `Date.current` is available, use it. Otherwise, fall back + # to `Date.today`. DEFAULT_STARTS_DATE = lambda do - Date.current + Date.respond_to?(:current) ? Date.current : Date.today end attr_reader :event, :options @@ -322,12 +327,14 @@ def each!(&) options end - private def as_date(date) # :nodoc: - case date + private def as_date(input) # :nodoc: + case input + when Time + input.to_date when String - Date.parse(date) + Date.parse(input) else - date + input end end end diff --git a/lib/recurrence/refinements/date.rb b/lib/recurrence/refinements/date.rb new file mode 100644 index 0000000..6bbf894 --- /dev/null +++ b/lib/recurrence/refinements/date.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Recurrence_ + module Refinements + refine Date.singleton_class do + def tomorrow + today.next_day + end + + def yesterday + today.prev_day + end + end + end +end diff --git a/lib/recurrence/refinements/time.rb b/lib/recurrence/refinements/time.rb new file mode 100644 index 0000000..41d520f --- /dev/null +++ b/lib/recurrence/refinements/time.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Recurrence_ + module Refinements + COMMON_YEAR_DAYS_IN_MONTH = [ + nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + ].freeze + + refine Time.singleton_class do + def days_in_month(month, year) + if month == 2 && ::Date.gregorian_leap?(year) + 29 + else + COMMON_YEAR_DAYS_IN_MONTH[month] + end + end + end + end +end diff --git a/recurrence.gemspec b/recurrence.gemspec index 3a1b49c..432fc5e 100644 --- a/recurrence.gemspec +++ b/recurrence.gemspec @@ -20,7 +20,6 @@ Gem::Specification.new do |s| end s.require_paths = ["lib"] - s.add_dependency "activesupport" s.add_development_dependency "minitest-utils" s.add_development_dependency "pry-meta" s.add_development_dependency "rake" diff --git a/test/recurrence/daily_recurring_test.rb b/test/recurrence/daily_recurring_test.rb index 0d2e108..ae57f08 100644 --- a/test/recurrence/daily_recurring_test.rb +++ b/test/recurrence/daily_recurring_test.rb @@ -3,6 +3,8 @@ require "test_helper" class DailyRecurringTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.daily @@ -10,26 +12,26 @@ class DailyRecurringTest < Minitest::Test end test "repeats until 1 month from now" do - date = 1.month.from_now + date = advance_months(1) r = recurrence(every: :day, until: date.to_date) assert_equal date.to_date, r.events[-1] end test "recurs through 1 month from now" do - date = 1.month.from_now + date = advance_months(1) r = recurrence(every: :day, through: date.to_date) assert_equal date.to_date, r.events[-1] end - test "starts 2 months ago (#{2.months.ago.to_date})" do - date = 2.months.ago + test "starts 2 months ago (#{advance_months(2)})" do + date = advance_months(2) r = recurrence(every: :day, starts: date.to_date) assert_equal date.to_date, r.events[0] - assert_equal (date + 1.day).to_date, r.events[1].to_date - assert_equal (date + 2.day).to_date, r.events[2].to_date + assert_equal (date + 1).to_date, r.events[1].to_date + assert_equal (date + 2).to_date, r.events[2].to_date end test "starts at 2008-03-19 and repeat until 2008-04-24" do @@ -87,9 +89,9 @@ class DailyRecurringTest < Minitest::Test end test "uses except" do - r = Recurrence.daily(except: Date.tomorrow) + r = Recurrence.daily(except: Date.tomorrow, until: advance_days(2)) refute_includes r.events, Date.tomorrow - assert_includes r.events, Date.tomorrow + 1.day + assert_includes r.events, Date.tomorrow + 1 end end diff --git a/test/recurrence/default_starts_date_test.rb b/test/recurrence/default_starts_date_test.rb index 1355d91..15eccd4 100644 --- a/test/recurrence/default_starts_date_test.rb +++ b/test/recurrence/default_starts_date_test.rb @@ -3,8 +3,10 @@ require "test_helper" class DefaultStartsDateTest < Minitest::Test + using Recurrence::Refinements + test "returns Date.current by default" do - assert_equal Date.current, Recurrence.default_starts_date + assert_equal Date.today, Recurrence.default_starts_date end test "requires only strings and procs" do @@ -18,7 +20,7 @@ class DefaultStartsDateTest < Minitest::Test assert_equal Date.tomorrow, Recurrence.default_starts_date - r = Recurrence.new(every: :day, until: 3.days.from_now.to_date) + r = Recurrence.new(every: :day, until: advance_days(3)) assert_equal Date.tomorrow, r.events.first end diff --git a/test/recurrence/except_test.rb b/test/recurrence/except_test.rb index 1d1fedc..56a4cde 100644 --- a/test/recurrence/except_test.rb +++ b/test/recurrence/except_test.rb @@ -3,23 +3,25 @@ require "test_helper" class ExceptTest < Minitest::Test + using Recurrence::Refinements + test "accepts only valid date strings or Dates" do assert_raises(ArgumentError) { recurrence(except: :symbol) } assert_raises(ArgumentError) { recurrence(except: "invalid") } end test "skips day specified in except" do - r = recurrence(every: :day, except: Date.tomorrow) + r = recurrence(every: :day, except: Date.tomorrow, until: advance_days(2)) - assert_includes r, Date.current + assert_includes r, Date.today refute_includes r, Date.tomorrow - assert_includes r, Date.tomorrow + 1.day + assert_includes r, Date.tomorrow + 1 end test "skips multiple days specified in except" do r = recurrence(every: :day, except: [Date.tomorrow, "2012-02-29"]) - assert_includes r, Date.current + assert_includes r, Date.today refute_includes r, Date.tomorrow refute_includes r, "2012-02-29" end diff --git a/test/recurrence/monthly_recurring/day_test.rb b/test/recurrence/monthly_recurring/day_test.rb index 25e064a..61d7435 100644 --- a/test/recurrence/monthly_recurring/day_test.rb +++ b/test/recurrence/monthly_recurring/day_test.rb @@ -3,6 +3,8 @@ require "test_helper" class MonthlyRecurringDayTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.monthly(on: 31) @@ -10,21 +12,21 @@ class MonthlyRecurringDayTest < Minitest::Test end test "repeats until 8 months from now" do - date = 8.months.from_now + date = advance_months(8) r = recurrence(every: :month, on: date.day, until: date.to_date) assert_equal date.to_date, r.events[-1] end test "repeats through 8 months from now" do - date = 8.months.from_now + date = advance_months(8) r = recurrence(every: :month, on: date.day, through: date.to_date) assert_equal date.to_date, r.events[-1] end test "starts 9 months ago" do - date = 9.months.ago + date = Date.today << 9 r = recurrence(every: :month, on: date.day, starts: date.to_date) @@ -150,10 +152,14 @@ class MonthlyRecurringDayTest < Minitest::Test end test "uses except" do - r = recurrence(every: :month, on: Date.current.day, - except: 8.months.from_now.to_date) - - assert_includes r.events, 7.months.from_now.to_date - refute_includes r.events, 8.months.from_now.to_date + r = recurrence(every: :month, + on: Date.today.day, + except: advance_months(7), + until: advance_months(9)) + + assert_includes r.events, advance_months(6) + refute_includes r.events, advance_months(7) + assert_includes r.events, advance_months(8) + assert_includes r.events, advance_months(9) end end diff --git a/test/recurrence/monthly_recurring/weekday_test.rb b/test/recurrence/monthly_recurring/weekday_test.rb index 03aa3ec..860934b 100644 --- a/test/recurrence/monthly_recurring/weekday_test.rb +++ b/test/recurrence/monthly_recurring/weekday_test.rb @@ -3,6 +3,8 @@ require "test_helper" class MonthlyRecurringWeekdayTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.daily(on: 5, weekday: :thursday) @@ -16,7 +18,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test end test "repeats until 8 months from now" do - date = 8.months.from_now + date = advance_months(8) week = ((date.day - 1) / 7) + 1 r = recurrence( every: :month, @@ -29,7 +31,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test end test "repeats through 8 months from now" do - date = 8.months.from_now + date = advance_months(8) week = ((date.day - 1) / 7) + 1 r = recurrence( every: :month, @@ -42,7 +44,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test end test "starts 9 months ago" do - date = 9.months.ago + date = advance_months(9) week = ((date.day - 1) / 7) + 1 r = recurrence( every: :month, diff --git a/test/recurrence/recurrence_test.rb b/test/recurrence/recurrence_test.rb index a33e3b2..e707897 100644 --- a/test/recurrence/recurrence_test.rb +++ b/test/recurrence/recurrence_test.rb @@ -3,6 +3,8 @@ require "test_helper" class RecurrenceTest < Minitest::Test + using Recurrence::Refinements + test "returns the first available date based on initialization" do r = recurrence(every: :year, on: [2, 31], starts: "2008-01-01") @@ -34,17 +36,17 @@ class RecurrenceTest < Minitest::Test test "returns next date" do r = recurrence(every: :day) - assert_equal Date.current.to_s, r.next.to_s - assert_equal Date.current.to_s, r.next.to_s + assert_equal Date.today.to_s, r.next.to_s + assert_equal Date.today.to_s, r.next.to_s end test "returns next date and advance internal state" do r = recurrence(every: :day) - assert_equal Date.current.to_s, r.next!.to_s - assert_equal 1.day.from_now.to_date.to_s, r.next!.to_s - assert_equal 2.days.from_now.to_date.to_s, r.next!.to_s - assert_equal 3.days.from_now.to_date.to_s, r.next!.to_s + assert_equal Date.today, r.next! + assert_equal advance_days(1), r.next! + assert_equal advance_days(2), r.next! + assert_equal advance_days(3), r.next! end test "requires :every option" do diff --git a/test/recurrence/weekly_recurring_test.rb b/test/recurrence/weekly_recurring_test.rb index 8716b09..384498d 100644 --- a/test/recurrence/weekly_recurring_test.rb +++ b/test/recurrence/weekly_recurring_test.rb @@ -3,6 +3,8 @@ require "test_helper" class WeeklyRecurringTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.weekly(on: :thursday) @@ -10,31 +12,31 @@ class WeeklyRecurringTest < Minitest::Test end test "repeats 6 weeks from now" do - date = 6.weeks.from_now + date = advance_weeks(6) r = recurrence(every: :week, on: date.wday, until: date.to_date) assert_equal date.to_date, r.events[-1] end test "repeats through 6 weeks from now" do - date = 6.weeks.from_now - r = recurrence(every: :week, on: date.wday, through: date.to_date) + date = advance_weeks(6) + r = recurrence(every: :week, on: date.wday, through: date) assert_equal date.to_date, r.events[-1] end - test "starts 3 months ago (#{3.months.ago.to_date})" do - date = 3.months.ago + test "starts 3 months ago (#{advance_months(3)})" do + date = advance_months(3) r = recurrence(every: :week, on: date.wday, starts: date.to_date) assert_equal date.to_date, r.events[0] - assert_equal (date + 1.week).to_date, r.events[1] - assert_equal (date + 2.weeks).to_date, r.events[2] - assert_equal (date + 3.weeks).to_date, r.events[3] - assert_equal (date + 4.weeks).to_date, r.events[4] - assert_equal (date + 5.weeks).to_date, r.events[5] - assert_equal (date + 6.weeks).to_date, r.events[6] + assert_equal advance_weeks(1, date), r.events[1] + assert_equal advance_weeks(2, date), r.events[2] + assert_equal advance_weeks(3, date), r.events[3] + assert_equal advance_weeks(4, date), r.events[4] + assert_equal advance_weeks(5, date), r.events[5] + assert_equal advance_weeks(6, date), r.events[6] end test "starts at 2008-02-29 and repeat until 2008-03-14" do @@ -149,11 +151,11 @@ class WeeklyRecurringTest < Minitest::Test end test "uses except" do - date = 6.weeks.from_now + date = advance_weeks(6) r = recurrence(every: :week, on: date.wday, - except: 2.weeks.from_now.to_date) + except: advance_weeks(2)) - assert_includes r.events, 1.week.from_now.to_date - refute_includes r.events, 2.weeks.from_now.to_date + assert_includes r.events, advance_weeks(1) + refute_includes r.events, advance_weeks(2) end end diff --git a/test/recurrence/yearly_recurring_test.rb b/test/recurrence/yearly_recurring_test.rb index b2b4179..c891ca6 100644 --- a/test/recurrence/yearly_recurring_test.rb +++ b/test/recurrence/yearly_recurring_test.rb @@ -3,6 +3,8 @@ require "test_helper" class YearlyRecurringTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.yearly(on: [12, 31]) @@ -10,7 +12,7 @@ class YearlyRecurringTest < Minitest::Test end test "repeats until 7 years from now" do - date = 7.years.from_now + date = advance_years(7) r = recurrence( every: :year, on: [date.month, date.day], @@ -21,7 +23,7 @@ class YearlyRecurringTest < Minitest::Test end test "repeats through 7 years from now" do - date = 7.years.from_now + date = advance_years(7) r = recurrence( every: :year, on: [date.month, date.day], @@ -32,7 +34,7 @@ class YearlyRecurringTest < Minitest::Test end test "starts 2 years ago" do - date = 2.years.ago + date = advance_years(2) r = recurrence( every: :year, on: [date.month, date.day], @@ -141,7 +143,7 @@ class YearlyRecurringTest < Minitest::Test r = Recurrence.yearly(on: [12, 31], except: "#{Time.now.year + 3}-12-31") - assert_includes r.events, "#{Time.now.year + 2}-12-31".to_date - refute_includes r.events, "#{Time.now.year + 3}-12-31".to_date + assert_includes r.events, Date.parse("#{Time.now.year + 2}-12-31") + refute_includes r.events, Date.parse("#{Time.now.year + 3}-12-31") end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 307fd23..8ae5fcc 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,11 +6,32 @@ require "recurrence" -Date::DATE_FORMATS[:date] = "%d/%m/%Y" -Time::DATE_FORMATS[:date] = "%d/%m/%Y" +# Date::DATE_FORMATS[:date] = "%d/%m/%Y" +# Time::DATE_FORMATS[:date] = "%d/%m/%Y" module Minitest class Test + module Ext + def advance_months(months, date = Date.today) + date >> months + end + + def advance_days(days, date = Date.today) + date + days + end + + def advance_years(years, date = Date.today) + date >> (years * 12) + end + + def advance_weeks(weeks, date = Date.today) + date + (weeks * 7) + end + end + + include Ext + extend Ext + def recurrence(options) Recurrence.new(options) end @@ -18,5 +39,9 @@ def recurrence(options) setup do Recurrence.default_starts_date = Recurrence::DEFAULT_STARTS_DATE end + + def self.advance_months(months, date = Date.today) + date >> months + end end end From 62acfc433f88ee9c1ef2c29d533d01111a6f9d1b Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 12:50:57 -0700 Subject: [PATCH 4/6] Incorporate pull requests. Close #33. Close #38. --- lib/recurrence/event/base.rb | 6 ++++-- lib/recurrence/event/monthly.rb | 4 ++-- lib/recurrence/event/yearly.rb | 18 +++++++++--------- test/recurrence/date_shift_test.rb | 12 ++++++++++++ test/recurrence/yearly_recurring_test.rb | 8 ++++++++ 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/lib/recurrence/event/base.rb b/lib/recurrence/event/base.rb index b04c782..c155722 100644 --- a/lib/recurrence/event/base.rb +++ b/lib/recurrence/event/base.rb @@ -82,8 +82,10 @@ def finished? # Common validation for inherited classes. # - private def valid_month_day?(day) - raise ArgumentError, "invalid day #{day}" unless (1..31).cover?(day) + private def validate_month_day(day) + return if (1..31).cover?(day) + + raise ArgumentError, "invalid day: #{day.inspect}" end # Check if the given key has a valid weekday (0 upto 6) or a valid weekday diff --git a/lib/recurrence/event/monthly.rb b/lib/recurrence/event/monthly.rb index 1f9060d..c678e45 100644 --- a/lib/recurrence/event/monthly.rb +++ b/lib/recurrence/event/monthly.rb @@ -26,7 +26,7 @@ class Monthly < Base # :nodoc: all @options[:weekday] = valid_weekday_or_weekday_name?(@options[:weekday]) else - valid_month_day?(@options[:on]) + validate_month_day(@options[:on]) end return unless @options[:interval].is_a?(Symbol) @@ -105,7 +105,7 @@ class Monthly < Base # :nodoc: all end private def shift_to(date) - @options[:on] = date.day + @options[:on] = date.day unless @options[:weekday] end private def valid_ordinal?(ordinal) diff --git a/lib/recurrence/event/yearly.rb b/lib/recurrence/event/yearly.rb index c87759d..560c4f2 100644 --- a/lib/recurrence/event/yearly.rb +++ b/lib/recurrence/event/yearly.rb @@ -19,12 +19,12 @@ class Yearly < Base # :nodoc: all }.freeze private def validate - valid_month_day?(@options[:on].last) + validate_month_day(@options[:on]&.last) if @options[:on].first.is_a?(Numeric) - valid_month?(@options[:on].first) + validate_month(@options[:on].first) else - valid_month_name?(@options[:on].first) + validate_month_name(@options[:on].first) @options[:on] = [MONTHS[@options[:on].first.to_s], @options[:on].last] end end @@ -50,16 +50,16 @@ class Yearly < Base # :nodoc: all @options[:on] = [date.month, date.day] end - private def valid_month?(month) - return false if (1..12).cover?(month) + private def validate_month(month) + return if (1..12).cover?(month) - raise ArgumentError, "invalid month #{month}" + raise ArgumentError, "invalid month: #{month.inspect}" end - private def valid_month_name?(month) - return false if MONTHS.key?(month.to_s) + private def validate_month_name(month) + return if MONTHS.key?(month.to_s) - raise ArgumentError, "invalid month #{month}" + raise ArgumentError, "invalid month: #{month.inspect}" end end end diff --git a/test/recurrence/date_shift_test.rb b/test/recurrence/date_shift_test.rb index 00908c6..87c30b5 100644 --- a/test/recurrence/date_shift_test.rb +++ b/test/recurrence/date_shift_test.rb @@ -75,4 +75,16 @@ class DateShiftTest < Minitest::Test assert_equal Date.new(2012, 2, 29), r.next end + + test "correctly recurrs for weekdays" do + r = recurrence(every: :month, + starts: "2011-01-31", + on: "first", + weekday: "monday", + shift: true) + + assert_equal Date.new(2011, 2, 7), r.events[0] + assert_equal Date.new(2011, 3, 7), r.events[1] + assert_equal Date.new(2011, 4, 4), r.events[2] + end end diff --git a/test/recurrence/yearly_recurring_test.rb b/test/recurrence/yearly_recurring_test.rb index c891ca6..3a6cb93 100644 --- a/test/recurrence/yearly_recurring_test.rb +++ b/test/recurrence/yearly_recurring_test.rb @@ -146,4 +146,12 @@ class YearlyRecurringTest < Minitest::Test assert_includes r.events, Date.parse("#{Time.now.year + 2}-12-31") refute_includes r.events, Date.parse("#{Time.now.year + 3}-12-31") end + + test "it doesn't fail when :on is not present" do + error = assert_raises ArgumentError do + Recurrence.yearly + end + + assert_equal "invalid day: nil", error.message + end end From 8ae43da50ca1812b09f9bd4e11cf5ca255330e8c Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 12:59:50 -0700 Subject: [PATCH 5/6] Rename method. --- lib/recurrence/event/base.rb | 7 +++---- lib/recurrence/event/monthly.rb | 20 +++++++++----------- lib/recurrence/event/weekly.rb | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/recurrence/event/base.rb b/lib/recurrence/event/base.rb index c155722..32616e7 100644 --- a/lib/recurrence/event/base.rb +++ b/lib/recurrence/event/base.rb @@ -92,17 +92,16 @@ def finished? # name (defined in the DAYS constant). If a weekday name (String) is # given, convert it to a weekday (Integer). # - private def valid_weekday_or_weekday_name?(value) + private def expand_weekday!(value) if value.is_a?(Numeric) unless (0..6).cover?(value) - raise ArgumentError, - "invalid day #{value}" + raise ArgumentError, "invalid weekday: #{value}" end value else weekday = WEEKDAYS[value.to_s] - raise ArgumentError, "invalid weekday #{value}" unless weekday + raise ArgumentError, "invalid weekday: #{value}" unless weekday weekday end diff --git a/lib/recurrence/event/monthly.rb b/lib/recurrence/event/monthly.rb index c678e45..b1845b7 100644 --- a/lib/recurrence/event/monthly.rb +++ b/lib/recurrence/event/monthly.rb @@ -12,26 +12,24 @@ class Monthly < Base # :nodoc: all private def validate if @options.key?(:weekday) - # Allow :on => :last, :weekday => :thursday contruction. if @options[:on].to_s == "last" @options[:on] = 5 elsif @options[:on].is_a?(Numeric) - valid_week?(@options[:on]) + validate_week(@options[:on]) else - valid_ordinal?(@options[:on]) + validate_ordinal(@options[:on]) @options[:on] = ORDINALS.index(@options[:on].to_s) + 1 end - @options[:weekday] = - valid_weekday_or_weekday_name?(@options[:weekday]) + @options[:weekday] = expand_weekday!(@options[:weekday]) else validate_month_day(@options[:on]) end return unless @options[:interval].is_a?(Symbol) - valid_interval?(@options[:interval]) + validate_interval(@options[:interval]) @options[:interval] = INTERVALS[@options[:interval]] end @@ -108,19 +106,19 @@ class Monthly < Base # :nodoc: all @options[:on] = date.day unless @options[:weekday] end - private def valid_ordinal?(ordinal) - return false if ORDINALS.include?(ordinal.to_s) + private def validate_ordinal(ordinal) + return if ORDINALS.include?(ordinal.to_s) raise ArgumentError, "invalid ordinal #{ordinal}" end - private def valid_interval?(interval) - return false if INTERVALS.key?(interval) + private def validate_interval(interval) + return if INTERVALS.key?(interval) raise ArgumentError, "invalid ordinal #{interval}" end - private def valid_week?(week) + private def validate_week(week) raise ArgumentError, "invalid week #{week}" unless (1..5).cover?(week) end end diff --git a/lib/recurrence/event/weekly.rb b/lib/recurrence/event/weekly.rb index cb3d232..fdb818b 100644 --- a/lib/recurrence/event/weekly.rb +++ b/lib/recurrence/event/weekly.rb @@ -5,7 +5,7 @@ module Event class Weekly < Base # :nodoc: all private def validate @options[:on] = Array(@options[:on]).inject([]) do |days, value| - days << valid_weekday_or_weekday_name?(value) + days << expand_weekday!(value) end @options[:on].sort! From cac8104e5b719fc7f8b8c0e05777d52590b52a7e Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 28 May 2025 13:07:29 -0700 Subject: [PATCH 6/6] Add simplecov. --- .gitignore | 1 + lib/recurrence/event/monthly.rb | 8 +++++--- lib/recurrence/namespace.rb | 2 +- recurrence.gemspec | 2 +- test/test_helper.rb | 7 ++++--- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 5e0acd4..4a374cb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .bundle html *.lock +coverage diff --git a/lib/recurrence/event/monthly.rb b/lib/recurrence/event/monthly.rb index b1845b7..46cd42f 100644 --- a/lib/recurrence/event/monthly.rb +++ b/lib/recurrence/event/monthly.rb @@ -109,17 +109,19 @@ class Monthly < Base # :nodoc: all private def validate_ordinal(ordinal) return if ORDINALS.include?(ordinal.to_s) - raise ArgumentError, "invalid ordinal #{ordinal}" + raise ArgumentError, "invalid ordinal: #{ordinal}" end private def validate_interval(interval) return if INTERVALS.key?(interval) - raise ArgumentError, "invalid ordinal #{interval}" + raise ArgumentError, "invalid ordinal: #{interval.inspect}" end private def validate_week(week) - raise ArgumentError, "invalid week #{week}" unless (1..5).cover?(week) + return if (1..5).cover?(week) + + raise ArgumentError, "invalid week: #{week.inspect}" end end end diff --git a/lib/recurrence/namespace.rb b/lib/recurrence/namespace.rb index 6ede6a7..0ddefb3 100644 --- a/lib/recurrence/namespace.rb +++ b/lib/recurrence/namespace.rb @@ -261,7 +261,7 @@ def events(options = {}) end end - # Works like SimplesIdeias::Recurrence::Namespace#events, but removes the + # Works like Recurrence::Namespace#events, but removes the # cache first. def events!(options = {}) reset! diff --git a/recurrence.gemspec b/recurrence.gemspec index 432fc5e..ed209ab 100644 --- a/recurrence.gemspec +++ b/recurrence.gemspec @@ -21,8 +21,8 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.add_development_dependency "minitest-utils" - s.add_development_dependency "pry-meta" s.add_development_dependency "rake" s.add_development_dependency "rubocop" s.add_development_dependency "rubocop-fnando" + s.add_development_dependency "simplecov" end diff --git a/test/test_helper.rb b/test/test_helper.rb index 8ae5fcc..b4a48fe 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,14 +1,15 @@ # frozen_string_literal: true +require "simplecov" + +SimpleCov.start + require "bundler/setup" require "minitest/utils" require "minitest/autorun" require "recurrence" -# Date::DATE_FORMATS[:date] = "%d/%m/%Y" -# Time::DATE_FORMATS[:date] = "%d/%m/%Y" - module Minitest class Test module Ext