toys 0.11.5 → 0.12.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.
@@ -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