toys-core 0.9.4 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +30 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +3 -3
  6. data/lib/toys-core.rb +11 -21
  7. data/lib/toys/acceptor.rb +0 -21
  8. data/lib/toys/arg_parser.rb +1 -22
  9. data/lib/toys/cli.rb +102 -70
  10. data/lib/toys/compat.rb +49 -41
  11. data/lib/toys/completion.rb +0 -21
  12. data/lib/toys/context.rb +0 -23
  13. data/lib/toys/core.rb +1 -22
  14. data/lib/toys/dsl/flag.rb +0 -21
  15. data/lib/toys/dsl/flag_group.rb +0 -21
  16. data/lib/toys/dsl/positional_arg.rb +0 -21
  17. data/lib/toys/dsl/tool.rb +135 -51
  18. data/lib/toys/errors.rb +0 -21
  19. data/lib/toys/flag.rb +0 -21
  20. data/lib/toys/flag_group.rb +0 -21
  21. data/lib/toys/input_file.rb +0 -21
  22. data/lib/toys/loader.rb +41 -78
  23. data/lib/toys/middleware.rb +146 -77
  24. data/lib/toys/mixin.rb +0 -21
  25. data/lib/toys/module_lookup.rb +3 -26
  26. data/lib/toys/positional_arg.rb +0 -21
  27. data/lib/toys/source_info.rb +49 -38
  28. data/lib/toys/standard_middleware/add_verbosity_flags.rb +0 -23
  29. data/lib/toys/standard_middleware/apply_config.rb +42 -0
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +7 -28
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +0 -23
  32. data/lib/toys/standard_middleware/show_help.rb +0 -23
  33. data/lib/toys/standard_middleware/show_root_version.rb +0 -23
  34. data/lib/toys/standard_mixins/bundler.rb +89 -0
  35. data/lib/toys/standard_mixins/exec.rb +124 -35
  36. data/lib/toys/standard_mixins/fileutils.rb +0 -21
  37. data/lib/toys/standard_mixins/gems.rb +2 -24
  38. data/lib/toys/standard_mixins/highline.rb +0 -21
  39. data/lib/toys/standard_mixins/terminal.rb +0 -21
  40. data/lib/toys/template.rb +0 -21
  41. data/lib/toys/tool.rb +22 -34
  42. data/lib/toys/utils/completion_engine.rb +0 -21
  43. data/lib/toys/utils/exec.rb +1 -21
  44. data/lib/toys/utils/gems.rb +174 -63
  45. data/lib/toys/utils/help_text.rb +0 -21
  46. data/lib/toys/utils/terminal.rb +46 -37
  47. data/lib/toys/wrappable_string.rb +0 -21
  48. metadata +25 -9
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
23
-
24
3
  module Toys
25
4
  module StandardMixins
26
5
  ##
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
23
-
24
3
  module Toys
25
4
  ##
26
5
  # A template definition. Template classes should include this module.
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
23
-
24
3
  require "set"
25
4
 
26
5
  module Toys
@@ -37,11 +16,12 @@ module Toys
37
16
  # Should be created only from the DSL via the Loader.
38
17
  # @private
39
18
  #
40
- def initialize(loader, parent, full_name, priority, middleware_stack)
19
+ def initialize(loader, parent, full_name, priority, middleware_stack, middleware_lookup)
41
20
  @parent = parent
42
21
  @full_name = full_name.dup.freeze
43
22
  @priority = priority
44
- @middleware_stack = middleware_stack
23
+ @built_middleware = middleware_stack.build(middleware_lookup)
24
+ @subtool_middleware_stack = middleware_stack.dup
45
25
 
46
26
  @acceptors = {}
47
27
  @mixins = {}
@@ -201,11 +181,20 @@ module Toys
201
181
  attr_reader :default_data
202
182
 
203
183
  ##
204
- # The middleware stack active for this tool.
184
+ # The stack of middleware specs used for subtools.
185
+ #
186
+ # This array may be modified in place.
187
+ #
188
+ # @return [Array<Toys::Middleware::Spec>]
189
+ #
190
+ attr_reader :subtool_middleware_stack
191
+
192
+ ##
193
+ # The stack of built middleware specs for this tool.
205
194
  #
