Skip to content

Commit

Permalink
Support Ruby Range in cron fields
Browse files Browse the repository at this point in the history
Signed-off-by: Jimmy Tanagra <[email protected]>
  • Loading branch information
jimtng committed Sep 11, 2023
1 parent 95e97e2 commit 84dd882
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 4 deletions.
16 changes: 13 additions & 3 deletions lib/openhab/dsl/rules/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,8 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil)
# The same rules for the standard
# [cron expression](https://www.quartz-scheduler.org/documentation/quartz-2.2.2/tutorials/tutorial-lesson-06.html)
# apply for each field. For example, multiple values can be separated
# with a comma within a string.
# with a comma within a string, and ranges can be specified with a dash or with
# a Ruby Range.
#
# @param [Integer, String, nil] second
# @param [Integer, String, nil] minute
Expand All @@ -1097,22 +1098,31 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil)
# @param [Integer, String, nil] dow
# @param [Integer, String, nil] year
# @param [Object] attach object to be attached to the trigger
# @example
#
# @example Using String values
# # Run every 3 minutes on Monday to Friday
# # equivalent to the cron expression "0 */3 * ? * MON-FRI *"
# rule "Using cron fields" do
# cron minute: "*/3", dow: "MON-FRI"
# run { logger.info "Cron rule executed" }
# end
#
# @example
# @example Defaults for unspecified fields
# # Run at midnight on the first day of January, February, and March
# # equivalent to the cron expression "0 0 0 1 JAN-MAR ? *"
# rule "Using cron fields" do
# cron month: "JAN-MAR"
# run { logger.info "Cron rule executed" }
# end
#
# @example Using Ruby Range values
# # Run on the hour, every hour between 1pm and 5pm
# # equivalent to the cron expression "0 0 13-17 ? * ? *"
# rule "Using cron fields" do
# cron hour: 13..17
# run { logger.info "Cron rule executed" }
# end
#
# @return [void]
#
def cron(expression = nil, attach: nil, **fields)
Expand Down
18 changes: 17 additions & 1 deletion lib/openhab/dsl/rules/triggers/cron/cron.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,23 @@ def self.from_fields(fields)
"unknown keyword#{"s" if extra_fields.size > 1}: #{extra_fields.map(&:inspect).join(", ")}"
end

fields = fields.transform_values { |value| value.to_s.delete(" ") }
fields = fields.to_h do |key, value|
if value.is_a?(Range)
if value.exclude_end?
raise ArgumentError,
"Range must be inclusive for '#{key}'. Try '#{value.begin}..#{value.end}' instead"
end

unless value.begin && value.end
raise ArgumentError,
"Range must have a beginning and ending for '#{key}'"
end

[key, "#{value.begin}-#{value.end}".delete(" ")]
else
[key, value.to_s.delete(" ")]
end
end
# convert fields' key dow->week, dom->day to look into EXPRESSION_MAP
fields_expression = fields.transform_keys { |key| FIELD_TO_EXPRESSION_KEY[key] }
# find the first expression map that has a field from fields.
Expand Down
16 changes: 16 additions & 0 deletions spec/openhab/dsl/rules/builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,22 @@ def self.test_cron_fields(expected, description: nil, caller: Kernel.caller, **k
test_cron_fields("0 0 0 1 2 ? *", month: 2)
test_cron_fields("0 0 0 1 1 ? 2023-2025", year: "2023-2025")
end

context "with ranges" do # examples are dynamically generated
test_cron_fields("12-14 * * ? * ? *", second: 12..14)
test_cron_fields("0 12-14 * ? * ? *", minute: 12..14)
test_cron_fields("0 0 12-14 ? * ? *", hour: 12..14)
test_cron_fields("0 0 0 ? * TUE-WED *", dow: :TUE..:WED)
test_cron_fields("0 0 0 12-14 * ? *", dom: 12..14)
test_cron_fields("0 0 0 1 2-5 ? *", month: 2..5)
test_cron_fields("0 0 0 1 FEB-MAY ? *", month: :FEB..:MAY)
test_cron_fields("0 0 0 1 1 ? 2023-2025", year: 2023..2025)

it "raises ArgumentError about unsupported ranges" do
expect { cron second: 12.. }.to raise_error(ArgumentError)
expect { cron second: 12...14 }.to raise_error(ArgumentError)
end
end
end
end

Expand Down

0 comments on commit 84dd882

Please sign in to comment.