mocktail 1.2.3 → 2.0.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 +4 -4
 - data/.github/workflows/main.yml +6 -5
 - data/.gitignore +3 -0
 - data/.standard.yml +8 -0
 - data/CHANGELOG.md +14 -0
 - data/Gemfile +6 -1
 - data/Gemfile.lock +98 -25
 - data/README.md +18 -922
 - data/Rakefile +0 -1
 - data/bin/console +1 -2
 - data/bin/tapioca +29 -0
 - data/lib/mocktail/collects_calls.rb +2 -0
 - data/lib/mocktail/debug.rb +13 -10
 - data/lib/mocktail/dsl.rb +2 -0
 - data/lib/mocktail/errors.rb +2 -0
 - data/lib/mocktail/explains_nils.rb +2 -0
 - data/lib/mocktail/explains_thing.rb +7 -4
 - data/lib/mocktail/grabs_original_method_parameters.rb +30 -0
 - data/lib/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +3 -1
 - data/lib/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +5 -1
 - data/lib/mocktail/handles_dry_call/fulfills_stubbing.rb +2 -0
 - data/lib/mocktail/handles_dry_call/logs_call.rb +2 -0
 - data/lib/mocktail/handles_dry_call/validates_arguments.rb +6 -4
 - data/lib/mocktail/handles_dry_call.rb +2 -0
 - data/lib/mocktail/handles_dry_new_call.rb +2 -0
 - data/lib/mocktail/imitates_type/ensures_imitation_support.rb +2 -0
 - data/lib/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +4 -1
 - data/lib/mocktail/imitates_type/makes_double/declares_dry_class.rb +32 -20
 - data/lib/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +2 -0
 - data/lib/mocktail/imitates_type/makes_double.rb +3 -0
 - data/lib/mocktail/imitates_type.rb +3 -1
 - data/lib/mocktail/initialize_based_on_type_system_mode_switching.rb +9 -0
 - data/lib/mocktail/initializes_mocktail.rb +5 -0
 - data/lib/mocktail/matcher_presentation.rb +4 -2
 - data/lib/mocktail/matchers/any.rb +4 -3
 - data/lib/mocktail/matchers/base.rb +10 -2
 - data/lib/mocktail/matchers/captor.rb +9 -0
 - data/lib/mocktail/matchers/includes.rb +2 -0
 - data/lib/mocktail/matchers/includes_hash.rb +9 -0
 - data/lib/mocktail/matchers/includes_key.rb +9 -0
 - data/lib/mocktail/matchers/includes_string.rb +9 -0
 - data/lib/mocktail/matchers/is_a.rb +2 -0
 - data/lib/mocktail/matchers/matches.rb +2 -0
 - data/lib/mocktail/matchers/not.rb +2 -0
 - data/lib/mocktail/matchers/numeric.rb +5 -4
 - data/lib/mocktail/matchers/that.rb +2 -0
 - data/lib/mocktail/matchers.rb +3 -0
 - data/lib/mocktail/raises_neato_no_method_error.rb +2 -0
 - data/lib/mocktail/records_demonstration.rb +2 -0
 - data/lib/mocktail/registers_matcher.rb +8 -3
 - data/lib/mocktail/registers_stubbing.rb +2 -0
 - data/lib/mocktail/replaces_next.rb +7 -1
 - data/lib/mocktail/replaces_type/redefines_new.rb +3 -1
 - data/lib/mocktail/replaces_type/redefines_singleton_methods.rb +14 -2
 - data/lib/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +37 -0
 - data/lib/mocktail/replaces_type.rb +6 -0
 - data/lib/mocktail/resets_state.rb +2 -0
 - data/lib/mocktail/share/bind.rb +7 -5
 - data/lib/mocktail/share/cleans_backtrace.rb +3 -5
 - data/lib/mocktail/share/creates_identifier.rb +16 -9
 - data/lib/mocktail/share/determines_matching_calls.rb +4 -2
 - data/lib/mocktail/share/stringifies_call.rb +6 -2
 - data/lib/mocktail/share/stringifies_method_name.rb +3 -1
 - data/lib/mocktail/simulates_argument_error/reconciles_args_with_params.rb +2 -0
 - data/lib/mocktail/simulates_argument_error/recreates_message.rb +2 -0
 - data/lib/mocktail/simulates_argument_error/transforms_params.rb +15 -8
 - data/lib/mocktail/simulates_argument_error.rb +2 -0
 - data/lib/mocktail/sorbet/mocktail/collects_calls.rb +18 -0
 - data/lib/mocktail/sorbet/mocktail/debug.rb +54 -0
 - data/lib/mocktail/sorbet/mocktail/dsl.rb +46 -0
 - data/lib/mocktail/sorbet/mocktail/errors.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/explains_nils.rb +41 -0
 - data/lib/mocktail/sorbet/mocktail/explains_thing.rb +137 -0
 - data/lib/mocktail/sorbet/mocktail/grabs_original_method_parameters.rb +33 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +27 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +24 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing.rb +45 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call/logs_call.rb +12 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call/validates_arguments.rb +45 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_call.rb +25 -0
 - data/lib/mocktail/sorbet/mocktail/handles_dry_new_call.rb +42 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type/ensures_imitation_support.rb +16 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +73 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/declares_dry_class.rb +136 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +28 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double.rb +29 -0
 - data/lib/mocktail/sorbet/mocktail/imitates_type.rb +29 -0
 - data/lib/mocktail/sorbet/mocktail/initialize_based_on_type_system_mode_switching.rb +11 -0
 - data/lib/mocktail/sorbet/mocktail/initializes_mocktail.rb +25 -0
 - data/lib/mocktail/sorbet/mocktail/matcher_presentation.rb +21 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/any.rb +27 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/base.rb +39 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/captor.rb +76 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/includes.rb +32 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/includes_hash.rb +12 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/includes_key.rb +12 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/includes_string.rb +12 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/is_a.rb +17 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/matches.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/not.rb +17 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/numeric.rb +27 -0
 - data/lib/mocktail/sorbet/mocktail/matchers/that.rb +32 -0
 - data/lib/mocktail/sorbet/mocktail/matchers.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/raises_neato_no_method_error.rb +93 -0
 - data/lib/mocktail/sorbet/mocktail/records_demonstration.rb +43 -0
 - data/lib/mocktail/sorbet/mocktail/registers_matcher.rb +65 -0
 - data/lib/mocktail/sorbet/mocktail/registers_stubbing.rb +31 -0
 - data/lib/mocktail/sorbet/mocktail/replaces_next.rb +55 -0
 - data/lib/mocktail/sorbet/mocktail/replaces_type/redefines_new.rb +32 -0
 - data/lib/mocktail/sorbet/mocktail/replaces_type/redefines_singleton_methods.rb +80 -0
 - data/lib/mocktail/sorbet/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +39 -0
 - data/lib/mocktail/sorbet/mocktail/replaces_type.rb +36 -0
 - data/lib/mocktail/sorbet/mocktail/resets_state.rb +14 -0
 - data/lib/mocktail/sorbet/mocktail/share/bind.rb +18 -0
 - data/lib/mocktail/sorbet/mocktail/share/cleans_backtrace.rb +22 -0
 - data/lib/mocktail/sorbet/mocktail/share/creates_identifier.rb +39 -0
 - data/lib/mocktail/sorbet/mocktail/share/determines_matching_calls.rb +72 -0
 - data/lib/mocktail/sorbet/mocktail/share/stringifies_call.rb +85 -0
 - data/lib/mocktail/sorbet/mocktail/share/stringifies_method_name.rb +16 -0
 - data/lib/mocktail/sorbet/mocktail/simulates_argument_error/reconciles_args_with_params.rb +27 -0
 - data/lib/mocktail/sorbet/mocktail/simulates_argument_error/recreates_message.rb +34 -0
 - data/lib/mocktail/sorbet/mocktail/simulates_argument_error/transforms_params.rb +58 -0
 - data/lib/mocktail/sorbet/mocktail/simulates_argument_error.rb +36 -0
 - data/lib/mocktail/sorbet/mocktail/sorbet.rb +3 -0
 - data/lib/mocktail/sorbet/mocktail/stringifies_method_signature.rb +53 -0
 - data/lib/mocktail/sorbet/mocktail/typed.rb +5 -0
 - data/lib/mocktail/sorbet/mocktail/value/cabinet.rb +91 -0
 - data/lib/mocktail/sorbet/mocktail/value/call.rb +51 -0
 - data/lib/mocktail/sorbet/mocktail/value/demo_config.rb +10 -0
 - data/lib/mocktail/sorbet/mocktail/value/double.rb +10 -0
 - data/lib/mocktail/sorbet/mocktail/value/double_data.rb +15 -0
 - data/lib/mocktail/sorbet/mocktail/value/explanation.rb +68 -0
 - data/lib/mocktail/sorbet/mocktail/value/explanation_data.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/value/fake_method_data.rb +11 -0
 - data/lib/mocktail/sorbet/mocktail/value/matcher_registry.rb +27 -0
 - data/lib/mocktail/sorbet/mocktail/value/no_explanation_data.rb +20 -0
 - data/lib/mocktail/sorbet/mocktail/value/signature.rb +35 -0
 - data/lib/mocktail/sorbet/mocktail/value/stubbing.rb +26 -0
 - data/lib/mocktail/sorbet/mocktail/value/top_shelf.rb +79 -0
 - data/lib/mocktail/sorbet/mocktail/value/type_replacement.rb +11 -0
 - data/lib/mocktail/sorbet/mocktail/value/type_replacement_data.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/value/unsatisfying_call.rb +9 -0
 - data/lib/mocktail/sorbet/mocktail/value/unsatisfying_call_explanation.rb +24 -0
 - data/lib/mocktail/sorbet/mocktail/value.rb +19 -0
 - data/lib/mocktail/sorbet/mocktail/verifies_call/finds_verifiable_calls.rb +21 -0
 - data/lib/mocktail/sorbet/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +15 -0
 - data/lib/mocktail/sorbet/mocktail/verifies_call/raises_verification_error.rb +74 -0
 - data/lib/mocktail/sorbet/mocktail/verifies_call.rb +37 -0
 - data/lib/mocktail/sorbet/mocktail/version.rb +12 -0
 - data/lib/mocktail/sorbet/mocktail.rb +154 -0
 - data/lib/mocktail/sorbet.rb +1 -0
 - data/lib/mocktail/stringifies_method_signature.rb +2 -0
 - data/lib/mocktail/typed.rb +3 -0
 - data/lib/mocktail/value/cabinet.rb +8 -1
 - data/lib/mocktail/value/call.rb +44 -12
 - data/lib/mocktail/value/demo_config.rb +6 -7
 - data/lib/mocktail/value/double.rb +6 -7
 - data/lib/mocktail/value/double_data.rb +11 -7
 - data/lib/mocktail/value/explanation.rb +28 -3
 - data/lib/mocktail/value/explanation_data.rb +14 -0
 - data/lib/mocktail/value/fake_method_data.rb +7 -6
 - data/lib/mocktail/value/matcher_registry.rb +2 -0
 - data/lib/mocktail/value/no_explanation_data.rb +16 -0
 - data/lib/mocktail/value/signature.rb +19 -27
 - data/lib/mocktail/value/stubbing.rb +11 -12
 - data/lib/mocktail/value/top_shelf.rb +5 -0
 - data/lib/mocktail/value/type_replacement.rb +7 -8
 - data/lib/mocktail/value/type_replacement_data.rb +10 -7
 - data/lib/mocktail/value/unsatisfying_call.rb +5 -6
 - data/lib/mocktail/value/unsatisfying_call_explanation.rb +18 -0
 - data/lib/mocktail/value.rb +5 -2
 - data/lib/mocktail/verifies_call/finds_verifiable_calls.rb +2 -0
 - data/lib/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +2 -0
 - data/lib/mocktail/verifies_call/raises_verification_error.rb +2 -0
 - data/lib/mocktail/verifies_call.rb +3 -0
 - data/lib/mocktail/version.rb +8 -1
 - data/lib/mocktail.rb +46 -5
 - data/mocktail.gemspec +8 -4
 - data/rbi/mocktail-pregenerated.rbi +1865 -0
 - data/rbi/mocktail.rbi +77 -0
 - data/rbi/sorbet-runtime.rbi +29 -0
 - data/spoom_report.html +1248 -0
 - metadata +130 -3
 
    
        data/Rakefile
    CHANGED
    
    
    
        data/bin/console
    CHANGED
    
    
    
        data/bin/tapioca
    ADDED
    
    | 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This file was generated by Bundler.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # The application 'tapioca' is installed as part of a gem, and
         
     | 