206
195
  # @return [Array<Toys::Middleware>]
207
196
  #
208
- attr_reader :middleware_stack
197
+ attr_reader :built_middleware
209
198
 
210
199
  ##
211
200
  # Info on the source of this tool.
@@ -473,18 +462,13 @@ module Toys
473
462
  ##
474
463
  # Sets the path to the file that defines this tool.
475
464
  # A tool may be defined from at most one path. If a different path is
476
- # already set, raises {Toys::ToolDefinitionError}
465
+ # already set, it is left unchanged.
477
466
  #
478
467
  # @param source [Toys::SourceInfo] Source info
479
468
  # @return [self]
480
469
  #
481
470
  def lock_source(source)
482
- if source_info && source_info.source != source.source
483
- raise ToolDefinitionError,
484
- "Cannot redefine tool #{display_name.inspect} in #{source.source_name}" \
485
- " (already defined in #{source_info.source_name})"
486
- end
487
- @source_info = source
471
+ @source_info ||= source
488
472
  self
489
473
  end
490
474
 
@@ -1073,7 +1057,7 @@ module Toys
1073
1057
  unless @definition_finished
1074
1058
  ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
1075
1059
  config_proc = proc {}
1076
- middleware_stack.reverse_each do |middleware|
1060
+ @built_middleware.reverse_each do |middleware|
1077
1061
  config_proc = make_config_proc(middleware, loader, config_proc)
1078
1062
  end
1079
1063
  config_proc.call
@@ -1306,7 +1290,11 @@ module Toys
1306
1290
  private
1307
1291
 
1308
1292
  def make_config_proc(middleware, loader, next_config)
1309
- proc { middleware.config(self, loader, &next_config) }
1293
+ if middleware.respond_to?(:config)
1294
+ proc { middleware.config(self, loader, &next_config) }
1295
+ else
1296
+ next_config
1297
+ end
1310
1298
  end
1311
1299
 
1312
1300
  def make_delegation_run_handler(target)
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
23
-
24
3
  require "shellwords"
25
4
 
26
5
  module Toys
@@ -1,26 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
23
-
3
+ require "rbconfig"
24
4
  require "logger"
25
5
  require "shellwords"
26
6
 
@@ -1,30 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019 Daniel Azuma
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
- # IN THE SOFTWARE.
22
- ;
3
+ require "monitor"
4
+ require "rubygems"
23
5
 
24
6
  module Toys
25
7
  module Utils
26
8
  ##
27
- # A helper module that activates and installs gems.
9
+ # A helper class that activates and installs gems and sets up bundler.
28
10
  #
29
11
  # This class is not loaded by default. Before using it directly, you should
30
12
  # `require "toys/utils/gems"`
@@ -58,6 +40,30 @@ module Toys
58
40
  end
59
41
  end
60
42
 
43
+ ##
44
+ # Failed to run Bundler
45
+ #
46
+ class BundlerFailedError < ::StandardError
47
+ end
48
+
49
+ ##
50
+ # Could not find a Gemfile
51
+ #
52
+ class GemfileNotFoundError < BundlerFailedError
53
+ end
54
+
55
+ ##
56
+ # The bundle is not and could not be installed
57
+ #
58
+ class BundleNotInstalledError < BundlerFailedError
59
+ end
60
+
61
+ ##
62
+ # Bundler has already been run; cannot do so again
63
+ #
64
+ class AlreadyBundledError < BundlerFailedError
65
+ end
66
+
61
67
  ##
62
68
  # Activate the given gem. If it is not present, attempt to install it (or
63
69
  # inform the user to update the bundle).
@@ -73,24 +79,42 @@ module Toys
73
79
  ##
74
80
  # Create a new gem activator.
75
81
  #
