toys-core 0.5.0 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd284ba44663511c6004cf0d742c4e1b5a703cbe596fd6612350be3b316ec60f
4
- data.tar.gz: 9fdc655c88053d669727399c4c4c7321d4d2ececd579c9420bcddb1f0a40d65d
3
+ metadata.gz: 0202fa141dcee7d96aacdec2092ace8041f5498e367596d8e2a5aa893fa64d36
4
+ data.tar.gz: 20ea445f7ad7a5ab964ad23434e57f4acb6ee567e6dc38837751246d32940ca5
5
5
  SHA512:
6
- metadata.gz: ccdfadc2660d171d39776f63e708839edfaef4b63e21a2ddc39feb6456763a0d54fa27cc611cd2c9180210c291fb1f2510fa925b9f9ebea98f7e3794d42ca00e
7
- data.tar.gz: e8d68670c340ac2a2b51a83a1201334ccbbda1a69464a25b9a376badfaf891895ffcc189ad54afbd1a2901b4673ded5fcb61296065ed205e98bfd05dbf4dc810
6
+ metadata.gz: 704d70fc544435c23cf3d0e88550fa3a3951bbb9b3611eabdc89c3c9871d3ad67098cce21fa54ffb130aa4da6d0901729471b9e698ae78a394f8ff750392205f
7
+ data.tar.gz: 57d9cacc5646fd39e0399bb2729d0a936d70f5d67a1f7a7e3d4b19a5d38ab699dc6b0e5044ae455fea3cdb0d46e44406bfc898074e71e2d9b3cc7882981886d7
@@ -1,5 +1,14 @@
1
1
  # Release History
2
2
 
3
+ ### 0.6.0 / 2018-10-22
4
+
5
+ * CHANGED: Replaced Toys::Definition::DataFinder with Toys::Definition::SourceInfo.
6
+ * CHANGED: Removed Toys::Definition#find_data. Use Toys::Definition#source_info and call find_data.
7
+ * ADDED: Context directory is kept in SourceInfo and available in the DSL and the tool runtime.
8
+ * IMPROVED: Optionally omit hidden subtools (i.e. names beginning with underscore)
9
+ from subtool lists.
10
+ * IMPROVED: Optionally omit non-runnable namespaces from recursive subtool lists.
11
+
3
12
  ### 0.5.0 / 2018-10-07
4
13
 
5
14
  * FIXED: Template instantiation was failing if the hosting tool was priority-masked.
@@ -68,8 +68,8 @@ require "toys/core_version"
68
68
  require "toys/definition/acceptor"
69
69
  require "toys/definition/alias"
70
70
  require "toys/definition/arg"
71
- require "toys/definition/data_finder"
72
71
  require "toys/definition/flag"
72
+ require "toys/definition/source_info"
73
73
  require "toys/definition/tool"
74
74
  require "toys/dsl/arg"
75
75
  require "toys/dsl/flag"
@@ -173,12 +173,12 @@ module Toys
173
173
  #
174
174
  # @param [Boolean] high_priority Add the config at the head of the priority
175
175
  # list rather than the tail.
176
- # @param [String] path The "path" that will be shown in documentation for
177
- # tools defined in this block. If omitted, a default unique string will
178
- # be generated.
176
+ # @param [String] name The source name that will be shown in documentation
177
+ # for tools defined in this block. If omitted, a default unique string
178
+ # will be generated.
179
179
  #
180
- def add_config_block(high_priority: false, path: nil, &block)
181
- @loader.add_block(high_priority: high_priority, path: path, &block)
180
+ def add_config_block(high_priority: false, name: nil, &block)
181
+ @loader.add_block(high_priority: high_priority, name: name, &block)
182
182
  self
183
183
  end
184
184
 
@@ -251,7 +251,7 @@ module Toys
251
251
  @loader.lookup(args.flatten)
252
252
  end
253
253
  ContextualError.capture_path(
254
- "Error during tool execution!", tool_definition.source_path,
254
+ "Error during tool execution!", tool_definition.source_info&.source_path,
255
255
  tool_name: tool_definition.full_name, tool_args: remaining
256
256
  ) do
257
257
  Runner.new(self, tool_definition).run(remaining, verbosity: verbosity)
@@ -34,5 +34,5 @@ module Toys
34
34
  # Current version of Toys core
35
35
  # @return [String]
36
36
  #
