rspec-sleeping_king_studios 2.4.0 → 2.6.0.rc.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/CHANGELOG.md +42 -0
- data/DEVELOPMENT.md +9 -18
- data/README.md +223 -2
- data/lib/rspec/sleeping_king_studios/examples/rspec_matcher_examples.rb +8 -4
- data/lib/rspec/sleeping_king_studios/matchers/built_in/include_matcher.rb +6 -1
- data/lib/rspec/sleeping_king_studios/matchers/built_in/respond_to_matcher.rb +10 -10
- data/lib/rspec/sleeping_king_studios/matchers/core/be_a_uuid.rb +17 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/be_a_uuid_matcher.rb +81 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/deep_match.rb +11 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/deep_matcher.rb +219 -0
- data/lib/rspec/sleeping_king_studios/matchers/core/delegate_method_matcher.rb +22 -9
- data/lib/rspec/sleeping_king_studios/matchers/core/have_changed_matcher.rb +5 -3
- data/lib/rspec/sleeping_king_studios/support/value_spy.rb +32 -7
- data/lib/rspec/sleeping_king_studios/version.rb +8 -8
- metadata +46 -15
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8dd0e24014dae4f35a224e0c8d244b7e20d827d22c88ddf966f7ae201b1e148e
         | 
| 4 | 
            +
              data.tar.gz: c3658bf3b7261942c9e166df38b7fdf5639688a074ca3a4cfb70992ae2698c8e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 8fb97318df347582c4204f6a7f61e359d87069952f24ef72e928514e97d84fd82372d77300c7e1f01669fea239526ea6021d28be151f49177939ad0760755667
         | 
| 7 | 
            +
              data.tar.gz: 2e1da21ffdd79e55c5891dd3a7f10247bb5aed761454ee1f7898275788cc77022ac3d0deefe3ac4a7470b0696d0ee6accb94809794b72f19a410bcd16dc4f787
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,47 @@ | |
| 1 1 | 
             
            # Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 2.6.0
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Deprecated the DelegateMethod matcher. Use `rspec-mocks` expectations instead.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Deprecated the Include matcher with a block. Use the `satisfy` matcher instead.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ### Matchers
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Updated RespondToMatcher to check the arity of `#initialize` when matching against a class and the method name is `:new`.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## 2.5.1
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Fixed a compatibility issue with Rails 6 configuration objects.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ## 2.5.0
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            Added support for Ruby 2.6, dropped support for Ruby 2.3.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### Matchers
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            Implemented the BeAUuidMatcher, which expects the value to be a UUID string.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            Implemented the DeepMatcher, which recursively compares data structures.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                expect(response).to deep_match(
         | 
| 28 | 
            +
                  status: 200,
         | 
| 29 | 
            +
                  body: {
         | 
| 30 | 
            +
                    order: {
         | 
| 31 | 
            +
                      id:    an_instance_of(Integer),
         | 
| 32 | 
            +
                      total: '9.99'
         | 
| 33 | 
            +
                    }
         | 
| 34 | 
            +
                  }
         | 
| 35 | 
            +
                )
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            Improved the HaveChangedMatcher to check the before and after `hash` value. This detects when an object is replaced by different but equal object, or when the initial object is a nested data structure (e.g. an Array of Hashes) and the internal elements are modified.
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Improved failure message of the HaveChangedMatcher when given a receiver and message.
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ## 2.4.1
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            Added support for RSpec 3.8.
         | 
| 44 | 
            +
             | 
| 3 45 | 
             
            ## 2.4.0
         | 
| 4 46 |  | 
| 5 47 | 
             
            Dropped support for RSpec 3.3.
         | 
    
        data/DEVELOPMENT.md
    CHANGED
    
    | @@ -1,26 +1,13 @@ | |
| 1 1 | 
             
            # Development Notes
         | 
| 2 2 |  | 
| 3 | 
            -
            ## Version 2. | 
| 3 | 
            +
            ## Version 2.6
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            - when skipping a shared example group with xinclude_examples, the generated context is named '(focused)'. It should instead be named '(skipped)'.
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            ### Features
         | 
| 10 | 
            -
             | 
| 11 | 
            -
            - Alias `have_reader`, etc as `define_reader`.
         | 
| 12 | 
            -
              - Also alias shared examples.
         | 
| 13 | 
            -
            - Alias `have_constant` as `define_constant`.
         | 
| 14 | 
            -
              - Alias #immutable as #frozen.
         | 
| 15 | 
            -
              - Also alias shared examples.
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            ### Maintenance
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            - Refactor all macro specs from spec/rspec/sleeping_king_studios/matchers/macros to the directories corresponding to the source files.
         | 
| 5 | 
            +
            - Deprecate `delegate_method` matcher, `include` matcher with block.
         | 
| 20 6 |  | 
| 21 7 | 
             
            ## Version 3.0
         | 
| 22 8 |  | 
| 23 9 | 
             
            - Extract out Rails-specific matchers to RSpec::SleepingKingStudios::Rails.
         | 
| 10 | 
            +
              - Drop Rails 3 support.
         | 
| 24 11 | 
             
            - Refactor property, constant matchers to Define$1Matcher.
         | 
| 25 12 | 
             
              - HaveConstantMatcher, HaveReaderMatcher, HavePredicateMatcher, HavePropertyMatcher, HaveWriterMatcher.
         | 
| 26 13 | 
             
              - Designate define_* macros as primary, have_* as aliases.
         | 
| @@ -31,6 +18,12 @@ | |
| 31 18 |  | 
| 32 19 | 
             
            ## Future Tasks
         | 
| 33 20 |  | 
| 21 | 
            +
            - DeepMatcher: |
         | 
| 22 | 
            +
              - indifferent - symbol/string keys
         | 
| 23 | 
            +
              - ordered - pre-sort arrays?
         | 
| 24 | 
            +
                - only homogenous arrays?
         | 
| 25 | 
            +
                - odd results unless equality comparison
         | 
| 26 | 
            +
             | 
| 34 27 | 
             
            ### Bug Fixes
         | 
| 35 28 |  | 
| 36 29 | 
             
            - false negative on #alias_method?
         | 
| @@ -58,8 +51,6 @@ | |
| 58 51 |  | 
| 59 52 | 
             
            - BeImmutableMatcher (NEW):
         | 
| 60 53 | 
             
              - Implement be_immutable matcher.
         | 
| 61 | 
            -
            - HaveChangedMatcher (NEW):
         | 
| 62 | 
            -
              - Add spy+matcher for expect(my_object, :my_method).to have_changed ?
         | 
| 63 54 | 
             
            - RespondToMatcher:
         | 
| 64 55 | 
             
              - Implement RespondToMatcher#with_optional_keywords, #with_required_keywords.
         | 
| 65 56 | 
             
              - Implement RespondToMatcher#with_at_least(N).arguments, equivalent to with(N).arguments.and_unlimited_arguments.
         | 
    
        data/README.md
    CHANGED
    
    | @@ -8,9 +8,9 @@ RSpec::SleepingKingStudios is tested against RSpec 3.3 through 3.7. | |
| 8 8 |  | 
| 9 9 | 
             
            Currently, the following versions of Ruby are officially supported:
         | 
| 10 10 |  | 
| 11 | 
            -
            * 2.3
         | 