| 
      
 8 
     | 
    
         
            +
            # this file is here to facilitate running it.
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require "pathname"
         
     | 
| 
      
 12 
     | 
    
         
            +
            ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
         
     | 
| 
      
 13 
     | 
    
         
            +
              Pathname.new(__FILE__).realpath)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            bundle_binstub = File.expand_path("../bundle", __FILE__)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            if File.file?(bundle_binstub)
         
     | 
| 
      
 18 
     | 
    
         
            +
              if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
         
     | 
| 
      
 19 
     | 
    
         
            +
                load(bundle_binstub)
         
     | 
| 
      
 20 
     | 
    
         
            +
              else
         
     | 
| 
      
 21 
     | 
    
         
            +
                abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
         
     | 
| 
      
 22 
     | 
    
         
            +
            Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            require "rubygems"
         
     | 
| 
      
 27 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            load Gem.bin_path("tapioca", "tapioca")
         
     | 
    
        data/lib/mocktail/debug.rb
    CHANGED
    
    | 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       2 
2 
     | 
    
         
             
              module Debug
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                # It would be easy and bad for the mocktail lib to call something like
         
     | 
| 
       4 
6 
     | 
    
         
             
                #
         
     | 
| 
       5 
7 
     | 
    
         
             
                #   double == other_double
         
     | 