37
- CORE_VERSION = "0.5.0"
37
+ CORE_VERSION = "0.6.0"
38
38
  end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Daniel Azuma
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ # * Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ # * Neither the name of the copyright holder, nor the names of any other
16
+ # contributors to this software, may be used to endorse or promote products
17
+ # derived from this software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ # POSSIBILITY OF SUCH DAMAGE.
30
+ ;
31
+
32
+ module Toys
33
+ module Definition
34
+ ##
35
+ # Information about source toys directories and files.
36
+ #
37
+ class SourceInfo
38
+ ##
39
+ # Create a SourceInfo.
40
+ # @private
41
+ #
42
+ def initialize(parent, context_directory, source, source_type, source_name, data_dir_name)
43
+ @parent = parent
44
+ @context_directory = context_directory
45
+ @source = source
46
+ @source_type = source_type
47
+ @source_path = source if source.is_a?(::String)
48
+ @source_proc = source if source.is_a?(::Proc)
49
+ @source_name = source_name
50
+ @data_dir =
51
+ if data_dir_name && @source_path
52
+ dir = ::File.join(::File.dirname(@source_path), data_dir_name)
53
+ dir if ::File.directory?(dir) && ::File.readable?(dir)
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Return the parent SourceInfo, or nil if this is the root.
59
+ # @return [Toys::Definition::SourceInfo,nil]
60
+ #
61
+ attr_reader :parent
62
+
63
+ ##
64
+ # Return the context directory path (normally the directory containing
65
+ # the toplevel toys file or directory). May return nil if there is no
66
+ # context (e.g. the tool is being defined from a block).
67
+ # @return [String,nil]
68
+ #
69
+ attr_reader :context_directory
70
+
71
+ ##
72
+ # Return the source, which may be a path or a proc.
73
+ # @return [String,Proc]
74
+ #
75
+ attr_reader :source
76
+
77
+ ##
78
+ # Return the type of source.
79
+ # @return [:file,:directory,:proc]
80
+ #
81
+ attr_reader :source_type
82
+
83
+ ##
84
+ # Return the path of the current source file or directory, or nil if this
85
+ # source is not a file system path.
86
+ # @return [String,nil]
87
+ #
88
+ attr_reader :source_path
89
+
90
+ ##
91
+ # Return the source proc, or nil if this source is not a proc.
92
+ # @return [Proc,nil]
93
+ #
94
+ attr_reader :source_proc
95
+
96
+ ##
97
+ # Return the user-visible name of this source.
98
+ # @return [String]
99
+ #
100
+ attr_reader :source_name
101
+ alias to_s source_name
102
+
103
+ ##
104
+ # Return the absolute path to the given data file or directory.
105
+ #
106
+ # @param [String] path The relative path to find
107
+ # @param [nil,:file,:directory] type Type of file system object to find,
108
+ # or nil to return any type.
109
+ # @return [String,nil] Absolute path of the result, or nil if not found.
110
+ #
111
+ def find_data(path, type: nil)
112
+ if @data_dir
113
+ full_path = ::File.join(@data_dir, path)
114
+ case type
115
+ when :file
116
+ return full_path if ::File.file?(full_path)
117
+ when :directory
118
+ return full_path if ::File.directory?(full_path)
119
+ else
120
+ return full_path if ::File.readable?(full_path)
121
+ end
122
+ end
123
+ parent&.find_data(path, type: type)
124
+ end
125
+
126
+ ##
127
+ # Create a child SourceInfo relative to the parent path.
128
+ # @private
129
+ #
130
+ def relative_child(filename, data_dir_name)
131
+ raise "Cannot create relative child of a proc" unless source_path
132
+ child_path = ::File.join(source_path, filename)
133
+ child_path, type = SourceInfo.check_path(child_path, true)
134
+ return nil unless child_path
135
+ SourceInfo.new(self, context_directory, child_path, type, child_path, data_dir_name)
136
+ end
137
+
138
+ ##
139
+ # Create a child SourceInfo with an absolute path.
140
+ # @private
141
+ #
142
+ def absolute_child(child_path)
143
+ child_path, type = SourceInfo.check_path(child_path, false)
144
+ SourceInfo.new(self, context_directory, child_path, type, child_path, nil)
145
+ end
146
+
147
+ ##
148
+ # Create a root source info for a file path.
149
+ # @private
150
+ #
151
+ def self.create_path_root(source_path)
152
+ source_path, type = check_path(source_path, false)
153
+ context_directory = ::File.dirname(source_path)
154
+ new(nil, context_directory, source_path, type, source_path, nil)
155
+ end
156
+
157
+ ##
158
+ # Create a root source info for a proc.
159
+ # @private
160
+ #
161
+ def self.create_proc_root(source_proc, source_name)
162
+ new(nil, nil, source_proc, :proc, source_name, nil)
163
+ end
164
+
165
+ ##
166
+ # Check a path and determine the canonical path and type.
167
+ # @private
168
+ #
169
+ def self.check_path(path, lenient)
170
+ path = ::File.expand_path(path)
171
+ unless ::File.readable?(path)
172
+ raise LoaderError, "Cannot read: #{path}" unless lenient
173
+ return [nil, nil]
174
+ end
175
+ if ::File.file?(path)
176
+ unless ::File.extname(path) == ".rb"
177
+ raise LoaderError, "File is not a ruby file: #{path}" unless lenient
178
+ return [nil, nil]
179
+ end
180
+ [path, :file]
181
+ elsif ::File.directory?(path)
182
+ [path, :directory]
183
+ else
184
+ raise LoaderError, "Unknown type: #{path}" unless lenient
185
+ [nil, nil]
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -91,8 +91,7 @@ module Toys
91
91
  def reset_definition(loader)
92
92
  @tool_class = DSL::Tool.new_class(@full_name, @priority, loader)
93
93
 
94
- @source_path = nil
95
- @data_finder = nil
94
+ @source_info = nil
96
95
  @definition_finished = false
97
96
 
98
97
  @desc = Utils::WrappableString.new("")
@@ -109,6 +108,7 @@ module Toys
109
108
 
110
109
  @disable_argument_parsing = false
111
110
  @includes_modules = false
111
+ @custom_context_directory = nil
112
112
  end
113
113
 
114
114
  ##
@@ -186,10 +186,18 @@ module Toys
186
186
  attr_reader :middleware_stack
187
187
 
188
188
  ##
189
- # Returns the path to the file that contains the definition of this tool.
190
- # @return [String]
189
+ # Returns info on the source of this tool, or nil if the source is not
190
+ # defined.
191
+ # @return [Toys::Definition::SourceInfo,nil]
192
+ #
193
+ attr_reader :source_info
194
+
195
+ ##
196
+ # Returns the custom context directory set for this tool, or nil if none
197
+ # is set.
198
+ # @return [String,nil]
191
199
  #
192
- attr_reader :source_path
200
+ attr_reader :custom_context_directory
193
201
 
194
202
  ##
195
203
  # Returns the local name of this tool.
@@ -366,17 +374,15 @@ module Toys
366
374
  # A tool may be defined from at most one path. If a different path is
367
375
  # already set, raises {Toys::ToolDefinitionError}
368
376
  #
369
- # @param [String] path The path to the file defining this tool
370
- # @param [Toys::Definition::DataFinder] data_finder Data finder
377
+ # @param [Toys::Definition::SourceInfo] source Source info
371
378
  #
372
- def lock_source_path(path, data_finder)
373
- if source_path && source_path != path
379
+ def lock_source(source)
380
+ if source_info && source_info.source != source.source
374
381
  raise ToolDefinitionError,
375
- "Cannot redefine tool #{display_name.inspect} in #{path}" \
376
- " (already defined in #{source_path})"
382
+ "Cannot redefine tool #{display_name.inspect} in #{source.source_name}" \
383
+ " (already defined in #{source_info.source_name})"
377
384
  end
378
- @source_path = path
379
- @data_finder = data_finder
385
+ @source_info = source
380
386
  end
381
387
 
382
388
  ##
@@ -671,15 +677,35 @@ module Toys
671
677
  end
672
678
 
673
679
  ##
674
- # Find the given data file or directory in this tool's search path.
680
+ # Set the custom context directory.
675
681
  #
