toys 0.11.5 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ ##
5
+ # Helpers for writing tool tests.
6
+ #
7
+ # EXPERIMENTAL: Interfaces are subject to change.
8
+ #
9
+ module Testing
10
+ ##
11
+ # Returns the Toys CLI for this test class. By default, a single CLI and
12
+ # Loader are shared by all tests in a given class (or _describe_ block).
13
+ #
14
+ # @return [Toys::CLI]
15
+ #
16
+ def toys_cli
17
+ self.class.toys_cli
18
+ end
19
+
20
+ ##
21
+ # Runs the tool corresponding to the given command line, provided as an
22
+ # array of arguments, and returns a {Toys::Exec::Result}.
23
+ #
24
+ # By default, a single CLI is shared among the tests in each test class or
25
+ # _describe_ block. Thus, tools are loaded only once, and the loader is
26
+ # shared across the tests. If you need to isolate loading for a test,
27
+ # create a separate CLI and pass it in using the `:cli` keyword argument.
28
+ #
29
+ # All other keyword arguments are the same as those defined by the
30
+ # `Toys::Utils::Exec` class. If a block is given, a
31
+ # `Toys::Utils::Exec::Controller` is yielded to it. For more info, see the
32
+ # documentation for `Toys::Utils::Exec#exec`.
33
+ #
34
+ # This method uses "fork" to isolate the run of the tool. On an environment
35
+ # without "fork" support, such as JRuby or Ruby on Windows, consider
36
+ # {#exec_separate_tool}.
37
+ #
38
+ # @param cmd [String,Array<String>] The command to execute.
39
+ # @param opts [keywords] The command options.
40
+ # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
41
+ # for the subprocess streams.
42
+ #
43
+ # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
44
+ # the process is running in the background.
45
+ # @return [Toys::Utils::Exec::Result] The result, if the process ran in
46
+ # the foreground.
47
+ #
48
+ def exec_tool(cmd, **opts, &block)
49
+ cli = opts.delete(:cli) || toys_cli
50
+ cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
51
+ cli.loader.lookup(cmd)
52
+ tool_caller = proc { ::Kernel.exit(cli.run(*cmd)) }
53
+ self.class.toys_exec.exec_proc(tool_caller, **opts, &block)
54
+ end
55
+
56
+ ##
57
+ # Runs the tool corresponding to the given command line, provided as an
58
+ # array of arguments, in a separately spawned process, and returns a
59
+ # {Toys::Exec::Result}.
60
+ #
61
+ # Unlike {#exec_tool}, this method does not use the shared CLI, but instead
62
+ # spawns a completely new Toys process for each run. It is thus slower than
63
+ # {#exec_tool}, but compatible with environments without "fork" support,
64
+ # such as JRuby or Ruby on Windows.
65
+ #
66
+ # Supported keyword arguments are the same as those defined by the
67
+ # `Toys::Utils::Exec` class. If a block is given, a
68
+ # `Toys::Utils::Exec::Controller` is yielded to it. For more info, see the
69
+ # documentation for `Toys::Utils::Exec#exec`.
70
+ #
71
+ # @param cmd [String,Array<String>] The command to execute.
72
+ # @param opts [keywords] The command options.
73
+ # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
74
+ # for the subprocess streams.
75
+ #
76
+ # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
77
+ # the process is running in the background.
78
+ # @return [Toys::Utils::Exec::Result] The result, if the process ran in
79
+ # the foreground.
80
+ #
81
+ def exec_separate_tool(cmd, **opts, &block)
82
+ cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
83
+ cmd = [::RbConfig.ruby, "--disable=gems", ::Toys.executable_path] + cmd
84
+ self.class.toys_exec.exec(cmd, **opts, &block)
85
+ end
86
+
87
+ ##
88
+ # Runs the tool corresponding to the given command line, and returns the
89
+ # data written to `STDOUT`. This is equivalent to calling {#exec_tool}
90
+ # with the keyword arguments `out: :capture, background: false`, and
91
+ # calling `#captured_out` on the result.
92
+ #
93
+ # Note: this method uses "fork" to execute the tool. If you are using an
94
+ # environment without "fork" support, such as JRuby oor Ruby on Windows,
95
+ # consider {#capture_separate_tool}.
96
+ #
97
+ # @param cmd [String,Array<String>] The command to execute.
98
+ # @param opts [keywords] The command options.
99
+ # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
100
+ # for the subprocess streams.
101
+ #
102
+ # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
103
+ # the process is running in the background.
104
+ # @return [Toys::Utils::Exec::Result] The result, if the process ran in
105
+ # the foreground.
106
+ #
107
+ def capture_tool(cmd, **opts, &block)
108
+ opts = opts.merge(out: :capture, background: false)
109
+ exec_tool(cmd, **opts, &block).captured_out
110
+ end
111
+
112
+ ##
113
+ # Runs the tool corresponding to the given command line, and returns the
114
+ # data written to `STDOUT`. This is equivalent to calling
115
+ # {#exec_separate_tool} with the keyword arguments
116
+ # `out: :capture, background: false`, and calling `#captured_out` on the
117
+ # result.
118
+ #
119
+ # Unlike {#capture_tool}, this method does not use "fork", and thus can be
120
+ # called in an environment such as JRuby or Ruby on Windows.
121
+ #
122
+ # @param cmd [String,Array<String>] The command to execute.
123
+ # @param opts [keywords] The command options.
124
+ # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
125
+ # for the subprocess streams.
126
+ #
127
+ # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
128
+ # the process is running in the background.
129
+ # @return [Toys::Utils::Exec::Result] The result, if the process ran in
130
+ # the foreground.
131
+ #
132
+ def capture_separate_tool(cmd, **opts, &block)
133
+ opts = opts.merge(out: :capture, background: false)
134
+ exec_separate_tool(cmd, **opts, &block).captured_out
135
+ end
136
+
137
+ ##
138
+ # Runs the tool corresponding to the given command line, managing streams
139
+ # using a controller. This is equivalent to calling {#exec_tool} with the
140
+ # keyword arguments:
141
+ #
142
+ # out: :controller,
143
+ # err: :controller,
144
+ # in: :controller,
145
+ # background: block.nil?
146
+ #
147
+ # If a block is given, the command is run in the foreground, the
148
+ # controller is passed to the block during the run, and a result object is
149
+ # returned. If no block is given, the command is run in the background, and
150
+ # the controller object is returned.
151
+ #
152
+ # @param cmd [String,Array<String>] The command to execute.
153
+ # @param opts [keywords] The command options.
154
+ # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
155
+ # for the subprocess streams.
156
+ #
157
+ # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
158
+ # the process is running in the background.
159
+ # @return [Toys::Utils::Exec::Result] The result, if the process ran in
160
+ # the foreground.
161
+ #
162
+ def control_tool(cmd, **opts, &block)
163
+ opts = opts.merge(out: :controller, err: :controller, in: :controller, background: block.nil?)
164
+ exec_tool(cmd, **opts, &block)
165
+ end
166
+
167
+ @toys_mutex = ::Mutex.new
168
+
169
+ # @private
170
+ def self.included(klass)
171
+ klass.extend(ClassMethods)
172
+ end
173
+
174
+ # @private
175
+ def self.toys_mutex
176
+ @toys_mutex
177
+ end
178
+
179
+ # @private
180
+ module ClassMethods
181
+ # @private
182
+ def toys_cli
183
+ Testing.toys_mutex.synchronize do
184
+ @toys_cli ||= StandardCLI.new
185
+ end
186
+ end
187
+
188
+ # @private
189
+ def toys_exec
190
+ Testing.toys_mutex.synchronize do
191
+ require "toys/utils/exec"
192
+ @toys_exec ||= Utils::Exec.new
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
data/lib/toys/version.rb CHANGED
@@ -5,5 +5,5 @@ module Toys
5
5
  # Current version of the Toys command line executable.
