tomo 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -6
  3. data/lib/tomo/cli.rb +0 -2
  4. data/lib/tomo/cli/common_options.rb +4 -12
  5. data/lib/tomo/cli/deploy_options.rb +2 -7
  6. data/lib/tomo/cli/parser.rb +1 -6
  7. data/lib/tomo/cli/project_options.rb +1 -3
  8. data/lib/tomo/cli/rules.rb +4 -22
  9. data/lib/tomo/cli/rules/switch.rb +1 -5
  10. data/lib/tomo/cli/rules/value_switch.rb +1 -2
  11. data/lib/tomo/cli/rules_evaluator.rb +3 -13
  12. data/lib/tomo/cli/usage.rb +1 -3
  13. data/lib/tomo/commands/default.rb +1 -3
  14. data/lib/tomo/commands/init.rb +24 -1
  15. data/lib/tomo/commands/run.rb +1 -3
  16. data/lib/tomo/configuration.rb +5 -11
  17. data/lib/tomo/configuration/dsl/hosts_and_settings.rb +1 -2
  18. data/lib/tomo/configuration/plugin_resolver.rb +1 -1
  19. data/lib/tomo/configuration/plugins_registry.rb +1 -2
  20. data/lib/tomo/configuration/plugins_registry/gem_resolver.rb +1 -1
  21. data/lib/tomo/configuration/unknown_environment_error.rb +1 -4
  22. data/lib/tomo/console.rb +6 -11
  23. data/lib/tomo/console/menu.rb +1 -2
  24. data/lib/tomo/host.rb +1 -2
  25. data/lib/tomo/plugin/bundler/tasks.rb +2 -7
  26. data/lib/tomo/plugin/core/tasks.rb +3 -12
  27. data/lib/tomo/plugin/env/tasks.rb +32 -10
  28. data/lib/tomo/plugin/git.rb +1 -4
  29. data/lib/tomo/plugin/git/tasks.rb +4 -14
  30. data/lib/tomo/plugin/nodenv/tasks.rb +1 -3
  31. data/lib/tomo/plugin/puma.rb +0 -3
  32. data/lib/tomo/plugin/puma/systemd/service.erb +1 -1
  33. data/lib/tomo/plugin/puma/tasks.rb +6 -15
  34. data/lib/tomo/plugin/rails/helpers.rb +1 -1
  35. data/lib/tomo/plugin/rails/tasks.rb +6 -4
  36. data/lib/tomo/plugin/testing.rb +1 -3
  37. data/lib/tomo/remote.rb +1 -3
  38. data/lib/tomo/runtime.rb +3 -6
  39. data/lib/tomo/runtime/concurrent_ruby_thread_pool.rb +1 -4
  40. data/lib/tomo/runtime/execution_plan.rb +1 -4
  41. data/lib/tomo/runtime/explanation.rb +1 -7
  42. data/lib/tomo/runtime/settings_required_error.rb +1 -3
  43. data/lib/tomo/runtime/task_runner.rb +1 -5
  44. data/lib/tomo/runtime/unknown_task_error.rb +1 -4
  45. data/lib/tomo/script.rb +1 -5
  46. data/lib/tomo/shell_builder.rb +5 -10
  47. data/lib/tomo/ssh/child_process.rb +2 -7
  48. data/lib/tomo/ssh/connection.rb +3 -16
  49. data/lib/tomo/ssh/connection_validator.rb +1 -4
  50. data/lib/tomo/ssh/executable_error.rb +1 -2
  51. data/lib/tomo/ssh/options.rb +2 -5
  52. data/lib/tomo/task_api.rb +4 -15
  53. data/lib/tomo/templates/config.rb.erb +7 -1
  54. data/lib/tomo/testing.rb +0 -2
  55. data/lib/tomo/testing/Dockerfile +1 -3
  56. data/lib/tomo/testing/connection.rb +1 -6
  57. data/lib/tomo/testing/docker_image.rb +4 -17
  58. data/lib/tomo/testing/local.rb +1 -3
  59. data/lib/tomo/testing/mock_plugin_tester.rb +27 -4
  60. data/lib/tomo/testing/ubuntu_setup.sh +1 -2
  61. data/lib/tomo/version.rb +1 -1
  62. metadata +8 -150
  63. data/lib/tomo/testing/docker_plugin_tester.rb +0 -39
  64. data/lib/tomo/testing/plugin_tester.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ad1c87df7e8fad626e90eb0606c0bb2899a3acf57eb2b9df969bd53c33332de
