toys 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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