toys-core 0.13.1 → 0.14.1

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.
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.