Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
/spec/reports/
/tmp/

# rubymine
.idea

# rspec failure tracking
.rspec_status
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Indonesian translations. ([#505](https://github.com/seejohnrun/ice_cube/pull/505)) by [@achmiral](https://github.com/achmiral)
- Support for BYSETPOS

### Changed
- Removed use of `delegate` method added in [66f1d797](https://github.com/ice-cube-ruby/ice_cube/commit/66f1d797092734563bfabd2132c024c7d087f683) , reverting to previous implementation. ([#522](https://github.com/ice-cube-ruby/ice_cube/pull/522)) by [@pacso](https://github.com/pacso)
Expand Down
1 change: 0 additions & 1 deletion ice_cube.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Gem::Specification.new do |s|
s.version = IceCube::VERSION
s.platform = Gem::Platform::RUBY
s.files = Dir["lib/**/*.rb", "config/**/*.yml"]
s.test_files = Dir.glob("spec/*.rb")
s.require_paths = ["lib"]

s.add_development_dependency("rake")
Expand Down
3 changes: 3 additions & 0 deletions lib/ice_cube.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ module Validations
autoload :YearlyInterval, "ice_cube/validations/yearly_interval"
autoload :HourlyInterval, "ice_cube/validations/hourly_interval"

autoload :MonthlyBySetPos, "ice_cube/validations/monthly_by_set_pos"
autoload :YearlyBySetPos, "ice_cube/validations/yearly_by_set_pos"

autoload :HourOfDay, "ice_cube/validations/hour_of_day"
autoload :MonthOfYear, "ice_cube/validations/month_of_year"
autoload :MinuteOfHour, "ice_cube/validations/minute_of_hour"
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/builders/string_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def ordinal(number)
ord = IceCube::I18n.t("ice_cube.integer.ordinals")[number] ||
IceCube::I18n.t("ice_cube.integer.ordinals")[number % 10] ||
IceCube::I18n.t("ice_cube.integer.ordinals")[:default]
number >= 0 ? ord : IceCube::I18n.t("ice_cube.integer.negative", ordinal: ord)
(number >= 0) ? ord : IceCube::I18n.t("ice_cube.integer.negative", ordinal: ord)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/input_alignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def interval_validation
end

def interval_value
@interval_value ||= rule_part == :interval ? value : interval_validation.interval
@interval_value ||= (rule_part == :interval) ? value : interval_validation.interval
end

def fixed_validations
@fixed_validations ||= @rule.validations.values.flatten.select { |v|
interval_type = (v.type == :wday ? :day : v.type)
interval_type = ((v.type == :wday) ? :day : v.type)
v.class < Validations::FixedValue &&
interval_type == rule.base_interval_validation.type
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/null_i18n.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module NullI18n
def self.t(key, options = {})
base = key.to_s.split(".").reduce(config) { |hash, current_key| hash[current_key] }

base = base[options[:count] == 1 ? "one" : "other"] if options[:count]
base = base[(options[:count] == 1) ? "one" : "other"] if options[:count]

case base
when Hash
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/occurrence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def to_s(format = nil)
else
t0, t1 = start_time.to_s, end_time.to_s
end
duration > 0 ? "#{t0} - #{t1}" : t0
(duration > 0) ? "#{t0} - #{t1}" : t0
end

def overnight?
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/parsers/ical_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def self.rule_from_ical(ical)
when "BYYEARDAY"
validations[:day_of_year] = value.split(",").map(&:to_i)
when "BYSETPOS"
# noop
params[:validations][:by_set_pos] = value.split(",").collect(&:to_i)
else
validations[name] = nil # invalid type
end
Expand Down
1 change: 1 addition & 0 deletions lib/ice_cube/rules/monthly_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MonthlyRule < ValidatedRule
# include Validations::DayOfYear # n/a

include Validations::MonthlyInterval
include Validations::MonthlyBySetPos

def initialize(interval = 1)
super
Expand Down
1 change: 1 addition & 0 deletions lib/ice_cube/rules/yearly_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class YearlyRule < ValidatedRule
include Validations::DayOfYear

include Validations::YearlyInterval
include Validations::YearlyBySetPos

def initialize(interval = 1)
super
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def previous_occurrences(num, from)
from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{from.inspect}"
return [] if from <= start_time
a = enumerate_occurrences(start_time, from - 1).to_a
a.size > num ? a[-1 * num, a.size] : a
(a.size > num) ? a[-1 * num, a.size] : a
end

# The remaining occurrences (same requirements as all_occurrences)
Expand Down
54 changes: 43 additions & 11 deletions lib/ice_cube/time_util.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "date"
require "time"
require "active_support"
require "active_support/core_ext"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like upstream won't accept this code if it uses ActiveSupport, since they want to keep it pure ruby.

ice-cube-ruby#449 (comment)
ice-cube-ruby#449 (comment)
ice-cube-ruby#449 (comment)

So if we did want to push this upstream we might need to replace them with pure ruby, but since that PR is still getting updates maybe we won't need to. So we can keep ActiveSupport for now and wait for upstream to fix & merge their version.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Achieve functionality first then optimise later.

That's a good rule. The only thing is that tech-debt can hide under the radar. But, still, you probably want the functionality first. So,

Achieve functionality first then remove tech-debt and optimise later.

It would be nice to push this upstream, but we don't really need to. So, let's deal with that later too. 👍


module IceCube
module TimeUtil
Expand Down Expand Up @@ -51,7 +53,7 @@ def self.match_zone(input_time, reference)
else
time.getlocal(reference.utc_offset)
end
Date === input_time ? beginning_of_date(time, reference) : time
(Date === input_time) ? beginning_of_date(time, reference) : time
end

# Ensure that this is either nil, or a time
Expand Down Expand Up @@ -193,6 +195,36 @@ def self.which_occurrence_in_month(time, wday)
[nth_occurrence_of_weekday, this_weekday_in_month_count]
end

# Use Activesupport CoreExt functions to manipulate time
def self.start_of_month time
time.beginning_of_month
end

# Use Activesupport CoreExt functions to manipulate time
def self.end_of_month time
time.end_of_month
end

# Use Activesupport CoreExt functions to manipulate time
def self.start_of_year time
time.beginning_of_year
end

# Use Activesupport CoreExt functions to manipulate time
def self.end_of_year time
time.end_of_year
end

# Use Activesupport CoreExt functions to manipulate time
def self.previous_month time
time - 1.month
end

# Use Activesupport CoreExt functions to manipulate time
def self.previous_year time
time - 1.year
end

# Get the days in the month for +time
def self.days_in_month(time)
date = Date.new(time.year, time.month, 1)
Expand Down Expand Up @@ -286,12 +318,12 @@ def to_time
def add(type, val)
type = :day if type == :wday
@time += case type
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
end
end

Expand All @@ -318,20 +350,20 @@ def sec=(value)
end

def clear_sec
@time.sec > 0 ? @time -= @time.sec : @time
(@time.sec > 0) ? @time -= @time.sec : @time
end

def clear_min
@time.min > 0 ? @time -= (@time.min * ONE_MINUTE) : @time
(@time.min > 0) ? @time -= (@time.min * ONE_MINUTE) : @time
end

def clear_hour
@time.hour > 0 ? @time -= (@time.hour * ONE_HOUR) : @time
(@time.hour > 0) ? @time -= (@time.hour * ONE_HOUR) : @time
end

# Move to the first of the month, 0 hours
def clear_day
@time.day > 1 ? @time -= (@time.day - 1) * ONE_DAY : @time
(@time.day > 1) ? @time -= (@time.day - 1) * ONE_DAY : @time
end

# Clear to january 1st
Expand Down
3 changes: 2 additions & 1 deletion lib/ice_cube/validated_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class ValidatedRule < Rule
:base_sec, :base_min, :base_day, :base_hour, :base_month, :base_wday,
:day_of_year, :second_of_minute, :minute_of_hour, :day_of_month,
:hour_of_day, :month_of_year, :day_of_week,
:interval
:interval,
:by_set_pos
]

attr_reader :validations
Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/validations/day_of_week.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ def dst_adjust?

def validate(step_time, start_time)
wday = step_time.wday
offset = day < wday ? (7 - wday + day) : (day - wday)
offset = (day < wday) ? (7 - wday + day) : (day - wday)
wrapper = TimeUtil::TimeWrapper.new(step_time)
wrapper.add :day, offset
loop do
which_occ, num_occ = TimeUtil.which_occurrence_in_month(wrapper.to_time, day)
this_occ = occ < 0 ? (num_occ + occ + 1) : occ
this_occ = (occ < 0) ? (num_occ + occ + 1) : occ
break offset if which_occ == this_occ
wrapper.add :day, 7
offset += 7
Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/validations/day_of_year.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def dst_adjust?

def validate(step_time, start_time)
days_in_year = TimeUtil.days_in_year(step_time)
yday = day < 0 ? day + days_in_year + 1 : day
yday = (day < 0) ? day + days_in_year + 1 : day
offset = yday - step_time.yday
offset >= 0 ? offset : offset + days_in_year
(offset >= 0) ? offset : offset + days_in_year
end

def build_s(builder)
Expand Down
6 changes: 3 additions & 3 deletions lib/ice_cube/validations/fixed_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def validate(time, start_time)
def validate_interval_lock(time, start_time)
t0 = starting_unit(start_time)
t1 = time.send(type)
t0 >= t1 ? t0 - t1 : INTERVALS[type] - t1 + t0
(t0 >= t1) ? t0 - t1 : INTERVALS[type] - t1 + t0
end

# Lock the hour if explicitly set by hour_of_day, but allow for the nearest
Expand Down Expand Up @@ -73,11 +73,11 @@ def validate_day_lock(time, start_time)
if value && value > 0
until_next_month = days_in_month + sleeps
else
until_next_month = start < 28 ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month = (start < 28) ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month += sleeps - month_overflow
end

sleeps >= 0 ? sleeps : until_next_month
(sleeps >= 0) ? sleeps : until_next_month
end

def starting_unit(start_time)
Expand Down
6 changes: 3 additions & 3 deletions lib/ice_cube/validations/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def validate(time, start_time)
def validate_interval_lock(time, start_time)
t0 = starting_unit(start_time)
t1 = time.send(type)
t0 >= t1 ? t0 - t1 : INTERVALS[type] - t1 + t0
(t0 >= t1) ? t0 - t1 : INTERVALS[type] - t1 + t0
end

# Lock the hour if explicitly set by hour_of_day, but allow for the nearest
Expand Down Expand Up @@ -73,11 +73,11 @@ def validate_day_lock(time, start_time)
if value && value > 0
until_next_month = days_in_month + sleeps
else
until_next_month = start < 28 ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month = (start < 28) ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month += sleeps - month_overflow
end

sleeps >= 0 ? sleeps : until_next_month
(sleeps >= 0) ? sleeps : until_next_month
end

def starting_unit(start_time)
Expand Down
77 changes: 77 additions & 0 deletions lib/ice_cube/validations/monthly_by_set_pos.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module IceCube
module Validations::MonthlyBySetPos
def by_set_pos(*by_set_pos)
return by_set_pos([by_set_pos]) if by_set_pos.is_a?(Integer)

unless by_set_pos.nil? || by_set_pos.is_a?(Array)
raise ArgumentError, "Expecting Array or nil value for count, got #{by_set_pos.inspect}"
end
by_set_pos.flatten!
by_set_pos.each do |set_pos|
unless (set_pos >= -366 && set_pos <= -1) ||
(set_pos <= 366 && set_pos >= 1)
raise ArgumentError, "Expecting number in [-366, -1] or [1, 366], got #{set_pos} (#{by_set_pos})"
end
end

@by_set_pos = by_set_pos
replace_validations_for(:by_set_pos, by_set_pos && [Validation.new(by_set_pos, self)])
self
end

class Validation
attr_reader :rule, :by_set_pos

def initialize(by_set_pos, rule)
@by_set_pos = by_set_pos
@rule = rule
end

def type
:day
end

def dst_adjust?
true
end

def validate(step_time, schedule)
start_of_month = TimeUtil.start_of_month step_time
end_of_month = TimeUtil.end_of_month step_time

new_schedule = IceCube::Schedule.new(TimeUtil.previous_month(step_time)) do |s|
s.add_recurrence_rule IceCube::Rule.from_hash(rule.to_hash.except(:by_set_pos, :count, :until))
end

occurrences = new_schedule.occurrences_between(start_of_month, end_of_month)
index = occurrences.index(step_time)
if index.nil?
1
else
positive_set_pos = index + 1
negative_set_pos = index - occurrences.length

if @by_set_pos.include?(positive_set_pos) || @by_set_pos.include?(negative_set_pos)
0
else
1
end
end
end

def build_s(builder)
builder.piece(:by_set_pos) << by_set_pos
end

def build_hash(builder)
builder[:by_set_pos] = by_set_pos
end

def build_ical(builder)
builder["BYSETPOS"] << by_set_pos
end

nil
end
end
end
Loading