| 
         @@ -12,26 +14,27 @@ module Mocktail 
     | 
|
| 
       12 
14 
     | 
    
         
             
                # happens unintentionally. This works in conjunction with the test
         
     | 
| 
       13 
15 
     | 
    
         
             
                # MockingMethodfulClassesTest, because it mocks every defined method on the
         
     | 
| 
       14 
16 
     | 
    
         
             
                # mocked BasicObject
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       15 
18 
     | 
    
         
             
                def self.guard_against_mocktail_accidentally_calling_mocks_if_debugging!
         
     | 
| 
       16 
19 
     | 
    
         
             
                  return unless ENV["MOCKTAIL_DEBUG_ACCIDENTAL_INTERNAL_MOCK_CALLS"]
         
     | 
| 
       17 
     | 
    
         
            -
                  raise
         
     | 
| 
       18 
     | 
    
         
            -
                rescue => e
         
     | 
| 
      
 20 
     | 
    
         
            +
                  raise Mocktail::Error
         
     | 
| 
      
 21 
     | 
    
         
            +
                rescue Mocktail::Error => e
         
     | 
| 
       19 
22 
     | 
    
         
             
                  base_path = Pathname.new(__FILE__).dirname.to_s
         
     | 
| 
       20 
     | 
    
         
            -
                  backtrace_minus_this_and_whoever_called_this = e.backtrace[2.. 
     | 
| 
       21 
     | 
    
         
            -
                  internal_call_sites = backtrace_minus_this_and_whoever_called_this 
     | 
| 
      
 23 
     | 
    
         
            +
                  backtrace_minus_this_and_whoever_called_this = e.backtrace&.[](2..)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  internal_call_sites = backtrace_minus_this_and_whoever_called_this&.take_while { |call_site|
         
     | 
| 
       22 
25 
     | 
    
         
             
                    # the "in `block" is very confusing but necessary to include lines after
         
     | 
| 
       23 
26 
     | 
    
         
             
                    # a stubs { blah.foo }.with { … } call, since that's when most of the
         
     | 
| 
       24 
27 
     | 
    
         
             
                    # good stuff happens
         
     | 
| 
       25 
28 
     | 
    
         
             
                    call_site.start_with?(base_path) || call_site.include?("in `block")
         
     | 
| 
       26 
     | 
    
         
            -
                  } 
     | 
| 
      
 29 
     | 
    
         
            +
                  }&.reject { |call_site| call_site.include?("in `block") } || []
         
     | 
| 
       27 