676
- # @param [String] path The path to find
677
- # @param [nil,:file,:directory] type Type of file system object to find,
678
- # or nil to return any type.
679
- # @return [String,nil] Absolute path of the result, or nil if not found.
682
+ # @param [String] dir
683
+ #
684
+ def custom_context_directory=(dir)
685
+ check_definition_state
686
+ @custom_context_directory = dir
687
+ end
688
+
689
+ ##
690
+ # Return the effective context directory.
691
+ # If there is a custom context directory, uses that. Otherwise, looks for
692
+ # a custom context directory up the tool ancestor chain. If none is
693
+ # found, uses the default context directory from the source info. It is
694
+ # possible for there to be no context directory at all, in which case,
695
+ # returns nil.
696
+ #
697
+ # @return [String,nil]
698
+ #
699
+ def context_directory
700
+ lookup_custom_context_directory || source_info&.context_directory
701
+ end
702
+
703
+ ##
704
+ # Lookup the custom context directory in this tool and its ancestors.
705
+ # @private
680
706
  #
681
- def find_data(path, type: nil)
682
- @data_finder ? @data_finder.find_data(path, type: type) : nil
707
+ def lookup_custom_context_directory
708
+ custom_context_directory || @parent&.lookup_custom_context_directory
683
709
  end
684
710
 
685
711
  ##
@@ -203,7 +203,7 @@ module Toys
203
203
  end
204
204
  end
205
205
  subtool_class = subtool.tool_class
206
- DSL::Tool.prepare(subtool_class, next_remaining, @__path, @__data_finder) do
206
+ DSL::Tool.prepare(subtool_class, next_remaining, source_info) do
207
207
  subtool_class.class_eval(&block)
208
208
  end
209
209
  self
@@ -230,7 +230,7 @@ module Toys
230
230
  # @return [Toys::DSL::Tool] self, for chaining.
231
231
  #
232
232
  def load(path)
233
- @__loader.load_path(path, @__words, @__remaining_words, @__priority)
233
+ @__loader.load_path(source_info, path, @__words, @__remaining_words, @__priority)
234
234
  self
235
235
  end
236
236
 
@@ -650,6 +650,15 @@ module Toys
650
650
  super(DSL::Tool.resolve_mixin(mod, cur_tool, @__loader))
651
651
  end
652
652
 
653
+ ##
654
+ # Return the current source info object.
655
+ #
656
+ # @return [Toys::Definition::SourceInfo] Source info.
657
+ #
658
+ def source_info
659
+ @__source.last
660
+ end
661
+
653
662
  ##
654
663
  # Find the given data path (file or directory)
655
664
  #
@@ -659,7 +668,32 @@ module Toys
659
668
  # @return [String,nil] Absolute path of the result, or nil if not found.
660
669
  #
661
670
  def find_data(path, type: nil)
662
- @__data_finder ? @__data_finder.find_data(path, type: type) : nil
671
+ source_info.find_data(path, type: type)
672
+ end
673
+
674
+ ##
675
+ # Return the context directory for this tool. Generally, this defaults
676
+ # to the directory containing the toys config directory structure being
677
+ # read, but it may be changed by setting a different context directory
678
+ # for the tool.
679
+ # May return nil if there is no context.
680
+ #
681
+ # @return [String,nil] Context directory
682
+ #
683
+ def context_directory
684
+ DSL::Tool.current_tool(self, false)&.context_directory || source_info.context_directory
685
+ end
686
+
687
+ ##
688
+ # Set a custom context directory for this tool.
689
+ #
690
+ # @param [String] dir Context directory
691
+ #
692
+ def set_context_directory(dir)
693
+ cur_tool = DSL::Tool.current_tool(self, false)
694
+ return if cur_tool.nil?
695
+ cur_tool.custom_context_directory = dir
696
+ self
663
697
  end
664
698
 
665
699
  ## @private
@@ -670,8 +704,7 @@ module Toys
670
704
  tool_class.instance_variable_set(:@__priority, priority)
671
705
  tool_class.instance_variable_set(:@__loader, loader)
672
706
  tool_class.instance_variable_set(:@__remaining_words, nil)
673
- tool_class.instance_variable_set(:@__path, nil)
674
- tool_class.instance_variable_set(:@__data_finder, nil)
707
+ tool_class.instance_variable_set(:@__source, [])
675
708
  tool_class
676
709
  end
677
710
 
@@ -697,23 +730,19 @@ module Toys
697
730
  tool_class.instance_variable_set(memoize_var, cur_tool)
698
731
  end
699
732
  if cur_tool && activate
700
- path = tool_class.instance_variable_get(:@__path)
701
- data_finder = tool_class.instance_variable_get(:@__data_finder)
702
- cur_tool.lock_source_path(path, data_finder)
733
+ source = tool_class.instance_variable_get(:@__source).last
734
+ cur_tool.lock_source(source)
703
735
  end
704
736
  cur_tool
705
737
  end
706
738
 
707
739
  ## @private
708
- def self.prepare(tool_class, remaining_words, path, data_finder)
740
+ def self.prepare(tool_class, remaining_words, source)
709
741
  tool_class.instance_variable_set(:@__remaining_words, remaining_words)
710
- tool_class.instance_variable_set(:@__path, path)
711
- tool_class.instance_variable_set(:@__data_finder, data_finder)
742
+ tool_class.instance_variable_get(:@__source).push(source)
712
743
  yield
713
744
  ensure
714
- tool_class.instance_variable_set(:@__remaining_words, nil)
715
- tool_class.instance_variable_set(:@__path, nil)
716
- tool_class.instance_variable_set(:@__data_finder, nil)
745
+ tool_class.instance_variable_get(:@__source).pop
717
746
  end
718
747
 
719
748
  ## @private
@@ -40,18 +40,19 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
40
40
  end
41
41
 
42
42
  ## @private
43
- def self.evaluate(tool_class, remaining_words, path, data_finder)
43
+ def self.evaluate(tool_class, remaining_words, source)
44
44
  namespace = ::Module.new
45
45
  namespace.module_eval do
46
46
  include ::Toys::Tool::Keys
47
47
  @tool_class = tool_class
48
48
  end
49
+ path = source.source_path
49
50
  basename = ::File.basename(path).tr(".-", "_").gsub(/\W/, "")
50
51
  name = "M#{namespace.object_id}_#{basename}"
51
52
  str = build_eval_string(name, ::IO.read(path))
52
53
  if str
53
54
  const_set(name, namespace)
54
- ::Toys::DSL::Tool.prepare(tool_class, remaining_words, path, data_finder) do
55
+ ::Toys::DSL::Tool.prepare(tool_class, remaining_words, source) do
55
56
  ::Toys::ContextualError.capture_path("Error while loading Toys config!", path) do
