Skip to content

Commit 3a55740

Browse files
authored
Merge branch 'master' into backwards-compatibility-ruby-2
2 parents 6054715 + ce99543 commit 3a55740

36 files changed

Lines changed: 1981 additions & 33 deletions

.github/workflows/tests.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/checkout@v4
3434
- uses: ruby/setup-ruby@v1
3535
with:
36-
ruby-version: '3.1'
36+
ruby-version: '3.4'
3737
bundler-cache: true
3838
- run: bundle exec standardrb
3939
test:
@@ -42,8 +42,8 @@ jobs:
4242
- lint
4343
strategy:
4444
matrix:
45-
rails: ['6.1', '7.0', '7.1']
46-
ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3']
45+
rails: ['6.1', '7.0', '7.1', '7.2', '8.0', '8.1']
46+
ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3', '3.4', '4.0']
4747
runs-on: ubuntu-latest
4848
env:
4949
RAILS_VERSION: ${{ matrix.rails }}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [UNRELEASED]
8+
### Added
9+
- Support for BYSETPOS with all rule frequencies (yearly, monthly, weekly, daily, hourly, minutely, secondly). ([#449](https://github.com/ice-cube-ruby/ice_cube/pull/449)) by [@nehresma](https://github.com/nehresma) and [@NicolasMarlier](https://github.com/NicolasMarlier)
10+
11+
### Changed
12+
- Updated CI test matrix to support Rails 7.2, 8.0, 8.1 and Ruby 3.2, 3.3, 3.4, 4.0 by [@nehresma](https://github.com/nehresma) and [@pacso](https://github.com/pacso)
13+
714
## [0.17.0] - 2024-07-18
815
### Added
916
- Indonesian translations. ([#505](https://github.com/seejohnrun/ice_cube/pull/505)) by [@achmiral](https://github.com/achmiral)

Gemfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ compatible_rails_versions = [
99
gem "activesupport", (ENV["RAILS_VERSION"] || compatible_rails_versions), require: false
1010
gem "i18n", require: false
1111
gem "tzinfo", require: false # only needed explicitly for RAILS_VERSION=3
12+
13+
gem "base64", require: false # remove base64 deprecation warnings for Ruby 3.3+
14+
gem "bigdecimal", require: false # remove bigdecimal deprecation warnings for Ruby 3.3+
15+
gem "mutex_m", require: false # ActiveSupport dependency on Ruby 3.4+
16+
gem "ostruct", require: false # remove ostruct deprecation warnings for Ruby 3.4+
17+
gem "logger", require: false # remove logger deprecation warnings for Ruby 3.4+
18+
gem "benchmark", require: false # remove benchmark deprecation warnings for Ruby 3.4+

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,31 @@ schedule.add_recurrence_rule IceCube::Rule.yearly(3).month_of_year(:march)
253253
schedule.add_recurrence_rule IceCube::Rule.yearly(3).month_of_year(3)
254254
```
255255

256+
### BYSETPOS (select the nth occurrence)
257+
258+
BYSETPOS selects the nth occurrence within each interval after all other BYxxx
259+
filters/expansions are applied. Use positive values (from the start) or
260+
negative values (from the end). Repeated values do not duplicate occurrences,
261+
and positions beyond the set size yield no occurrence for that interval.
262+
RFC 5545 requires BYSETPOS to be used with another BYxxx rule part; IceCube
263+
allows BYSETPOS without another BYxxx and applies it to the single occurrence
264+
in each interval.
265+
266+
```ruby
267+
# last weekday of the month
268+
schedule.add_recurrence_rule(
269+
IceCube::Rule.monthly.day(:monday, :tuesday, :wednesday, :thursday, :friday).by_set_pos(-1)
270+
)
271+
272+
# second occurrence in each day's expanded set
273+
schedule.add_recurrence_rule(
274+
IceCube::Rule.daily.hour_of_day(9, 17).by_set_pos(2)
275+
)
276+
```
277+
278+
Note: If you expand with BYHOUR/BYMINUTE/BYSECOND, any unspecified smaller
279+
time components are inherited from the schedule's start_time.
280+
256281
### Hourly (by hour of day)
257282

258283
```ruby

ice_cube.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ Gem::Specification.new do |s|
2222
s.add_development_dependency("rake")
2323
s.add_development_dependency("rspec", "> 3")
2424
s.add_development_dependency("standard")
25+
s.add_development_dependency("benchmark")
2526
end

lib/ice_cube.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ module Validations
5050
autoload :YearlyInterval, "ice_cube/validations/yearly_interval"
5151
autoload :HourlyInterval, "ice_cube/validations/hourly_interval"
5252

53+
autoload :BySetPosHelper, "ice_cube/validations/by_set_pos_helper"
54+
autoload :SecondlyBySetPos, "ice_cube/validations/secondly_by_set_pos"
55+
autoload :MinutelyBySetPos, "ice_cube/validations/minutely_by_set_pos"
56+
autoload :HourlyBySetPos, "ice_cube/validations/hourly_by_set_pos"
57+
autoload :DailyBySetPos, "ice_cube/validations/daily_by_set_pos"
58+
autoload :WeeklyBySetPos, "ice_cube/validations/weekly_by_set_pos"
59+
autoload :MonthlyBySetPos, "ice_cube/validations/monthly_by_set_pos"
60+
autoload :YearlyBySetPos, "ice_cube/validations/yearly_by_set_pos"
61+
5362
autoload :HourOfDay, "ice_cube/validations/hour_of_day"
5463
autoload :MonthOfYear, "ice_cube/validations/month_of_year"
5564
autoload :MinuteOfHour, "ice_cube/validations/minute_of_hour"

lib/ice_cube/occurrence.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,8 @@ def to_time
8585
# time formats and is only used when ActiveSupport is available.
8686
#
8787
def to_s(format = nil)
88-
if format && to_time.public_method(:to_s).arity != 0
89-
t0, t1 = start_time.to_s(format), end_time.to_s(format)
90-
else
91-
t0, t1 = start_time.to_s, end_time.to_s
92-
end
88+
t0 = format_time(start_time, format)
89+
t1 = format_time(end_time, format)
9390
(duration > 0) ? "#{t0} - #{t1}" : t0
9491
end
9592

@@ -98,5 +95,18 @@ def overnight?
9895
midnight = Time.new(offset.year, offset.month, offset.day)
9996
midnight < end_time
10097
end
98+
99+
private
100+
101+
# Normalize formatted output across ActiveSupport versions:
102+
# Rails 7.1+ prefers to_fs, older versions use to_formatted_s or to_s(:format).
103+
def format_time(time, format)
104+
return time.to_s unless format
105+
return time.to_fs(format) if time.respond_to?(:to_fs)
106+
return time.to_formatted_s(format) if time.respond_to?(:to_formatted_s)
107+
return time.to_s(format) if time.public_method(:to_s).arity != 0
108+
109+
time.to_s
110+
end
101111
end
102112
end

lib/ice_cube/parsers/ical_parser.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@ module IceCube
22
class IcalParser
33
def self.schedule_from_ical(ical_string, options = {})
44
data = {}
5+
6+
# First join lines that are wrapped
7+
lines = []
58
ical_string.each_line do |line|
9+
if lines[-1] && line =~ /\A[ \t].+/
10+
lines[-1] = lines[-1].strip + line.sub(/\A[ \t]+/, "")
11+
else
12+
lines << line
13+
end
14+
end
15+
16+
lines.each do |line|
617
(property, value) = line.split(":")
718
(property, _tzid) = property.split(";")
819
case property
@@ -75,7 +86,7 @@ def self.rule_from_ical(ical)
7586
when "BYYEARDAY"
7687
validations[:day_of_year] = value.split(",").map(&:to_i)
7788
when "BYSETPOS"
78-
# noop
89+
validations[:by_set_pos] = value.split(",").map(&:to_i)
7990
else
8091
validations[name] = nil # invalid type
8192
end

lib/ice_cube/rule.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def self.from_ical(ical)
3636
end
3737

3838
# Yaml implementation
39-
def to_yaml(*args)
40-
YAML.dump(to_hash, *args)
39+
def to_yaml(*)
40+
YAML.dump(to_hash, *)
4141
end
4242

4343
# From yaml
@@ -49,11 +49,11 @@ def to_hash
4949
raise MethodNotImplemented, "Expected to be overridden by subclasses"
5050
end
5151

52-
def next_time(time, schedule, closing_time)
52+
def next_time(time, schedule, closing_time, increment: true)
5353
end
5454

5555
def on?(time, schedule)
56-
next_time(time, schedule, time).to_i == time.to_i
56+
next_time(time, schedule, time, increment: false).to_i == time.to_i
5757
end
5858

5959
class << self

lib/ice_cube/rules/daily_rule.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class DailyRule < ValidatedRule
1010
# include Validations::DayOfYear # n/a
1111

1212
include Validations::DailyInterval
13+
include Validations::DailyBySetPos
1314

1415
def initialize(interval = 1)
1516
super

0 commit comments

Comments
 (0)