| 12 11 | 
             
            * 2.4
         | 
| 13 12 | 
             
            * 2.5
         | 
| 13 | 
            +
            * 2.6
         | 
| 14 14 |  | 
| 15 15 | 
             
            For Ruby 2.0 support, use version 2.1 or earlier: `gem "rspec-sleeping_king_studios", "~> 2.1.1"`.
         | 
| 16 16 |  | 
| @@ -322,7 +322,166 @@ A simplified syntax for re-using shared context or examples without having to ex | |
| 322 322 |  | 
| 323 323 | 
             
            (also `::xwrap_context`) A shortcut for wrapping the context or examples in an automatically-skipped `describe` block, similar to the built-in `xit` and `xdescribe` methods.
         | 
| 324 324 |  | 
| 325 | 
            -
            ##  | 
| 325 | 
            +
            ## Contracts
         | 
| 326 | 
            +
             | 
| 327 | 
            +
            ```ruby
         | 
| 328 | 
            +
            require 'rspec/sleepingkingstudios/contract'
         | 
| 329 | 
            +
            ```
         | 
| 330 | 
            +
             | 
| 331 | 
            +
            A Contract encapsulates a set of RSpec expectations, which can then be used when defining a spec.
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            ```ruby
         | 
| 334 | 
            +
            module GreetContract
         | 
| 335 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 336 | 
            +
             | 
| 337 | 
            +
              describe '#greet' do
         | 
| 338 | 
            +
                it { expect(subject).to respond_to(:greet).with(1).argument }
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                it { expect(subject.greet 'programs').to be == 'Greetings, programs!' }
         | 
| 341 | 
            +
              end
         | 
| 342 | 
            +
            end
         | 
| 343 | 
            +
             | 
| 344 | 
            +
            RSpec.describe Greeter do
         | 
| 345 | 
            +
              include GreetContract
         | 
| 346 | 
            +
            end
         | 
| 347 | 
            +
            ```
         | 
| 348 | 
            +
             | 
| 349 | 
            +
            Using a contract allows for examples to be shared between different specs, or even between projects.
         | 
| 350 | 
            +
             | 
| 351 | 
            +
            ### Contract Methods
         | 
| 352 | 
            +
             | 
| 353 | 
            +
            Not all RSpec methods are defined in a Contract. Only methods that define an example (`it`) or an example group (`context` or `describe`) can be used at the top level of a Contract. However, all RSpec methods (including methods that modify the current scope, such as `let` and the `before`/`around`/`after` filters) can be used inside an example group as normal.
         | 
| 354 | 
            +
             | 
| 355 | 
            +
            #### `::context`
         | 
| 356 | 
            +
             | 
| 357 | 
            +
            Defines an example group inside the contract. This example group will be defined on all specs that include the contract.
         | 
| 358 | 
            +
             | 
| 359 | 
            +
            ```ruby
         | 
| 360 | 
            +
            module TransformationContract
         | 
| 361 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 362 | 
            +
             | 
| 363 | 
            +
              context 'when the moon is full' do
         | 
| 364 | 
            +
                let(:moon_phase) { :full }
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                it { expect(werewolf).to be_transformed }
         | 
| 367 | 
            +
              end
         | 
| 368 | 
            +
            end
         | 
| 369 | 
            +
            ```
         | 
| 370 | 
            +
             | 
| 371 | 
            +
            #### `::describe`
         | 
| 372 | 
            +
             | 
| 373 | 
            +
            Defines an example group inside the contract. This example group will be defined on all specs that include the contract.
         | 
| 374 | 
            +
             | 
| 375 | 
            +
            ```ruby
         | 
| 376 | 
            +
            module SilverContract
         | 
| 377 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 378 | 
            +
             | 
| 379 | 
            +
              describe 'with a silver weapon' do
         | 
| 380 | 
            +
                before(:example) do
         | 
| 381 | 
            +
                  weapon.material = 'silver'
         | 
| 382 | 
            +
                end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                it 'should kill the werewolf' do
         | 
| 385 | 
            +
                  expect(attack(werewolf, weapon)).to change(werewolf, :alive?).to be false
         | 
| 386 | 
            +
                end
         | 
| 387 | 
            +
              end
         | 
| 388 | 
            +
            end
         | 
| 389 | 
            +
            ```
         | 
| 390 | 
            +
             | 
| 391 | 
            +
            #### `::it`
         | 
| 392 | 
            +
             | 
| 393 | 
            +
            Defines an example inside the contract.
         | 
| 394 | 
            +
             | 
| 395 | 
            +
            ```ruby
         | 
| 396 | 
            +
            module HowlingContract
         | 
| 397 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 398 | 
            +
             | 
| 399 | 
            +
              it { expect(werewolf).to respond_to(:howl) }
         | 
| 400 | 
            +
            end
         | 
| 401 | 
            +
            ```
         | 
| 402 | 
            +
             | 
| 403 | 
            +
            #### `::shared_context`
         | 
| 404 | 
            +
             | 
| 405 | 
            +
            Defines a shared example group.
         | 
| 406 | 
            +
             | 
| 407 | 
            +
            ```ruby
         | 
| 408 | 
            +
            module MoonContract
         | 
| 409 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 410 | 
            +
             | 
| 411 | 
            +
              shared_context 'when the moon is full' do
         | 
| 412 | 
            +
                before(:example) { moon.phase = :full }
         | 
| 413 | 
            +
              end
         | 
| 414 | 
            +
            end
         | 
| 415 | 
            +
            ```
         | 
| 416 | 
            +
             | 
| 417 | 
            +
            **Note:** When the `Contract` is included in an RSpec example group, any shared example groups defined at the top level of a contract are also included in that example group, even outside of the contract itself. This may cause namespace collisions with shared example groups defined elsewhere in the example group or by other included contracts.
         | 
| 418 | 
            +
             | 
| 419 | 
            +
            #### `::shared_examples`
         | 
| 420 | 
            +
             | 
| 421 | 
            +
            Defines a shared example group.
         | 
| 422 | 
            +
             | 
| 423 | 
            +
            ```ruby
         | 
| 424 | 
            +
            module HairContract
         | 
| 425 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 426 | 
            +
             | 
| 427 | 
            +
              shared_examples 'should be hairy' do
         | 
| 428 | 
            +
                describe '#hairy?' do
         | 
| 429 | 
            +
                  it { expect(werewolf.hairy?).to be true }
         | 
| 430 | 
            +
                end
         | 
| 431 | 
            +
              end
         | 
| 432 | 
            +
            end
         | 
| 433 | 
            +
            ```
         | 
| 434 | 
            +
             | 
| 435 | 
            +
            **Note:** When the `Contract` is included in an RSpec example group, any shared example groups defined at the top level of a contract are also included in that example group, even outside of the contract itself. This may cause namespace collisions with shared example groups defined elsewhere in the example group or by other included contracts.
         | 
| 436 | 
            +
             | 
| 437 | 
            +
            ### Developing Contracts
         | 
| 438 | 
            +
             | 