56
57
  # rubocop:disable Security/Eval
57
58
  eval(str, __binding, path, 0)
@@ -89,7 +89,7 @@ module Toys
89
89
  @index_file_name = index_file_name
90
90
  @preload_file_name = preload_file_name
91
91
  @preload_directory_name = preload_directory_name
92
- @empty_data_finder = Definition::DataFinder.create_empty(data_directory_name)
92
+ @data_directory_name = data_directory_name
93
93
  @middleware_stack = middleware_stack
94
94
  @worklist = []
95
95
  @tool_data = {}
@@ -101,16 +101,17 @@ module Toys
101
101
  ##
102
102
  # Add a configuration file/directory to the loader.
103
103
  #
104
- # @param [String,Array<String>] path One or more paths to add.
104
+ # @param [String,Array<String>] paths One or more paths to add.
105
105
  # @param [Boolean] high_priority If true, add this path at the top of the
106
106
  # priority list. Defaults to false, indicating the new path should be
107
107
  # at the bottom of the priority list.
108
108
  #
109
- def add_path(path, high_priority: false)
110
- paths = Array(path)
109
+ def add_path(paths, high_priority: false)
110
+ paths = Array(paths)
111
111
  priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
112
- paths.each do |p|
113
- @worklist << [:file, check_path(p), [], @empty_data_finder, priority]
112
+ paths.each do |path|
113
+ source = Definition::SourceInfo.create_path_root(path)
114
+ @worklist << [source, [], priority]
114
115
  end
115
116
  self
116
117
  end
@@ -121,14 +122,15 @@ module Toys
121
122
  # @param [Boolean] high_priority If true, add this block at the top of the
122
123
  # priority list. Defaults to false, indicating the block should be at
123
124
  # the bottom of the priority list.
124
- # @param [String] path The "path" that will be shown in documentation for
125
- # tools defined in this block. If omitted, a default unique string will
126
- # be generated.
125
+ # @param [String] name The source name that will be shown in documentation
126
+ # for tools defined in this block. If omitted, a default unique string
127
+ # will be generated.
127
128
  #
128
- def add_block(high_priority: false, path: nil, &block)
129
- path ||= "(Block #{block.object_id})"
129
+ def add_block(high_priority: false, name: nil, &block)
130
+ name ||= "(Code block #{block.object_id})"
130
131
  priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
131
- @worklist << [block, path, [], @empty_data_finder, priority]
132
+ source = Definition::SourceInfo.create_proc_root(block, name)
133
+ @worklist << [source, [], priority]
132
134
  self
133
135
  end
134
136
 
@@ -173,9 +175,11 @@ module Toys
173
175
  # @param [Array<String>] words The name of the parent tool
174
176
  # @param [Boolean] recursive If true, return all subtools recursively
175
177
  # rather than just the immediate children (the default)
178
+ # @param [Boolean] include_hidden If true, include hidden subtools,
179
+ # e.g. names beginning with underscores.
176
180
  # @return [Array<Toys::Definition::Tool,Toys::Definition::Alias>]
177
181
  #
178
- def list_subtools(words, recursive: false)
182
+ def list_subtools(words, recursive: false, include_hidden: false)
179
183
  load_for_prefix(words)
180
184
  found_tools = []
181
185
  len = words.length
@@ -190,6 +194,7 @@ module Toys
190
194
  found_tools << tool unless tool.nil?
191
195
  end
192
196
  sort_tools_by_name(found_tools)
197
+ include_hidden ? found_tools : filter_hidden_subtools(found_tools)
193
198
  end
194
199
 
195
200
  ##
@@ -309,30 +314,14 @@ module Toys
309
314
  end
310
315
 
311
316
  ##
312
- # Load configuration from the given path.
317
+ # Load configuration from the given path. This is called from the `load`
318
+ # directive in the DSL.
313
319
  #
314
320
  # @private
315
321
  #
316
- def load_path(path, words, remaining_words, priority)
317
- load_validated_path(check_path(path), words, remaining_words, @empty_data_finder, priority)
318
- end
319
-
320
- ##
321
- # Load configuration from the given proc.
322
- #
323
- # @private
324
- #
325
- def load_proc(proc, words, remaining_words, priority, path)
326
- if remaining_words
327
- tool_class = get_tool_definition(words, priority).tool_class
328
- ::Toys::DSL::Tool.prepare(tool_class, remaining_words, path, @empty_data_finder) do
329
- ::Toys::ContextualError.capture("Error while loading Toys config!") do
330
- tool_class.class_eval(&proc)
331
- end
332
- end
333
- else
334
- @worklist << [proc, path, words, @empty_data_finder, priority]
335
- end
322
+ def load_path(parent_source, path, words, remaining_words, priority)
323
+ source = parent_source.absolute_child(path)
324
+ load_validated_path(source, words, remaining_words, priority)
336
325
  end
337
326
 
338
327
  ##
@@ -441,59 +430,71 @@ module Toys
441
430
  def load_for_prefix(prefix)
442
431
  cur_worklist = @worklist
443
432
  @worklist = []
444
- cur_worklist.each do |source, path, words, data_finder, priority|
433
+ cur_worklist.each do |source, words, priority|
445
434
  remaining_words = calc_remaining_words(prefix, words)
446
- if source.respond_to?(:call)
447
- load_proc(source, words, remaining_words, priority, path)
448
- elsif source == :file
449
- load_validated_path(path, words, remaining_words, data_finder, priority)
435
+ if source.source_proc
436
+ load_proc(source, words, remaining_words, priority)
437
+ elsif source.source_path
438
+ load_validated_path(source, words, remaining_words, priority)
439
+ end
440
+ end
441
+ end
442
+
443
+ def load_proc(source, words, remaining_words, priority)
444
+ if remaining_words
445
+ tool_class = get_tool_definition(words, priority).tool_class
446
+ DSL::Tool.prepare(tool_class, remaining_words, source) do
447
+ ContextualError.capture("Error while loading Toys config!") do
448
+ tool_class.class_eval(&source.source_proc)
449
+ end
450
450
  end
451
+ else
452
+ @worklist << [source, words, priority]
451
453
  end
452
454
  end
453
455
 
454
- def load_validated_path(path, words, remaining_words, data_finder, priority)
456
+ def load_validated_path(source, words, remaining_words, priority)
455
457
  if remaining_words
456
- load_relevant_path(path, words, remaining_words, data_finder, priority)
458
+ load_relevant_path(source, words, remaining_words, priority)
457
459
  else
