toys-core 0.13.1 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/toys/flag.rb CHANGED
@@ -92,6 +92,7 @@ module Toys
92
92
  # The type of flag (`:boolean` or `:value`)
93
93
  # @return [:boolean] if this is a boolean flag (i.e. no value)
94
94
  # @return [:value] if this flag takes a value (even if optional)
95
+ # @return [nil] if this flag is indeterminate
95
96
  #
96
97
  attr_reader :flag_type
97
98
 
@@ -128,7 +129,9 @@ module Toys
128
129
  attr_reader :canonical_str
129
130
 
130
131
  ##
131
- # @private
132
+ # This method is accessible for testing only.
133
+ #
134
+ # @private This interface is internal and subject to change without warning.
132
135
  #
133
136
  def configure_canonical(canonical_flag_type, canonical_value_type,
134
137
  canonical_value_label, canonical_value_delim)
@@ -683,6 +686,7 @@ module Toys
683
686
  @group = group
684
687
  @key = key
685
688
  @flag_syntax = Array(flags).map { |s| Syntax.new(s) }
689
+ @explicit_acceptor = !acceptor.nil?
686
690
  @acceptor = Acceptor.create(acceptor)
687
691
  @handler = resolve_handler(handler)
688
692
  @desc = WrappableString.make(desc)
@@ -737,7 +741,7 @@ module Toys
737
741
  end
738
742
  if flag_str
739
743
  needs_val = @value_completion != Completion::EMPTY ||
740
- ![::Object, ::TrueClass, ::FalseClass].include?(@acceptor.well_known_spec) ||
744
+ @explicit_acceptor ||
741
745
  ![nil, true, false].include?(@default)
742
746
  flag_str = "#{flag_str} VALUE" if needs_val
743
747
  @flag_syntax << Syntax.new(flag_str)
@@ -771,6 +775,10 @@ module Toys
771
775
  analyze_flag_syntax(flag)
772
776
  end
773
777
  @flag_type ||= :boolean
778
+ if @flag_type == :boolean && @explicit_acceptor
779
+ raise ToolDefinitionError,
780
+ "Flag #{key.inspect} cannot have an acceptor because it does not take a value."
781
+ end
774
782
  @value_type ||= :required if @flag_type == :value
775
783
  flag_syntax.each do |flag|
776
784
  flag.configure_canonical(@flag_type, @value_type, @value_label, @value_delim)
@@ -6,14 +6,7 @@
6
6
  #
7
7
  module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
8
8
  ##
9
- # @private
10
- #
11
- def self.__binding
12
- binding
13
- end
14
-
15
- ##
16
- # @private
9
+ # @private This interface is internal and subject to change without warning.
17
10
  #
18
11
  def self.evaluate(tool_class, words, priority, remaining_words, source, loader)
19
12
  namespace = ::Module.new
@@ -37,6 +30,13 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
37
30
  end
38
31
  end
39
32
 
33
+ ##
34
+ # @private
35
+ #
36
+ def self.__binding
37
+ binding
38
+ end
39
+
40
40
  ##
41
41
  # @private
42
42
  #
data/lib/toys/loader.rb CHANGED
@@ -205,10 +205,11 @@ module Toys
205
205
  high_priority: false,
206
206
  update: false,
207
207
  context_directory: nil)
208
+ git_cache = @git_cache || Loader.default_git_cache
209
+ path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
208
210
  @mutex.synchronize do
209
211
  raise "Cannot add a git source after tool loading has started" if @loading_started
210
212
  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
