toys 0.3.1 → 0.3.2

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.
@@ -1,432 +0,0 @@
1
- # Copyright 2018 Daniel Azuma
2
- #
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # * Redistributions of source code must retain the above copyright notice,
9
- # this list of conditions and the following disclaimer.
10
- # * Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # * Neither the name of the copyright holder, nor the names of any other
14
- # contributors to this software, may be used to endorse or promote products
15
- # derived from this software without specific prior written permission.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
- # POSSIBILITY OF SUCH DAMAGE.
28
- ;
29
-
30
- module Toys
31
- ##
32
- # This class defines the DSL for a toys configuration file.
33
- #
34
- # A toys configuration defines one or more named tools. It provides syntax
35
- # for setting the description, defining switches and arguments, specifying
36
- # how to execute the tool, and requesting helper modules and other services.
37
- # It also lets you define subtools, nested arbitrarily deep, using blocks.
38
- #
39
- # Generally ConfigDSL is invoked from the {Loader}. Applications should not
40
- # need to create instances of ConfigDSL directly.
41
- #
42
- # ## Simple example
43
- #
44
- # Create a file called `.toys.rb` in the current directory, with the
45
- # following contents:
46
- #
47
- # tool "greet" do
48
- # desc "Prints a simple greeting"
49
- #
50
- # optional_arg :recipient, default: "world"
51
- #
52
- # execute do
53
- # puts "Hello, #{self[:recipient]}!"
54
- # end
55
- # end
56
- #
57
- # Now you can execute it using:
58
- #
59
- # toys greet
60
- #
61
- # or try:
62
- #
63
- # toys greet rubyists
64
- #
65
- class ConfigDSL
66
- ##
67
- # Create an instance of the DSL.
68
- # @private
69
- #
70
- # @param [String] path The path to the config file being evaluated
71
- # @param [Toys::Tool] tool The tool being defined at the top level
72
- # @param [Array<String>,nil] remaining_words Arguments remaining in the
73
- # current lookup.
74
- # @param [Integer] priority Priority of this configuration
75
- # @param [Toys::Loader] loader Current active loader
76
- # @param [:tool,:append,:group] type Type of tool being configured
77
- #
78
- # @return [Toys::ConfigDSL]
79
- #
80
- def initialize(path, tool, remaining_words, priority, loader, type)
81
- @path = path
82
- @tool = tool
83
- @remaining_words = remaining_words
84
- @priority = priority
85
- @loader = loader
86
- @type = type
87
- end
88
-
89
- ##
90
- # Create a subtool.
91
- #
92
- # If the subtool is not an alias, you must provide a block defining the
93
- # subtool.
94
- #
95
- # If the subtool is already defined (either as a tool or a group), the old
96
- # definition is discarded and replaced with the new definition. If the old
97
- # tool was a group, all its descendants are also discarded, recursively.
98
- #
99
- # @param [String] word The name of the subtool
100
- # @param [String,nil] alias_of If set, this subtool is set to be an alias
101
- # of the given subtool name. Defaults to `nil`, indicating the subtool
102
- # is not an alias.
103
- #
104
- def tool(word, alias_of: nil, &block)
105
- word = word.to_s
106
- subtool = @loader.get_or_create_tool(@tool.full_name + [word], @priority, assume_parent: true)
107
- return self if subtool.nil?
108
- if alias_of
109
- if block
110
- raise ToolDefinitionError, "Cannot take a block with alias_of"
111
- end
112
- subtool.make_alias_of_word(alias_of.to_s)
113
- return self
114
- end
115
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
116
- ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :tool, block)
117
- self
118
- end
119
- alias name tool
120
-
121
- ##
122
- # Append subtools to an existing group.
123
- #
124
- # Pass a group name to this method to "reopen" that group. You must provide
125
- # a block. In that block, you may not modify any properties of the group
126
- # itself, but you may add or replace subtools within the group.
127
- #
128
- # @param [String] word The name of the group.
129
- #
130
- def append(word, &block)
131
- word = word.to_s
132
- subtool = @loader.get_or_create_tool(@tool.full_name + [word], nil, assume_parent: true)
133
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
134
- ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :append, block)
135
- self
136
- end
137
-
138
- ##
139
- # Create a group subtool. You must provide a block defining the group's
140
- # properties and contents.
141
- #
142
- # If the subtool is already defined (either as a tool or a group), the old
143
- # definition is discarded and replaced with the new definition.
144
- #
145
- # @param [String] word The name of the group
146
- #
147
- def group(word, &block)
148
- word = word.to_s
149
- subtool = @loader.get_or_create_tool(@tool.full_name + [word], @priority, assume_parent: true)
150
- return self if subtool.nil?
151
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
152
- ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :group, block)
153
- self
154
- end
155
-
156
- ##
157
- # Create an alias of the current tool.
158
- #
159
- # @param [String] word The name of the alias
160
- #
161
- def alias_as(word)
162
- if @tool.root?
163
- raise ToolDefinitionError, "Cannot make an alias of the root tool"
164
- end
165
- if @type == :group || @type == :append
166
- raise ToolDefinitionError, "Cannot make an alias of a group"
167
- end
168
- alias_name = @tool.full_name.slice(0..-2) + [word.to_s]
169
- alias_tool = @loader.get_or_create_tool(alias_name, @priority)
170
- alias_tool.make_alias_of(@tool.simple_name) if alias_tool
171
- self
172
- end
173
-
174
- ##
175
- # Make the current tool an alias of the given sibling
176
- #
177
- # @param [String] word The name of the sibling tool to alias
178
- #
179
- def alias_of(word)
180
- if @tool.root?
181
- raise ToolDefinitionError, "Cannot make the root tool an alias"
182
- end
183
- if @type == :group || @type == :append
184
- raise ToolDefinitionError, "Cannot make a group an alias"
185
- end
186
- @tool.make_alias_of(word.to_s)
187
- self
188
- end
189
-
190
- ##
191
- # Include another config file or directory at the current location.
192
- #
193
- # @param [String] path The file or directory to include.
194
- #
195
- def include(path)
196
- @tool.yield_definition do
197
- @loader.include_path(path, @tool.full_name, @remaining_words, @priority)
198
- end
199
- self
200
- end
201
-
202
- ##
203
- # Expand the given template in the current location.
204
- #
205
- # The template may be specified as a class or a well-known template name.
206
- # You may also provide arguments to pass to the template.
207
- #
208
- # @param [Class,String,Symbol] template_class The template, either as a
209
- # class or a well-known name.
210
- # @param [Object...] args Template arguments
211
- #
212
- def expand(template_class, *args)
213
- unless template_class.is_a?(::Class)
214
- name = template_class.to_s
215
- template_class = Templates.lookup(name)
216
- if template_class.nil?
217
- raise ToolDefinitionError, "Template not found: #{name.inspect}"
218
- end
219
- end
220
- template = template_class.new(*args)
221
- yield template if block_given?
222
- instance_exec(template, &template_class.expander)
223
- self
224
- end
225
-
226
- ##
227
- # Set the long description for the current tool. The long description is
228
- # displayed in the usage documentation for the tool itself.
229
- #
230
- # @param [String] desc The long description string.
231
- #
232
- def long_desc(desc)
233
- if @type == :append
234
- raise ToolDefinitionError, "Cannot set the description when appending"
235
- end
236
- @tool.long_desc = desc
237
- self
238
- end
239
-
240
- ##
241
- # Set the short description for the current tool. The short description is
242
- # displayed with the tool in a command list. You may also use the
243
- # equivalent method `short_desc`.
244
- #
245
- # @param [String] desc The short description string.
246
- #
247
- def desc(desc)
248
- if @type == :append
249
- raise ToolDefinitionError, "Cannot set the description when appending"
250
- end
251
- @tool.desc = desc
252
- self
253
- end
254
- alias short_desc desc
255
-
256
- ##
257
- # Add a switch to the current tool. Each switch must specify a key which
258
- # the executor may use to obtain the switch value from the context.
259
- # You may then provide the switches themselves in `OptionParser` form.
260
- #
261
- # @param [Symbol] key The key to use to retrieve the value from the
262
- # execution context.
263
- # @param [String...] switches The switches in OptionParser format.
264
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
265
- # @param [Object] default The default value. This is the value that will
266
- # be set in the context if this switch is not provided on the command
267
- # line. Defaults to `nil`.
268
- # @param [String,nil] doc The documentation for the switch, which appears
269
- # in the usage documentation. Defaults to `nil` for no documentation.
270
- # @param [Boolean] only_unique If true, any switches that are already
271
- # defined in this tool are removed from this switch. For example, if
272
- # an earlier switch uses `-a`, and this switch wants to use both
273
- # `-a` and `-b`, then only `-b` will be assigned to this switch.
274
- # Defaults to false.
275
- # @param [Proc,nil] handler An optional handler for setting/updating the
276
- # value. If given, it should take two arguments, the new given value
277
- # and the previous value, and it should return the new value that
278
- # should be set. The default handler simply replaces the previous
279
- # value. i.e. the default is effectively `-> (val, _prev) { val }`.
280
- #
281
- def switch(key, *switches,
282
- accept: nil, default: nil, doc: nil, only_unique: false, handler: nil)
283
- if @type == :append
284
- raise ToolDefinitionError, "Cannot add a switch when appending"
285
- end
286
- @tool.add_switch(key, *switches,
287
- accept: accept, default: default, doc: doc,
288
- only_unique: only_unique, handler: handler)
289
- self
290
- end
291
-
292
- ##
293
- # Add a required positional argument to the current tool. You must specify
294
- # a key which the executor may use to obtain the argument value from the
295
- # context.
296
- #
297
- # @param [Symbol] key The key to use to retrieve the value from the
298
- # execution context.
299
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
300
- # @param [String,nil] doc The documentation for the switch, which appears
301
- # in the usage documentation. Defaults to `nil` for no documentation.
302
- #
303
- def required_arg(key, accept: nil, doc: nil)
304
- if @type == :append
305
- raise ToolDefinitionError, "Cannot add an argument when appending"
306
- end
307
- @tool.add_required_arg(key, accept: accept, doc: doc)
308
- self
309
- end
310
-
311
- ##
312
- # Add an optional positional argument to the current tool. You must specify
313
- # a key which the executor may use to obtain the argument value from the
314
- # context. If an optional argument is not given on the command line, the
315
- # value is set to the given default.
316
- #
317
- # @param [Symbol] key The key to use to retrieve the value from the
318
- # execution context.
319
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
320
- # @param [Object] default The default value. This is the value that will
321
- # be set in the context if this argument is not provided on the command
322
- # line. Defaults to `nil`.
323
- # @param [String,nil] doc The documentation for the argument, which appears
324
- # in the usage documentation. Defaults to `nil` for no documentation.
325
- #
326
- def optional_arg(key, accept: nil, default: nil, doc: nil)
327
- if @type == :append
328
- raise ToolDefinitionError, "Cannot add an argument when appending"
329
- end
330
- @tool.add_optional_arg(key, accept: accept, default: default, doc: doc)
331
- self
332
- end
333
-
334
- ##
335
- # Specify what should be done with unmatched positional arguments. You must
336
- # specify a key which the executor may use to obtain the remaining args
337
- # from the context.
338
- #
339
- # @param [Symbol] key The key to use to retrieve the value from the
340
- # execution context.
341
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
342
- # @param [Object] default The default value. This is the value that will
343
- # be set in the context if no unmatched arguments are provided on the
344
- # command line. Defaults to the empty array `[]`.
345
- # @param [String,nil] doc The documentation for the remaining arguments,
346
- # which appears in the usage documentation. Defaults to `nil` for no
347
- # documentation.
348
- #
349
- def remaining_args(key, accept: nil, default: [], doc: nil)
350
- if @type == :append
351
- raise ToolDefinitionError, "Cannot add an argument when appending"
352
- end
353
- @tool.set_remaining_args(key, accept: accept, default: default, doc: doc)
354
- self
355
- end
356
-
357
- ##
358
- # Specify the executor for this tool. This is a block that will be called,
359
- # with `self` set to a {Toys::Context}.
360
- #
361
- def execute(&block)
362
- if @type == :group || @type == :append
363
- raise ToolDefinitionError, "Cannot set the executor of a group"
364
- end
365
- @tool.executor = block
366
- self
367
- end
368
-
369
- ##
370
- # Define a helper method that may be called from this tool's executor.
371
- # You must provide a name for the method, and a block for the method
372
- # definition.
373
- #
374
- # @param [String,Symbol] name Name of the method. May not begin with an
375
- # underscore.
376
- #
377
- def helper(name, &block)
378
- if @type == :group || @type == :append
379
- raise ToolDefinitionError, "Cannot define a helper method to a group"
380
- end
381
- @tool.add_helper(name, &block)
382
- self
383
- end
384
-
385
- ##
386
- # Specify that the given module should be mixed in to this tool's executor.
387
- # Effectively, the module is added to the {Toys::Context} object.
388
- # You may either provide a module directly, or specify the name of a
389
- # well-known module.
390
- #
391
- # @param [Module,Symbol] mod Module or name of well-known module.
392
- #
393
- def use(mod)
394
- if @type == :group || @type == :append
395
- raise ToolDefinitionError, "Cannot use a helper module in a group"
396
- end
397
- @tool.use_module(mod)
398
- self
399
- end
400
-
401
- ## @private
402
- def _binding
403
- binding
404
- end
405
-
406
- ## @private
407
- def self.evaluate(path, tool, remaining_words, priority, loader, type, source)
408
- dsl = new(path, tool, remaining_words, priority, loader, type)
409
- if type == :append
410
- eval_source(dsl, path, source)
411
- else
412
- tool.defining_from(path) do
413
- eval_source(dsl, path, source)
414
- tool.finish_definition
415
- end
416
- end
417
- tool
418
- end
419
-
420
- ## @private
421
- def self.eval_source(dsl, path, source)
422
- case source
423
- when String
424
- # rubocop:disable Security/Eval
425
- eval(source, dsl._binding, path, 1)
426
- # rubocop:enable Security/Eval
427
- when ::Proc
428
- dsl.instance_eval(&source)
429
- end
430
- end
431
- end
432
- end
data/lib/toys/context.rb DELETED
@@ -1,278 +0,0 @@
1
- # Copyright 2018 Daniel Azuma
2
- #
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # * Redistributions of source code must retain the above copyright notice,
9
- # this list of conditions and the following disclaimer.
10
- # * Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # * Neither the name of the copyright holder, nor the names of any other
14
- # contributors to this software, may be used to endorse or promote products
15
- # derived from this software without specific prior written permission.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
- # POSSIBILITY OF SUCH DAMAGE.
28
- ;
29
-
30
- require "logger"
31
-
32
- module Toys
33
- ##
34
- # The object context in effect during the execution of a tool.
35
- #
36
- # The context is generally a hash of key-value pairs.
37
- # Keys that begin with two underscores are reserved common elements of the
38
- # context such as the tool being executed, or the verbosity level.
39
- # Other keys are available for use by your tool. Generally, they are set
40
- # by switches and arguments in your tool. Context values may also be set
41
- # by middleware. By convention, middleware-set keys begin with a single
42
- # underscore.
43
- #
44
- class Context
45
- ##
46
- # Context key for the verbosity value. Verbosity is an integer defaulting
47
- # to 0, with higher values meaning more verbose and lower meaning quieter.
48
- # @return [Symbol]
49
- #
50
- VERBOSITY = :__verbosity
51
-
52
- ##
53
- # Context key for the `Toys::Tool` object being executed.
54
- # @return [Symbol]
55
- #
56
- TOOL = :__tool
57
-
58
- ##
59
- # Context key for the full name of the tool being executed. Value is an
60
- # array of strings.
61
- # @return [Symbol]
62
- #
63
- TOOL_NAME = :__tool_name
64
-
65
- ##
66
- # Context key for the active `Toys::Loader` object.
67
- # @return [Symbol]
68
- #
69
- LOADER = :__loader
70
-
71
- ##
72
- # Context key for the active `Logger` object.
73
- # @return [Symbol]
74
- #
75
- LOGGER = :__logger
76
-
77
- ##
78
- # Context key for the name of the toys binary. Value is a string.
79
- # @return [Symbol]
80
- #
81
- BINARY_NAME = :__binary_name
82
-
83
- ##
84
- # Context key for the argument list passed to the current tool. Value is
85
- # an array of strings.
86
- # @return [Symbol]
87
- #
88
- ARGS = :__args
89
-
90
- ##
91
- # Context key for the usage error raised. Value is a string if there was
92
- # an error, or nil if there was no error.
93
- # @return [Symbol]
94
- #
95
- USAGE_ERROR = :__usage_error
96
-
97
- ##
98
- # Create a Context object. Applications generally will not need to create
99
- # these objects directly; they are created by the tool when it is preparing
100
- # for execution.
101
- # @private
102
- #
103
- # @param [Toys::Context::Base] context_base
104
- # @param [Hash] data
105
- #
106
- def initialize(context_base, data)
107
- @_context_base = context_base
108
- @_data = data
109
- @_data[LOADER] = context_base.loader
110
- @_data[BINARY_NAME] = context_base.binary_name
111
- @_data[LOGGER] = context_base.logger
112
- end
113
-
114
- ##
115
- # Return the verbosity as an integer.
116
- # @return [Integer]
117
- #
118
- def verbosity
119
- @_data[VERBOSITY]
120
- end
121
-
122
- ##
123
- # Return the tool being executed.
124
- # @return [Toys::Tool]
125
- #
126
- def tool
127
- @_data[TOOL]
128
- end
129
-
130
- ##
131
- # Return the name of the tool being executed, as an array of strings.
132
- # @return [Array[String]]
133
- #
134
- def tool_name
135
- @_data[TOOL_NAME]
136
- end
137
-
138
- ##
139
- # Return the raw arguments passed to the tool, as an array of strings.
140
- # This does not include the tool name itself.
141
- # @return [Array[String]]
142
- #
143
- def args
144
- @_data[ARGS]
145
- end
146
-
147
- ##
148
- # Return any usage error detected during argument parsing, or `nil` if
149
- # no error was detected.
150
- # @return [String,nil]
151
- #
152
- def usage_error
153
- @_data[USAGE_ERROR]
154
- end
155
-
156
- ##
157
- # Return the logger for this execution.
158
- # @return [Logger]
159
- #
160
- def logger
161
- @_data[LOGGER]
162
- end
163
-
164
- ##
165
- # Return the active loader that can be used to get other tools.
166
- # @return [Toys::Loader]
167
- #
168
- def loader
169
- @_data[LOADER]
170
- end
171
-
172
- ##
173
- # Return the name of the binary that was executed.
174
- # @return [String]
175
- #
176
- def binary_name
177
- @_data[BINARY_NAME]
178
- end
179
-
180
- ##
181
- # Return a piece of raw data by key.
182
- #
183
- # @param [Symbol] key
184
- # @return [Object]
185
- #
186
- def [](key)
187
- @_data[key]
188
- end
189
-
190
- ##
191
- # Set a piece of data by key.
192
- #
193
- # Most tools themselves will not need to do this directly. It is generally
194
- # used by middleware to modify the context and affect execution of tools
195
- # that use it.
196
- #
197
- # @param [Symbol] key
198
- # @param [Object] value
199
- #
200
- def []=(key, value)
201
- @_data[key] = value
202
- end
203
-
204
- ##
205
- # Return an option value by key.
206
- #
207
- # @param [Symbol] key
208
- # @return [Object]
209
- #
210
- def option(key)
211
- @_data[key]
212
- end
213
-
214
- ##
215
- # Returns the subset of the context that does not include well-known keys
216
- # such as tool and verbosity. Technically, this includes all keys that do
217
- # not begin with two underscores.
218
- #
219
- # @return [Hash]
220
- #
221
- def options
222
- @_data.select do |k, _v|
223
- !k.is_a?(::Symbol) || !k.to_s.start_with?("__")
224
- end
225
- end
226
-
227
- ##
228
- # Execute another tool, given by the provided arguments.
229
- #
230
- # @param [String...] args Command line arguments defining another tool
231
- # to run, along with parameters and switches.
232
- # @param [Boolean] exit_on_nonzero_status If true, exit immediately if the
233
- # run returns a nonzero error code.
234
- # @return [Integer] The resulting status code
235
- #
236
- def run(*args, exit_on_nonzero_status: false)
237
- code = @_context_base.run(args.flatten, verbosity: @_data[VERBOSITY])
238
- exit(code) if exit_on_nonzero_status && !code.zero?
239
- code
240
- end
241
-
242
- ##
243
- # Exit immediately with the given status code
244
- #
245
- # @param [Integer] code The status code, which should be 0 for no error,
246
- # or nonzero for an error condition.
247
- #
248
- def exit(code)
249
- throw :result, code
250
- end
251
-
252
- ##
253
- # Common context data
254
- # @private
255
- #
256
- class Base
257
- def initialize(loader, binary_name, logger)
258
- @loader = loader
259
- @binary_name = binary_name || ::File.basename($PROGRAM_NAME)
260
- @logger = logger || ::Logger.new(::STDERR)
261
- @base_level = @logger.level
262
- end
263
-
264
- attr_reader :loader
265
- attr_reader :binary_name
266
- attr_reader :logger
267
- attr_reader :base_level
268
-
269
- def run(args, verbosity: 0)
270
- @loader.execute(self, args, verbosity: verbosity)
271
- end
272
-
273
- def create_context(data)
274
- Context.new(self, data)
275
- end
276
- end
277
- end
278
- end