toys-core 0.11.5 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +5 -2
  5. data/docs/guide.md +1 -1
  6. data/lib/toys/acceptor.rb +13 -4
  7. data/lib/toys/arg_parser.rb +7 -7
  8. data/lib/toys/cli.rb +170 -120
  9. data/lib/toys/compat.rb +71 -23
  10. data/lib/toys/completion.rb +18 -6
  11. data/lib/toys/context.rb +24 -15
  12. data/lib/toys/core.rb +6 -2
  13. data/lib/toys/dsl/base.rb +87 -0
  14. data/lib/toys/dsl/flag.rb +26 -20
  15. data/lib/toys/dsl/flag_group.rb +18 -14
  16. data/lib/toys/dsl/internal.rb +206 -0
  17. data/lib/toys/dsl/positional_arg.rb +26 -16
  18. data/lib/toys/dsl/tool.rb +180 -218
  19. data/lib/toys/errors.rb +64 -8
  20. data/lib/toys/flag.rb +662 -656
  21. data/lib/toys/flag_group.rb +24 -10
  22. data/lib/toys/input_file.rb +13 -7
  23. data/lib/toys/loader.rb +293 -140
  24. data/lib/toys/middleware.rb +46 -22
  25. data/lib/toys/mixin.rb +10 -8
  26. data/lib/toys/positional_arg.rb +21 -20
  27. data/lib/toys/settings.rb +914 -0
  28. data/lib/toys/source_info.rb +147 -35
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
  30. data/lib/toys/standard_middleware/apply_config.rb +6 -4
  31. data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
  32. data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
  33. data/lib/toys/standard_middleware/show_help.rb +19 -5
  34. data/lib/toys/standard_middleware/show_root_version.rb +2 -0
  35. data/lib/toys/standard_mixins/bundler.rb +24 -15
  36. data/lib/toys/standard_mixins/exec.rb +43 -34
  37. data/lib/toys/standard_mixins/fileutils.rb +3 -1
  38. data/lib/toys/standard_mixins/gems.rb +21 -17
  39. data/lib/toys/standard_mixins/git_cache.rb +46 -0
  40. data/lib/toys/standard_mixins/highline.rb +8 -8
  41. data/lib/toys/standard_mixins/terminal.rb +5 -5
  42. data/lib/toys/standard_mixins/xdg.rb +56 -0
  43. data/lib/toys/template.rb +11 -9
  44. data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
  45. data/lib/toys/utils/completion_engine.rb +7 -2
  46. data/lib/toys/utils/exec.rb +162 -132
  47. data/lib/toys/utils/gems.rb +85 -60
  48. data/lib/toys/utils/git_cache.rb +813 -0
  49. data/lib/toys/utils/help_text.rb +117 -37
  50. data/lib/toys/utils/terminal.rb +11 -3
  51. data/lib/toys/utils/xdg.rb +293 -0
  52. data/lib/toys/wrappable_string.rb +9 -2
  53. data/lib/toys-core.rb +18 -6
  54. metadata +14 -7
data/lib/toys/loader.rb CHANGED
@@ -8,9 +8,6 @@ module Toys
8
8
  # appropriate tool given a set of command line arguments.
9
9
  #
10
10
  class Loader
11
- # @private
12
- BASE_PRIORITY = -999_999
13
-
14
11
  ##
15
12
  # Create a Loader
16
13
  #
@@ -44,18 +41,17 @@ module Toys
44
41
  # @param template_lookup [Toys::ModuleLookup] A lookup for
45
42
  # well-known template classes. Defaults to an empty lookup.
46
43
  #
47
- def initialize(
48
- index_file_name: nil,
49
- preload_dir_name: nil,
50
- preload_file_name: nil,
51
- data_dir_name: nil,
52
- lib_dir_name: nil,
53
- middleware_stack: [],
54
- extra_delimiters: "",
55
- mixin_lookup: nil,
56
- middleware_lookup: nil,
57
- template_lookup: nil
58
- )
44
+ def initialize(index_file_name: nil,
45
+ preload_dir_name: nil,
46
+ preload_file_name: nil,
47
+ data_dir_name: nil,
48
+ lib_dir_name: nil,
49
+ middleware_stack: [],
50
+ extra_delimiters: "",
51
+ mixin_lookup: nil,
52
+ middleware_lookup: nil,
53
+ template_lookup: nil,
54
+ git_cache: nil)
59
55
  if index_file_name && ::File.extname(index_file_name) != ".rb"
