Skip to content

Commit

Permalink
Add support for bounded enumerables
Browse files Browse the repository at this point in the history
  • Loading branch information
lavoiesl committed Jun 7, 2024
1 parent 0920983 commit 502b213
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 4 deletions.
27 changes: 23 additions & 4 deletions app/helpers/maintenance_tasks/tasks_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module MaintenanceTasks
#
# @api private
module TasksHelper
MAX_INCLUSION_ITEMS = 1000

STATUS_COLOURS = {
"new" => ["is-primary"],
"enqueued" => ["is-primary is-light"],
Expand Down Expand Up @@ -101,10 +103,21 @@ def csv_file_download_path(run)
)
end

# Resolves values covered by the inclusion validator for a task attribute.
# Only Arrays are supported.
# Procs and lambdas are also supported, but only if they take no arguments and return an Array.
# Option types such as Symbols, and Range are not supported and return nil.
# Resolves values covered by the inclusion validator for a task attribute,
# but only if it can be resolved to an array in a stateless manner.
#
# Examples of supported constraints:
# - Arrays
# - Enumerable that can be converted to an Array
# - Procs and lambdas, but only if they take no arguments and return a value meeting the requirements above.
#
# Examples of unsupported constraints, which would return nil:
# - Symbols (which would normally resolve to an instance method)
# - Procs and lambdas that take an argument (which would be the instance)
# - Unbounded ranges (e.g. `1..`)
#
# If the resulting Array is larger than #MAX_INCLUSION_ITEMS, the method will return nil.
# This is meant to make sure the process does not misbehave if the inclusion range is giant or infinite.
#
# Returned values are used to populate a dropdown list of options.
#
Expand All @@ -120,6 +133,12 @@ def resolve_inclusion_value(task_class, parameter_name)

in_option = inclusion_validator.options[:in] || inclusion_validator.options[:within]
in_option = in_option.call if in_option.is_a?(Proc) && in_option.arity.zero?

if in_option.is_a?(Enumerable)
items = in_option.take(MAX_INCLUSION_ITEMS + 1).to_a
in_option = items if items.size <= MAX_INCLUSION_ITEMS
end

in_option if in_option.is_a?(Array)
end

Expand Down
4 changes: 4 additions & 0 deletions test/dummy/app/tasks/maintenance/params_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ class ParamsTask < MaintenanceTasks::Task
attribute :text_integer_attr_proc_arg, :integer
attribute :text_integer_attr_undefined_symbol, :integer
attribute :text_integer_attr_unbounded_range, :integer
attribute :text_integer_attr_bounded_range, :integer
attribute :text_integer_attr_enumerable, :integer

validates_inclusion_of :text_integer_attr_proc_no_arg, in: proc { [100, 200, 300] }, allow_nil: true
validates_inclusion_of :text_integer_attr_proc_arg, in: proc { |_task| [100, 200, 300] }, allow_nil: true
validates_inclusion_of :text_integer_attr_undefined_symbol, in: :undefined_symbol, allow_nil: true
validates_inclusion_of :text_integer_attr_unbounded_range, in: (100..), allow_nil: true
validates_inclusion_of :text_integer_attr_bounded_range, in: (100..120), allow_nil: true
validates_inclusion_of :text_integer_attr_enumerable, in: (100..120).step(5), allow_nil: true

class << self
attr_accessor :fast_task
Expand Down
2 changes: 2 additions & 0 deletions test/helpers/maintenance_tasks/tasks_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class TasksHelperTest < ActionView::TestCase
"integer_dropdown_attr",
"boolean_dropdown_attr",
"text_integer_attr_proc_no_arg",
"text_integer_attr_bounded_range",
"text_integer_attr_enumerable",
].each do |attribute|
assert_match "Select a value", markup(attribute).squish
end
Expand Down
2 changes: 2 additions & 0 deletions test/models/maintenance_tasks/task_data_show_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class TaskDataShowTest < ActiveSupport::TestCase
"text_integer_attr_proc_arg",
"text_integer_attr_undefined_symbol",
"text_integer_attr_unbounded_range",
"text_integer_attr_bounded_range",
"text_integer_attr_enumerable",
],
TaskDataShow.new("Maintenance::ParamsTask").parameter_names,
)
Expand Down
12 changes: 12 additions & 0 deletions test/system/maintenance_tasks/tasks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ class TasksTest < ApplicationSystemTestCase
integer_dropdown_field_options = integer_dropdown_field.find_all("option").map { |option| option[:value] }
assert_equal(["", "100", "200", "300"], integer_dropdown_field_options)

integer_dropdown_field = page.find_field("task[text_integer_attr_bounded_range]")
assert_equal("select", integer_dropdown_field.tag_name)
assert_equal("select-one", integer_dropdown_field[:type])
integer_dropdown_field_options = integer_dropdown_field.find_all("option").map { |option| option[:value] }
assert_equal([""] + (100..120).to_a.map(&:to_s), integer_dropdown_field_options)

integer_dropdown_field = page.find_field("task[text_integer_attr_enumerable]")
assert_equal("select", integer_dropdown_field.tag_name)
assert_equal("select-one", integer_dropdown_field[:type])
integer_dropdown_field_options = integer_dropdown_field.find_all("option").map { |option| option[:value] }
assert_equal(["", "100", "105", "110", "115", "120"], integer_dropdown_field_options)

boolean_dropdown_field = page.find_field("task[boolean_dropdown_attr]")
assert_equal("select", boolean_dropdown_field.tag_name)
assert_equal("select-one", boolean_dropdown_field[:type])
Expand Down

0 comments on commit 502b213

Please sign in to comment.