213
  source = SourceInfo.create_git_root(git_remote, git_path, git_commit, path, priority,
213
214
  context_directory: context_directory,
214
215
  data_dir_name: @data_dir_name,
@@ -257,43 +258,48 @@ module Toys
257
258
  def lookup_specific(words)
258
259
  words = @delimiter_handler.split_path(words.first) if words.size == 1
259
260
  load_for_prefix(words)
260
- tool = get_tool_data(words, false)&.cur_definition
261
+ tool = @mutex.synchronize { get_tool_data(words, false)&.cur_definition }
261
262
  finish_definitions_in_tree(words) if tool
262
263
  tool
263
264
  end
264
265
 
265
266
  ##
266
267
  # Returns a list of subtools for the given path, loading from the
267
- # configuration if necessary.
268
+ # configuration if necessary. The list will be sorted by name.
268
269
  #
269
270
  # @param words [Array<String>] The name of the parent tool
270
271
  # @param recursive [Boolean] If true, return all subtools recursively
271
272
  # rather than just the immediate children (the default)
272
273
  # @param include_hidden [Boolean] If true, include hidden subtools,
273
- # e.g. names beginning with underscores.
274
+ # i.e. names beginning with underscores. Defaults to false.
275
+ # @param include_namespaces [Boolean] If true, include namespaces,
276
+ # i.e. tools that are not runnable but have descendents that would have
277
+ # been listed by the current filters. Defaults to false.
278
+ # @param include_non_runnable [Boolean] If true, include tools that have
279
+ # no children and are not runnable. Defaults to false.
274
280
  # @return [Array<Toys::ToolDefinition>] An array of subtools.
275
281
  #
276
- def list_subtools(words, recursive: false, include_hidden: false)
282
+ def list_subtools(words,
283
+ recursive: false,
284
+ include_hidden: false,
285
+ include_namespaces: false,
286
+ include_non_runnable: false)
277
287
  load_for_prefix(words)
278
- found_tools = []
279
288
  len = words.length
280
- all_cur_definitions.each do |tool|
289
+ found_tools = all_cur_definitions.find_all do |tool|
281
290
  name = tool.full_name
282
- next if name.empty?
283
- if recursive
284
- next if name.length <= len || name.slice(0, len) != words
285
- else
286
- next unless name.slice(0..-2) == words
287
- end
288
- found_tools << tool
291
+ name.length > len && name.slice(0, len) == words &&
292
+ (include_hidden || name[len..-1].none? { |word| word.start_with?("_") })
289
293
  end
290
- sort_tools_by_name(found_tools)
291
- include_hidden ? found_tools : filter_hidden_subtools(found_tools)
294
+ found_tools.sort_by!(&:full_name)
295
+ found_tools = filter_non_runnable_tools(found_tools, include_namespaces, include_non_runnable)
296
+ found_tools.select! { |tool| tool.full_name.length == len + 1 } unless recursive
297
+ found_tools
292
298
  end
293
299
 
294
300
  ##
295
- # Returns true if the given path has at least one subtool. Loads from the
296
- # configuration if necessary.
301
+ # Returns true if the given path has at least one subtool, even if they are
302
+ # hidden or non-runnable. Loads from the configuration if necessary.
297
303
  #
298
304
  # @param words [Array<String>] The name of the parent tool
299
305
  # @return [Boolean]
@@ -301,13 +307,10 @@ module Toys
301
307
  def has_subtools?(words) # rubocop:disable Naming/PredicateName
302
308
  load_for_prefix(words)
303
309
  len = words.length
304
- all_cur_definitions.each do |tool|
310
+ all_cur_definitions.any? do |tool|
305
311
  name = tool.full_name
306
- if !name.empty? && name.length > len && name.slice(0, len) == words
307
- return true
308
- end
312
+ name.length > len && name.slice(0, len) == words
309
313
  end
310
- false
311
314
  end
312
315
 
313
316
  ##
@@ -323,13 +326,17 @@ module Toys
323
326
  @delimiter_handler.split_path(str.to_s)
324
327
  end
325
328
 
329
+ #### INTERNAL METHODS ####
330
+
326
331
  ##
327
332
  # Get or create the tool definition for the given name and priority.
328
333
  #
329
- # @private
334
+ # @private This interface is internal and subject to change without warning.
330
335
  #
331
336
  def get_tool(words, priority, tool_class = nil)
332
- get_tool_data(words, true).get_tool(priority, self, tool_class)
337
+ @mutex.synchronize do
338
+ get_tool_data(words, true).get_tool(priority, self, tool_class)
339
+ end
333
340
  end
334
341
 
335
342
  ##
@@ -339,17 +346,19 @@ module Toys
339
346
  # the active priority, returns `nil`. If the given priority is higher than
340
347
  # the active priority, returns and activates a new tool.
341
348
  #
342
- # @private
349
+ # @private This interface is internal and subject to change without warning.
343
350
  #
344
351
  def activate_tool(words, priority)
345
- get_tool_data(words, true).activate_tool(priority, self)
352
+ @mutex.synchronize do
353
+ get_tool_data(words, true).activate_tool(priority, self)
354
+ end
346
355
  end
347
356
 
348
357
  ##
349
358
  # Returns true if the given tool name currently exists in the loader.
350
359
  # Does not load the tool if not found.
351
360
  #
352
- # @private
361
+ # @private This interface is internal and subject to change without warning.
353
362
  #
354
363
  def tool_defined?(words)
355
364
  @tool_data.key?(words)
@@ -359,7 +368,7 @@ module Toys
359
368
  # Build a new tool.
360
369
  # Called only from ToolData.
361
370
  #
362
- # @private
371
+ # @private This interface is internal and subject to change without warning.
363
372
  #
364
373
  def build_tool(words, priority, tool_class = nil)
365
374
  parent = words.empty? ? nil : get_tool(words.slice(0..-2), priority)
@@ -372,7 +381,7 @@ module Toys
372
381
  # Stop search at the given priority. Returns true if successful.
373
382
  # Called only from the DSL.
374
383
  #
375
- # @private
384
+ # @private This interface is internal and subject to change without warning.
376
385
  #
377
386
  def stop_loading_at_priority(priority)
378
387
  @mutex.synchronize do
@@ -385,7 +394,7 @@ module Toys
385
394
  ##
386
395
  # Loads the subtree under the given prefix.
387
396
  #
388
- # @private
397
+ # @private This interface is internal and subject to change without warning.
389
398
  #
390
399
  def load_for_prefix(prefix)
391
400
  @mutex.synchronize do
@@ -408,7 +417,7 @@ module Toys
408
417
  ##
409
418
  # Attempt to get a well-known mixin module for the given symbolic name.
410
419
  #
411
- # @private
420
+ # @private This interface is internal and subject to change without warning.
412
421
  #
413
422
  def resolve_standard_mixin(name)
414
423
  @mixin_lookup.lookup(name)
@@ -417,7 +426,7 @@ module Toys
417
426
  ##
418
427
  # Attempt to get a well-known template class for the given symbolic name.
419
428
  #
420
- # @private
429
+ # @private This interface is internal and subject to change without warning.
421
430
  #
422
431
  def resolve_standard_template(name)
423
432
  @template_lookup.lookup(name)
@@ -427,7 +436,7 @@ module Toys
427
436
  # Load configuration from the given path. This is called from the `load`
428
437
  # directive in the DSL.
429
438
  #
430
- # @private
439
+ # @private This interface is internal and subject to change without warning.
431
440
  #
432
441
  def load_path(parent_source, path, words, remaining_words, priority)
433
442
  if parent_source.git_remote
@@ -444,10 +453,11 @@ module Toys
444
453
  # Load configuration from the given git remote. This is called from the
445
454
  # `load_git` directive in the DSL.
446
455
  #
447
- # @private
456
+ # @private This interface is internal and subject to change without warning.
448
457
  #
449
458
  def load_git(parent_source, git_remote, git_path, git_commit, words, remaining_words, priority,
450
459
  update: false)
460
+ git_cache = @git_cache || Loader.default_git_cache
451
461
  path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
452
462
  source = parent_source.git_child(git_remote, git_path, git_commit, path)
453
463
  @mutex.synchronize do
@@ -458,7 +468,7 @@ module Toys
458
468
  ##
459
469
  # Load a subtool block. Called from the `tool` directive in the DSL.
460
470
  #
461
- # @private
471
+ # @private This interface is internal and subject to change without warning.
462
472
  #
463
473
  def load_block(parent_source, block, words, remaining_words, priority)
464
474
  source = parent_source.proc_child(block)
@@ -467,22 +477,27 @@ module Toys
467
477
  end
468
478
  end
469
479
 
480
+ @git_cache_mutex = ::Monitor.new
481
+ @default_git_cache = nil
482
+
470
483
  ##
471
- # Get a GitCache.
484
+ # Get a global default GitCache.
472
485
  #
473
- # @private
486
+ # @private This interface is internal and subject to change without warning.
474
487
  #
475
- def git_cache
476
- @git_cache ||= begin
477
- require "toys/utils/git_cache"
478
- Utils::GitCache.new
488
+ def self.default_git_cache
489
+ @git_cache_mutex.synchronize do
490
+ @default_git_cache ||= begin
491
+ require "toys/utils/git_cache"
492
+ Utils::GitCache.new
493
+ end
479
494
  end
480
495
  end
481
496
 
482
497
  ##
483
498
  # Determine the next setting for remaining_words, given a word.
484
499
  #
485
- # @private
500
+ # @private This interface is internal and subject to change without warning.
486
501
  #
487
502
  def self.next_remaining_words(remaining_words, word)
488
503
  if remaining_words.nil?
@@ -495,26 +510,35 @@ module Toys
495
510
  end
496
511
 
497
512
  ##
498
- # Tool data
513
+ # An internal object managing the various definitions for a specific tool
514
+ # tool name and their priorities, and tracking which, if any, has been
515
+ # activated.
516
+ #
517
+ # This class is not thread-safe by itself. The caller must protect access
518
+ # with a mutex.
499
519
  #
500
520
  # @private
501
521
  #
502
522
  class ToolData
503
523
  ##
524
+ # Create an empty tool data with no definitions.
525
+ #
504
526
  # @private
505
527
  #
506
528
  def initialize(words)
507
529
  @words = validate_words(words)
508
530
  @definitions = {}
509
531
  @top_priority = @active_priority = nil
510
- @mutex = ::Monitor.new
511
532
  end
512
533
 
513
534
  ##
535
+ # Return the current "best" definition, which is either the active
536
+ # definition, or, if none, the current highest-priority definition.
537
+ #
514
538
  # @private
515
539
  #
516
540
  def cur_definition
517
- @mutex.synchronize { active_definition || top_definition }
541
+ active_definition || top_definition
518
542
  end
519
543
 
520
544
  ##
@@ -525,30 +549,37 @@ module Toys
525
549
  end
526
550
 
527
551
  ##
552
+ # Ensure there is a tool definition of the given priority, creating it if
553
+ # needed, and return it. A tool class may be provided, but only if the
554
+ # tool definition has not yet been created.
555
+ #
528
556
  # @private
529
557
  #
530
558
  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)
