Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce value objects for complex parameter types #414

Closed
wants to merge 4 commits into from
Closed
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
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,41 @@ to run. Since arguments are specified in the user interface via text area
inputs, it's important to check that they conform to the format your Task
expects, and to sanitize any inputs if necessary.

The gem provides custom `ActiveModel::Type::Value` objects for complex parameter
types:

- `IntegerArrayType`: Accepts a comma-delimited string of integers and turns it
into an array of integers.
- `StringArrayType`: Accepts an alphanumeric, comma-delimited string and turns
it into an array of strings.

If you use one of these types for a parameter in your Task, your input will be
coerced appropriately. **You may still want write validations for your input,
as type coercion is fairly lax. For example, if you input "xyz" for an
`IntegerArrayType` parameter, it will be cast to `[0]`.**

```ruby
# app/tasks/maintenance/update_posts_via_params_task.rb
module Maintenance
class UpdatePostsViaParamsTask < MaintenanceTasks::Task
attribute :post_ids, IntegerArrayType.new
validates :post_ids, presence: true

def collection
Post.where(ids: post_ids)
end

def count
collection.count
end

def process(post)
post.update!(content: "New content added on #{Time.now.utc}")
end
end
end
```

### Considerations when writing Tasks

MaintenanceTasks relies on the queue adapter configured for your application to
Expand Down
18 changes: 18 additions & 0 deletions app/models/maintenance_tasks/task/integer_array_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true
module MaintenanceTasks
class Task
# Type class representing an array of integers. Tasks using the Attributes
# API for parameter support can use this class to turn input from the UI
# into an array of integers within their Task.
class IntegerArrayType < ActiveModel::Type::Value
# Casts string from form input field to an array of integers.
#
# @param input [String] the value to cast to an array of integers.
# @return [Array<Integer>] the data cast as an array of integers.
def cast(input)
return unless input.present?
input.split(",").map(&:to_i)
end
end
end
end
18 changes: 18 additions & 0 deletions app/models/maintenance_tasks/task/string_array_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true
module MaintenanceTasks
class Task
# Type class representing an array of strings. Tasks using the Attributes
# API for parameter support can use this class to turn input from the UI
# into an array of strings within their Task.
class StringArrayType < ActiveModel::Type::Value
# Casts string from form input field to an array of strings.
#
# @param input [String] the value to cast to an array of strings.
# @return [Array<Integer>] the data cast as an array of strings.
def cast(input)
return unless input.present?
input.split(",")
end
end
end
end
15 changes: 3 additions & 12 deletions test/dummy/app/tasks/maintenance/params_task.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
# frozen_string_literal: true
module Maintenance
class ParamsTask < MaintenanceTasks::Task
attribute :post_ids, :string

validates :post_ids,
presence: true,
format: { with: /\A(\s?\d+(,\s?\d+\s?)*)\z/, allow_blank: true }
attribute :post_ids, IntegerArrayType.new
validates :post_ids, presence: true

class << self
attr_accessor :fast_task
end

def collection
Post.where(id: post_ids_array)
Post.where(id: post_ids)
end

def count
Expand All @@ -24,11 +21,5 @@ def process(post)

post.update!(content: "New content added on #{Time.now.utc}")
end

private

def post_ids_array
post_ids.split(",")
end
end
end
4 changes: 2 additions & 2 deletions test/models/maintenance_tasks/run_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class RunTest < ActiveSupport::TestCase
test "invalid if associated Task has parameters and they are invalid" do
run = Run.new(
task_name: "Maintenance::ParamsTask",
arguments: { post_ids: "xyz" }
arguments: { post_ids: "" }
)
refute_predicate run, :valid?
end
Expand Down Expand Up @@ -312,7 +312,7 @@ class RunTest < ActiveSupport::TestCase
run.validate_task_arguments

assert_predicate run, :valid?
assert_equal "1,2,3", run.task.post_ids
assert_equal [1, 2, 3], run.task.post_ids
end

private
Expand Down
24 changes: 24 additions & 0 deletions test/models/maintenance_tasks/task/integer_array_type_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true
require "test_helper"

module MaintenanceTasks
class Task
class IntegerArrayTypeTest < ActiveSupport::TestCase
test "#cast returns nil if input not present" do
assert_nil IntegerArrayType.new.cast("")
end

test "#cast converts string to array of strings" do
assert_equal [1, 2, 3], IntegerArrayType.new.cast("1,2,3")
end

test "#cast converts string to array of integers, excluding extra whitespace" do
assert_equal [123, 456, 78], IntegerArrayType.new.cast("123 ,456, 78 ")
end

test "#cast handles non-integer input" do
assert_equal [0], IntegerArrayType.new.cast("abc")
end
end
end
end
16 changes: 16 additions & 0 deletions test/models/maintenance_tasks/task/string_array_type_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true
require "test_helper"

module MaintenanceTasks
class Task
class StringArrayTypeTest < ActiveSupport::TestCase
test "#cast returns nil if input not present" do
assert_nil StringArrayType.new.cast("")
end

test "#cast converts string to array of strings" do
assert_equal ["abc", "def"], StringArrayType.new.cast("abc,def")
end
end
end
end
6 changes: 3 additions & 3 deletions test/system/maintenance_tasks/runs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ class RunsTest < ApplicationSystemTestCase
visit maintenance_tasks_path

click_on("Maintenance::ParamsTask")
fill_in("_task_arguments_post_ids", with: "xyz")
fill_in("_task_arguments_post_ids", with: "")
click_on "Run"

alert_text = "Validation failed: Arguments are invalid: :post_ids is "\
"invalid"
alert_text = "Validation failed: Arguments are invalid: "\
":post_ids can't be blank"
assert_text alert_text
end

Expand Down