60
56
  raise ::ArgumentError, "Illegal index file name #{index_file_name.inspect}"
61
57
  end
@@ -71,30 +67,82 @@ module Toys
71
67
  @loading_started = false
72
68
  @worklist = []
73
69
  @tool_data = {}
70
+ @roots_by_priority = {}
74
71
  @max_priority = @min_priority = 0
75
- @stop_priority = BASE_PRIORITY
72
+ @stop_priority = -999_999
76
73
  @min_loaded_priority = 999_999
77
74
  @middleware_stack = Middleware.stack(middleware_stack)
78
75
  @delimiter_handler = DelimiterHandler.new(extra_delimiters)
79
- get_tool([], BASE_PRIORITY)
76
+ @git_cache = git_cache
77
+ get_tool([], -999_999)
80
78
  end
81
79
 
82
80
  ##
83
81
  # Add a configuration file/directory to the loader.
84
82
  #
85
- # @param paths [String,Array<String>] One or more paths to add.
83
+ # @param path [String] A single path to add.
86
84
  # @param high_priority [Boolean] If true, add this path at the top of the
87
85
  # priority list. Defaults to false, indicating the new path should be
88
86
  # at the bottom of the priority list.
87
+ # @param source_name [String] A custom name for the root source. Optional.
88
+ # @param context_directory [String,nil,:path,:parent] The context directory
89
+ # for tools loaded from this path. You can pass a directory path as a
90
+ # string, `:path` to denote the given path, `:parent` to denote the
91
+ # given path's parent directory, or `nil` to denote no context.
92
+ # Defaults to `:parent`.
93
+ # @return [self]
94
+ #
95
+ def add_path(path,
96
+ high_priority: false,
97
+ source_name: nil,
98
+ context_directory: :parent)
99
+ @mutex.synchronize do
100
+ raise "Cannot add a path after tool loading has started" if @loading_started
101
+ priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
102
+ source = SourceInfo.create_path_root(path, priority,
103
+ context_directory: context_directory,
104
+ data_dir_name: @data_dir_name,
105
+ lib_dir_name: @lib_dir_name,
106
+ source_name: source_name)
107
+ @roots_by_priority[priority] = source
108
+ @worklist << [source, [], priority]
109
+ end
110
+ self
111
+ end
112
+
113
+ ##
114
+ # Add a set of configuration files/directories from a common directory to
115
+ # the loader. The set of paths will be added at the same priority level and
116
+ # will share a root.
117
+ #
118
+ # @param root_path [String] A root path to be seen as the root source. This
119
+ # should generally be a directory containing the paths to add.
120
+ # @param relative_paths [String,Array<String>] One or more paths to add, as
121
+ # relative paths from the common root.
122
+ # @param high_priority [Boolean] If true, add the paths at the top of the
123
+ # priority list. Defaults to false, indicating the new paths should be
124
+ # at the bottom of the priority list.
125
+ # @param context_directory [String,nil,:path,:parent] The context directory
126
+ # for tools loaded from this path. You can pass a directory path as a
127
+ # string, `:path` to denote the given root path, `:parent` to denote
128
+ # the given root path's parent directory, or `nil` to denote no context.
129
+ # Defaults to `:path`.
89
130
  # @return [self]
90
131
  #
91
- def add_path(paths, high_priority: false)
92
- paths = Array(paths)
132
+ def add_path_set(root_path, relative_paths,
133
+ high_priority: false,
134
+ context_directory: :path)
135
+ relative_paths = Array(relative_paths)
93
136
  @mutex.synchronize do
94
137
  raise "Cannot add a path after tool loading has started" if @loading_started
95
138
  priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
96
- paths.each do |path|
97
- source = SourceInfo.create_path_root(path, @data_dir_name, @lib_dir_name)
139
+ root_source = SourceInfo.create_path_root(root_path, priority,
140
+ context_directory: context_directory,
141
+ data_dir_name: @data_dir_name,
142
+ lib_dir_name: @lib_dir_name)
143
+ @roots_by_priority[priority] = root_source
144
+ relative_paths.each do |path, individual_name|
145
+ source = root_source.relative_child(path, source_name: individual_name)
98
146
  @worklist << [source, [], priority]