559
+ if @top_priority.nil? || @top_priority < priority
560
+ @top_priority = priority
561
+ end
562
+ if tool_class && @definitions.include?(priority)
563
+ raise ToolDefinitionError, "Tool already defined for #{@words.inspect}"
539
564
  end
565
+ @definitions[priority] ||= loader.build_tool(@words, priority, tool_class)
540
566
  end
541
567
 
542
568
  ##
569
+ # Attempt to activate the tool with the given priority, and return it.
570
+ # If the given priority tool is already active, returns it.
571
+ # If a lower priority tool is already active, activates the given higher
572
+ # priority tool and returns it.
573
+ # If a higher priority tool is already active, does nothing and returns
574
+ # nil.
575
+ #
543
576
  # @private
544
577
  #
545
578
  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
579
+ return active_definition if @active_priority == priority
580
+ return nil if @active_priority && @active_priority > priority
581
+ @active_priority = priority
582
+ get_tool(priority, loader)
552
583
  end
553
584
 
554
585
  private
@@ -610,10 +641,15 @@ module Toys
610
641
 
611
642
  private
612
643
 
644
+ ##
645
+ # Return a snapshot of all the current tool definitions that have been
646
+ # loaded. No additional loading is done. The returned array is not in any
647
+ # particular order.
648
+ #
613
649
  def all_cur_definitions