30 
     | 
    
         | 
| 
       28 
31 
     | 
    
         
             
                  approved_call_sites = [
         
     | 
| 
       29 
     | 
    
         
            -
                     
     | 
| 
       30 
     | 
    
         
            -
                     
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
      
 32 
     | 
    
         
            +
                    /fulfills_stubbing.rb:(16|20)/,
         
     | 
| 
      
 33 
     | 
    
         
            +
                    /validates_arguments.rb:(18|23)/,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    /validates_arguments.rb:(21|26)/
         
     | 
| 
       32 
35 
     | 
    
         
             
                  ]
         
     | 
| 
       33 
36 
     | 
    
         
             
                  if internal_call_sites.any? && approved_call_sites.none? { |approved_call_site|
         
     | 
| 
       34 
     | 
    
         
            -
                    internal_call_sites.first 
     | 
| 
      
 37 
     | 
    
         
            +
                    internal_call_sites.first&.match?(approved_call_site)
         
     | 
| 
       35 
38 
     | 
    
         
             
                  }
         
     | 
| 
       36 
39 
     | 
    
         
             
                    raise Error.new <<~MSG
         
     | 
| 
       37 
40 
     | 
    
         
             
                      Unauthorized internal call of a mock internally by Mocktail itself:
         
     | 
| 
         @@ -40,7 +43,7 @@ module Mocktail 
     | 
|
| 
       40 
43 
     | 
    
         | 
| 
       41 
44 
     | 
    
         
             
                      Offending call's complete stack trace:
         
     | 
| 
       42 
45 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
                      #{backtrace_minus_this_and_whoever_called_this 
     | 
| 
      
 46 
     | 
    
         
            +
                      #{backtrace_minus_this_and_whoever_called_this&.join("\n")}
         
     | 
| 
       44 
47 
     | 
    
         
             
                      ==END OFFENDING TRACE==
         
     | 
| 
       45 
48 
     | 
    
         
             
                    MSG
         
     | 
| 
       46 
49 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/mocktail/dsl.rb
    CHANGED
    
    
    
        data/lib/mocktail/errors.rb
    CHANGED
    
    
| 
         @@ -3,6 +3,8 @@ require_relative "share/stringifies_call" 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       5 
5 
     | 
    
         
             
              class ExplainsThing
         
     | 
| 
      
 6 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
                def initialize
         
     | 
| 
       7 
9 
     | 
    
         
             
                  @stringifies_method_name = StringifiesMethodName.new
         
     | 
| 
       8 
10 
     | 
    
         
             
                  @stringifies_call = StringifiesCall.new
         
     | 
| 
         @@ -11,7 +13,8 @@ module Mocktail 
     | 
|
| 
       11 
13 
     | 
    
         
             
                def explain(thing)
         
     | 
| 
       12 
14 
     | 
    
         
             
                  if (double = Mocktail.cabinet.double_for_instance(thing))
         
     | 
| 
       13 
15 
     | 
    
         
             
                    double_explanation(double)
         
     | 