99
147
  end
100
148
  end
@@ -107,19 +155,65 @@ module Toys
107
155
  # @param high_priority [Boolean] If true, add this block at the top of the
108
156
  # priority list. Defaults to false, indicating the block should be at
109
157
  # the bottom of the priority list.
110
- # @param name [String] The source name that will be shown in documentation
111
- # for tools defined in this block. If omitted, a default unique string
112
- # will be generated.
158
+ # @param source_name [String] The source name that will be shown in
159
+ # documentation for tools defined in this block. If omitted, a default
160
+ # unique string will be generated.
113
161
  # @param block [Proc] The block of configuration, executed in the context
114
162
  # of the tool DSL {Toys::DSL::Tool}.
163
+ # @param context_directory [String,nil] The context directory for tools
164
+ # loaded from this block. You can pass a directory path as a string, or
165
+ # `nil` to denote no context. Defaults to `nil`.
115
166
  # @return [self]
116
167
  #
117
- def add_block(high_priority: false, name: nil, &block)
118
- name ||= "(Code block #{block.object_id})"
168
+ def add_block(high_priority: false,
169
+ source_name: nil,
170
+ context_directory: nil,
171
+ &block)
119
172
  @mutex.synchronize do
120
173
  raise "Cannot add a block after tool loading has started" if @loading_started
121
174
  priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
122
- source = SourceInfo.create_proc_root(block, name, @data_dir_name, @lib_dir_name)
175
+ source = SourceInfo.create_proc_root(block, priority,
176
+ context_directory: context_directory,
177
+ source_name: source_name,
178
+ data_dir_name: @data_dir_name,
179
+ lib_dir_name: @lib_dir_name)
180
+ @roots_by_priority[priority] = source
181
+ @worklist << [source, [], priority]
182
+ end
183
+ self
184
+ end
185
+
186
+ ##
187
+ # Add a configuration git source to the loader.
188
+ #
189
+ # @param git_remote [String] The git repo URL
190
+ # @param git_path [String] The path to the relevant file or directory in
191
+ # the repo. Specify the empty string to use the entire repo.
192
+ # @param git_commit [String] The git ref (i.e. SHA, tag, or branch name)
193
+ # @param high_priority [Boolean] If true, add this path at the top of the
194
+ # priority list. Defaults to false, indicating the new path should be
195
+ # at the bottom of the priority list.
196
+ # @param update [Boolean] If the commit is not a SHA, pulls any updates
197
+ # from the remote. Defaults to false, which uses a local cache and does
198
+ # not update if the commit has been fetched previously.
199
+ # @param context_directory [String,nil] The context directory for tools
200
+ # loaded from this source. You can pass a directory path as a string,
201
+ # or `nil` to denote no context. Defaults to `nil`.
202
+ # @return [self]
203
+ #
204
+ def add_git(git_remote, git_path, git_commit,
205
+ high_priority: false,
206
+ update: false,
207
+ context_directory: nil)
208
+ @mutex.synchronize do
209
+ raise "Cannot add a git source after tool loading has started" if @loading_started
210
+ priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
211
+ path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
212
+ source = SourceInfo.create_git_root(git_remote, git_path, git_commit, path, priority,
213
+ context_directory: context_directory,
214
+ data_dir_name: @data_dir_name,
215
+ lib_dir_name: @lib_dir_name)
216
+ @roots_by_priority[priority] = source
123
217
  @worklist << [source, [], priority]
124
218
  end
125
219
  self
@@ -136,7 +230,7 @@ module Toys
136
230
  # that are not part of the tool name and should be passed as tool args.
137
231
  #
138
232
  # @param args [Array<String>] Command line arguments
139
- # @return [Array(Toys::Tool,Array<String>)]
233
+ # @return [Array(Toys::ToolDefinition,Array<String>)]
140
234
  #
141
235
  def lookup(args)
142
236
  orig_prefix, args = @delimiter_handler.find_orig_prefix(args)