614
650
  result = []
615
651
  @mutex.synchronize do
616
- @tool_data.map do |_name, td|
652
+ @tool_data.each_value do |td|
617
653
  tool = td.cur_definition
618
654
  result << tool unless tool.nil?
619
655
  end
@@ -621,10 +657,12 @@ module Toys
621
657
  result
622
658
  end
623
659
 
660
+ ##
661
+ # Get or create the ToolData for the given name.
662
+ # Caller must own the mutex.
663
+ #
624
664
  def get_tool_data(words, create)
625
- @mutex.synchronize do
626
- create ? (@tool_data[words] ||= ToolData.new(words)) : @tool_data[words]
627
- end
665
+ create ? (@tool_data[words] ||= ToolData.new(words)) : @tool_data[words]
628
666
  end
629
667
 
630
668
  ##
@@ -641,6 +679,10 @@ module Toys
641
679
  end
642
680
  end
643
681
 
682
+ ##
683
+ # Loads from a proc source.
684
+ # Caller must own the mutex.
685
+ #
644
686
  def load_proc(source, words, remaining_words, priority)
645
687
  if remaining_words
646
688
  update_min_loaded_priority(priority)
@@ -655,6 +697,10 @@ module Toys
655
697
  end
656
698
  end
657
699
 
700
+ ##
701
+ # Load from a file path source that is known to exist.
702
+ # Caller must own the mutex.
703
+ #
658
704
  def load_validated_path(source, words, remaining_words, priority)