| 439 | 
            +
            ```ruby
         | 
| 440 | 
            +
            module VampireContract
         | 
| 441 | 
            +
              extend RSpec::SleepingKingStudios::Contract
         | 
| 442 | 
            +
              extend RSpec::SleepingKingStudios::Contracts::Development
         | 
| 443 | 
            +
             | 
| 444 | 
            +
              fdescribe '#drink' do
         | 
| 445 | 
            +
                it { expect(drink 'blood').to be true }
         | 
| 446 | 
            +
             | 
| 447 | 
            +
                xit { expect(drink 'holy water').to be false }
         | 
| 448 | 
            +
              end
         | 
| 449 | 
            +
             | 
| 450 | 
            +
              pending
         | 
| 451 | 
            +
            end
         | 
| 452 | 
            +
            ```
         | 
| 453 | 
            +
             | 
| 454 | 
            +
            The `RSpec::SleepingKingStudios::Contracts::Development` module provides methods for defining focused or pending examples and example groups. These are intended for use when developing a contract, and should not be included in the final version. Having skipped or focused example groups in a shared contract can have unexpected effects when the contract is included by the end user.
         | 
| 455 | 
            +
             | 
| 456 | 
            +
            #### `::fcontext`
         | 
| 457 | 
            +
             | 
| 458 | 
            +
            Defines a focused example group inside the contract.
         | 
| 459 | 
            +
             | 
| 460 | 
            +
            #### `::fdescribe`
         | 
| 461 | 
            +
             | 
| 462 | 
            +
            Defines a focused example group inside the contract.
         | 
| 463 | 
            +
             | 
| 464 | 
            +
            #### `::fit`
         | 
| 465 | 
            +
             | 
| 466 | 
            +
            Defines a focused example inside the contract.
         | 
| 467 | 
            +
             | 
| 468 | 
            +
            #### `::pending`
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            Marks the contract as pending.
         | 
| 471 | 
            +
             | 
| 472 | 
            +
            #### `::xcontext`
         | 
| 473 | 
            +
             | 
| 474 | 
            +
            Defines a skipped example group inside the contract.
         | 
| 475 | 
            +
             | 
| 476 | 
            +
            #### `::xdescribe`
         | 
| 477 | 
            +
             | 
| 478 | 
            +
            Defines a skipped example group inside the contract.
         | 
| 479 | 
            +
             | 
| 480 | 
            +
            #### `::xit`
         | 
| 481 | 
            +
             | 
| 482 | 
            +
            Defines a skipped example inside the contract.
         | 
| 483 | 
            +
             | 
| 484 | 
            +
            ## Matchers
         | 
| 326 485 |  | 
| 327 486 | 
             
            To enable a custom matcher, simply require the associated file. Matchers can be required individually or by category:
         | 
| 328 487 |  | 
| @@ -447,6 +606,22 @@ Checks if the object aliases the specified method with the specified other name. | |
| 447 606 |  | 
| 448 607 | 
             
            * **`#as`:** Required. Expects one String or Symbol, which is the name of the generated method.
         | 
| 449 608 |  | 
| 609 | 
            +
            #### `#be_a_uuid` Matcher
         | 
| 610 | 
            +
             | 
| 611 | 
            +
                require 'rspec/sleeping_king_studios/matchers/core/be_a_uuid'
         | 
| 612 | 
            +
             | 
| 613 | 
            +
            **Aliases:** `#a_uuid`.
         | 
| 614 | 
            +
             | 
| 615 | 
            +
            **How To Use:**
         | 
| 616 | 
            +
             | 
| 617 | 
            +
                # With an object comparison.
         | 
| 618 | 
            +
                expect(string).to be_a_uuid
         | 
| 619 | 
            +
             | 
| 620 | 
            +
                # Inside a composable matcher.
         | 
| 621 | 
            +
                expect(array).to include(a_uuid)
         | 
| 622 | 
            +
             | 
| 623 | 
            +
            **Parameters:** None.
         | 
| 624 | 
            +
             | 
| 450 625 | 
             
            #### `#be_boolean` Matcher
         | 
| 451 626 |  | 
| 452 627 | 
             
                require 'rspec/sleeping_king_studios/matchers/core/be_boolean'
         | 
| @@ -488,6 +663,52 @@ Verifies that the actual object can be constructed using `::new`. Can take an op | |
| 488 663 | 
             
            * **`#with_keywords`:** (also `and_keywords`) Expects one or more String or Symbol arguments. Verifies that the class's constructor accepts each provided keyword or has a variadic keyword of the form `**params`. As of 2.1.0 and required keywords, verifies that all required keywords are provided.
         | 
| 489 664 | 
             
            * **`#with_arbitrary_keywords`:** (also `and_arbitrary_keywords`) No parameters. Verifies that the class's constructor accepts any keyword arguments via a variadic keyword of the form `**params`.
         | 
| 490 665 |  | 
| 666 | 
            +
            #### `#deep_match` Matcher
         | 
| 667 | 
            +
             | 
| 668 | 
            +
                require 'rspec/sleeping_king_studios/matchers/core/deep_match'
         | 
| 669 | 
            +
             | 
| 670 | 
            +
            Performs a recursive comparison between two object or data structures. Also supports using RSpec matchers as values in the expected object.
         | 
| 671 | 
            +
             | 
| 672 | 
            +
            **How To Use:**
         | 
| 673 | 
            +
             | 
| 674 | 
            +
                expected = {
         | 
| 675 | 
            +
                  status: 200,
         | 
| 676 | 
            +
                  body: {
         | 
| 677 | 
            +
                    order: {
         | 
| 678 | 
            +
                      id:    an_instance_of(Integer),
         | 
| 679 | 
            +
                      total: '9.99'
         | 
| 680 | 
            +
                    }
         | 
| 681 | 
            +
                  }
         | 
| 682 | 
            +
                }
         | 
| 683 | 
            +
                expect(response).to deep_match(expected)
         | 
| 684 | 
            +
             | 
| 685 | 
            +
            When the value does not match the expectation, the failure message will provide a detailed diff showing added, missing, and changed values.
         | 
| 686 | 
            +
             | 
| 687 | 
            +
                response = {
         | 
| 688 | 
            +
                  status: 400,
         | 
| 689 | 
            +
                  body: {
         | 
| 690 | 
            +
                    order: {
         | 
| 691 | 
            +
                      fulfilled: false,
         | 
| 692 | 
            +
                      total:     '19.99'
         | 
| 693 | 
            +
                    }
         | 
| 694 | 
            +
                  },
         | 
| 695 | 
            +
                  errors: ['Insufficient funds']
         | 
| 696 | 
            +
                }
         | 
| 697 | 
            +
                expect(response).to deep_match(expected)
         | 
| 698 | 
            +
                # Failure/Error: expect(response).to deep_match(expected)
         | 
| 699 | 
            +
                #
         | 
| 700 | 
            +
                #   expected: == {:body=>{:order=>{:id=>an instance of Integer, :total=>"9.99"}}, :status=>200}
         | 
| 701 | 
            +
                #        got:    {:status=>400, :body=>{:order=>{:fulfilled=>false, :total=>"19.99"}}, :errors=>["Insufficient funds"]}
         | 
| 702 | 
            +
                #
         | 
| 703 | 
            +
                #   (compared using HashDiff)
         | 
| 704 | 
            +
                #
         | 
| 705 | 
            +
                #   Diff:
         | 
| 706 | 
            +
                #   + :body.:order.:fulfilled => got false
         | 