@@ -157,13 +251,13 @@ module Toys
157
251
  # the given name, returns `nil`.
158
252
  #
159
253
  # @param words [Array<String>] The tool name
160
- # @return [Toys::Tool] if the tool was found
254
+ # @return [Toys::ToolDefinition] if the tool was found
161
255
  # @return [nil] if no such tool exists
162
256
  #
163
257
  def lookup_specific(words)
164
258
  words = @delimiter_handler.split_path(words.first) if words.size == 1
165
259
  load_for_prefix(words)
166
- tool = get_tool_data(words).cur_definition
260
+ tool = get_tool_data(words, false)&.cur_definition
167
261
  finish_definitions_in_tree(words) if tool
168
262
  tool
169
263
  end
@@ -177,7 +271,7 @@ module Toys
177
271
  # rather than just the immediate children (the default)
178
272
  # @param include_hidden [Boolean] If true, include hidden subtools,
179
273
  # e.g. names beginning with underscores.
180
- # @return [Array<Toys::Tool>] An array of subtools.
274
+ # @return [Array<Toys::ToolDefinition>] An array of subtools.
181
275
  #
182
276
  def list_subtools(words, recursive: false, include_hidden: false)
183
277
  load_for_prefix(words)
@@ -234,8 +328,8 @@ module Toys
234
328
  #
235
329
  # @private
236
330
  #
237
- def get_tool(words, priority)
238
- get_tool_data(words).get_tool(priority, self)
331
+ def get_tool(words, priority, tool_class = nil)
332
+ get_tool_data(words, true).get_tool(priority, self, tool_class)
239
333
  end
240
334
 
241
335
  ##
@@ -248,7 +342,7 @@ module Toys
248
342
  # @private
249
343
  #
250
344
  def activate_tool(words, priority)
251
- get_tool_data(words).activate_tool(priority, self)
345
+ get_tool_data(words, true).activate_tool(priority, self)
252
346
  end
253
347
 
254
348
  ##
@@ -267,10 +361,11 @@ module Toys
267
361
  #
268
362
  # @private
269
363
  #
270
- def build_tool(words, priority)
364
+ def build_tool(words, priority, tool_class = nil)
271
365
  parent = words.empty? ? nil : get_tool(words.slice(0..-2), priority)
272
366
  middleware_stack = parent ? parent.subtool_middleware_stack : @middleware_stack
273
- Tool.new(self, parent, words, priority, middleware_stack, @middleware_lookup)
367
+ ToolDefinition.new(parent, words, priority, @roots_by_priority[priority],
368
+ middleware_stack, @middleware_lookup, tool_class)
274
369
  end
275
370
 
276
371
  ##
@@ -335,12 +430,31 @@ module Toys
335
430
  # @private
336
431
  #
337
432
  def load_path(parent_source, path, words, remaining_words, priority)
433
+ if parent_source.git_remote
434
+ raise LoaderError,
435
+ "Git source #{parent_source.source_name} tried to load from the local file system"
436
+ end
338
437
  source = parent_source.absolute_child(path)
339
438
  @mutex.synchronize do
340
439
  load_validated_path(source, words, remaining_words, priority)
341
440
  end
342
441
  end
343
442
 
443
+ ##
444
+ # Load configuration from the given git remote. This is called from the
445
+ # `load_git` directive in the DSL.
446
+ #
447
+ # @private
448
+ #
449
+ def load_git(parent_source, git_remote, git_path, git_commit, words, remaining_words, priority,
450
+ update: false)
451
+ path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
452
+ source = parent_source.git_child(git_remote, git_path, git_commit, path)
453
+ @mutex.synchronize do
454
+ load_validated_path(source, words, remaining_words, priority)
455
+ end
456
+ end
457
+
344
458
  ##
345
459
  # Load a subtool block. Called from the `tool` directive in the DSL.
346
460
  #
@@ -353,6 +467,18 @@ module Toys
353
467
  end
354
468
  end
355
469
 
470
+ ##
471
+ # Get a GitCache.
472
+ #
473
+ # @private
474
+ #
475
+ def git_cache
476
+ @git_cache ||= begin
477
+ require "toys/utils/git_cache"
478
+ Utils::GitCache.new
479
+ end
480
+ end
481
+
356
482
  ##