659
705
  if remaining_words
660
706
  load_relevant_path(source, words, remaining_words, priority)
@@ -663,6 +709,11 @@ module Toys
663
709
  end
664
710
  end
665
711
 
712
+ ##
713
+ # Load from a file path source that is known to exist and is known to be
714
+ # relevant to the current load request.
715
+ # Caller must own the mutex.
716
+ #
666
717
  def load_relevant_path(source, words, remaining_words, priority)
667
718
  if source.source_type == :file
668
719
  update_min_loaded_priority(priority)
@@ -677,12 +728,20 @@ module Toys
677
728
  end
678
729
  end
679
730
 
731
+ ##
732
+ # Load an index file in a directory source.
733
+ # Caller must own the mutex.
734
+ #
680
735
  def load_index_in(source, words, remaining_words, priority)
681
736
  return unless @index_file_name
682
737
  index_source = source.relative_child(@index_file_name)
683
738
  load_relevant_path(index_source, words, remaining_words, priority) if index_source
684
739
  end
685
740
 
741
+ ##
742
+ # Load non-index file in a directory source.
743
+ # Caller must own the mutex.
744
+ #
686
745
  def load_child_in(source, child, words, remaining_words, priority)
687
746
  return if child.start_with?(".") || child == @index_file_name ||
688
747
  child == @preload_file_name || child == @preload_dir_name ||
@@ -695,10 +754,17 @@ module Toys
695
754
  load_validated_path(child_source, next_words, next_remaining, priority)
696
755
  end
697
756
 
757
+ ##
758
+ # Update min_loaded_priority to the given value.
759
+ # Caller must own the mutex.
760
+ #
698
761
  def update_min_loaded_priority(priority)
699
762
  @min_loaded_priority = priority if @min_loaded_priority > priority
700
763
  end
701
764
 
765
+ ##
766
+ # Look for and require any preloads.
767
+ #
702
768
  def do_preload(path)
703
769
  if @preload_file_name
704
770
  preload_file = ::File.join(path, @preload_file_name)
@@ -709,13 +775,16 @@ module Toys
709
775
  if @preload_dir_name
710
776
  preload_dir = ::File.join(path, @preload_dir_name)
711
777
  if ::File.directory?(preload_dir) && ::File.readable?(preload_dir)
712
- preload_dir_contents(preload_dir)
778
+ require_dir_contents(preload_dir)
713
779
  end
714
780
  end
715
781
  end
716
782
 
717
- def preload_dir_contents(preload_dir)
718
- ::Dir.entries(preload_dir).each do |child|
783
+ ##
784
+ # Require the contents of the given directory.
785
+ #
786
+ def require_dir_contents(preload_dir)
787
+ ::Dir.entries(preload_dir).sort.each do |child|
719
788
  next unless ::File.extname(child) == ".rb"
720
789
  preload_file = ::File.join(preload_dir, child)
721
790
  next if !::File.file?(preload_file) || !::File.readable?(preload_file)
@@ -723,31 +792,6 @@ module Toys
723
792
  end
724
793
  end
725
794
 
726
- def sort_tools_by_name(tools)
727
- tools.sort! do |a, b|
728
- a = a.full_name
729
- b = b.full_name
730
- while !a.empty? && !b.empty? && a.first == b.first
731
- a = a.slice(1..-1)
732
- b = b.slice(1..-1)
733
- end
734
- a.first.to_s <=> b.first.to_s
735
- end
736
- end
737
-
738
- def filter_hidden_subtools(tools)
739
- result = []
740
- tools.each_with_index do |tool, index|
741
- result << tool unless tool_hidden?(tool, tools[index + 1])
742
- end
743
- result
744
- end
745
-
746
- def tool_hidden?(tool, next_tool)
747
- return true if tool.full_name.any? { |n| n.start_with?("_") }
748
- !tool.runnable? && next_tool && next_tool.full_name.slice(0..-2) == tool.full_name
749
- end
750
-
751
795
  def calc_remaining_words(words1, words2)
752
796
  index = 0
753
797
  lengths = [words1.length, words2.length]
@@ -757,5 +801,29 @@ module Toys
757
801
  index += 1
