Skip to content

Commit

Permalink
Add support for CSV content from stdin in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
promulo committed May 24, 2022
1 parent fd1e650 commit c8b09fd
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 3 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,13 +508,28 @@ Alternatively, you can run your Task in the command line:
bundle exec maintenance_tasks perform Maintenance::UpdatePostsTask
```

To run a Task that processes CSVs from the command line, use the --csv option:
To run a Task that processes CSVs from the command line, use the `--csv` option:

```sh-session
bundle exec maintenance_tasks perform Maintenance::ImportPostsTask --csv "path/to/my_csv.csv"
```

To run a Task that takes arguments from the command line, use the --arguments
The `--csv` option also works with CSV content coming from the standard input:

```sh-session
curl "some/remote/csv" | bundle exec maintenance_tasks perform Maintenance::ImportPostsTask --csv
```

Or even typed inline:

```sh-session
bundle exec maintenance_tasks perform Maintenance::ImportPostsTask --csv -
foo,bar,baz
1,2,3
^D
```

To run a Task that takes arguments from the command line, use the `--arguments`
option, passing arguments as a set of \<key>:\<value> pairs:

```sh-session
Expand Down
9 changes: 8 additions & 1 deletion lib/maintenance_tasks/cli.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "stringio"
require "thor"

module MaintenanceTasks
Expand Down Expand Up @@ -53,9 +54,15 @@ def perform(name)

def csv_file
csv_option = options[:csv]
if csv_option
return unless csv_option.present?

if csv_option == "-" || !$stdin.tty?
{ io: StringIO.new($stdin.read), filename: File.basename("stdin.csv") }
else
{ io: File.open(csv_option), filename: File.basename(csv_option) }
end
rescue Errno::ENOENT
raise ArgumentError, "CSV file not found or specified."
end
end
end
43 changes: 43 additions & 0 deletions test/lib/maintenance_tasks/cli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,49 @@ class CLITest < ActiveSupport::TestCase
@cli.perform("MyCsvTask")
end

test "#perform runs a CSV Task with the CSV from stdin when --csv option used" do
task = mock(name: "MyCsvTask")
csv_string = "foo,bar\n1,2\n"
actual_stdin = $stdin
$stdin = StringIO.new(csv_string)

csv_string_io = StringIO.new(csv_string)
expected_attachable = { io: csv_string_io, filename: "stdin.csv" }

# 'csv' is the fallback value Thor uses when you don't supply any.
@cli.expects(:options).at_least_once.returns(csv: "csv")
StringIO.expects(:new).with(csv_string).returns(csv_string_io)
Runner.expects(:run)
.with(name: "MyCsvTask", csv_file: expected_attachable, arguments: {})
.returns(task)
@cli.expects(:say_status)
.with(:success, "MyCsvTask was enqueued.", :green)

@cli.perform("MyCsvTask")

$stdin = actual_stdin
end

test "#perform prints an error message when no CSV file is provided to --csv option" do
# 'csv' is the fallback value Thor uses when you don't supply any.
@cli.expects(:options).at_least_once.returns(csv: "csv")
@cli.expects(:say_status)
.with(:error, "CSV file not found or specified.", :red)

@cli.perform("MyCsvTask")
end

test "#perform prints an error message when provided CSV file does not exist" do
bad_csv_file_path = "foo.csv"
# 'csv' is the fallback value Thor uses when you don't supply any.
@cli.expects(:options).at_least_once.returns(csv: bad_csv_file_path)
File.expects(:open).with(bad_csv_file_path).raises(Errno::ENOENT)
@cli.expects(:say_status)
.with(:error, "CSV file not found or specified.", :red)

@cli.perform("MyCsvTask")
end

test "#perform runs a Task with the supplied arguments when --arguments option used" do
task = mock(name: "MyParamsTask")
arguments = { "post_ids": "1,2,3" }
Expand Down

0 comments on commit c8b09fd

Please sign in to comment.