6
6
  # @return [String]
7
7
  #
8
- VERSION = "0.11.5"
8
+ VERSION = "0.12.0"
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.5
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-28 00:00:00.000000000 Z
11
+ date: 2021-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: toys-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.11.5
19
+ version: 0.12.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.11.5
26
+ version: 0.12.0
27
27
  description: Toys is a configurable command line tool. Write commands in Ruby using
28
28
  a simple DSL, and Toys will provide the command line executable and take care of
29
29
  all the details such as argument parsing, online help, and error reporting. Toys
@@ -44,7 +44,9 @@ files:
44
44
  - README.md
45
45
  - bin/toys
46
46
  - builtins/do.rb
47
- - builtins/system.rb
47
+ - builtins/system/bash-completion.rb
48
+ - builtins/system/test.rb
49
+ - builtins/system/update.rb
48
50
  - docs/guide.md
49
51
  - lib/toys.rb
50
52
  - lib/toys/standard_cli.rb
@@ -56,6 +58,7 @@ files:
56
58
  - lib/toys/templates/rspec.rb
57
59
  - lib/toys/templates/rubocop.rb
58
60
  - lib/toys/templates/yardoc.rb
61
+ - lib/toys/testing.rb
59
62
  - lib/toys/version.rb
60
63
  - share/bash-completion-remove.sh