| 707 | 
            +
                #   - :body.:order.:id => expected an instance of Integer
         | 
| 708 | 
            +
                #   ~ :body.:order.:total => expected "9.99", got "19.99"
         | 
| 709 | 
            +
                #   + :errors => got ["Insufficient funds"]
         | 
| 710 | 
            +
                #   ~ :status => expected 200, got 400
         | 
| 711 | 
            +
             | 
| 491 712 | 
             
            #### `#delegate_method` Matcher
         | 
| 492 713 |  | 
| 493 714 | 
             
                require 'rspec/sleeping_king_studios/matchers/core/delegate_method'
         | 
| @@ -8,14 +8,14 @@ require 'rspec/sleeping_king_studios/matchers/base_matcher' | |
| 8 8 | 
             
            module RSpec::SleepingKingStudios::Examples::RSpecMatcherExamples
         | 
| 9 9 | 
             
              extend RSpec::SleepingKingStudios::Concerns::SharedExampleGroup
         | 
| 10 10 |  | 
| 11 | 
            -
              private def  | 
| 11 | 
            +
              private def rspec_config
         | 
| 12 12 | 
             
                RSpec.configuration.sleeping_king_studios
         | 
| 13 | 
            -
              end # method  | 
| 13 | 
            +
              end # method rspec_config
         | 
| 14 14 |  | 
| 15 15 | 
             
              private def compare_message actual, expected
         | 
| 16 16 | 
             
                case expected
         | 
| 17 17 | 
             
                when String
         | 
| 18 | 
            -
                  if  | 
| 18 | 
            +
                  if rspec_config.examples.match_string_failure_message_as == :exact
         | 
| 19 19 | 
             
                    expect(actual).to be == expected
         | 
| 20 20 | 
             
                  else
         | 
| 21 21 | 
             
                    expect(actual).to include expected
         | 
| @@ -29,8 +29,12 @@ module RSpec::SleepingKingStudios::Examples::RSpecMatcherExamples | |
| 29 29 | 
             
                end # when
         | 
| 30 30 | 
             
              end # method compare_message
         | 
| 31 31 |  | 
| 32 | 
            +
              private def format_expected(object)
         | 
| 33 | 
            +
                RSpec::Support::ObjectFormatter.format(object)
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 32 36 | 
             
              private def handle_missing_failure_message message
         | 
| 33 | 
            -
                case  | 
| 37 | 
            +
                case rspec_config.examples.handle_missing_failure_message_with
         | 
| 34 38 | 
             
                when :pending
         | 
| 35 39 | 
             
                  skip message
         | 
| 36 40 | 
             
                when :exception
         | 
| @@ -17,7 +17,12 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn | |
| 17 17 | 
             
                #   should return true if and only if the item matches the desired
         | 
| 18 18 | 
             
                #   predicate.
         | 
| 19 19 | 
             
                def initialize *expected, &block
         | 
| 20 | 
            -
                   | 
| 20 | 
            +
                  if block_given?
         | 
| 21 | 
            +
                    SleepingKingStudios::Tools::CoreTools
         | 
| 22 | 
            +
                      .deprecate('IncludeMatcher with a block')
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      expected << block
         | 
| 25 | 
            +
                  end
         | 
| 21 26 |  | 
| 22 27 | 
             
                  if expected.empty? && !allow_empty_matcher?
         | 
| 23 28 | 
             
                    raise ArgumentError,
         | 
| @@ -87,18 +87,18 @@ module RSpec::SleepingKingStudios::Matchers::BuiltIn | |
| 87 87 |  | 
| 88 88 | 
             
                    method =
         | 
| 89 89 | 
             
                      begin
         | 
| 90 | 
            -
                        actual. | 
| 90 | 
            +
                        if actual.is_a?(Class) && method_name.intern == :new
         | 
| 91 | 
            +
                          actual.instance_method(:initialize)
         | 
| 92 | 
            +
                        else
         | 
| 93 | 
            +
                          actual.method(method_name)
         | 
| 94 | 
            +
                        end
         | 
| 91 95 | 
             
                      rescue NameError
         | 
| 92 | 
            -
                         | 
| 93 | 
            -
             | 
| 96 | 
            +
                        @failing_method_reasons[method_name] = {
         | 
| 97 | 
            +
                          :is_not_a_method => true
         | 
| 98 | 
            +
                        } # end hash
         | 
| 94 99 |  | 
| 95 | 
            -
             | 
| 96 | 
            -
                       | 
| 97 | 
            -
                        :is_not_a_method => true
         | 
| 98 | 
            -
                      } # end hash
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                      next false
         | 
| 101 | 
            -
                    end # unless
         | 
| 100 | 
            +
                        next false
         | 
| 101 | 
            +
                      end
         | 
| 102 102 |  | 
| 103 103 | 
             
                    next true unless method_signature_expectation?
         | 
| 104 104 |  | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rspec/sleeping_king_studios/matchers/core/be_a_uuid_matcher'
         | 
| 4 | 
            +
            require 'rspec/sleeping_king_studios/matchers/macros'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RSpec::SleepingKingStudios::Matchers::Macros
         | 
| 7 | 
            +
              # @see RSpec::SleepingKingStudios::Matchers::Core::BeAUuidMatcher#matches?
         | 
| 8 | 
            +
              def be_a_uuid
         | 
| 9 | 
            +
                RSpec::SleepingKingStudios::Matchers::Core::BeAUuidMatcher.new
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # @!method a_uuid
         | 
| 13 | 
            +
              #   @see RSpec::SleepingKingStudios::Matchers::Core::BeBooleanMatcher#matches?
         | 
| 14 | 
            +
              alias_matcher :a_uuid, :be_a_uuid do |description|
         | 
| 15 | 
            +
                'a UUID'
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rspec/sleeping_king_studios/matchers/base_matcher'
         | 
| 4 | 
            +
            require 'rspec/sleeping_king_studios/matchers/core'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RSpec::SleepingKingStudios::Matchers::Core
         | 
| 7 | 
            +
              # Matcher for testing whether an object is a UUID string.
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # @since 2.5.0
         | 
| 10 | 
            +
              class BeAUuidMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
         | 
| 11 | 
            +
                # (see BaseMatcher#description)
         | 
| 12 | 
            +
                def description
         | 
| 13 | 
            +
                  'be a UUID'
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                # (see BaseMatcher#failure_message)
         | 
| 17 | 
            +
                def failure_message
         | 
| 18 | 
            +
                  message = super() + ', but '
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  return message + 'was not a String' unless string?
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  return message + 'was too short' if too_short?
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  return message + 'was too long' if too_long?
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  return message + 'has invalid characters' if invalid_characters?
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  return message + 'the format is invalid' unless valid_format?
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  message
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                # Checks if the object is a UUID string.
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # @param [Object] actual The object to check.
         | 
| 36 | 
            +
                #
         | 
| 37 | 
            +
                # @return [Boolean] true if the object is a string with the correct format;
         | 
| 38 | 
            +
                #   otherwise false.
         | 
| 39 | 
            +
                def matches?(actual)
         | 
| 40 | 
            +
                  super
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  string? && valid_length? && valid_characters? && valid_format?
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                private
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def invalid_characters?
         | 