758
802
  end
759
803
  end
804
+
805
+ ##
806
+ # Given a sorted list of tools, filter out non-runnable tools, subject to
807
+ # the given settings.
808
+ #
809
+ def filter_non_runnable_tools(tools, include_namespaces, include_non_runnable)
810
+ return tools if include_namespaces && include_non_runnable
811
+
812
+ # This is a bit of a clever algorithm, sorry. We iterate over the sorted
813
+ # list of tools backwards (i.e. a reverse depth-first traversal) and
814
+ # apply the runnable and namespace filters.
815
+ # We determine whether a non-runnable tool is a namespace (i.e. has a
816
+ # runnable descendent) by tracking the state "kept_depth" representing
817
+ # the longest tool name length of a tool that we have kept and whose
818
+ # parent has yet to be traversed. Thus, when we traverse a non-runnable
819
+ # node, we can tell whether we have kept at least one child.
820
+ kept_depth = 0
821
+ tools.reverse_each.select do |tool|
822
+ cur_len = tool.full_name.length
823
+ keep = tool.runnable? || (kept_depth > cur_len ? include_namespaces : include_non_runnable)
824
+ kept_depth = cur_len if keep || kept_depth > cur_len
825
+ keep
826
+ end.reverse
827
+ end
760
828
  end
761
829
  end
@@ -141,7 +141,9 @@ module Toys
141
141
  end
142
142
 
143
143
  ##
144
- # @private
144
+ # Create a spec from an array specification.
145
+ #
146
+ # @private This interface is internal and subject to change without warning.
145
147
  #
146
148
  def spec_from_array(array)
147
149
  middleware = array.first
@@ -168,7 +170,7 @@ module Toys
168
170
  end
169
171
 
170
172
  ##
171
- # A base class that provides default NOP implementations of the middleware
173
+ # A base class that provides default no-op implementation of the middleware
172
174
  # interface. This base class may optionally be subclassed by a middleware
173
175
  # implementation.
174
176
  #
@@ -266,7 +268,9 @@ module Toys
266
268
  end
267
269
 
268
270
  ##
269
- # @private
271
+ # Internal constructor. Use {Toys::Middleware.spec} instead.
272
+ #
273
+ # @private This interface is internal and subject to change without warning.
270
274
  #
271
275
  def initialize(object, name, args, kwargs, block)
272
276
  @object = object
@@ -278,7 +282,20 @@ module Toys
278
282
  end
279
283
 
280
284
  ##
281
- # A stack of middleware specs.
285
+ # A stack of middleware specs, which can be applied in order to a tool.
286
+ #
287
+ # A middleware stack is separated into three groups:
288
+ #
289
+ # * {#pre_specs}, which are applied first.
290
+ # * {#default_specs}, which are applied next. The default specs are set
291
+ # when the stack is created and are generally not modified.
292
+ # * {#post_specs}, which are applied third.
293
+ #
294
+ # When adding middleware to a stack, you should normally add them to the
295
+ # pre or post specs. By default, {Stack#add} appends to the pre specs,
296
+ # inserting new middleware just before the defaults.
297
+ #
298
+ # Use {Toys::Middleware.stack} to create a middleware stack.
282
299
  #
283
300
  class Stack
284
301
  ##
@@ -356,7 +373,9 @@ module Toys
356
373
  end
357
374
 
358
375
  ##
359
- # @private
376
+ # Internal constructor. Use {Toys::Middleware.stack} instead.
377
+ #
378
+ # @private This interface is internal and subject to change without warning.
360
379
  #
361
380
  def initialize(default_specs: nil, pre_specs: nil, post_specs: nil)
362
381
  @pre_specs = pre_specs || []
data/lib/toys/settings.rb CHANGED
@@ -334,7 +334,7 @@ module Toys
334
334
  attr_reader :type_description
335
335
 
336
336
  ##
337
- # @private
337
+ # @private This interface is internal and subject to change without warning.
338
338
  #
339
339
  def initialize(value, settings_class, field_name, type_description)
340
340
  @value = value
@@ -835,7 +835,7 @@ module Toys
835
835
  end
836
836
 
837
837
  ##
838
- # @private
838
+ # @private This interface is internal and subject to change without warning.
839
839
  #
840
840
  # Returns the fields hash. This is shared between the settings class and
841
841
  # all its instances.