76
- # @param input [IO] Input IO
77
- # @param output [IO] Output IO
78
- # @param suppress_confirm [Boolean] Suppress the confirmation prompt and
79
- # just use the given `default_confirm` value. Default is false,
80
- # indicating the confirmation prompt appears by default.
81
- # @param default_confirm [Boolean] Default response for the confirmation
82
- # prompt. Default is true.
82
+ # @param on_missing [:confirm,:error,:install] What to do if a needed gem
83
+ # is not installed. Possible values:
84
+ # * `:confirm` - prompt the user on whether to install
85
+ # * `:error` - raise an exception
86
+ # * `:install` - just install the gem
87
+ # The default is `:confirm`.
88
+ # @param on_conflict [:error,:warn,:ignore] What to do if bundler has
89
+ # already been run with a different Gemfile. Possible values:
90
+ # * `:error` - raise an exception
91
+ # * `:ignore` - just silently proceed without bundling again
92
+ # * `:warn` - print a warning and proceed without bundling again
93
+ # The default is `:error`.
94
+ # @param terminal [Toys::Utils::Terminal] Terminal to use (optional)
95
+ # @param input [IO] Input IO (optional, defaults to STDIN)
96
+ # @param output [IO] Output IO (optional, defaults to STDOUT)
97
+ # @param suppress_confirm [Boolean] Deprecated. Use `on_missing` instead.
98
+ # @param default_confirm [Boolean] Deprecated. Use `on_missing` instead.
83
99
  #
84
- def initialize(input: $stdin,
85
- output: $stderr,
86
- suppress_confirm: false,
87
- default_confirm: true)
88
- require "toys/utils/terminal"
89
- require "toys/utils/exec"
90
- @terminal = Utils::Terminal.new(input: input, output: output)
91
- @exec = Utils::Exec.new
92
- @suppress_confirm = suppress_confirm ? true : false
93
- @default_confirm = default_confirm ? true : false
100
+ def initialize(on_missing: nil,
101
+ on_conflict: nil,
102
+ terminal: nil,
103
+ input: nil,
104
+ output: nil,
105
+ suppress_confirm: nil,
106
+ default_confirm: nil)
107
+ @default_confirm = default_confirm || default_confirm.nil? ? true : false
108
+ @on_missing = on_missing ||
109
+ if suppress_confirm
110
+ @default_confirm ? :install : :error
111
+ else
112
+ :confirm
113
+ end
114
+ @on_conflict = on_conflict || :error
115
+ @terminal = terminal
116
+ @input = input || ::STDIN
117
+ @output = output || ::STDOUT
94
118
  end
95
119
 
96
120
  ##
@@ -102,13 +126,56 @@ module Toys
102
126
  # @return [void]
103
127
  #
104
128
  def activate(name, *requirements)
105
- gem(name, *requirements)
106
- rescue ::Gem::LoadError => e
107
- handle_activation_error(e, name, requirements)
129
+ Gems.synchronize do
130
+ begin
131
+ gem(name, *requirements)
132
+ rescue ::Gem::LoadError => e
133
+ handle_activation_error(e, name, requirements)
134
+ end
135
+ end
136
+ end
137
+
138
+ ##
139
+ # Set up the bundle.
140
+ #
141
+ # @param groups [Array<String>] The groups to include in setup
142
+ # @param search_dirs [Array<String>] Directories to search for a Gemfile
143
+ # @return [void]
144
+ #
145
+ def bundle(groups: nil,
146
+ search_dirs: nil)
147
+ Gems.synchronize do
148
+ gemfile_path = find_gemfile(Array(search_dirs))
149
+ activate("bundler", "~> 2.1")
150
+ if configure_gemfile(gemfile_path)
151
+ setup_bundle(gemfile_path, groups || [])
152
+ end
153
+ end
154
+ end
155
+
156
+ @global_mutex = ::Monitor.new
157
+
158
+ ## @private
159
+ def self.synchronize(&block)
160
+ @global_mutex.synchronize(&block)
108
161
  end
109
162
 
110
163
  private
111
164
 
165
+ def terminal
166
+ @terminal ||= begin
167
+ require "toys/utils/terminal"
168
+ Utils::Terminal.new(input: @input, output: @output)
169
+ end
170
+ end
171
+
172
+ def exec_util
173
+ @exec_util ||= begin
174
+ require "toys/utils/exec"
175
+ Utils::Exec.new
176
+ end
177
+ end
178
+
112
179
  def handle_activation_error(error, name, requirements)
113
180
  is_missing_spec =
114
181
  if defined?(::Gem::MissingSpecError)
@@ -116,11 +183,11 @@ module Toys
116
183
  else
117
184
  error.message.include?("Could not find")
118
185
  end