| 48 | 
            +
                  @invalid_characters ||= @actual.match?(/[^A-Fa-f0-9\-]/)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def string?
         | 
| 52 | 
            +
                  @string ||= @actual.is_a?(String)
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def too_long?
         | 
| 56 | 
            +
                  @too_long ||= @actual.length > 36
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def too_short?
         | 
| 60 | 
            +
                  @too_short ||= @actual.length < 36
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def uuid_format
         | 
| 64 | 
            +
                  chars = '[A-Fa-f0-9\-]'
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  /\A#{chars}{8}-#{chars}{4}-#{chars}{4}-#{chars}{4}-#{chars}{12}\z/
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def valid_characters?
         | 
| 70 | 
            +
                  !invalid_characters?
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def valid_format?
         | 
| 74 | 
            +
                  @valid_format || @actual.match?(uuid_format)
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def valid_length?
         | 
| 78 | 
            +
                  !too_short? && !too_long?
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rspec/sleeping_king_studios/matchers/core/deep_matcher'
         | 
| 4 | 
            +
            require 'rspec/sleeping_king_studios/matchers/macros'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RSpec::SleepingKingStudios::Matchers::Macros
         | 
| 7 | 
            +
              # @see RSpec::SleepingKingStudios::Matchers::Core::BeBooleanMatcher#matches?
         | 
| 8 | 
            +
              def deep_match(expected)
         | 
| 9 | 
            +
                RSpec::SleepingKingStudios::Matchers::Core::DeepMatcher.new(expected)
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
| @@ -0,0 +1,219 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'hashdiff'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'rspec/sleeping_king_studios/matchers/base_matcher'
         | 
| 6 | 
            +
            require 'rspec/sleeping_king_studios/matchers/core'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module RSpec::SleepingKingStudios::Matchers::Core
         | 
| 9 | 
            +
              # Matcher for performing a deep comparison between two objects.
         | 
| 10 | 
            +
              #
         | 
| 11 | 
            +
              # @since 2.5.0
         | 
| 12 | 
            +
              class DeepMatcher < RSpec::SleepingKingStudios::Matchers::BaseMatcher
         | 
| 13 | 
            +
                # @param [Object] expected The expected object.
         | 
| 14 | 
            +
                def initialize(expected)
         | 
| 15 | 
            +
                  @expected = expected
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # (see BaseMatcher#description)
         | 
| 19 | 
            +
                def description
         | 
| 20 | 
            +
                  "match #{format_expected(@expected)}"
         | 
| 21 | 
            +
                end # method description
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                # Inverse of #matches? method.
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # @param [Object] actual The object to check.
         | 
| 26 | 
            +
                #
         | 
| 27 | 
            +
                # @return [Boolean] true if the actual object does not match the
         | 
| 28 | 
            +
                #   expectation, otherwise true.
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                # @see #matches?
         | 
| 31 | 
            +
                def does_not_match? actual
         | 
| 32 | 
            +
                  super
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  if matcher?(@expected)
         | 
| 35 | 
            +
                    delegate_to_negated_matcher(@expected)
         | 
| 36 | 
            +
                  elsif @expected.is_a?(Array) && actual.is_a?(Array)
         | 
| 37 | 
            +
                    diff_arrays_negated
         | 
| 38 | 
            +
                  elsif @expected.is_a?(Hash) && actual.is_a?(Hash)
         | 
| 39 | 
            +
                    diff_hashes_negated
         | 
| 40 | 
            +
                  else
         | 
| 41 | 
            +
                    delegate_to_negated_matcher(equality_matcher)
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  !@matches
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                # (see BaseMatcher#failure_message)
         | 
| 48 | 
            +
                def failure_message
         | 
| 49 | 
            +
                  @failure_message
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                # (see BaseMatcher#failure_message_when_negated)
         | 
| 53 | 
            +
                def failure_message_when_negated
         | 
| 54 | 
            +
                  @failure_message_when_negated
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                # Performs a deep comparison between the actual object and the expected
         | 
| 58 | 
            +
                # object. The type of comparison depends on the type of the expected object:
         | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # - If the expected object is an RSpec matcher, the #matches? method on the
         | 
| 61 | 
            +
                #   matcher is called with the expected object.
         | 
| 62 | 
            +
                # - If the expected object is an Array, then each item is compared based on
         | 
| 63 | 
            +
                #   the type of the expected item.
         | 
| 64 | 
            +
                # - If the expected object is a Hash, then the keys must match and each
         | 
| 65 | 
            +
                #   value is compared based on the type of the expected value.
         | 
| 66 | 
            +
                # - Otherwise, the two objects are compared using an equality comparison.
         | 
| 67 | 
            +
                #
         | 
| 68 | 
            +
                # @param [Object] actual The object to check.
         | 
| 69 | 
            +
                #
         | 
| 70 | 
            +
                # @return [Boolean] true if the actual object matches the expectation,
         | 
| 71 | 
            +
                #   otherwise false.
         | 
| 72 | 
            +
                def matches?(actual)
         | 
| 73 | 
            +
                  super
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  if matcher?(@expected)
         | 
| 76 | 
            +
                    delegate_to_matcher(@expected)
         | 
| 77 | 
            +
                  elsif @expected.is_a?(Array) && actual.is_a?(Array)
         | 
| 78 | 
            +
                    diff_arrays
         | 
| 79 | 
            +
                  elsif @expected.is_a?(Hash)  && actual.is_a?(Hash)
         | 
| 80 | 
            +
                    diff_hashes
         | 
| 81 | 
            +
                  else
         | 
| 82 | 
            +
                    delegate_to_matcher(equality_matcher)
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  @matches
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                private
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def compare_arrays(expected, actual)
         | 
| 91 | 
            +
                  compare_hashes({ _ary: expected }, { _ary: actual })
         | 
| 92 | 
            +
                    .map { |(char, path, *values)| [char, path[1..-1], *values] }
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def compare_hashes(expected, actual)
         | 
| 96 | 
            +
                  HashDiff.diff(expected, actual, array_path: true, use_lcs: false) \
         | 
| 97 | 
            +
                  do |path, exp, act|
         | 
| 98 | 
            +
                    # Handle missing keys with matcher values.
         | 
| 99 | 
            +
                    next nil unless nested_key?(actual, path)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    next exp.matches?(act) if matcher?(exp)
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def delegate_to_matcher(matcher)
         | 
| 106 | 
            +
                  @matches = matcher.matches?(actual)
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  return if @matches
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  @failure_message = matcher.failure_message
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def delegate_to_negated_matcher(matcher)
         | 
| 114 | 
            +
                  @matches =
         | 
| 115 | 
            +
                    if matcher.respond_to?(:does_not_match?)
         | 
| 116 | 
            +
                      !matcher.does_not_match?(actual)
         | 
| 117 | 
            +
                    else
         | 
| 118 | 
            +
                      matcher.matches?(actual)
         | 
| 119 | 
            +
                    end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  return unless @matches
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  @failure_message_when_negated = matcher.failure_message_when_negated
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def diff_arrays
         | 
| 127 | 
            +
                  diff     = compare_arrays(@expected, actual)
         | 
| 128 | 
            +
                  @matches = diff.empty?
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  @failure_message = format_message(diff)
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                def diff_arrays_negated
         | 