458
- @worklist << [:file, path, words, data_finder, priority]
460
+ @worklist << [source, words, priority]
459
461
  end
460
462
  end
461
463
 
462
- def load_relevant_path(path, words, remaining_words, data_finder, priority)
463
- if ::File.extname(path) == ".rb"
464
+ def load_relevant_path(source, words, remaining_words, priority)
465
+ if source.source_type == :file
464
466
  tool_class = get_tool_definition(words, priority).tool_class
465
- InputFile.evaluate(tool_class, remaining_words, path, data_finder)
467
+ InputFile.evaluate(tool_class, remaining_words, source)
466
468
  else
467
- do_preload(path)
468
- data_finder = data_finder.finder_for(path)
469
- load_index_in(path, words, remaining_words, data_finder, priority)
470
- ::Dir.entries(path).each do |child|
471
- load_child_in(path, child, words, remaining_words, data_finder, priority)
469
+ do_preload(source.source_path)
470
+ load_index_in(source, words, remaining_words, priority)
471
+ ::Dir.entries(source.source_path).each do |child|
472
+ load_child_in(source, child, words, remaining_words, priority)
472
473
  end
473
474
  end
474
475
  end
475
476
 
476
- def load_index_in(path, words, remaining_words, data_finder, priority)
477
+ def load_index_in(source, words, remaining_words, priority)
477
478
  return unless @index_file_name
478
- index_path = ::File.join(path, @index_file_name)
479
- index_path = check_path(index_path, type: :file, lenient: true)
480
- load_relevant_path(index_path, words, remaining_words, data_finder, priority) if index_path
479
+ index_source = source.relative_child(@index_file_name, @data_directory_name)
480
+ load_relevant_path(index_source, words, remaining_words, priority) if index_source
481
481
  end
482
482
 
483
- def load_child_in(path, child, words, remaining_words, data_finder, priority)
484
- return if child.start_with?(".")
485
- return if child == @index_file_name
486
- child_path = check_path(::File.join(path, child))
483
+ def load_child_in(source, child, words, remaining_words, priority)
484
+ return if child.start_with?(".") || child == @index_file_name ||
485
+ child == @preload_file_name || child == @preload_directory_name ||
486
+ child == @data_directory_name
487
+ child_source = source.relative_child(child, @data_directory_name)
487
488
  child_word = ::File.basename(child, ".rb")
488
489
  next_words = words + [child_word]
489
490
  next_remaining = Loader.next_remaining_words(remaining_words, child_word)
490
- load_validated_path(child_path, next_words, next_remaining, data_finder, priority)
491
+ load_validated_path(child_source, next_words, next_remaining, priority)
491
492
  end
492
493
 
493
494
  def do_preload(path)
494
495
  if @preload_file_name
495
496
  preload_file = ::File.join(path, @preload_file_name)
496
- if !::File.directory?(preload_file) && ::File.readable?(preload_file)
497
+ if ::File.file?(preload_file) && ::File.readable?(preload_file)
497
498
  require preload_file
498
499
  end
499
500
  end
@@ -501,37 +502,17 @@ module Toys
501
502
  preload_dir = ::File.join(path, @preload_directory_name)
502
503
  if ::File.directory?(preload_dir) && ::File.readable?(preload_dir)
503
504
  ::Dir.entries(preload_dir).each do |child|
505
+ next unless ::File.extname(child) == ".rb"
504
506
  preload_file = ::File.join(preload_dir, child)
505
- if !::File.directory?(preload_file) && ::File.readable?(preload_file)
506
- require preload_file
507
- end
507
+ next if !::File.file?(preload_file) || !::File.readable?(preload_file)
508
+ require preload_file
508
509
  end
509
510
  end
510
511
  end
511
512
  end
512
513
 
513
- def check_path(path, lenient: false, type: nil)
514
- path = ::File.expand_path(path)
515
- type ||= ::File.extname(path) == ".rb" ? :file : :dir
516
- case type
517
- when :file
518
- if ::File.directory?(path) || !::File.readable?(path)
519
- return nil if lenient
520
- raise LoaderError, "Cannot read file #{path}"
521
- end
522
- when :dir
523
- if !::File.directory?(path) || !::File.readable?(path)
524
- return nil if lenient
525
- raise LoaderError, "Cannot read directory #{path}"
526
- end
527
- else
528
- raise ::ArgumentError, "Illegal type #{type}"
529
- end
530
- path
531
- end
532
-
533
514
  def sort_tools_by_name(tools)
534
- tools.sort do |a, b|
515
+ tools.sort! do |a, b|
535
516
  a = a.full_name
536
517
  b = b.full_name
537
518
  while !a.empty? && !b.empty? && a.first == b.first
@@ -542,6 +523,19 @@ module Toys
542
523
  end
543
524
  end
544
525
 
526
+ def filter_hidden_subtools(tools)
527
+ result = []
528
+ tools.each_with_index do |tool, index|
529
+ next if tool.full_name.any? { |n| n.start_with?("_") }
530
+ unless tool.runnable?
531
+ next_tool = tools[index + 1]
532
+ next if next_tool && next_tool.full_name.slice(0..-2) == tool.full_name
533
+ end
534
+ result << tool
535
+ end
536
+ result
537
+ end
538
+
545
539
  def calc_remaining_words(words1, words2)
546
540
  index = 0
547
541
  lengths = [words1.length, words2.length]
@@ -79,6 +79,7 @@ module Toys
79
79
  def create_data(args, base_verbosity)
80
80
  data = @tool_definition.default_data.dup
81
81
  data[Tool::Keys::TOOL_DEFINITION] = @tool_definition
82
+ data[Tool::Keys::TOOL_SOURCE] = @tool_definition.source_info
82
83
  data[Tool::Keys::TOOL_NAME] = @tool_definition.full_name
83
84
  data[Tool::Keys::VERBOSITY] = base_verbosity
84
85
  data[Tool::Keys::ARGS] = args
@@ -74,6 +74,12 @@ module Toys
74
74
  #
75
75
  DEFAULT_SEARCH_FLAGS = ["-s WORD", "--search=WORD"].freeze
76
76
 
77
+ ##
78
+ # Default show-all-subtools flags
79
+ # @return [Array<String>]
80
+ #
81
+ DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS = ["--all"].freeze
82
+
77
83
  ##
78
84
  # Key set when the show help flag is present
79
85
  # @return [Object]