357
483
  # Determine the next setting for remaining_words, given a word.
358
484
  #
@@ -368,6 +494,120 @@ module Toys
368
494
  end
369
495
  end
370
496
 
497
+ ##
498
+ # Tool data
499
+ #
500
+ # @private
501
+ #
502
+ class ToolData
503
+ ##
504
+ # @private
505
+ #
506
+ def initialize(words)
507
+ @words = validate_words(words)
508
+ @definitions = {}
509
+ @top_priority = @active_priority = nil
510
+ @mutex = ::Monitor.new
511
+ end
512
+
513
+ ##
514
+ # @private
515
+ #
516
+ def cur_definition
517
+ @mutex.synchronize { active_definition || top_definition }
518
+ end
519
+
520
+ ##
521
+ # @private
522
+ #
523
+ def empty?
524
+ @definitions.empty?
525
+ end
526
+
527
+ ##
528
+ # @private
529
+ #
530
+ def get_tool(priority, loader, tool_class = nil)
531
+ @mutex.synchronize do
532
+ if @top_priority.nil? || @top_priority < priority
533
+ @top_priority = priority
534
+ end
535
+ if tool_class && @definitions.include?(priority)
536
+ raise ToolDefinitionError, "Tool already defined for #{@words.inspect}"
537
+ end
538
+ @definitions[priority] ||= loader.build_tool(@words, priority, tool_class)
539
+ end
540
+ end
541
+
542
+ ##
543
+ # @private
544
+ #
545
+ def activate_tool(priority, loader)
546
+ @mutex.synchronize do
547
+ return active_definition if @active_priority == priority
548
+ return nil if @active_priority && @active_priority > priority
549
+ @active_priority = priority
550
+ get_tool(priority, loader)
551
+ end
552
+ end
553
+
554
+ private
555
+
556
+ def validate_words(words)
557
+ words.each do |word|
558
+ if /[[:cntrl:] #"$&'()*;<>\[\\\]\^`{|}]/.match(word)
559
+ raise ToolDefinitionError, "Illegal characters in name #{word.inspect}"
560
+ end
561
+ end
562
+ end
563
+
564
+ def top_definition
565
+ @top_priority ? @definitions[@top_priority] : nil
566
+ end
567
+
568
+ def active_definition
569
+ @active_priority ? @definitions[@active_priority] : nil
570
+ end
571
+ end
572
+
573
+ ##
574
+ # An object that handles name delimiting.
575
+ #
576
+ # @private
577
+ #
578
+ class DelimiterHandler
579
+ ##
580
+ # @private
581
+ #
582
+ def initialize(extra_delimiters)
583
+ unless %r{^[[:space:]./:]*$}.match?(extra_delimiters)
584
+ raise ::ArgumentError, "Illegal delimiters in #{extra_delimiters.inspect}"
585
+ end
586
+ chars = ::Regexp.escape(extra_delimiters.chars.uniq.join)
587
+ @delimiters = ::Regexp.new("[[:space:]#{chars}]")
588
+ end
589
+
590
+ ##
591
+ # @private
592
+ #
593
+ def split_path(str)
594
+ str.split(@delimiters)
595
+ end
596
+
597
+ ##
598
+ # @private
599
+ #
600
+ def find_orig_prefix(args)
601
+ first_split = (args.first || "").split(@delimiters)
602
+ if first_split.size > 1
603
+ args = first_split + args.slice(1..-1)
604
+ return [first_split, args]
605
+ end
606
+ orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
607
+ [orig_prefix, args]
608
+ end
609
+ end
610
+
371
611
  private
372
612
 
373
613
  def all_cur_definitions
@@ -381,8 +621,10 @@ module Toys
381
621
  result
382
622
  end
383
623
 
384
- def get_tool_data(words)
385
- @mutex.synchronize { @tool_data[words] ||= ToolData.new(words) }
624
+ def get_tool_data(words, create)
625
+ @mutex.synchronize do
626
+ create ? (@tool_data[words] ||= ToolData.new(words)) : @tool_data[words]
627
+ end
386
628
  end
387
629
 
388
630
  ##
@@ -403,7 +645,7 @@ module Toys
403
645
  if remaining_words
404
646
  update_min_loaded_priority(priority)
405
647
  tool_class = get_tool(words, priority).tool_class
406
- DSL::Tool.prepare(tool_class, remaining_words, source) do
648
+ DSL::Internal.prepare(tool_class, words, priority, remaining_words, source, self) do
407
649
  ContextualError.capture("Error while loading Toys config!") do
408
650
  tool_class.class_eval(&source.source_proc)
409
651
  end
@@ -425,7 +667,7 @@ module Toys
425
667
  if source.source_type == :file
426
668
  update_min_loaded_priority(priority)
427
669
  tool_class = get_tool(words, priority).tool_class
428
- InputFile.evaluate(tool_class, remaining_words, source)
670
+ InputFile.evaluate(tool_class, words, priority, remaining_words, source, self)
429
671
  else
430
672
  do_preload(source.source_path)
431
673
  load_index_in(source, words, remaining_words, priority)
@@ -467,16 +709,20 @@ module Toys
467
709
  if @preload_dir_name
468
710
  preload_dir = ::File.join(path, @preload_dir_name)
469
711
  if ::File.directory?(preload_dir) && ::File.readable?(preload_dir)
470
- ::Dir.entries(preload_dir).each do |child|
471
- next unless ::File.extname(child) == ".rb"
472
- preload_file = ::File.join(preload_dir, child)
473
- next if !::File.file?(preload_file) || !::File.readable?(preload_file)
474
- require preload_file
475
- end
712
+ preload_dir_contents(preload_dir)
476
713
  end
477
714
  end
478
715
  end
479
716
 
717
+ def preload_dir_contents(preload_dir)
718
+ ::Dir.entries(preload_dir).each do |child|
719
+ next unless ::File.extname(child) == ".rb"
720
+ preload_file = ::File.join(preload_dir, child)
721
+ next if !::File.file?(preload_file) || !::File.readable?(preload_file)
722
+ require preload_file
723
+ end
724
+ end
725
+
480
726
  def sort_tools_by_name(tools)
481
727
  tools.sort! do |a, b|
482
728
  a = a.full_name
@@ -511,98 +757,5 @@ module Toys
511
757
  index += 1
512
758
  end
513
759
  end
514
-
515
- ##
516
- # Tool data
517
- #
518
- # @private
519
- #
520
- class ToolData
521
- # @private
522
- def initialize(words)
523
- @words = words
524
- @definitions = {}
525
- @top_priority = @active_priority = nil
526
- @mutex = ::Monitor.new
527
- end
528
-
529
- # @private
530
- def cur_definition
531
- @mutex.synchronize { active_definition || top_definition }
532
- end
533
-
534
- # @private
535
- def empty?
536
- @definitions.empty?
537
- end
538
-
539
- # @private
540
- def get_tool(priority, loader)
541
- @mutex.synchronize do
542
- if @top_priority.nil? || @top_priority < priority
543
- @top_priority = priority
544
- end
545
- @definitions[priority] ||= loader.build_tool(@words, priority)
546
- end
547
- end
548
-
549
- # @private
550
- def activate_tool(priority, loader)
551
- @mutex.synchronize do
552
- return active_definition if @active_priority == priority
553
- return nil if @active_priority && @active_priority > priority
554
- @active_priority = priority
555
- get_tool(priority, loader)
556
- end
557
- end
558
-
559
- private
560
-
561
- def top_definition
562
- @top_priority ? @definitions[@top_priority] : nil
563
- end
564
-
565
- def active_definition
566
- @active_priority ? @definitions[@active_priority] : nil
567
- end
568
- end
569
-
570
- ##
571
- # An object that handles name delimiting.
572
- #
573
- # @private
574
- #
575
- class DelimiterHandler
576
- ## @private
577
- ALLOWED_DELIMITERS = %r{^[\./:]*$}.freeze
578
- private_constant :ALLOWED_DELIMITERS
579
-
580
- ## @private
581
- def initialize(extra_delimiters)
582
- unless ALLOWED_DELIMITERS =~ extra_delimiters
583
- raise ::ArgumentError, "Illegal delimiters in #{extra_delimiters.inspect}"
584
- end
585
- chars = ::Regexp.escape(extra_delimiters.chars.uniq.join)
586
- @extra_delimiters = chars.empty? ? nil : ::Regexp.new("[#{chars}]")
587
- end
588
-
589
- ## @private
590
- def split_path(str)
591
- @extra_delimiters ? str.split(@extra_delimiters) : [str]
592
- end
593
-
594
- ## @private
595
- def find_orig_prefix(args)
596
- if @extra_delimiters
597
- first_split = (args.first || "").split(@extra_delimiters)
598
- if first_split.size > 1
599
- args = first_split + args.slice(1..-1)
600
- return [first_split, args]
601
- end
602
- end
603
- orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
604
- [orig_prefix, args]
605
- end
606
- end
607
760
  end
608
761
  end
@@ -37,7 +37,7 @@ module Toys
37
37
  # This basic implementation does nothing and simply yields to the next
38
38
  # middleware.
39
39
  #
40
- # @param tool [Toys::Tool] The tool definition to modify.
40
+ # @param tool [Toys::ToolDefinition] The tool definition to modify.
41
41
  # @param loader [Toys::Loader] The loader that loaded this tool.
42
42
  # @return [void]
43
43
  #
@@ -140,7 +140,9 @@ module Toys
140
140
  end
141
141
  end
142
142
 
143
- ## @private
143
+ ##
144
+ # @private
145
+ #
144
146
  def spec_from_array(array)
145
147
  middleware = array.first
146
148
  if !middleware.is_a?(::String) && !middleware.is_a?(::Symbol) && !middleware.is_a?(::Class)
@@ -238,16 +240,12 @@ module Toys
238
240
  #
239
241
  attr_reader :block
240
242
 
241
- ## @private
242
- def initialize(object, name, args, kwargs, block)
243
- @object = object
244
- @name = name
245
- @args = args
246
- @kwargs = kwargs
247
- @block = block
248
- end
249
-
250
- ## @private
243
+ ##
244
+ # Equality check
245
+ #
246
+ # @param other [Object]
247
+ # @return [Boolean]
248
+ #
251
249
  def ==(other)
252
250
  other.is_a?(Spec) &&
253
251
  object.eql?(other.object) &&
@@ -258,10 +256,25 @@ module Toys
258
256
  end
259
257
  alias eql? ==
260
258
 
261
- ## @private
259
+ ##
260
+ # Return the hash code
261
+ #
262
+ # @return [Integer]
263
+ #
262
264
  def hash
263
265
  [object, name, args, kwargs, block].hash
264
266
  end
267
+
268
+ ##
269
+ # @private
270
+ #
271
+ def initialize(object, name, args, kwargs, block)
272
+ @object = object
273
+ @name = name
274
+ @args = args
275
+ @kwargs = kwargs
276
+ @block = block
277
+ end
265
278
  end
266
279
 
267
280
  ##
@@ -319,14 +332,12 @@ module Toys
319
332
  (@pre_specs + @default_specs + @post_specs).map { |spec| spec.build(middleware_lookup) }
320
333
  end
321
334
 
322
- ## @private
323
- def initialize(default_specs: nil, pre_specs: nil, post_specs: nil)
324
- @pre_specs = pre_specs || []
325
- @post_specs = post_specs || []
326
- @default_specs = default_specs || []
327
- end
328
-
329
- ## @private
335
+ ##
336
+ # Equality check
337
+ #
338
+ # @param other [Object]
339
+ # @return [Boolean]
340
+ #
330
341
  def ==(other)
331
342
  other.is_a?(Stack) &&
332
343
  pre_specs.eql?(other.pre_specs) &&
@@ -335,10 +346,23 @@ module Toys
335
346
  end
336
347
  alias eql? ==
337
348
 
338
- ## @private
349
+ ##
350
+ # Return the hash code
351
+ #
352
+ # @return [Integer]
353
+ #
339
354
  def hash
340
355
  [@pre_specs, @default_specs, @post_specs].hash
341
356
  end
357
+
358
+ ##
359
+ # @private
360
+ #
361
+ def initialize(default_specs: nil, pre_specs: nil, post_specs: nil)
362
+ @pre_specs = pre_specs || []
363
+ @post_specs = post_specs || []
364
+ @default_specs = default_specs || []
365
+ end
342
366
  end
343
367
  end
344
368
  end