| 
       14 
     | 
    
         
            -
                  elsif ( 
     | 
| 
      
 16 
     | 
    
         
            +
                  elsif (thing.is_a?(Module) || thing.is_a?(Class)) &&
         
     | 
| 
      
 17 
     | 
    
         
            +
                      (type_replacement = TopShelf.instance.type_replacement_if_exists_for(thing))
         
     | 
| 
       15 
18 
     | 
    
         
             
                    replaced_type_explanation(type_replacement)
         
     | 
| 
       16 
19 
     | 
    
         
             
                  elsif (fake_method_explanation = fake_method_explanation_for(thing))
         
     | 
| 
       17 
20 
     | 
    
         
             
                    fake_method_explanation
         
     | 
| 
         @@ -67,7 +70,7 @@ module Mocktail 
     | 
|
| 
       67 
70 
     | 
    
         
             
                def data_for_type_replacement(type_replacement)
         
     | 
| 
       68 
71 
     | 
    
         
             
                  TypeReplacementData.new(
         
     | 
| 
       69 
72 
     | 
    
         
             
                    type: type_replacement.type,
         
     | 
| 
       70 
     | 
    
         
            -
                    replaced_method_names: type_replacement.replacement_methods 
     | 
| 
      
 73 
     | 
    
         
            +
                    replaced_method_names: type_replacement.replacement_methods&.map(&:name)&.sort || [],
         
     | 
| 
       71 
74 
     | 
    
         
             
                    calls: Mocktail.cabinet.calls.select { |call|
         
     | 
| 
       72 
75 
     | 
    
         
             
                      call.double == type_replacement.type
         
     | 
| 
       73 
76 
     | 
    
         
             
                    },
         
     | 
| 
         @@ -81,7 +84,7 @@ module Mocktail 
     | 
|
| 
       81 
84 
     | 
    
         
             
                  type_replacement_data = data_for_type_replacement(type_replacement)
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
       83 
86 
     | 
    
         
             
                  ReplacedTypeExplanation.new(type_replacement_data, <<~MSG)
         
     | 
| 
       84 
     | 
    
         
            -
                    `#{type_replacement.type}' is a #{type_replacement.type.class.to_s.downcase} that has had its  
     | 
| 
      
 87 
     | 
    
         
            +
                    `#{type_replacement.type}' is a #{type_replacement.type.class.to_s.downcase} that has had its methods faked.
         
     | 
| 
       85 
88 
     | 
    
         | 
| 
       86 
89 
     | 
    
         
             
                    It has these mocked methods:
         
     | 
| 
       87 
90 
     | 
    
         
             
                    #{type_replacement_data.replaced_method_names.map { |method| "  - #{method}" }.join("\n")}
         
     | 
| 
         @@ -116,7 +119,7 @@ module Mocktail 
     | 
|
| 
       116 
119 
     | 
    
         
             
                end
         
     | 
| 
       117 
120 
     | 
    
         | 
| 
       118 
121 
     | 
    
         
             
                def no_explanation(thing)
         
     | 
| 
       119 
     | 
    
         
            -
                  NoExplanation.new(thing,
         
     | 
| 
      
 122 
     | 
    
         
            +
                  NoExplanation.new(NoExplanationData.new(thing: thing),
         
     | 
| 
       120 
123 
     | 
    
         
             
                    "Unfortunately, Mocktail doesn't know what this thing is: #{thing.inspect}")
         
     | 
| 
       121 
124 
     | 
    
         
             
                end
         
     | 
| 
       122 
125 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Mocktail
         
     | 
| 
      
 2 
     | 
    
         
            +
              class GrabsOriginalMethodParameters
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                # Sorbet wraps the original method in a sig wrapper, so we need to unwrap it.
         
     | 
| 
      
 6 
     | 
    
         
            +
                # The value returned from `owner.instance_method(method_name)` does not have
         
     | 
| 
      
 7 
     | 
    
         
            +
                # the real parameters values available, as they'll have been erased
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # If the method isn't wrapped by Sorbet, this will return the #instance_method,
         
     | 
| 
      
 10 
     | 
    
         
            +
                # per usual
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def grab(method)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  return [] unless method
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  if (wrapped_method = sorbet_wrapped_method(method))
         
     | 
| 
      
 16 
     | 
    
         
            +
                    wrapped_method.parameters
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    method.parameters
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                private
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def sorbet_wrapped_method(method)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  return unless defined?(::T::Private::Methods)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  T::Private::Methods.signature_for_method(method)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -3,6 +3,8 @@ require_relative "../../share/bind" 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       5 
5 
     | 
    
         
             
              class DescribesUnsatisfiedStubbing
         
     | 
| 
      
 6 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
                def initialize
         
     | 
| 
       7 
9 
     | 
    
         
             
                  @cleans_backtrace = CleansBacktrace.new
         
     | 
| 
       8 
10 
     | 
    
         
             
                end
         
     | 
| 
         @@ -14,7 +16,7 @@ module Mocktail 
     | 
|
| 
       14 
16 
     | 
    
         
             
                      Bind.call(dry_call.double, :==, stubbing.recording.double) &&
         
     | 
| 
       15 
17 
     | 
    
         
             
                        dry_call.method == stubbing.recording.method
         
     | 
| 
       16 
18 
     | 
    
         
             
                    },
         
     | 
| 
       17 
     | 
    
         
            -
                    backtrace: @cleans_backtrace.clean(Error.new).backtrace
         
     | 
| 
      
 19 
     | 
    
         
            +
                    backtrace: @cleans_backtrace.clean(Error.new).backtrace || []
         
     | 
| 
       18 
20 
     | 
    
         
             
                  )
         
     | 
| 
       19 
21 
     | 
    
         
             
                end
         
     | 
| 
       20 
22 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2,14 +2,18 @@ require_relative "../../share/determines_matching_calls" 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       4 
4 
     | 
    
         
             
              class FindsSatisfaction
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       5 
7 
     | 
    
         
             
                def initialize
         
     | 
| 
       6 
8 
     | 
    
         
             
                  @determines_matching_calls = DeterminesMatchingCalls.new
         
     | 
| 
       7 
9 
     | 
    
         
             
                end
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
11 
     | 
    
         
             
                def find(dry_call)
         
     | 
| 
       10 
12 
     | 
    
         
             
                  Mocktail.cabinet.stubbings.reverse.find { |stubbing|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    demo_config_times = stubbing.demo_config.times
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       11 
15 
     | 
    
         
             
                    @determines_matching_calls.determine(dry_call, stubbing.recording, stubbing.demo_config) &&
         
     | 
| 
       12 
     | 
    
         
            -
                      ( 
     | 
| 
      
 16 
     | 
    
         
            +
                      (demo_config_times.nil? || demo_config_times > stubbing.satisfaction_count)
         
     | 
| 
       13 
17 
     | 
    
         
             
                  }
         
     | 
| 
       14 
18 
     | 
    
         
             
                end
         
     | 
| 
       15 
19 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       2 
2 
     | 
    
         
             
              class ValidatesArguments
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                def self.disable!
         
     | 
| 
       4 
6 
     | 
    
         
             
                  Thread.current[:mocktail_arity_validation_disabled] = true
         
     | 
| 
       5 
7 
     | 
    
         
             
                end
         
     | 
| 
         @@ -9,16 +11,16 @@ module Mocktail 
     | 
|
| 
       9 
11 
     | 
    
         
             
                end
         
     | 
| 
       10 
12 
     | 
    
         | 
| 
       11 
13 
     | 
    
         
             
                def self.disabled?
         
     | 
| 
       12 
     | 
    
         
            -
                  Thread.current[:mocktail_arity_validation_disabled]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  !!Thread.current[:mocktail_arity_validation_disabled]
         
     | 
| 
       13 
15 
     | 
    
         
             
                end
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
                def self.optional(disable, &blk)
         
     | 
| 
       16 
18 
     | 
    
         
             
                  return blk.call unless disable
         
     | 
| 
       17 
19 
     | 
    
         | 
| 
       18 
20 
     | 
    
         
             
                  disable!
         
     | 
| 
       19 
     | 
    
         
            -
                  blk.call 
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
      
 21 
     | 
    
         
            +
                  ret = blk.call
         
     | 
| 
      
 22 
     | 
    
         
            +
                  enable!
         
     | 
| 
      
 23 
     | 
    
         
            +
                  ret
         
     | 
| 
       22 
24 
     | 
    
         
             
                end
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
       24 
26 
     | 
    
         
             
                def initialize
         
     | 
| 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       2 
2 
     | 
    
         
             
              class ReconstructsCall
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                def reconstruct(double:, call_binding:, default_args:, dry_class:, type:, method:, original_method:, signature:)
         
     | 
| 
       4 
6 
     | 
    
         
             
                  Call.new(
         
     | 
| 
       5 
7 
     | 
    
         
             
                    singleton: false,
         
     | 
| 
         @@ -37,7 +39,8 @@ module Mocktail 
     | 
|
| 
       37 
39 
     | 
    
         
             
                def non_default_args(params, default_args)
         
     | 
| 
       38 
40 
     | 
    
         
             
                  named_args = params.allowed
         
     | 
| 
       39 
41 
     | 
    
         
             
                    .reject { |p| default_args&.key?(p) }
         
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
      
 42 
     | 
    
         
            +
                  rest_param = params.rest
         
     | 
| 
      
 43 
     | 
    
         
            +
                  rest_arg = if rest_param && !default_args&.key?(rest_param)
         
     | 
| 
       41 
44 
     | 
    
         
             
                    params.rest
         
     | 
| 
       42 
45 
     | 
    
         
             
                  end
         
     | 
| 
       43 
46 
     | 
    
         | 
| 
         @@ -2,60 +2,72 @@ require_relative "declares_dry_class/reconstructs_call" 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       4 
4 
     | 
    
         
             
              class DeclaresDryClass
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                DEFAULT_ANCESTORS = Class.new(Object).ancestors[1..]
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       5 
9 
     | 
    
         
             
                def initialize
         
     | 
| 
       6 
10 
     | 
    
         
             
                  @raises_neato_no_method_error = RaisesNeatoNoMethodError.new
         
     | 
| 
       7 
11 
     | 
    
         
             
                  @transforms_params = TransformsParams.new
         
     | 
| 
       8 
12 
     | 
    
         
             
                  @stringifies_method_signature = StringifiesMethodSignature.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @grabs_original_method_parameters = GrabsOriginalMethodParameters.new
         
     | 
| 
       9 
14 
     | 
    
         
             
                end
         
     | 
| 
       10 
15 
     | 
    
         | 
| 
       11 
16 
     | 
    
         
             
                def declare(type, instance_methods)
         
     | 
| 
       12 
17 
     | 
    
         
             
                  dry_class = Class.new(Object) {
         
     | 
| 
       13 
     | 
    
         
            -
                    include type if type. 
     | 
| 
      
 18 
     | 
    
         
            +
                    include type if type.is_a?(Module) && !type.is_a?(Class)
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                     
     | 
| 
      
 20 
     | 
    
         
            +
                    define_method :initialize do |*args, **kwargs, &blk|
         
     | 
| 
       16 
21 
     | 
    
         
             
                    end
         
     | 
| 
       17 
22 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
       19 
     | 
    
         
            -
                       
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
                    [:is_a?, :kind_of?].each do |method_name|
         
     | 
| 
      
 24 
     | 
    
         
            +
                      define_method method_name, ->(thing) {
         
     | 
| 
      
 25 
     | 
    
         
            +
                        # Mocktails extend from Object, so share the same ancestors, plus the passed type
         
     | 
| 
      
 26 
     | 
    
         
            +
                        [type, *DEFAULT_ANCESTORS].include?(thing)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      }
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
       22 
29 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                    if type. 
     | 
| 
      
 30 
     | 
    
         
            +
                    if type.is_a?(Class)
         
     | 
| 
       24 
31 
     | 
    
         
             
                      define_method :instance_of?, ->(thing) {
         
     | 
| 
       25 
32 
     | 
    
         
             
                        type == thing
         
     | 
| 
       26 
33 
     | 
    
         
             
                      }
         
     | 
| 
       27 
34 
     | 
    
         
             
                    end
         
     | 
| 
       28 
35 
     | 
    
         
             
                  }
         
     | 
| 
       29 
36 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                   
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  #  
     | 
| 
      
 37 
     | 
    
         
            +
                  add_more_methods!(dry_class, type, instance_methods)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  dry_class  # This is all fake! That's the whole point—it's not a real Foo, it's just some new class that quacks like a Foo
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                private
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                # These have special implementations, but if the user defines
         
     | 
| 
      
 45 
     | 
    
         
            +
                # any of them on the object itself, then they'll be replaced with normal
         
     | 
| 
      
 46 
     | 
    
         
            +
                # mocked methods. YMMV
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def add_more_methods!(dry_class, type, instance_methods)
         
     | 
| 
       33 
49 
     | 
    
         
             
                  add_stringify_methods!(dry_class, :to_s, type, instance_methods)
         
     | 
| 
       34 
50 
     | 
    
         
             
                  add_stringify_methods!(dry_class, :inspect, type, instance_methods)
         
     | 
| 
       35 
51 
     | 
    
         
             
                  define_method_missing_errors!(dry_class, type, instance_methods)
         
     | 
| 
       36 
52 
     | 
    
         | 
| 
       37 
53 
     | 
    
         
             
                  define_double_methods!(dry_class, type, instance_methods)
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                  dry_class
         
     | 
| 
       40 
54 
     | 
    
         
             
                end
         
     | 
| 
       41 
55 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                private
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
56 
     | 
    
         
             
                def define_double_methods!(dry_class, type, instance_methods)
         
     | 
| 
       45 
     | 
    
         
            -
                  instance_methods.each do | 
     | 
| 
       46 
     | 
    
         
            -
                    dry_class.undef_method( 
     | 
| 
       47 
     | 
    
         
            -
                    parameters = type.instance_method( 
     | 
| 
      
 57 
     | 
    
         
            +
                  instance_methods.each do |method_name|
         
     | 
| 
      
 58 
     | 
    
         
            +
                    dry_class.undef_method(method_name) if dry_class.method_defined?(method_name)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    parameters = @grabs_original_method_parameters.grab(type.instance_method(method_name))
         
     | 
| 
       48 
60 
     | 
    
         
             
                    signature = @transforms_params.transform(Call.new, params: parameters)
         
     | 
| 
       49 
61 
     | 
    
         
             
                    method_signature = @stringifies_method_signature.stringify(signature)
         
     | 
| 
       50 
62 
     | 
    
         
             
                    __mocktail_closure = {
         
     | 
| 
       51 
63 
     | 
    
         
             
                      dry_class: dry_class,
         
     | 
| 
       52 
64 
     | 
    
         
             
                      type: type,
         
     | 
| 
       53 
     | 
    
         
            -
                      method:  
     | 
| 
       54 
     | 
    
         
            -
                      original_method: type.instance_method( 
     | 
| 
      
 65 
     | 
    
         
            +
                      method: method_name,
         
     | 
| 
      
 66 
     | 
    
         
            +
                      original_method: type.instance_method(method_name),
         
     | 
| 
       55 
67 
     | 
    
         
             
                      signature: signature
         
     | 
| 
       56 
68 
     | 
    
         
             
                    }
         
     | 
| 
       57 
69 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                    dry_class.define_method  
     | 
| 
      
 70 
     | 
    
         
            +
                    dry_class.define_method method_name,
         
     | 
| 
       59 
71 
     | 
    
         
             
                      eval(<<-RUBBY, binding, __FILE__, __LINE__ + 1) # standard:disable Security/Eval
         
     | 
| 
       60 
72 
     | 
    
         
             
                        ->#{method_signature} do
         
     | 
| 
       61 
73 
     | 
    
         
             
                          ::Mocktail::Debug.guard_against_mocktail_accidentally_calling_mocks_if_debugging!
         
     | 
| 
         @@ -3,6 +3,8 @@ require_relative "makes_double/gathers_fakeable_instance_methods" 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       5 
5 
     | 
    
         
             
              class MakesDouble
         
     | 
| 
      
 6 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
                def initialize
         
     | 
| 
       7 
9 
     | 
    
         
             
                  @declares_dry_class = DeclaresDryClass.new
         
     | 
| 
       8 
10 
     | 
    
         
             
                  @gathers_fakeable_instance_methods = GathersFakeableInstanceMethods.new
         
     | 
| 
         @@ -11,6 +13,7 @@ module Mocktail 
     | 
|
| 
       11 
13 
     | 
    
         
             
                def make(type)
         
     | 
| 
       12 
14 
     | 
    
         
             
                  dry_methods = @gathers_fakeable_instance_methods.gather(type)
         
     | 
| 
       13 
15 
     | 
    
         
             
                  dry_type = @declares_dry_class.declare(type, dry_methods)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       14 
17 
     | 
    
         
             
                  Double.new(
         
     | 
| 
       15 
18 
     | 
    
         
             
                    original_type: type,
         
     | 
| 
       16 
19 
     | 
    
         
             
                    dry_type: dry_type,
         
     | 
| 
         @@ -3,8 +3,10 @@ require_relative "imitates_type/makes_double" 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       5 
5 
     | 
    
         
             
              class ImitatesType
         
     | 
| 
      
 6 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 7 
     | 
    
         
            +
                extend T::Generic
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       6 
9 
     | 
    
         
             
                def initialize
         
     | 
| 
       7 
     | 
    
         
            -
                  @top_shelf = TopShelf.instance
         
     | 
| 
       8 
10 
     | 
    
         
             
                  @ensures_imitation_support = EnsuresImitationSupport.new
         
     | 
| 
       9 
11 
     | 
    
         
             
                  @makes_double = MakesDouble.new
         
     | 
| 
       10 
12 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative "typed"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Constant boolean, so won't statically type-check, but `T.unsafe` can't be used
         
     | 
| 
      
 4 
     | 
    
         
            +
            # because we haven't required sorbet-runtime yet
         
     | 
| 
      
 5 
     | 
    
         
            +
            if eval("Mocktail::TYPED", binding, __FILE__, __LINE__)
         
     | 
| 
      
 6 
     | 
    
         
            +
              require "sorbet-runtime"
         
     | 
| 
      
 7 
     | 
    
         
            +
            else
         
     | 
| 
      
 8 
     | 
    
         
            +
              require "#{Gem.loaded_specs["sorbet-eraser"].gem_dir}/lib/t"
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,9 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       2 
2 
     | 
    
         
             
              class InitializesMocktail
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                def init
         
     | 
| 
       4 
6 
     | 
    
         
             
                  [
         
     | 
| 
       5 
7 
     | 
    
         
             
                    Mocktail::Matchers::Any,
         
     | 
| 
       6 
8 
     | 
    
         
             
                    Mocktail::Matchers::Includes,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    Mocktail::Matchers::IncludesString,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    Mocktail::Matchers::IncludesKey,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    Mocktail::Matchers::IncludesHash,
         
     | 
| 
       7 
12 
     | 
    
         
             
                    Mocktail::Matchers::IsA,
         
     | 
| 
       8 
13 
     | 
    
         
             
                    Mocktail::Matchers::Matches,
         
     | 
| 
       9 
14 
     | 
    
         
             
                    Mocktail::Matchers::Not,
         
     | 
| 
         @@ -1,12 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail
         
     | 
| 
       2 
2 
     | 
    
         
             
              class MatcherPresentation
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                def respond_to_missing?(name, include_private = false)
         
     | 
| 
       4 
6 
     | 
    
         
             
                  !!MatcherRegistry.instance.get(name) || super
         
     | 
| 
       5 
7 
     | 
    
         
             
                end
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                def method_missing(name, *args, **kwargs, &blk)
         
     | 
| 
      
 9 
     | 
    
         
            +
                def method_missing(name, *args, **kwargs, &blk) # standard:disable Style/ArgumentsForwarding
         
     | 
| 
       8 
10 
     | 
    
         
             
                  if (matcher = MatcherRegistry.instance.get(name))
         
     | 
| 
       9 
     | 
    
         
            -
                    matcher.new(*args, **kwargs, &blk)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    matcher.new(*args, **kwargs, &blk) # standard:disable Style/ArgumentsForwarding
         
     | 
| 
       10 
12 
     | 
    
         
             
                  else
         
     | 
| 
       11 
13 
     | 
    
         
             
                    super
         
     | 
| 
       12 
14 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -1,12 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail::Matchers
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Any < Base
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                def self.matcher_name
         
     | 
| 
       4 
6 
     | 
    
         
             
                  :any
         
     | 
| 
       5 
7 
     | 
    
         
             
                end
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                 
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                def initialize # standard:disable Style/RedundantInitialize
         
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Empty initialize is necessary b/c Base default expects an argument
         
     | 
| 
       10 
11 
     | 
    
         
             
                end
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
                def match?(actual)
         
     | 
| 
         @@ -1,17 +1,25 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Mocktail::Matchers
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Base
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 4 
     | 
    
         
            +
                extend T::Helpers
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                if Mocktail::TYPED && T::Private::RuntimeLevels.default_checked_level != :never
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       3 
10 
     | 
    
         
             
                # Custom matchers can receive any args, kwargs, or block they want. Usually
         
     | 
| 
       4 
11 
     | 
    
         
             
                # single-argument, though, so that's defaulted here and in #insepct
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       5 
13 
     | 
    
         
             
                def initialize(expected)
         
     | 
| 
       6 
14 
     | 
    
         
             
                  @expected = expected
         
     | 
| 
       7 
15 
     | 
    
         
             
                end
         
     | 
| 
       8 
16 
     | 
    
         | 
| 
       9 
17 
     | 
    
         
             
                def self.matcher_name
         
     | 
| 
       10 
     | 
    
         
            -
                  raise Mocktail:: 
     | 
| 
      
 18 
     | 
    
         
            +
                  raise Mocktail::InvalidMatcherError.new("The `matcher_name` class method must return a valid method name")
         
     | 
| 
       11 
19 
     | 
    
         
             
                end
         
     | 
| 
       12 
20 
     | 
    
         | 
| 
       13 
21 
     | 
    
         
             
                def match?(actual)
         
     | 
| 
       14 
     | 
    
         
            -
                  raise Mocktail:: 
     | 
| 
      
 22 
     | 
    
         
            +
                  raise Mocktail::InvalidMatcherError.new("Matchers must implement `match?(argument)`")
         
     | 
| 
       15 
23 
     | 
    
         
             
                end
         
     | 
| 
       16 
24 
     | 
    
         | 
| 
       17 
25 
     | 
    
         
             
                def inspect
         
     | 
| 
         @@ -13,7 +13,11 @@ module Mocktail::Matchers 
     | 
|
| 
       13 
13 
     | 
    
         
             
              # See Mockito, which is the earliest implementation I know of:
         
     | 
| 
       14 
14 
     | 
    
         
             
              # https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Captor.html
         
     | 
| 
       15 
15 
     | 
    
         
             
              class Captor
         
     | 
| 
      
 16 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       16 
18 
     | 
    
         
             
                class Capture < Mocktail::Matchers::Base
         
     | 
| 
      
 19 
     | 
    
         
            +
                  extend T::Sig
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       17 
21 
     | 
    
         
             
                  def self.matcher_name
         
     | 
| 
       18 
22 
     | 
    
         
             
                    :capture
         
     | 
| 
       19 
23 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -40,7 +44,12 @@ module Mocktail::Matchers 
     | 
|
| 
       40 
44 
     | 
    
         
             
                  end
         
     | 
| 
       41 
45 
     | 
    
         
             
                end
         
     | 
| 
       42 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
                # This T.untyped is intentional. Even though a Capture is surely returned,
         
     | 
| 
      
 48 
     | 
    
         
            +
                # in order for a verification demonstration to pass its own type check,
         
     | 
| 
      
 49 
     | 
    
         
            +
                # it needs to think it's being returned whatever parameter is expected
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
       43 
51 
     | 
    
         
             
                attr_reader :capture
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
       44 
53 
     | 
    
         
             
                def initialize
         
     | 
| 
       45 
54 
     | 
    
         
             
                  @capture = Capture.new
         
     | 
| 
       46 
55 
     | 
    
         
             
                end
         
     |