@@ -104,6 +110,12 @@ module Toys
104
110
  #
105
111
  SEARCH_STRING_KEY = Object.new.freeze
106
112
 
113
+ ##
114
+ # Key for the show-all-subtools setting
115
+ # @return [Object]
116
+ #
117
+ SHOW_ALL_SUBTOOLS_KEY = Object.new.freeze
118
+
107
119
  ##
108
120
  # Key for the tool name
109
121
  # @return [Object]
@@ -155,8 +167,19 @@ module Toys
155
167
  # * The `false` value for no flags. (Default)
156
168
  # * A proc that takes a tool and returns any of the above.
157
169
  #
170
+ # @param [Boolean,Array<String>,Proc] show_all_subtools_flags Specify
171
+ # flags to show all subtools, including hidden tools and non-runnable
172
+ # namespaces. The value may be any of the following:
173
+ #
174
+ # * An array of flags.
175
+ # * The `true` value to use {DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS}.
176
+ # * The `false` value for no flags. (Default)
177
+ # * A proc that takes a tool and returns any of the above.
178
+ #
158
179
  # @param [Boolean] default_recursive Whether to search recursively for
159
180
  # subtools by default. Default is `false`.
181
+ # @param [Boolean] default_show_all_subtools Whether to show all subtools
182
+ # by default. Default is `false`.
160
183
  # @param [Boolean] fallback_execution Cause the tool to display its own
161
184
  # help text if it is not otherwise runnable. This is mostly useful
162
185
  # for namespaces, which have children are not runnable. Default is
@@ -179,7 +202,9 @@ module Toys
179
202
  list_flags: false,
180
203
  recursive_flags: false,
181
204
  search_flags: false,
205
+ show_all_subtools_flags: false,
182
206
  default_recursive: false,
207
+ default_show_all_subtools: false,
183
208
  fallback_execution: false,
184
209
  allow_root_args: false,
185
210
  show_source_path: false,
@@ -191,7 +216,9 @@ module Toys
191
216
  @list_flags = list_flags
192
217
  @recursive_flags = recursive_flags
193
218
  @search_flags = search_flags
219
+ @show_all_subtools_flags = show_all_subtools_flags
194
220
  @default_recursive = default_recursive ? true : false
221
+ @default_show_all_subtools = default_show_all_subtools ? true : false
195
222
  @fallback_execution = fallback_execution
196
223
  @allow_root_args = allow_root_args
197
224
  @show_source_path = show_source_path
@@ -212,6 +239,7 @@ module Toys
212
239
  if (!help_flags.empty? || !list_flags.empty? || @fallback_execution) && has_subtools
213
240
  add_recursive_flags(tool_definition)
214
241
  add_search_flags(tool_definition)
242
+ add_show_all_subtools_flags(tool_definition)
215
243
  end
216
244
  if !help_flags.empty? || !usage_flags.empty? || !list_flags.empty?
217
245
  add_root_args(tool_definition)
@@ -223,16 +251,18 @@ module Toys
223
251
  ##
224
252
  # Display help text if requested.
225
253
  #
226
- def run(tool)
254
+ def run(tool) # rubocop:disable Metrics/AbcSize
227
255
  if tool[SHOW_USAGE_KEY]
228
256
  terminal.puts(get_help_text(tool).usage_string(wrap_width: terminal.width))
229
257
  elsif tool[SHOW_LIST_KEY]
230
258
  terminal.puts(get_help_text(tool).list_string(recursive: tool[RECURSIVE_SUBTOOLS_KEY],
231
259
  search: tool[SEARCH_STRING_KEY],
260
+ include_hidden: tool[SHOW_ALL_SUBTOOLS_KEY],
232
261
  wrap_width: terminal.width))
233
262
  elsif should_show_help(tool)
234
263
  output_help(get_help_text(tool).help_string(recursive: tool[RECURSIVE_SUBTOOLS_KEY],
235
264
  search: tool[SEARCH_STRING_KEY],
265
+ include_hidden: tool[SHOW_ALL_SUBTOOLS_KEY],
236
266
  show_source_path: @show_source_path,
237
267
  wrap_width: terminal.width))
238
268
  else
@@ -288,67 +318,82 @@ module Toys
288
318
  end
289
319
 
290
320
  def add_help_flags(tool_definition)
291
- help_flags = resolve_flags_spec(@help_flags, tool_definition, DEFAULT_HELP_FLAGS)
292
- unless help_flags.empty?
321
+ flags = resolve_flags_spec(@help_flags, tool_definition, DEFAULT_HELP_FLAGS)
322
+ unless flags.empty?
293
323
  tool_definition.add_flag(
294
- SHOW_HELP_KEY, help_flags,
324
+ SHOW_HELP_KEY, flags,
295
325
  report_collisions: false,
296
326
  desc: "Display help for this tool"
297
327
  )
298
328
  end
299
- help_flags
329
+ flags
300
330
  end
301
331
 
302
332
  def add_usage_flags(tool_definition)
303
- usage_flags = resolve_flags_spec(@usage_flags, tool_definition, DEFAULT_USAGE_FLAGS)
304
- unless usage_flags.empty?
333
+ flags = resolve_flags_spec(@usage_flags, tool_definition, DEFAULT_USAGE_FLAGS)
334
+ unless flags.empty?
305
335
  tool_definition.add_flag(
306
- SHOW_USAGE_KEY, usage_flags,
336
+ SHOW_USAGE_KEY, flags,
307
337
  report_collisions: false,
308
338
  desc: "Display a brief usage string for this tool"
309
339
  )
310
340
  end
311
- usage_flags
341
+ flags
312
342
  end
313
343
 
314
344
  def add_list_flags(tool_definition)
315
- list_flags = resolve_flags_spec(@list_flags, tool_definition, DEFAULT_LIST_FLAGS)
316
- unless list_flags.empty?
345
+ flags = resolve_flags_spec(@list_flags, tool_definition, DEFAULT_LIST_FLAGS)
346
+ unless flags.empty?
317
347
  tool_definition.add_flag(
318
- SHOW_LIST_KEY, list_flags,
348
+ SHOW_LIST_KEY, flags,
319
349
  report_collisions: false,
320
350
  desc: "List the subtools under this tool"
321
351
  )
322
352
  end
323
- list_flags
353
+ flags
324
354
  end
325
355
 
326
356
  def add_recursive_flags(tool_definition)