| 134 | 
            +
                  diff     = compare_arrays(@expected, actual)
         | 
| 135 | 
            +
                  @matches = diff.empty?
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  @failure_message_when_negated =
         | 
| 138 | 
            +
                    "`expect(#{format_expected(@expected)}).not_to be == #{actual.inspect}`"
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                def diff_hashes
         | 
| 142 | 
            +
                  diff     = compare_hashes(@expected, actual)
         | 
| 143 | 
            +
                  @matches = diff.empty?
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  @failure_message = format_message(diff)
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def diff_hashes_negated
         | 
| 149 | 
            +
                  diff     = compare_hashes(@expected, actual)
         | 
| 150 | 
            +
                  @matches = diff.empty?
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  @failure_message_when_negated =
         | 
| 153 | 
            +
                    "`expect(#{format_expected(@expected)}).not_to be == #{actual.inspect}`"
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                def equality_matcher
         | 
| 157 | 
            +
                  matchers_delegate.be == @expected
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def format_diff(diff)
         | 
| 161 | 
            +
                  diff
         | 
| 162 | 
            +
                    .sort_by { |(char, path, *_values)| [path.map(&:to_s)] }
         | 
| 163 | 
            +
                    .map { |item| format_diff_item(*item) }
         | 
| 164 | 
            +
                    .join "\n"
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                def format_diff_item(char, path, *values)
         | 
| 168 | 
            +
                  "#{char} #{format_diff_path(path)} => #{format_diff_values(char, values)}"
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                def format_diff_path(path)
         | 
| 172 | 
            +
                  path.map(&:inspect).join('.')
         | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                def format_diff_values(char, values)
         | 
| 176 | 
            +
                  case char
         | 
| 177 | 
            +
                  when '-'
         | 
| 178 | 
            +
                    "expected #{format_expected(values.first)}"
         | 
| 179 | 
            +
                  when '~'
         | 
| 180 | 
            +
                    "expected #{format_expected(values.first)}, got #{values.last.inspect}"
         | 
| 181 | 
            +
                  when '+'
         | 
| 182 | 
            +
                    "got #{values.last.inspect}"
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
                end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                def format_expected(object)
         | 
| 187 | 
            +
                  RSpec::Support::ObjectFormatter.format(object)
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                def format_message(diff)
         | 
| 191 | 
            +
                  "expected: == #{format_expected(@expected)}\n" \
         | 
| 192 | 
            +
                  "     got:    #{@actual.inspect}\n" \
         | 
| 193 | 
            +
                  "\n" \
         | 
| 194 | 
            +
                  "(compared using HashDiff)\n" \
         | 
| 195 | 
            +
                  "\n" \
         | 
| 196 | 
            +
                  "Diff:\n" \
         | 
| 197 | 
            +
                  "#{format_diff(diff)}"
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                def matcher?(object)
         | 
| 201 | 
            +
                  %i[description failure_message failure_message_when_negated matches?]
         | 
| 202 | 
            +
                    .all? { |method_name| object.respond_to?(method_name) }
         | 
| 203 | 
            +
                end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                def matchers_delegate
         | 
| 206 | 
            +
                  Object.new.extend RSpec::Matchers
         | 
| 207 | 
            +
                end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                def nested_key?(object, path)
         | 
| 210 | 
            +
                  key    = path.last
         | 
| 211 | 
            +
                  object = object.dig(*path[0...-1]) if path.size > 1
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                  return object.key?(key) if object.is_a?(Hash)
         | 
| 214 | 
            +
                  return object.size > key if object.is_a?(Array)
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                  false
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
              end
         | 
| 219 | 
            +
            end
         | 
| @@ -125,6 +125,8 @@ module RSpec::SleepingKingStudios::Matchers::Core | |
| 125 125 |  | 
| 126 126 | 
             
                # (see BaseMatcher#matches?)
         | 
| 127 127 | 
             
                def matches? actual
         | 
| 128 | 
            +
                  SleepingKingStudios::Tools::CoreTools.deprecate('DelegateMethodMatcher')
         | 
| 129 | 
            +
             | 
| 128 130 | 
             
                  super
         | 
| 129 131 |  | 
| 130 132 | 
             
                  raise ArgumentError.new('must specify a target') if @target.nil?
         | 
| @@ -187,14 +189,22 @@ module RSpec::SleepingKingStudios::Matchers::Core | |
| 187 189 |  | 
| 188 190 | 
             
                private
         | 
| 189 191 |  | 
| 190 | 
            -
                def call_method | 
| 192 | 
            +
                def call_method(arguments:, keywords:, expected_return: DEFAULT_EXPECTED_RETURN)
         | 
| 191 193 | 
             
                  if @expected_block
         | 
| 192 194 | 
             
                    @received_block = false
         | 
| 193 195 | 
             
                    block           = ->(*args, **kwargs, &block) {}
         | 
| 194 196 |  | 
| 195 | 
            -
                     | 
| 197 | 
            +
                    if keywords.empty?
         | 
| 198 | 
            +
                      return_value = @actual.send(@expected, *arguments, &block)
         | 
| 199 | 
            +
                    else
         | 
| 200 | 
            +
                      return_value = @actual.send(@expected, *arguments, **keywords, &block)
         | 
| 201 | 
            +
                    end
         | 
| 196 202 | 
             
                  else
         | 
| 197 | 
            -
                     | 
| 203 | 
            +
                    if keywords.empty?
         | 
| 204 | 
            +
                      return_value = @actual.send(@expected, *arguments)
         | 
| 205 | 
            +
                    else
         | 
| 206 | 
            +
                      return_value = @actual.send(@expected, *arguments, **keywords)
         | 
| 207 | 
            +
                    end
         | 
| 198 208 | 
             
                  end
         | 
| 199 209 |  | 
| 200 210 | 
             
                  @received_return_values << return_value
         | 
| @@ -215,19 +225,22 @@ module RSpec::SleepingKingStudios::Matchers::Core | |
| 215 225 | 
             
                def delegates_method?
         | 
| 216 226 | 
             
                  stub_target!
         | 
| 217 227 |  | 
| 218 | 
            -
                  args = @expected_arguments.dup
         | 
| 219 | 
            -
                  args << @expected_keywords unless @expected_keywords.empty?
         | 
| 220 | 
            -
             | 
| 221 228 | 
             
                  if @expected_return_values.empty?
         | 