61
64
  - share/bash-completion.sh
@@ -63,10 +66,10 @@ homepage: https://github.com/dazuma/toys
63
66
  licenses:
64
67
  - MIT
65
68
  metadata:
66
- changelog_uri: https://dazuma.github.io/toys/gems/toys/v0.11.5/file.CHANGELOG.html
69
+ changelog_uri: https://dazuma.github.io/toys/gems/toys/v0.12.0/file.CHANGELOG.html
67
70
  source_code_uri: https://github.com/dazuma/toys/tree/main/toys
68
71
  bug_tracker_uri: https://github.com/dazuma/toys/issues
69
- documentation_uri: https://dazuma.github.io/toys/gems/toys/v0.11.5
72
+ documentation_uri: https://dazuma.github.io/toys/gems/toys/v0.12.0
70
73
  post_install_message:
71
74
  rdoc_options: []
72
75
  require_paths:
@@ -75,14 +78,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
78
  requirements:
76
79
  - - ">="
77
80
  - !ruby/object:Gem::Version
78
- version: 2.3.0
81
+ version: 2.4.0
79
82
  required_rubygems_version: !ruby/object:Gem::Requirement
80
83
  requirements:
81
84
  - - ">="
82
85
  - !ruby/object:Gem::Version
83
86
  version: '0'
84
87
  requirements: []
85
- rubygems_version: 3.1.4
88
+ rubygems_version: 3.1.6
86
89
  signing_key:
87
90
  specification_version: 4
88
91
  summary: A configurable command line tool
