Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.bundle
html
*.lock
coverage
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ inherit_gem:
rubocop-fnando: .rubocop.yml

AllCops:
TargetRubyVersion: 2.5
TargetRubyVersion: 3.3
NewCops: enable
Exclude:
- bin/**/*
Expand Down
2 changes: 1 addition & 1 deletion lib/recurrence.rb
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
11 changes: 0 additions & 11 deletions lib/recurrence/event.rb

This file was deleted.

16 changes: 10 additions & 6 deletions lib/recurrence/event/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
30 changes: 15 additions & 15 deletions lib/recurrence/event/monthly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lib/recurrence/event/weekly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
14 changes: 7 additions & 7 deletions lib/recurrence/event/yearly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
7 changes: 0 additions & 7 deletions lib/recurrence/handler.rb

This file was deleted.

2 changes: 2 additions & 0 deletions lib/recurrence/handler/fall_back.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 29 additions & 22 deletions lib/recurrence/namespace.rb
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!
Expand All @@ -280,14 +285,14 @@ def events!(options = {})
# r.each
# #=> #<Enumerator: [Mon, 15 Nov 2010, Tue, 16 Nov 2010]: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)
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions lib/recurrence/refinements/date.rb
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions lib/recurrence/refinements/time.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion lib/recurrence/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ module Version
MAJOR = 1
MINOR = 3
PATCH = 0
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}".freeze
end
end
10 changes: 4 additions & 6 deletions recurrence.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading
Loading