| 222 | 
            -
                    call_method( | 
| 229 | 
            +
                    call_method(arguments: @expected_arguments, keywords: @expected_keywords)
         | 
| 223 230 | 
             
                  else
         | 
| 224 231 | 
             
                    @expected_return_values.each do |return_value|
         | 
| 225 | 
            -
                      call_method( | 
| 232 | 
            +
                      call_method(arguments: @expected_arguments, keywords: @expected_keywords, expected_return: return_value)
         | 
| 226 233 | 
             
                    end # each
         | 
| 227 234 | 
             
                  end # if-else
         | 
| 228 235 |  | 
| 229 236 | 
             
                  matcher = RSpec::Mocks::Matchers::HaveReceived.new(@expected)
         | 
| 230 | 
            -
                   | 
| 237 | 
            +
                  if !@expected_arguments.empty? && !@expected_keywords.empty?
         | 
| 238 | 
            +
                    matcher = matcher.with(*@expected_arguments, **@expected_keywords)
         | 
| 239 | 
            +
                  elsif !@expected_arguments.empty?
         | 
| 240 | 
            +
                    matcher = matcher.with(*@expected_arguments)
         | 
| 241 | 
            +
                  elsif !@expected_keywords.empty?
         | 
| 242 | 
            +
                    matcher = matcher.with(**@expected_keywords)
         | 
| 243 | 
            +
                  end
         | 
| 231 244 |  | 
| 232 245 | 
             
                  unless @expected_return_values.empty?
         | 
| 233 246 | 
             
                    matcher = matcher.exactly(@expected_return_values.count).times
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'rspec/sleeping_king_studios/matchers/base_matcher'
         | 
| 2 4 | 
             
            require 'rspec/sleeping_king_studios/matchers/core'
         | 
| 3 5 | 
             
            require 'rspec/sleeping_king_studios/support/value_spy'
         | 
| @@ -76,7 +78,7 @@ module RSpec::SleepingKingStudios::Matchers::Core | |
| 76 78 | 
             
                  unless @match_initial_value.nil? || @match_initial_value
         | 
| 77 79 | 
             
                    return "expected #{value_spy.description} to have initially " \
         | 
| 78 80 | 
             
                      "been #{@expected_initial_value.inspect}, but was " \
         | 
| 79 | 
            -
                      "#{ | 
| 81 | 
            +
                      "#{value_spy.initial_inspect}"
         | 
| 80 82 | 
             
                  end
         | 
| 81 83 |  | 
| 82 84 | 
             
                  message = "expected #{value_spy.description} to have changed"
         | 
| @@ -107,13 +109,13 @@ module RSpec::SleepingKingStudios::Matchers::Core | |
| 107 109 | 
             
                  unless @match_initial_value.nil? || @match_initial_value
         | 
| 108 110 | 
             
                    return "expected #{value_spy.description} to have initially " \
         | 
| 109 111 | 
             
                      "been #{@expected_initial_value.inspect}, but was " \
         | 
| 110 | 
            -
                      "#{ | 
| 112 | 
            +
                      "#{value_spy.initial_inspect}"
         | 
| 111 113 | 
             
                  end
         | 
| 112 114 |  | 
| 113 115 | 
             
                  message = "expected #{value_spy.description} not to have changed"
         | 
| 114 116 |  | 
| 115 117 | 
             
                  message <<
         | 
| 116 | 
            -
                    ", but did change from #{ | 
| 118 | 
            +
                    ", but did change from #{value_spy.initial_inspect} to " <<
         | 
| 117 119 | 
             
                    current_value.inspect
         | 
| 118 120 |  | 
| 119 121 | 
             
                  message
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'rspec/sleeping_king_studios/support'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module RSpec::SleepingKingStudios::Support
         | 
| @@ -23,8 +25,8 @@ module RSpec::SleepingKingStudios::Support | |
| 23 25 | 
             
              #   value.initial_value #=> 4
         | 
| 24 26 | 
             
              #   value.current_value #=> 3
         | 
| 25 27 | 
             
              class ValueSpy
         | 
| 26 | 
            -
                # @overload initialize( | 
| 27 | 
            -
                #   @param [Object]  | 
| 28 | 
            +
                # @overload initialize(receiver, method_name)
         | 
| 29 | 
            +
                #   @param [Object] receiver The object to watch.
         | 
| 28 30 | 
             
                #
         | 
| 29 31 | 
             
                #   @param [Symbol, String] method_name The name of the method to watch.
         | 
| 30 32 | 
             
                #
         | 
| @@ -32,23 +34,34 @@ module RSpec::SleepingKingStudios::Support | |
| 32 34 | 
             
                #   @yield The value to watch. The block will be called each time the value
         | 
| 33 35 | 
             
                #     is requested, and the return value of the block will be given as the
         | 
| 34 36 | 
             
                #     current value.
         | 
| 35 | 
            -
                def initialize( | 
| 37 | 
            +
                def initialize(receiver = nil, method_name = nil, &block)
         | 
| 36 38 | 
             
                  @observed_block = if block_given?
         | 
| 37 39 | 
             
                    block
         | 
| 38 40 | 
             
                  else
         | 
| 39 41 | 
             
                    @method_name = method_name
         | 
| 40 42 |  | 
| 41 | 
            -
                    -> {  | 
| 43 | 
            +
                    -> { receiver.send(method_name) }
         | 
| 42 44 | 
             
                  end
         | 
| 43 45 |  | 
| 44 | 
            -
                  @ | 
| 46 | 
            +
                  @receiver        = receiver
         | 
| 47 | 
            +
                  @initial_hash    = current_value.hash
         | 
| 48 | 
            +
                  @initial_inspect = current_value.inspect
         | 
| 49 | 
            +
                  @initial_value   = current_value
         | 
| 45 50 | 
             
                end
         | 
| 46 51 |  | 
| 52 | 
            +
                # @return [Integer] the hash of the watched value at the time the spy was
         | 
| 53 | 
            +
                #   initialized.
         | 
| 54 | 
            +
                attr_reader :initial_hash
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # @return [String] the string representation of the watched value at the
         | 
| 57 | 
            +
                #   time the spy was initialized
         | 
| 58 | 
            +
                attr_reader :initial_inspect
         | 
| 59 | 
            +
             | 
| 47 60 | 
             
                # @return [Object] the watched value at the time the spy was initialized.
         | 
| 48 61 | 
             
                attr_reader :initial_value
         | 
| 49 62 |  | 
| 50 63 | 
             
                def changed?
         | 
| 51 | 
            -
                   | 
| 64 | 
            +
                  initial_value != current_value || initial_hash != current_value.hash
         | 
| 52 65 | 
             
                end
         | 
| 53 66 |  | 
| 54 67 | 
             
                # @return [Object] the watched value when #current_value is called.
         | 
| @@ -61,7 +74,19 @@ module RSpec::SleepingKingStudios::Support | |
| 61 74 | 
             
                def description
         | 
| 62 75 | 
             
                  return 'result' unless @method_name
         | 
| 63 76 |  | 
| 64 | 
            -
                   | 
| 77 | 
            +
                  format_message
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                private
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                attr_reader :method_name
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                attr_reader :receiver
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def format_message
         | 
| 87 | 
            +
                  return "#{receiver}.#{method_name}" if receiver.is_a?(Module)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  "#{receiver.class}##{method_name}"
         | 
| 65 90 | 
             
                end
         | 
| 66 91 | 
             
              end
         | 
| 67 92 | 
             
            end
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # frozen_string_literals: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module RSpec
         | 
| 4 4 | 
             
              module SleepingKingStudios
         | 
| @@ -11,13 +11,13 @@ module RSpec | |
| 11 11 | 
             
                  # Major version.
         | 
| 12 12 | 
             
                  MAJOR = 2
         | 
| 13 13 | 
             
                  # Minor version.
         | 
| 14 | 
            -
                  MINOR =  | 
| 14 | 
            +
                  MINOR = 6
         | 
| 15 15 | 
             
                  # Patch version.
         | 
| 16 16 | 
             
                  PATCH = 0
         | 
| 17 17 | 
             
                  # Prerelease version.
         | 
| 18 | 
            -
                  PRERELEASE =  | 
| 18 | 
            +
                  PRERELEASE = :rc
         | 
| 19 19 | 
             
                  # Build metadata.
         | 
| 20 | 
            -
                  BUILD =  | 
| 20 | 
            +
                  BUILD = 0
         | 
| 21 21 |  | 
| 22 22 | 
             
                  # Generates the gem version string from the Version constants.
         | 
| 23 23 | 
             
                  #
         | 
| @@ -36,9 +36,9 @@ module RSpec | |
| 36 36 | 
             
                    str << ".#{build}" unless build.nil? || (build.respond_to?(:empty?) && build.empty?)
         | 
| 37 37 |  | 
| 38 38 | 
             
                    str
         | 
| 39 | 
            -
                  end | 
| 40 | 
            -
                end | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 41 |  | 
| 42 42 | 
             
                VERSION = Version.to_gem_version
         | 
| 43 | 
            -
              end | 
| 44 | 
            -
            end | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rspec-sleeping_king_studios
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.6.0.rc.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Rob "Merlin" Smith
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2021-02-14 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: hashdiff
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 0.3.8
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: 0.3.8
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: rspec
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -28,16 +42,22 @@ dependencies: | |
| 28 42 | 
             
              name: sleeping_king_studios-tools
         | 
| 29 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 44 | 
             
                requirements:
         | 
| 31 | 
            -
                - - " | 
| 45 | 
            +
                - - ">="
         | 
| 32 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version:  | 
| 47 | 
            +
                    version: 0.8.0
         | 
| 48 | 
            +
                - - "<"
         | 
| 49 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 50 | 
            +
                    version: '2'
         | 
| 34 51 | 
             
              type: :runtime
         | 
| 35 52 | 
             
              prerelease: false
         | 
| 36 53 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 54 | 
             
                requirements:
         | 
| 38 | 
            -
                - - " | 
| 55 | 
            +
                - - ">="
         | 
| 56 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 57 | 
            +
                    version: 0.8.0
         | 
| 58 | 
            +
                - - "<"
         | 
| 39 59 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version: ' | 
| 60 | 
            +
                    version: '2'
         | 
| 41 61 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 62 | 
             
              name: appraisal
         | 
| 43 63 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -106,14 +126,20 @@ dependencies: | |
| 106 126 | 
             
                requirements:
         | 
| 107 127 | 
             
                - - "~>"
         | 
| 108 128 | 
             
                  - !ruby/object:Gem::Version
         | 
| 109 | 
            -
                    version: '0. | 
| 129 | 
            +
                    version: '0.4'
         | 
| 130 | 
            +
                - - ">="
         | 
| 131 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 132 | 
            +
                    version: 0.4.1
         | 
| 110 133 | 
             
              type: :development
         | 
| 111 134 | 
             
              prerelease: false
         | 
| 112 135 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 113 136 | 
             
                requirements:
         | 
| 114 137 | 
             
                - - "~>"
         | 
| 115 138 | 
             
                  - !ruby/object:Gem::Version
         | 
| 116 | 
            -
                    version: '0. | 
| 139 | 
            +
                    version: '0.4'
         | 
| 140 | 
            +
                - - ">="
         | 
| 141 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 142 | 
            +
                    version: 0.4.1
         | 
| 117 143 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 118 144 | 
             
              name: aruba
         | 
| 119 145 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -151,7 +177,7 @@ dependencies: | |
| 151 177 | 
             
                    version: '3.0'
         | 
| 152 178 | 
             
                - - "<"
         | 
| 153 179 | 
             
                  - !ruby/object:Gem::Version
         | 
| 154 | 
            -
                    version: ' | 
| 180 | 
            +
                    version: '7.0'
         | 
| 155 181 | 
             
              type: :development
         | 
| 156 182 | 
             
              prerelease: false
         | 
| 157 183 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -161,7 +187,7 @@ dependencies: | |
| 161 187 | 
             
                    version: '3.0'
         | 
| 162 188 | 
             
                - - "<"
         | 
| 163 189 | 
             
                  - !ruby/object:Gem::Version
         | 
| 164 | 
            -
                    version: ' | 
| 190 | 
            +
                    version: '7.0'
         | 
| 165 191 | 
             
            description: |2
         | 
| 166 192 | 
             
                  A collection of RSpec patches and custom matchers. The features can be
         | 
| 167 193 | 
             
                  included individually or by category. For more information, check out the
         | 
| @@ -217,10 +243,14 @@ files: | |
| 217 243 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/alias_method.rb
         | 
| 218 244 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/alias_method_matcher.rb
         | 
| 219 245 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/all.rb
         | 
| 246 | 
            +
            - lib/rspec/sleeping_king_studios/matchers/core/be_a_uuid.rb
         | 
| 247 | 
            +
            - lib/rspec/sleeping_king_studios/matchers/core/be_a_uuid_matcher.rb
         | 
| 220 248 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/be_boolean.rb
         | 
| 221 249 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/be_boolean_matcher.rb
         | 
| 222 250 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/construct.rb
         | 
| 223 251 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/construct_matcher.rb
         | 
| 252 | 
            +
            - lib/rspec/sleeping_king_studios/matchers/core/deep_match.rb
         | 
| 253 | 
            +
            - lib/rspec/sleeping_king_studios/matchers/core/deep_matcher.rb
         | 
| 224 254 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/delegate_method.rb
         | 
| 225 255 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/delegate_method_matcher.rb
         | 
| 226 256 | 
             
            - lib/rspec/sleeping_king_studios/matchers/core/have_changed.rb
         | 
| @@ -247,7 +277,9 @@ files: | |
| 247 277 | 
             
            homepage: http://sleepingkingstudios.com
         | 
| 248 278 | 
             
            licenses:
         | 
| 249 279 | 
             
            - MIT
         | 
| 250 | 
            -
            metadata: | 
| 280 | 
            +
            metadata:
         | 
| 281 | 
            +
              bug_tracker_uri: https://github.com/sleepingkingstudios/rspec-sleeping_king_studios/issues
         | 
| 282 | 
            +
              source_code_uri: https://github.com/sleepingkingstudios/rspec-sleeping_king_studios
         | 
| 251 283 | 
             
            post_install_message: 
         | 
| 252 284 | 
             
            rdoc_options: []
         | 
| 253 285 | 
             
            require_paths:
         | 
| @@ -259,12 +291,11 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 259 291 | 
             
                  version: '0'
         | 
| 260 292 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 261 293 | 
             
              requirements:
         | 
| 262 | 
            -
              - - " | 
| 294 | 
            +
              - - ">"
         | 
| 263 295 | 
             
                - !ruby/object:Gem::Version
         | 
| 264 | 
            -
                  version:  | 
| 296 | 
            +
                  version: 1.3.1
         | 
| 265 297 | 
             
            requirements: []
         | 
| 266 | 
            -
             | 
| 267 | 
            -
            rubygems_version: 2.7.6
         | 
| 298 | 
            +
            rubygems_version: 3.1.2
         | 
| 268 299 | 
             
            signing_key: 
         | 
| 269 300 | 
             
            specification_version: 4
         | 
| 270 301 | 
             
            summary: A collection of RSpec patches and custom matchers.
         |