data/builtins/system.rb DELETED
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- desc "A set of system commands for Toys"
4
-
5
- long_desc "Contains tools that inspect, configure, and update Toys itself."
6
-
7
- tool "version" do
8
- desc "Print the current Toys version"
9
-
10
- def run
11
- puts ::Toys::VERSION
12
- end
13
- end
14
-
15
- tool "update" do
16
- desc "Update Toys if a newer version is available"
17
-
18
- long_desc "Checks rubygems for a newer version of Toys. If one is available, downloads" \
19
- " and installs it."
20
-
21
- flag :yes, "-y", "--yes", desc: "Do not ask for interactive confirmation"
22
-
23
- include :exec
24
- include :terminal
25
-
26
- def run
27
- require "rubygems"
28
- configure_exec(exit_on_nonzero_status: true)
29
- version_info = spinner(leading_text: "Checking rubygems for the latest release... ",
30
- final_text: "Done.\n") do
31
- capture(["gem", "list", "-q", "-r", "-e", "toys"])
32
- end
33
- if version_info =~ /toys\s\((.+)\)/
34
- latest_version = ::Gem::Version.new(::Regexp.last_match(1))
35
- cur_version = ::Gem::Version.new(::Toys::VERSION)
36
- if latest_version > cur_version
37
- prompt = "Update Toys from #{cur_version} to #{latest_version}? "
38
- exit(1) unless yes || confirm(prompt, default: true)
39
- result = spinner(leading_text: "Installing Toys version #{latest_version}... ",
40
- final_text: "Done.\n") do
41
- exec(["gem", "install", "toys", "--version", latest_version.to_s],
42
- out: :capture, err: :capture)
43
- end
44
- if result.error?
45
- puts(result.captured_out + result.captured_err)
46
- puts("Toys failed to install version #{latest_version}", :red, :bold)
47
- exit(1)
48
- end
49
- puts("Toys successfully installed version #{latest_version}", :green, :bold)
50
- elsif latest_version < cur_version
51
- puts("Toys is already at experimental version #{cur_version}, which is later than" \
52
- " the latest released version #{latest_version}",
53
- :yellow, :bold)
54
- else
55
- puts("Toys is already at the latest version: #{latest_version}", :green, :bold)
56
- end
57
- else
58
- puts("Could not get latest Toys version", :red, :bold)
59
- exit(1)
60
- end
61
- end
62
- end
63
-
64
- tool "bash-completion" do
65
- desc "Bash tab completion for Toys"
66
-
67
- long_desc \
68
- "Tools that manage tab completion for Toys in the bash shell.",
69
- "",
70
- "To install tab completion for Toys, execute the following line in a bash shell, or" \
71
- " include it in an init file such as your .bashrc:",
72
- [" $(toys system bash-completion install)"],
73
- "",
74
- "To remove tab completion, execute:",
75
- [" $(toys system bash-completion remove)"],
76
- "",
77
- "It is also possible to install completions for different executable names if you have" \
78
- " aliases for Toys. See the help for the \"install\" and \"remove\" tools for details.",
79
- "",
80
- "The \"eval\" tool is the actual completion command invoked by bash when it needs to" \
81
- " complete a toys command line. You shouldn't need to invoke it directly."
82
-
83
- tool "eval" do
84
- desc "Tab completion command (executed by bash)"
85
-
86
- long_desc \
87
- "Completion command invoked by bash to compete a toys command line. Generally you do not" \
88
- " need to invoke this directly. It reads the command line context from the COMP_LINE" \
89
- " and COMP_POINT environment variables, and outputs completion candidates to stdout."
90
-
91
- disable_argument_parsing
92
-
93
- def run
94
- require "toys/utils/completion_engine"
95
- result = ::Toys::Utils::CompletionEngine::Bash.new(cli).run
96
- if result > 1
97
- logger.fatal("This tool must be invoked as a bash completion command.")
98
- end
99
- exit(result)
100
- end
101
- end
102
-
103
- tool "install" do
104
- desc "Install bash tab completion"
105
-
106
- long_desc \
107
- "Outputs a command to set up Toys tab completion in the current bash shell.",
108
- "",
109
- "To use, execute the following line in a bash shell, or include it in an init file" \
110
- " such as your .bashrc:",
111
- [" $(toys system bash-completion install)"],
112
- "",
113
- "This will associate the toys tab completion logic with the `toys` executable by default." \
114
- " If you have aliases for the toys executable, pass them as arguments. e.g.",
115
- [" $(toys system bash-completion install my-toys-alias another-alias)"]
116
-
117
- remaining_args :executable_names,
118
- desc: "Names of executables for which to set up tab completion" \
119
- " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
120
-
121
- def run
122
- require "shellwords"
123
- path = ::File.join(::File.dirname(__dir__), "share", "bash-completion.sh")
124
- exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
125
- puts Shellwords.join(["source", path] + exes)
126
- end
127
- end
128
-
129
- tool "remove" do
130
- desc "Remove bash tab completion"
131
-
132
- long_desc \
133
- "Outputs a command to remove Toys tab completion from the current bash shell.",
134
- "",
135
- "To use, execute the following line in a bash shell:",
136
- [" $(toys system bash-completion remove)"],
137
- "",
138
- "If you have other names or aliases for the toys executable, pass them as arguments. e.g.",
139
- [" $(toys system bash-completion remove my-toys-alias another-alias)"]
140
-
141
- remaining_args :executable_names,
142
- desc: "Names of executables for which to set up tab completion" \
143
- " (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
144
-
145
- def run
146
- require "shellwords"
147
- path = ::File.join(::File.dirname(__dir__), "share", "bash-completion-remove.sh")
148
- exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
149
- puts Shellwords.join(["source", path] + exes)
150
- end
151
- end
152
- end