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/.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.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..32616e7 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 @@ -79,25 +82,26 @@ 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 # 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 63695f5..46cd42f 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 - valid_month_day?(@options[:on]) + 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 @@ -40,7 +38,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) @@ -105,23 +103,25 @@ 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) + 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 valid_interval?(interval) + private def validate_interval(interval) return if INTERVALS.key?(interval) - raise ArgumentError, "invalid ordinal #{interval}" + raise ArgumentError, "invalid ordinal: #{interval.inspect}" end - private def valid_week?(week) - raise ArgumentError, "invalid week #{week}" unless (1..5).cover?(week) + private def validate_week(week) + return if (1..5).cover?(week) + + raise ArgumentError, "invalid week: #{week.inspect}" end end end diff --git a/lib/recurrence/event/weekly.rb b/lib/recurrence/event/weekly.rb index 2d877a9..fdb818b 100644 --- a/lib/recurrence/event/weekly.rb +++ b/lib/recurrence/event/weekly.rb @@ -4,8 +4,8 @@ class Recurrence_ module Event class Weekly < Base # :nodoc: all private def validate - @options[:on] = Array.wrap(@options[:on]).inject([]) do |days, value| - days << valid_weekday_or_weekday_name?(value) + @options[:on] = Array(@options[:on]).inject([]) do |days, value| + days << expand_weekday!(value) end @options[:on].sort! diff --git a/lib/recurrence/event/yearly.rb b/lib/recurrence/event/yearly.rb index eab5ba0..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) + 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) + 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/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 463d81e..0ddefb3 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 @@ -179,11 +184,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 @@ -256,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! @@ -280,14 +285,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) @@ -322,12 +327,14 @@ def each!(&block) 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/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..ed209ab 100644 --- a/recurrence.gemspec +++ b/recurrence.gemspec @@ -3,28 +3,26 @@ 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 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 + 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" s.add_development_dependency "rubocop" s.add_development_dependency "rubocop-fnando" + s.add_development_dependency "simplecov" end diff --git a/test/recurrence/daily_recurring_test.rb b/test/recurrence/daily_recurring_test.rb index dbee189..ae57f08 100644 --- a/test/recurrence/daily_recurring_test.rb +++ b/test/recurrence/daily_recurring_test.rb @@ -3,34 +3,41 @@ require "test_helper" class DailyRecurringTest < Minitest::Test + using Recurrence::Refinements + 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 + 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_s(: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 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 +46,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 +54,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 +62,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 +73,7 @@ class DailyRecurringTest < Minitest::Test until: "2008-04-25", interval: 2 ) + assert_equal "2008-04-24", r.events[-1].to_s end @@ -73,12 +84,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) + r = Recurrence.daily(except: Date.tomorrow, until: advance_days(2)) + + refute_includes r.events, Date.tomorrow + assert_includes r.events, Date.tomorrow + 1 end end diff --git a/test/recurrence/date_shift_test.rb b/test/recurrence/date_shift_test.rb index b77a3ec..87c30b5 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] @@ -70,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/default_starts_date_test.rb b/test/recurrence/default_starts_date_test.rb index b22d116..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 @@ -15,9 +17,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) + r = Recurrence.new(every: :day, until: advance_days(3)) + 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..56a4cde 100644 --- a/test/recurrence/except_test.rb +++ b/test/recurrence/except_test.rb @@ -3,24 +3,26 @@ 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 r.include?(Date.current) - refute r.include?(Date.tomorrow) - assert r.include?(Date.tomorrow + 1.day) + assert_includes r, Date.today + refute_includes r, Date.tomorrow + 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 r.include?(Date.current) - refute r.include?(Date.tomorrow) - refute r.include?("2012-02-29") + assert_includes r, Date.today + 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..61d7435 100644 --- a/test/recurrence/monthly_recurring/day_test.rb +++ b/test/recurrence/monthly_recurring/day_test.rb @@ -3,27 +3,33 @@ require "test_helper" class MonthlyRecurringDayTest < Minitest::Test + using Recurrence::Refinements + 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 + 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) + assert_equal date.to_date, r.events[0] end @@ -37,6 +43,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 +58,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 +72,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-10-27", r.events[0].to_s end @@ -76,6 +85,7 @@ class MonthlyRecurringDayTest < Minitest::Test starts: starts, through: "2009-01-01" ) + assert_equal "2009-01-27", r.events[-1].to_s end @@ -88,6 +98,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 +115,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 +131,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,14 +147,19 @@ class MonthlyRecurringDayTest < Minitest::Test until: "2010-01-01", repeat: 5 ) + assert_equal 5, r.events.size end test "uses except" do - 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) + 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/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..860934b 100644 --- a/test/recurrence/monthly_recurring/weekday_test.rb +++ b/test/recurrence/monthly_recurring/weekday_test.rb @@ -3,18 +3,22 @@ require "test_helper" class MonthlyRecurringWeekdayTest < Minitest::Test + using Recurrence::Refinements + 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 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, @@ -22,11 +26,12 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, 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) week = ((date.day - 1) / 7) + 1 r = recurrence( every: :month, @@ -34,11 +39,12 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, through: date.to_date ) + assert_equal date.to_date, r.events[-1] 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, @@ -46,6 +52,7 @@ class MonthlyRecurringWeekdayTest < Minitest::Test weekday: date.wday, starts: date.to_date ) + assert_equal date.to_date, r.events[0] end @@ -60,6 +67,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 +83,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 +98,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 +118,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..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") @@ -27,23 +29,24 @@ class RecurrenceTest < Minitest::Test test "returns passed-in options" do r = recurrence(every: :day) options = {every: :day} + assert_equal options, r.options end 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 @@ -76,7 +79,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..384498d 100644 --- a/test/recurrence/weekly_recurring_test.rb +++ b/test/recurrence/weekly_recurring_test.rb @@ -3,34 +3,40 @@ require "test_helper" class WeeklyRecurringTest < Minitest::Test + using Recurrence::Refinements + 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 + 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_s(: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 @@ -43,6 +49,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 +65,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 +80,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 +99,7 @@ class WeeklyRecurringTest < Minitest::Test until: "2011-01-01", repeat: 5 ) + assert_equal 5, r.events.size end @@ -102,6 +112,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 +129,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,15 +146,16 @@ class WeeklyRecurringTest < Minitest::Test starts: starts, until: "2009-01-01" ) + assert_equal "2008-09-27", r.events[0].to_s 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 r.events.include?(1.week.from_now.to_date) - refute r.events.include?(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 4a979ac..3a6cb93 100644 --- a/test/recurrence/yearly_recurring_test.rb +++ b/test/recurrence/yearly_recurring_test.rb @@ -3,38 +3,44 @@ require "test_helper" class YearlyRecurringTest < Minitest::Test + using Recurrence::Refinements + test "recurs until limit date" do r = Recurrence.yearly(on: [12, 31]) + assert_equal Date.parse("2037-12-31"), r.events[-1] 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], until: date.to_date ) + assert_equal date.to_date, r.events[-1] 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], through: date.to_date ) + assert_equal date.to_date, r.events[-1] end test "starts 2 years ago" do - date = 2.years.ago + date = advance_years(2) r = recurrence( every: :year, on: [date.month, date.day], starts: date.to_date ) + assert_equal date.to_date, r.events[0] end @@ -48,6 +54,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 +69,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 +83,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 +99,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, repeat: 5 ) + assert_equal 5, r.events.size end @@ -103,6 +113,7 @@ class YearlyRecurringTest < Minitest::Test starts: starts, through: ends ) + assert_equal "2019-06-07", r.events[-1].to_s end @@ -111,6 +122,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 +130,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 +143,15 @@ 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, 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 diff --git a/test/test_helper.rb b/test/test_helper.rb index 307fd23..b4a48fe 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,16 +1,38 @@ # 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 + 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 +40,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