4
- data.tar.gz: 49ce5c513dfccbe4db1269e0c9385e39aa9940690cd7a6e86c561d54eeb89b54
3
+ metadata.gz: '098582c6866a637c63527b1bf1fecd347686cf1472ddf4e93b11002180b4e0c3'
4
+ data.tar.gz: 74ea3479cd90f9d3cf038d7b8e6cffa0200a98a51562798ba1b852ca66866f4e
5
5
  SHA512:
6
- metadata.gz: 1f2459465ef849c4dc012f594bcb672317909945c1061cb54a9f8e94acb07e91ed04334d28347f1dd4d708e10903ab168a142b4632289e5e9c375aa882c8bdc9
7
- data.tar.gz: 3946a734793b215eb57296eb602ac9bdaf68791c9df541d381b41bd89ee0bc9681fff27a506dd72e5d427bee757727f613f8db7624216eefaf808062740ba9a8
6
+ metadata.gz: 7ddd09600b3eae9914c7641b08f169f7f802297cf76170b7faffc0d0cb75cd0af22071c5e8d358c244464efe00672d4573054f30a2947b258aaef3af98e9ed74
7
+ data.tar.gz: 78c334db75ce535dd00c434fb3a4a927af4369d7f747cc3776334146c9c41bbae785ddb15e4ec42bdea59a66223a3836644f6bf0ec74e42f57c6de85fc822025
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/tomo.svg)](https://rubygems.org/gems/tomo)
4
4
  [![Travis](https://img.shields.io/travis/mattbrictson/tomo.svg?label=travis)](https://travis-ci.org/mattbrictson/tomo)
5
- [![Circle](https://circleci.com/gh/mattbrictson/tomo.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/tomo?branch=master)
5
+ [![Circle](https://circleci.com/gh/mattbrictson/tomo/tree/main.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/tomo?branch=main)
6
6
  [![Code Climate](https://codeclimate.com/github/mattbrictson/tomo/badges/gpa.svg)](https://codeclimate.com/github/mattbrictson/tomo)
7
7
 
8
8
  Tomo is a friendly command-line tool for deploying Rails apps. It is a new alternative to Capistrano, Mina, and Shipit that aims for simplicity and developer happiness.
@@ -64,7 +64,7 @@ host "user@hostname.or.ip.address"
64
64
  set application: "my-rails-app"
65
65
  set deploy_to: "/var/www/%{application}"
66
66
  set git_url: "git@github.com:my-username/my-rails-app.git"
67
- set git_branch: "master"
67
+ set git_branch: "main"
68
68
  # ...
69
69
 
70
70
  setup do
@@ -191,7 +191,6 @@ Read the [Writing Custom Tasks](https://tomo-deploy.com/tutorials/writing-custom
191
191
  - [Result](https://tomo-deploy.com/api/Result/)
192
192
  - [TaskLibrary](https://tomo-deploy.com/api/TaskLibrary/)
193
193
  - [Testing::MockPluginTester](https://tomo-deploy.com/api/testing/MockPluginTester/)
194
- - [Testing::DockerPluginTester](https://tomo-deploy.com/api/testing/DockerPluginTester/)
195
194
 
196
195
  ## FAQ
197
196
 
@@ -226,7 +225,7 @@ Next run `tomo setup` for _both_ apps; this will set everything up for both user
226
225
 
227
226
  ## Support
228
227
 
229
- This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you'd like to help by submitting a pull request, or if you've discovered a bug that needs my attention, please let me know. Check out [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/master/CONTRIBUTING.md) to get started. Happy hacking! —Matt
228
+ This project is a labor of love and I can only spend a few hours a week maintaining it, at most. If you'd like to help by submitting a pull request, or if you've discovered a bug that needs my attention, please let me know. Check out [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/main/CONTRIBUTING.md) to get started. Happy hacking! —Matt
230
229
 
231
230
  ## License
232
231
 
@@ -234,8 +233,8 @@ The gem is available as open source under the terms of the [MIT License](https:/
234
233
 
235
234
  ## Code of conduct
236
235
 
237
- Everyone interacting in the Tomo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattbrictson/tomo/blob/master/CODE_OF_CONDUCT.md).
236
+ Everyone interacting in the Tomo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattbrictson/tomo/blob/main/CODE_OF_CONDUCT.md).
238
237
 
239
238
  ## Contribution guide
240
239
 
241
- Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/master/CONTRIBUTING.md) guidelines before you dive in.
240
+ Interested in filing a bug report, feature request, or opening a PR? Excellent! Please read the short [CONTRIBUTING.md](https://github.com/mattbrictson/tomo/blob/main/CONTRIBUTING.md) guidelines before you dive in.
@@ -55,7 +55,6 @@ module Tomo
55
55
  argv << "" if argv.shift == "--complete"
56
56
  end
57
57
 
58
- # rubocop:disable Metrics/CyclomaticComplexity
59
58
  def lookup_command(argv)
60
59
  command_name = argv.first unless Completions.active? && argv.length == 1
61
60
  command_name = Abbrev.abbrev(COMMANDS.keys)[command_name]
@@ -65,7 +64,6 @@ module Tomo
65
64
  command = COMMANDS[command_name] || Tomo::Commands::Default
66
65
  [command, command_name]
67
66
  end
68
- # rubocop:enable Metrics/CyclomaticComplexity
69
67
 
70
68
  def task_format?(arg)
71
69
  arg.to_s.match?(/\A\S+:\S*\z/)
@@ -1,22 +1,15 @@
1
1
  module Tomo
2
2
  class CLI
3
3
  module CommonOptions
4
- # rubocop:disable Metrics/MethodLength
5
- def self.included(mod)
4
+ def self.included(mod) # rubocop:disable Metrics/MethodLength
6
5
  mod.class_eval do
7
- option :color,
8
- "--[no-]color",
9
- "Enable/disable color output" do |color|
6
+ option :color, "--[no-]color", "Enable/disable color output" do |color|
10
7
  Colors.enabled = color
11
8
  end
12
- option :debug,
13
- "--[no-]debug",
14
- "Enable/disable verbose debug logging" do |debug|
9
+ option :debug, "--[no-]debug", "Enable/disable verbose debug logging" do |debug|
15
10
  Tomo.debug = debug
16
11
  end
17
- option :trace,
18
- "--[no-]trace",
19
- "Display full backtrace on error" do |trace|
12
+ option :trace, "--[no-]trace", "Display full backtrace on error" do |trace|
20
13
  CLI.show_backtrace = trace
21
14
  end
22
15
  option :help, "-h, --help", "Print this documentation" do |_help|
@@ -27,7 +20,6 @@ module Tomo
27
20
  after_parse :dump_runtime_info
28
21
  end
29
22
  end
30
- # rubocop:enable Metrics/MethodLength
31
23
 
32
24
  private
33
25
 
@@ -1,8 +1,7 @@
1
1
  module Tomo
2
2
  class CLI
3
3
  module DeployOptions
4
- # rubocop:disable Metrics/MethodLength
5
- def self.included(mod)
4
+ def self.included(mod) # rubocop:disable Metrics/MethodLength
6
5
  mod.class_eval do
7
6
  option :environment,
8
7
  "-e, --environment ENVIRONMENT",
@@ -23,7 +22,6 @@ module Tomo
23
22
  after_parse :prompt_for_environment
24
23
  end
25
24
  end
26
- # rubocop:enable Metrics/MethodLength
27
25
 
28
26
  private
29
27
 
@@ -49,10 +47,7 @@ module Tomo
49
47
  return if envs.empty?
50
48
  return unless Console.interactive?
51
49
 
52
- options[:environment] = Console.menu(
53
- "Choose an environment:",
54
- choices: envs
55
- )
50
+ options[:environment] = Console.menu("Choose an environment:", choices: envs)
56
51
  end
57
52
  end
58
53
  end
@@ -46,12 +46,7 @@ module Tomo
46
46
  attr_reader :rules, :usage, :after_parse_methods
47
47
 
48
48
  def evaluate(argv, state, literal:)
49
- RulesEvaluator.evaluate(
50
- rules: rules.to_a,
51
- argv: argv,
52
- state: state,
53
- literal: literal
54
- )
49
+ RulesEvaluator.evaluate(rules: rules.to_a, argv: argv, state: state, literal: literal)
55
50
  end
56
51
 
57
52
  def check_required_rules(state)
@@ -3,9 +3,7 @@ module Tomo
3
3
  module ProjectOptions
4
4
  def self.included(mod)
5
5
  mod.class_eval do
6
- option :project,
7
- "-c, --config PATH",
8
- "Location of project config (default: #{DEFAULT_CONFIG_PATH})"
6
+ option :project, "-c, --config PATH", "Location of project config (default: #{DEFAULT_CONFIG_PATH})"
9
7
  end
10
8
  end
11
9
 
@@ -51,21 +51,11 @@ module Tomo
51
51
  attr_reader :rules
52
52
 
53
53
  def optional_arg_rule(spec, values_proc)
54
- Rules::Argument.new(
55
- spec,
56
- values_proc: values_proc,
57
- required: false,
58
- multiple: false
59
- )
54
+ Rules::Argument.new(spec, values_proc: values_proc, required: false, multiple: false)
60
55
  end
61
56
 
62
57
  def required_arg_rule(spec, values_proc)
63
- Rules::Argument.new(
64
- spec,
65
- values_proc: values_proc,
66
- required: true,
67
- multiple: false
68
- )
58
+ Rules::Argument.new(spec, values_proc: values_proc, required: true, multiple: false)
69
59
  end
70
60
 
71
61
  def mutiple_optional_args_rule(spec, values_proc)
@@ -73,10 +63,7 @@ module Tomo
73
63
  end
74
64
 
75
65
  def on_off_switch_rule(key, name, _values_proc, callback_proc)
76
- Rules::Switch.new(key,
77
- "--#{name}",
78
- "--no-#{name}",
79
- callback_proc: callback_proc) do |arg|
66
+ Rules::Switch.new(key, "--#{name}", "--no-#{name}", callback_proc: callback_proc) do |arg|
80
67
  arg == "--#{name}"
81
68
  end
82
69
  end
@@ -86,12 +73,7 @@ module Tomo
86
73
  end
87
74
 
88
75
  def value_switch_rule(key, *switches, values_proc, callback_proc)
89
- Rules::ValueSwitch.new(
90
- key,
91
- *switches,
92
- values_proc: values_proc,
93
- callback_proc: callback_proc
94
- )
76
+ Rules::ValueSwitch.new(key, *switches, values_proc: values_proc, callback_proc: callback_proc)
95
77
  end
96
78
  end
97
79
  end
@@ -1,10 +1,6 @@
1
1
  class Tomo::CLI::Rules
2
2
  class Switch
3
- def initialize(key,
4
- *switches,
5
- required: false,
6
- callback_proc:,
7
- &convert_proc)
3
+ def initialize(key, *switches, required: false, callback_proc:, &convert_proc)
8
4
  @key = key
9
5
  @switches = switches
10
6
  @callback_proc = callback_proc
@@ -51,8 +51,7 @@ class Tomo::CLI::Rules
51
51
  end
52
52
 
53
53
  def raise_missing_value(switch)
54
- raise Tomo::CLI::Error,
55
- "Please specify a value for the #{yellow(switch)} option."
54
+ raise Tomo::CLI::Error, "Please specify a value for the #{yellow(switch)} option."
56
55
  end
57
56
  end
58
57
  end
@@ -41,11 +41,7 @@ module Tomo
41
41
  def complete_if_needed(matched_rules, *matched_args)
42
42
  return unless Completions.active?
43
43
 
44
- completions.print_completions_and_exit(
45
- matched_rules,
46
- *matched_args,
47
- state: state
48
- )
44
+ completions.print_completions_and_exit(matched_rules, *matched_args, state: state)
49
45
  end
50
46
 
51
47
  def remaining_rules
@@ -56,15 +52,9 @@ module Tomo
56
52
 
57
53
  def raise_unrecognized_args
58
54
  problem_arg = argv.first
59
- type = if literal || !problem_arg.start_with?("-")
60
- "arg"
61
- else
62
- "option"
63
- end
55
+ type = literal || !problem_arg.start_with?("-") ? "arg" : "option"
64
56
 
65
- raise CLI::Error,
66
- "#{Colors.yellow(problem_arg)} is not a recognized #{type} "\
67
- "for this tomo command."
57
+ raise CLI::Error, "#{Colors.yellow(problem_arg)} is not a recognized #{type} for this tomo command."
68
58
  end
69
59
  end
70
60
  end
@@ -18,9 +18,7 @@ module Tomo
18
18
  end
19
19
 
20
20
  def to_s
21
- indent([
22
- "", banner_proc.call, "Options:", "", indent(options_help), "\n"
23
- ].join("\n"))
21
+ indent(["", banner_proc.call, "Options:", "", indent(options_help), "\n"].join("\n"))
24
22
  end
25
23
 
26
24
  private
@@ -42,9 +42,7 @@ module Tomo
42
42
  # do anything, so if we got this far, something has gone wrong.
43
43
 
44
44
  if options.any?
45
- raise CLI::Error,
46
- "Options must be specified after the command: " +
47
- yellow("tomo #{args.first} [options]")
45
+ raise CLI::Error, "Options must be specified after the command: " + yellow("tomo #{args.first} [options]")
48
46
  end
49
47
 
50
48
  raise_unrecognized_command(args.first)
@@ -70,6 +70,14 @@ module Tomo
70
70
  nil
71
71
  end
72
72
 
73
+ def git_branch
74
+ return unless File.file?(".git/config")
75
+
76
+ `git rev-parse --abbrev-ref HEAD`.chomp
77
+ rescue SystemCallError
78
+ nil
79
+ end
80
+
73
81
  def node_version
74
82
  `node --version`.chomp.sub(/^v/i, "")
75
83
  rescue SystemCallError
@@ -82,10 +90,25 @@ module Tomo
82
90
  nil
83
91
  end
84
92
 
93
+ def rubocop?
94
+ File.exist?(".rubocop.yml")
95
+ end
96
+
97
+ def erb_2_2_or_later?
98
+ erb_version = Gem::Version.new(ERB.version[/\d[\d.]+/])
99
+ Gem::Requirement.new(">= 2.2").satisfied_by?(erb_version)
100
+ end
101
+
85
102
  def config_rb_template(app)
86
103
  path = File.expand_path("../templates/config.rb.erb", __dir__)
87
104
  template = IO.read(path)
88
- ERB.new(template).result(binding)
105
+
106
+ # TODO: remove once we drop Ruby 2.5 support?
107
+ if erb_2_2_or_later?
108
+ ERB.new(template, trim_mode: "-").result(binding)
109
+ else
110
+ ERB.new(template, nil, "-").result(binding)
111
+ end
89
112
  end
90
113
  end
91
114
  end
@@ -3,9 +3,7 @@ module Tomo
3
3
  class Run < CLI::Command
4
4
  include CLI::DeployOptions
5
5
 
6
- option :privileged,
7
- "--[no-]privileged",
8
- "Run the task using a privileged user (e.g. root)"
6
+ option :privileged, "--[no-]privileged", "Run the task using a privileged user (e.g. root)"
9
7
 
10
8
  include CLI::ProjectOptions
11
9
  include CLI::CommonOptions
@@ -3,16 +3,13 @@ module Tomo
3
3
  autoload :DSL, "tomo/configuration/dsl"
4
4
  autoload :Environment, "tomo/configuration/environment"
5
5
  autoload :Glob, "tomo/configuration/glob"
6
- autoload :PluginFileNotFoundError,
7
- "tomo/configuration/plugin_file_not_found_error"
6
+ autoload :PluginFileNotFoundError, "tomo/configuration/plugin_file_not_found_error"
8
7
  autoload :PluginsRegistry, "tomo/configuration/plugins_registry"
9
8
  autoload :ProjectNotFoundError, "tomo/configuration/project_not_found_error"
10
9
  autoload :RoleBasedTaskFilter, "tomo/configuration/role_based_task_filter"
11
- autoload :UnknownEnvironmentError,
12
- "tomo/configuration/unknown_environment_error"
10
+ autoload :UnknownEnvironmentError, "tomo/configuration/unknown_environment_error"
13
11
  autoload :UnknownPluginError, "tomo/configuration/unknown_plugin_error"
14
- autoload :UnspecifiedEnvironmentError,
15
- "tomo/configuration/unspecified_environment_error"
12
+ autoload :UnspecifiedEnvironmentError, "tomo/configuration/unspecified_environment_error"
16
13
 
17
14
  def self.from_config_rb(path=DEFAULT_CONFIG_PATH)
18
15
  ProjectNotFoundError.raise_with(path: path) unless File.file?(path)
@@ -27,8 +24,7 @@ module Tomo
27
24
  raise DSL::ErrorFormatter.decorate(e, path, config_rb&.lines)
28
25
  end
29
26
 
30
- attr_accessor :environments, :deploy_tasks, :setup_tasks, :hosts, :plugins,
31
- :settings, :task_filter, :path
27
+ attr_accessor :environments, :deploy_tasks, :setup_tasks, :hosts, :plugins, :settings, :task_filter, :path
32
28
 
33
29
  def initialize
34
30
  @environments = {}
@@ -116,9 +112,7 @@ module Tomo
116
112
  end
117
113
 
118
114
  def raise_unknown_environment(environ)
119
- UnknownEnvironmentError.raise_with(
120
- name: environ, known_environments: environments.keys
121
- )
115
+ UnknownEnvironmentError.raise_with(name: environ, known_environments: environments.keys)
122
116
  end
123
117
  end
124
118
  end
@@ -7,8 +7,7 @@ module Tomo
7
7
  self
8
8
  end
9
9
 
10
- def host(address, port: 22, roles: [],
11
- log_prefix: nil, privileged_user: "root")
10
+ def host(address, port: 22, roles: [], log_prefix: nil, privileged_user: "root")
12
11
  @config.hosts << Host.parse(
13
12
  address,
14
13
  privileged_user: privileged_user,
@@ -39,7 +39,7 @@ module Tomo
39
39
  def constantize(path)
40
40
  parts = path.split("/")
41
41
  parts.reduce(Object) do |parent, part|
42
- child = part.gsub(/^[a-z]|_[a-z]/) { |str| str.chars.last.upcase }
42
+ child = part.gsub(/^[a-z]|_[a-z]/) { |str| str[-1].upcase }
43
43
  parent.const_get(child, false)
44
44
  end
45
45
  end
@@ -1,8 +1,7 @@
1
1
  module Tomo
2
2
  class Configuration
3
3
  class PluginsRegistry
4
- autoload :FileResolver,
5
- "tomo/configuration/plugins_registry/file_resolver"
4
+ autoload :FileResolver, "tomo/configuration/plugins_registry/file_resolver"
6
5
  autoload :GemResolver, "tomo/configuration/plugins_registry/gem_resolver"
7
6
 
8
7
  attr_reader :helper_modules, :settings
@@ -39,7 +39,7 @@ module Tomo
39
39
  def constantize(path)
40
40
  parts = path.split("/")
41
41
  parts.reduce(Object) do |parent, part|
42
- child = part.gsub(/^[a-z]|_[a-z]/) { |str| str.chars.last.upcase }
42
+ child = part.gsub(/^[a-z]|_[a-z]/) { |str| str[-1].upcase }
43
43
  parent.const_get(child, false)
44
44
  end
45
45
  end
@@ -36,10 +36,7 @@ module Tomo
36
36
  end
37
37
 
38
38
  def suggestions
39
- @_suggestions ||= Error::Suggestions.new(
40
- dictionary: known_environments,
41
- word: name
42
- )
39
+ @_suggestions ||= Error::Suggestions.new(dictionary: known_environments, word: name)
43
40
  end
44
41
  end
45
42
  end
@@ -12,22 +12,20 @@ module Tomo
12
12
  def_delegators :@instance, :interactive?, :prompt, :menu
13
13
  end
14
14
 
15
- def initialize(env=ENV, input=$stdin)
15
+ def initialize(env=ENV, input=$stdin, output=$stdout)
16
16
  @env = env
17
17
  @input = input
18
+ @output = output
18
19
  end
19
20
 
20
21
  def interactive?
21
- input.respond_to?(:raw) &&
22
- input.respond_to?(:tty?) &&
23
- input.tty? &&
24
- !ci?
22
+ input.respond_to?(:raw) && input.respond_to?(:tty?) && input.tty? && !ci?
25
23
  end
26
24
 
27
25
  def prompt(question)
28
26
  assert_interactive
29
27
 
30
- print question
28
+ output.print question
31
29
  line = input.gets
32
30
  raise_non_interactive if line.nil?
33
31
 
@@ -42,7 +40,7 @@ module Tomo
42
40
 
43
41
  private
44
42
 
45
- attr_reader :env, :input
43
+ attr_reader :env, :input, :output
46
44
 
47
45
  CI_VARS = %w[
48
46
  JENKINS_HOME
@@ -66,10 +64,7 @@ module Tomo
66
64
  end
67
65
 
68
66
  def raise_non_interactive
69
- NonInteractiveError.raise_with(
70
- task: Runtime::Current.task,
71
- ci_var: (env.keys & CI_VARS).first
72
- )
67
+ NonInteractiveError.raise_with(task: Runtime::Current.task, ci_var: (env.keys & CI_VARS).first)
73
68
  end
74
69
  end
75
70
  end