327
- recursive_flags = resolve_flags_spec(@recursive_flags, tool_definition,
328
- DEFAULT_RECURSIVE_FLAGS)
329
- if recursive_flags.empty?
357
+ flags = resolve_flags_spec(@recursive_flags, tool_definition, DEFAULT_RECURSIVE_FLAGS)
358
+ if flags.empty?
330
359
  tool_definition.default_data[RECURSIVE_SUBTOOLS_KEY] = @default_recursive
331
360
  else
332
361
  tool_definition.add_flag(
333
- RECURSIVE_SUBTOOLS_KEY, recursive_flags,
362
+ RECURSIVE_SUBTOOLS_KEY, flags,
334
363
  report_collisions: false, default: @default_recursive,
335
364
  desc: "List all subtools recursively when displaying help" \
336
365
  " (default is #{@default_recursive})"
337
366
  )
338
367
  end
339
- recursive_flags
368
+ flags
340
369
  end
341
370
 
342
371
  def add_search_flags(tool_definition)
343
- search_flags = resolve_flags_spec(@search_flags, tool_definition, DEFAULT_SEARCH_FLAGS)
344
- unless search_flags.empty?
372
+ flags = resolve_flags_spec(@search_flags, tool_definition, DEFAULT_SEARCH_FLAGS)
373
+ unless flags.empty?
345
374
  tool_definition.add_flag(
346
- SEARCH_STRING_KEY, search_flags,
375
+ SEARCH_STRING_KEY, flags,
347
376
  report_collisions: false,
348
377
  desc: "Search subtools for the given regular expression when displaying help"
349
378
  )
350
379
  end
351
- search_flags
380
+ flags
381
+ end
382
+
383
+ def add_show_all_subtools_flags(tool_definition)
384
+ flags = resolve_flags_spec(@show_all_subtools_flags, tool_definition,
385
+ DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS)
386
+ if flags.empty?
387
+ tool_definition.default_data[SHOW_ALL_SUBTOOLS_KEY] = @default_show_all_subtools
388
+ else
389
+ tool_definition.add_flag(
390
+ SHOW_ALL_SUBTOOLS_KEY, flags,
391
+ report_collisions: false, default: @default_show_all_subtools,
392
+ desc: "List all subtools including hidden subtools and namespaces" \
393
+ " (default is #{@default_show_all_subtools})"
394
+ )
395
+ end
396
+ flags
352
397
  end
353
398
 
354
399
  def add_root_args(tool_definition)
@@ -78,6 +78,13 @@ module Toys
78
78
  #
79
79
  TOOL_DEFINITION = ::Object.new.freeze
80
80
 
81
+ ##
82
+ # Context key for the `Toys::Definition::SourceInfo` describing the
83
+ # source of this tool.
84
+ # @return [Object]
85
+ #
86
+ TOOL_SOURCE = ::Object.new.freeze
87
+
81
88
  ##
82
89
  # Context key for the full name of the tool being executed. Value is an
83
90
  # array of strings.
@@ -159,6 +166,14 @@ module Toys
159
166
  @__data[Keys::TOOL_DEFINITION]
160
167
  end
161
168
 
169
+ ##
170
+ # Return the source of the tool being executed.
171
+ # @return [Toys::Definition::SourceInfo]
172
+ #
173
+ def tool_source
174
+ @__data[Keys::TOOL_SOURCE]
175
+ end
176
+
162
177
  ##
163
178
  # Return the name of the tool being executed, as an array of strings.
164
179
  # @return [Array[String]]
@@ -268,7 +283,20 @@ module Toys
268
283
  # @return [String,nil] Absolute path of the result, or nil if not found.
269
284
  #
270
285
  def find_data(path, type: nil)
271
- @__data[Keys::TOOL_DEFINITION].find_data(path, type: type)
286
+ @__data[Keys::TOOL_SOURCE].find_data(path, type: type)
287
+ end
288
+
289
+ ##
290
+ # Return the context directory for this tool. Generally, this defaults
291
+ # to the directory containing the toys config directory structure being
292
+ # read, but it may be changed by setting a different context directory
293
+ # for the tool.
294
+ # May return nil if there is no context.
295
+ #
296
+ # @return [String,nil] Context directory
297
+ #
298
+ def context_directory
299
+ @__data[Keys::TOOL_DEFINITION].context_directory
272
300
  end
273
301
 
274
302
  ##
@@ -83,7 +83,9 @@ module Toys
83
83
  # Generate a short usage string.
84
84
  #
85
85
  # @param [Boolean] recursive If true, and the tool is a namespace,
86
- # display all subcommands recursively. Defaults to false.
86
+ # display all subtools recursively. Defaults to false.
87
+ # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
88
+ # names begin with underscore.) Default is false.
87
89
  # @param [Integer] left_column_width Width of the first column. Default
88
90
  # is {DEFAULT_LEFT_COLUMN_WIDTH}.
89
91
  # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
@@ -92,10 +94,11 @@ module Toys
92
94
  #
93
95
  # @return [String] A usage string.
94
96
  #
95
- def usage_string(recursive: false, left_column_width: nil, indent: nil, wrap_width: nil)
97
+ def usage_string(recursive: false, include_hidden: false,
98
+ left_column_width: nil, indent: nil, wrap_width: nil)
96
99
  left_column_width ||= DEFAULT_LEFT_COLUMN_WIDTH
97
100
  indent ||= DEFAULT_INDENT
98
- subtools = find_subtools(recursive, nil)
101
+ subtools = find_subtools(recursive, nil, include_hidden)
99
102
  assembler = UsageStringAssembler.new(@tool, @binary_name, subtools,
100
103
  indent, left_column_width, wrap_width)
101
104
  assembler.result
@@ -105,9 +108,11 @@ module Toys
105
108
  # Generate a long help string.
106
109
  #
107
110
  # @param [Boolean] recursive If true, and the tool is a namespace,
108
- # display all subcommands recursively. Defaults to false.
111
+ # display all subtools recursively. Defaults to false.
109
112
  # @param [String,nil] search An optional string to search for when
110
- # listing subcommands. Defaults to `nil` which finds all subcommands.
113
+ # listing subtools. Defaults to `nil` which finds all subtools.
114
+ # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
115
+ # names begin with underscore.) Default is false.
111
116
  # @param [Boolean] show_source_path If true, shows the source path
112
117
  # section. Defaults to false.
113
118
  # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
@@ -119,11 +124,12 @@ module Toys
119
124
  #
120
125
  # @return [String] A usage string.