119
- unless is_missing_spec
186
+ if !is_missing_spec || @on_missing == :error
120
187
  report_error(name, requirements, error)
121
188
  return
122
189
  end
123
- install_gem(name, requirements)
190
+ confirm_and_install_gem(name, requirements)
124
191
  begin
125
192
  gem(name, *requirements)
126
193
  rescue ::Gem::LoadError => e
@@ -132,28 +199,16 @@ module Toys
132
199
  "#{name.inspect}, #{requirements.map(&:inspect).join(', ')}"
133
200
  end
134
201
 
135
- def install_gem(name, requirements)
136
- requirements_text = gem_requirements_text(name, requirements)
137
- response =
138
- if @suppress_confirm
139
- @default_confirm
140
- else
141
- @terminal.confirm("Gem needed: #{requirements_text}. Install? ",
142
- default: @default_confirm)
202
+ def confirm_and_install_gem(name, requirements)
203
+ if @on_missing == :confirm
204
+ requirements_text = gem_requirements_text(name, requirements)
205
+ response = terminal.confirm("Gem needed: #{requirements_text}. Install? ",
206
+ default: @default_confirm)
207
+ unless response
208
+ raise InstallFailedError, "Canceled installation of needed gem: #{requirements_text}"
143
209
  end
144
- unless response
145
- raise InstallFailedError, "Canceled installation of needed gem: #{requirements_text}"
146
- end
147
- perform_install(name, requirements)
148
- end
149
-
150
- def perform_install(name, requirements)
151
- result = @terminal.spinner(leading_text: "Installing gem #{name}... ",
152
- final_text: "Done.\n") do
153
- @exec.exec(["gem", "install", name, "--version", requirements.join(",")],
154
- out: :capture, err: :capture)
155
210
  end
156
- @terminal.puts(result.captured_out + result.captured_err)
211
+ result = exec_util.exec(["gem", "install", name, "--version", requirements.join(",")])
157
212
  if result.error?
158
213
  raise InstallFailedError, "Failed to install gem #{name}"
159
214
  end
@@ -167,6 +222,62 @@ module Toys
167
222
  end
168
223
  raise ActivationFailedError, err.message
169
224
  end
225
+
226
+ def find_gemfile(search_dirs)
227
+ search_dirs.each do |dir|
228
+ gemfile_path = ::File.join(dir, "Gemfile")
229
+ return gemfile_path if ::File.readable?(gemfile_path)
230
+ end
231
+ raise GemfileNotFoundError, "Gemfile not found"
232
+ end
233
+
234
+ def configure_gemfile(gemfile_path)
235
+ old_path = ::ENV["BUNDLE_GEMFILE"]
236
+ if old_path && gemfile_path != old_path
237
+ case @on_conflict
238
+ when :warn
239
+ terminal.puts("Warning: could not set up bundler because it is already set up.", :red)
240
+ when :error
241
+ raise AlreadyBundledError, "Could not set up bundler because it is already set up"
242
+ end
243
+ return false
244
+ end
245
+ ::ENV["BUNDLE_GEMFILE"] = gemfile_path
246
+ true
247
+ end
248
+
249
+ def setup_bundle(gemfile_path, groups)
250
+ require "bundler"
251
+ begin
252
+ ::Bundler.setup(*groups)
253
+ rescue ::Bundler::GemNotFound
254
+ install_bundle(gemfile_path)
255
+ ::Bundler.reset!
256
+ ::Bundler.setup(*groups)
257
+ end
258
+ end
259
+
260
+ def permission_to_bundle?
261
+ case @on_missing
262
+ when :install
263
+ true
264
+ when :error
265
+ false
266
+ else
267
+ terminal.confirm("Your bundle is not complete. Install? ", default: @default_confirm)
268
+ end
269
+ end
270
+
271
+ def install_bundle(gemfile_path)
272
+ gemfile_dir = ::File.dirname(gemfile_path)
273
+ unless permission_to_bundle?
274
+ raise BundleNotInstalledError,
275
+ "Your bundle is not installed. Consider running" \
276
+ " `cd #{gemfile_dir} && bundle install`"
277
+ end
278
+ require "bundler/cli"
279
+ ::Bundler::CLI.start(["install"])
280
+ end
170
281
  end
171
282
  end
172
283
  end