121
126
  #
122
- def help_string(recursive: false, search: nil, show_source_path: false,
127
+ def help_string(recursive: false, search: nil, include_hidden: false,
128
+ show_source_path: false,
123
129
  indent: nil, indent2: nil, wrap_width: nil, styled: true)
124
130
  indent ||= DEFAULT_INDENT
125
131
  indent2 ||= DEFAULT_INDENT
126
- subtools = find_subtools(recursive, search)
132
+ subtools = find_subtools(recursive, search, include_hidden)
127
133
  assembler = HelpStringAssembler.new(@tool, @binary_name, subtools, search, show_source_path,
128
134
  indent, indent2, wrap_width, styled)
129
135
  assembler.result
@@ -133,9 +139,11 @@ module Toys
133
139
  # Generate a subtool list string.
134
140
  #
135
141
  # @param [Boolean] recursive If true, and the tool is a namespace,
136
- # display all subcommands recursively. Defaults to false.
142
+ # display all subtools recursively. Defaults to false.
137
143
  # @param [String,nil] search An optional string to search for when
138
- # listing subcommands. Defaults to `nil` which finds all subcommands.
144
+ # listing subtools. Defaults to `nil` which finds all subtools.
145
+ # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
146
+ # names begin with underscore.) Default is false.
139
147
  # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
140
148
  # @param [Integer,nil] wrap_width Wrap width of the column, or `nil` to
141
149
  # disable wrap. Default is `nil`.
@@ -143,10 +151,10 @@ module Toys
143
151
  #
144
152
  # @return [String] A usage string.
145
153
  #
146
- def list_string(recursive: false, search: nil,
154
+ def list_string(recursive: false, search: nil, include_hidden: false,
147
155
  indent: nil, wrap_width: nil, styled: true)
148
156
  indent ||= DEFAULT_INDENT
149
- subtools = find_subtools(recursive, search)
157
+ subtools = find_subtools(recursive, search, include_hidden)
150
158
  assembler = ListStringAssembler.new(@tool, subtools, recursive, search,
151
159
  indent, wrap_width, styled)
152
160
  assembler.result
@@ -154,8 +162,9 @@ module Toys
154
162
 
155
163
  private
156
164
 
157
- def find_subtools(recursive, search)
158
- subtools = @loader.list_subtools(@tool.full_name, recursive: recursive)
165
+ def find_subtools(recursive, search, include_hidden)
166
+ subtools = @loader.list_subtools(@tool.full_name,
167
+ recursive: recursive, include_hidden: include_hidden)
159
168
  return subtools if search.nil? || search.empty?
160
169
  regex = ::Regexp.new(search, ::Regexp::IGNORECASE)
161
170
  subtools.find_all do |tool|
@@ -387,10 +396,10 @@ module Toys
387
396
  end
388
397
 
389
398
  def add_source_section
390
- return unless @tool.source_path && @show_source_path
399
+ return unless @show_source_path && @tool.source_info&.source_name
391
400
  @lines << ""
392
401
  @lines << bold("SOURCE")
393
- @lines << indent_str("Defined in #{@tool.source_path}")
402
+ @lines << indent_str("Defined in #{@tool.source_info.source_name}")
394
403
  end
395
404
 
396
405
  def add_description_section
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.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: 2018-10-08 00:00:00.000000000 Z
11
+ date: 2018-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.59.1
89
+ version: 0.59.2
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.59.1
96
+ version: 0.59.2
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: yard
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -127,8 +127,8 @@ files:
127
127
  - lib/toys/definition/acceptor.rb
128
128
  - lib/toys/definition/alias.rb
129
129
  - lib/toys/definition/arg.rb
130
- - lib/toys/definition/data_finder.rb
131
130
  - lib/toys/definition/flag.rb
131
+ - lib/toys/definition/source_info.rb
132
132
  - lib/toys/definition/tool.rb
133
133
  - lib/toys/dsl/arg.rb
134
134
  - lib/toys/dsl/flag.rb
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright 2018 Daniel Azuma
4
- #
5
- # All rights reserved.
6
- #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
9
- #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
30
- ;
31
-
32
- module Toys
33
- module Definition
34
- ##
35
- # Finds data files.
36
- #
37
- class DataFinder
38
- ##
39
- # Create a new finder.
40
- #
41
- # @param [String,nil] data_name Name of the data directory, or nil if
42
- # data directories are disabled.
43
- # @param [Toys::Definition::DataFinder,nil] parent The parent, or nil if
44
- # this is the root.
45
- # @param [String,nil] directory The data directory, or nil for none.
46
- # @private
47
- #
48
- def initialize(data_name, parent, directory)
49
- @data_name = data_name
50
- @parent = parent
51
- @directory = directory
52
- end
53
-
54
- ##
55
- # Create a new finder for the given directory.
56
- #
57
- # @param [String] directory Toys directory path
58
- # @return [Toys::Definition::DataFinder] The finder
59
- #
60
- def finder_for(directory)
61
- return self if @data_name.nil?
62
- directory = ::File.join(directory, @data_name)
63
- return self unless ::File.directory?(directory)
64
- DataFinder.new(@data_name, self, directory)
65
- end
66
-
67
- ##
68
- # Return the absolute path to the given data file or directory.
69
- #
70
- # @param [String] path The relative path to find
71
- # @param [nil,:file,:directory] type Type of file system object to find,
72
- # or nil to return any type.
73
- # @return [String,nil] Absolute path of the result, or nil if not found.
74
- #
75
- def find_data(path, type: nil)
76
- return nil if @directory.nil?
77
- full_path = ::File.join(@directory, path)
78
- case type
79
- when :file
80
- return full_path if ::File.file?(full_path)
81
- when :directory
82
- return full_path if ::File.directory?(full_path)
83
- else
84
- return full_path if ::File.readable?(full_path)
85
- end
86
- @parent.find_data(path, type: type)
87
- end
88
-
89
- ##
90
- # Create an empty finder.
91
- #
92
- # @param [String,nil] data_name Name of the data directory, or nil if
93
- # data directories are disabled.
94
- # @return [Toys::Definition::DataFinder]
95
- #
96
- def self.create_empty(data_name)
97
- new(data_name, nil, nil)
98
- end
99
-
100
- ##
101
- # A default empty finder.
102
- #
103
- # @return [Toys::Definition::DataFinder]
104
- #
105
- EMPTY = create_empty(nil)
106
- end
107
- end
108
- end