The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.md
    │   └── feature_request.md
    └── workflows
    │   ├── ci.yml
    │   ├── dynamic-readme.yml
    │   ├── dynamic-security.yml
    │   └── rubocop.yml
├── .gitignore
├── .python-version
├── .rubocop.yml
├── .ruby-version
├── .tool-versions
├── .yardopts
├── Appraisals
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── MAINTAINING.md
├── README.md
├── REPRODUCTION_SCRIPT.rb
├── Rakefile
├── SECURITY.md
├── bin
    └── setup
├── custom_plan.rb
├── doc_config
    ├── gh-pages
    │   └── index.html.erb
    └── yard
    │   ├── setup.rb
    │   └── templates
    │       └── default
    │           ├── fulldoc
    │               └── html
    │               │   ├── css
    │               │       ├── bootstrap.css
    │               │       ├── full_list.css
    │               │       ├── global.css
    │               │       ├── solarized.css
    │               │       └── style.css
    │               │   ├── full_list.erb
    │               │   ├── full_list_class.erb
    │               │   ├── full_list_method.erb
    │               │   ├── js
    │               │       ├── app.js
    │               │       ├── full_list.js
    │               │       ├── jquery.stickyheaders.js
    │               │       └── underscore.min.js
    │               │   └── setup.rb
    │           ├── layout
    │               └── html
    │               │   ├── breadcrumb.erb
    │               │   ├── fonts.erb
    │               │   ├── footer.erb
    │               │   ├── layout.erb
    │               │   ├── search.erb
    │               │   └── setup.rb
    │           ├── method_details
    │               └── html
    │               │   └── source.erb
    │           └── module
    │               └── html
    │                   └── box_info.erb
├── docs
    └── errors
    │   └── NonCaseSwappableValueError.md
├── gemfiles
    ├── rails_6_1.gemfile
    ├── rails_6_1.gemfile.lock
    ├── rails_7_0.gemfile
    ├── rails_7_0.gemfile.lock
    ├── rails_7_1.gemfile
    ├── rails_7_1.gemfile.lock
    ├── rails_7_2.gemfile
    └── rails_7_2.gemfile.lock
├── lib
    ├── shoulda-matchers.rb
    └── shoulda
    │   ├── matchers.rb
    │   └── matchers
    │       ├── action_controller.rb
    │       ├── action_controller
    │           ├── callback_matcher.rb
    │           ├── filter_param_matcher.rb
    │           ├── flash_store.rb
    │           ├── permit_matcher.rb
    │           ├── redirect_to_matcher.rb
    │           ├── render_template_matcher.rb
    │           ├── render_with_layout_matcher.rb
    │           ├── rescue_from_matcher.rb
    │           ├── respond_with_matcher.rb
    │           ├── route_matcher.rb
    │           ├── route_params.rb
    │           ├── session_store.rb
    │           ├── set_flash_matcher.rb
    │           ├── set_session_matcher.rb
    │           └── set_session_or_flash_matcher.rb
    │       ├── active_model.rb
    │       ├── active_model
    │           ├── allow_value_matcher.rb
    │           ├── allow_value_matcher
    │           │   ├── attribute_changed_value_error.rb
    │           │   ├── attribute_does_not_exist_error.rb
    │           │   ├── attribute_setter.rb
    │           │   ├── attribute_setter_and_validator.rb
    │           │   ├── attribute_setters.rb
    │           │   ├── attribute_setters_and_validators.rb
    │           │   ├── successful_check.rb
    │           │   └── successful_setting.rb
    │           ├── comparison_matcher.rb
    │           ├── disallow_value_matcher.rb
    │           ├── errors.rb
    │           ├── have_secure_password_matcher.rb
    │           ├── helpers.rb
    │           ├── numericality_matchers.rb
    │           ├── numericality_matchers
    │           │   ├── even_number_matcher.rb
    │           │   ├── numeric_type_matcher.rb
    │           │   ├── odd_number_matcher.rb
    │           │   ├── only_integer_matcher.rb
    │           │   ├── range_matcher.rb
    │           │   └── submatchers.rb
    │           ├── qualifiers.rb
    │           ├── qualifiers
    │           │   ├── allow_blank.rb
    │           │   ├── allow_nil.rb
    │           │   ├── ignore_interference_by_writer.rb
    │           │   └── ignoring_interference_by_writer.rb
    │           ├── validate_absence_of_matcher.rb
    │           ├── validate_acceptance_of_matcher.rb
    │           ├── validate_comparison_of_matcher.rb
    │           ├── validate_confirmation_of_matcher.rb
    │           ├── validate_exclusion_of_matcher.rb
    │           ├── validate_inclusion_of_matcher.rb
    │           ├── validate_length_of_matcher.rb
    │           ├── validate_numericality_of_matcher.rb
    │           ├── validate_presence_of_matcher.rb
    │           ├── validation_matcher.rb
    │           ├── validation_matcher
    │           │   └── build_description.rb
    │           ├── validation_message_finder.rb
    │           └── validator.rb
    │       ├── active_record.rb
    │       ├── active_record
    │           ├── accept_nested_attributes_for_matcher.rb
    │           ├── association_matcher.rb
    │           ├── association_matchers.rb
    │           ├── association_matchers
    │           │   ├── counter_cache_matcher.rb
    │           │   ├── dependent_matcher.rb
    │           │   ├── inverse_of_matcher.rb
    │           │   ├── join_table_matcher.rb
    │           │   ├── model_reflection.rb
    │           │   ├── model_reflector.rb
    │           │   ├── option_verifier.rb
    │           │   ├── optional_matcher.rb
    │           │   ├── order_matcher.rb
    │           │   ├── required_matcher.rb
    │           │   ├── source_matcher.rb
    │           │   └── through_matcher.rb
    │           ├── define_enum_for_matcher.rb
    │           ├── encrypt_matcher.rb
    │           ├── have_attached_matcher.rb
    │           ├── have_db_column_matcher.rb
    │           ├── have_db_index_matcher.rb
    │           ├── have_implicit_order_column.rb
    │           ├── have_readonly_attribute_matcher.rb
    │           ├── have_rich_text_matcher.rb
    │           ├── have_secure_token_matcher.rb
    │           ├── normalize_matcher.rb
    │           ├── serialize_matcher.rb
    │           ├── uniqueness.rb
    │           ├── uniqueness
    │           │   ├── model.rb
    │           │   ├── namespace.rb
    │           │   ├── test_model_creator.rb
    │           │   └── test_models.rb
    │           └── validate_uniqueness_of_matcher.rb
    │       ├── configuration.rb
    │       ├── doublespeak.rb
    │       ├── doublespeak
    │           ├── double.rb
    │           ├── double_collection.rb
    │           ├── double_implementation_registry.rb
    │           ├── method_call.rb
    │           ├── object_double.rb
    │           ├── proxy_implementation.rb
    │           ├── stub_implementation.rb
    │           └── world.rb
    │       ├── error.rb
    │       ├── independent.rb
    │       ├── independent
    │           ├── delegate_method_matcher.rb
    │           └── delegate_method_matcher
    │           │   └── target_not_defined_error.rb
    │       ├── integrations.rb
    │       ├── integrations
    │           ├── configuration.rb
    │           ├── configuration_error.rb
    │           ├── inclusion.rb
    │           ├── libraries.rb
    │           ├── libraries
    │           │   ├── action_controller.rb
    │           │   ├── active_model.rb
    │           │   ├── active_record.rb
    │           │   ├── missing_library.rb
    │           │   ├── rails.rb
    │           │   └── routing.rb
    │           ├── rails.rb
    │           ├── registry.rb
    │           ├── test_frameworks.rb
    │           └── test_frameworks
    │           │   ├── active_support_test_case.rb
    │           │   ├── minitest_4.rb
    │           │   ├── minitest_5.rb
    │           │   ├── missing_test_framework.rb
    │           │   ├── rspec.rb
    │           │   └── test_unit.rb
    │       ├── matcher_context.rb
    │       ├── rails_shim.rb
    │       ├── routing.rb
    │       ├── util.rb
    │       ├── util
    │           └── word_wrap.rb
    │       ├── version.rb
    │       └── warn.rb
├── script
    ├── install_gems_in_all_appraisals
    ├── run_all_tests
    ├── supported_ruby_versions
    ├── update_gem_in_all_appraisals
    └── update_gems_in_all_appraisals
├── shoulda-matchers.gemspec
├── spec
    ├── acceptance
    │   ├── active_model_integration_spec.rb
    │   ├── active_record_integration_spec.rb
    │   ├── independent_matchers_spec.rb
    │   ├── multiple_libraries_integration_spec.rb
    │   └── rails_integration_spec.rb
    ├── acceptance_spec_helper.rb
    ├── doublespeak_spec_helper.rb
    ├── report_warnings.rb
    ├── spec_helper.rb
    ├── support
    │   ├── acceptance
    │   │   ├── adds_shoulda_matchers_to_project.rb
    │   │   ├── helpers.rb
    │   │   ├── helpers
    │   │   │   ├── active_model_helpers.rb
    │   │   │   ├── active_record_helpers.rb
    │   │   │   ├── array_helpers.rb
    │   │   │   ├── base_helpers.rb
    │   │   │   ├── command_helpers.rb
    │   │   │   ├── file_helpers.rb
    │   │   │   ├── gem_helpers.rb
    │   │   │   ├── minitest_helpers.rb
    │   │   │   ├── n_unit_helpers.rb
    │   │   │   ├── pluralization_helpers.rb
    │   │   │   ├── rails_migration_helpers.rb
    │   │   │   ├── rails_version_helpers.rb
    │   │   │   ├── rspec_helpers.rb
    │   │   │   ├── ruby_version_helpers.rb
    │   │   │   └── step_helpers.rb
    │   │   └── matchers
    │   │   │   ├── have_output.rb
    │   │   │   ├── indicate_number_of_tests_was_run_matcher.rb
    │   │   │   └── indicate_that_tests_were_run_matcher.rb
    │   ├── tests
    │   │   ├── bundle.rb
    │   │   ├── command_runner.rb
    │   │   ├── current_bundle.rb
    │   │   ├── database.rb
    │   │   ├── database_adapters
    │   │   │   ├── config
    │   │   │   │   ├── postgresql.yml
    │   │   │   │   └── sqlite3.yml
    │   │   │   ├── postgresql.rb
    │   │   │   └── sqlite3.rb
    │   │   ├── database_configuration.rb
    │   │   ├── database_configuration_registry.rb
    │   │   ├── filesystem.rb
    │   │   └── version.rb
    │   └── unit
    │   │   ├── active_record
    │   │       └── create_table.rb
    │   │   ├── attribute.rb
    │   │   ├── capture.rb
    │   │   ├── change_value.rb
    │   │   ├── configuration.rb
    │   │   ├── create_model_arguments
    │   │       ├── basic.rb
    │   │       ├── has_many.rb
    │   │       └── uniqueness_matcher.rb
    │   │   ├── helpers
    │   │       ├── active_model_helpers.rb
    │   │       ├── active_model_versions.rb
    │   │       ├── active_record_versions.rb
    │   │       ├── active_resource_builder.rb
    │   │       ├── allow_value_matcher_helpers.rb
    │   │       ├── application_configuration_helpers.rb
    │   │       ├── class_builder.rb
    │   │       ├── column_type_helpers.rb
    │   │       ├── confirmation_matcher_helpers.rb
    │   │       ├── controller_builder.rb
    │   │       ├── database_helpers.rb
    │   │       ├── i18n_faker.rb
    │   │       ├── mailer_builder.rb
    │   │       ├── message_helpers.rb
    │   │       ├── model_builder.rb
    │   │       ├── rails_versions.rb
    │   │       └── validation_matcher_scenario_helpers.rb
    │   │   ├── i18n.rb
    │   │   ├── load_environment.rb
    │   │   ├── matchers
    │   │       ├── deprecate.rb
    │   │       ├── fail_with_message_including_matcher.rb
    │   │       ├── fail_with_message_matcher.rb
    │   │       ├── match_against.rb
    │   │       └── print_warning_including.rb
    │   │   ├── model_creation_strategies
    │   │       ├── active_model.rb
    │   │       └── active_record.rb
    │   │   ├── model_creators.rb
    │   │   ├── model_creators
    │   │       ├── active_model.rb
    │   │       ├── active_record.rb
    │   │       ├── active_record
    │   │       │   ├── has_and_belongs_to_many.rb
    │   │       │   ├── has_many.rb
    │   │       │   └── uniqueness_matcher.rb
    │   │       └── basic.rb
    │   │   ├── rails_application.rb
    │   │   ├── record_builder_with_i18n_validation_message.rb
    │   │   ├── record_validating_confirmation_builder.rb
    │   │   ├── record_with_different_error_attribute_builder.rb
    │   │   ├── record_with_unrelated_error_builder.rb
    │   │   ├── shared_examples
    │   │       ├── ignoring_interference_by_writer.rb
    │   │       ├── numerical_submatcher.rb
    │   │       └── set_session_or_flash.rb
    │   │   └── validation_matcher_scenario.rb
    ├── unit
    │   └── shoulda
    │   │   └── matchers
    │   │       ├── action_controller
    │   │           ├── callback_matcher_spec.rb
    │   │           ├── filter_param_matcher_spec.rb
    │   │           ├── permit_matcher_spec.rb
    │   │           ├── redirect_to_matcher_spec.rb
    │   │           ├── render_template_matcher_spec.rb
    │   │           ├── render_with_layout_matcher_spec.rb
    │   │           ├── rescue_from_matcher_spec.rb
    │   │           ├── respond_with_matcher_spec.rb
    │   │           ├── route_matcher_spec.rb
    │   │           ├── route_params_spec.rb
    │   │           ├── set_flash_matcher_spec.rb
    │   │           ├── set_session_matcher_spec.rb
    │   │           └── set_session_or_flash_matcher_spec.rb
    │   │       ├── active_model
    │   │           ├── allow_value_matcher_spec.rb
    │   │           ├── disallow_value_matcher_spec.rb
    │   │           ├── have_secure_password_matcher_spec.rb
    │   │           ├── helpers_spec.rb
    │   │           ├── validate_absence_of_matcher_spec.rb
    │   │           ├── validate_acceptance_of_matcher_spec.rb
    │   │           ├── validate_comparison_of_matcher_spec.rb
    │   │           ├── validate_confirmation_of_matcher_spec.rb
    │   │           ├── validate_exclusion_of_matcher_spec.rb
    │   │           ├── validate_inclusion_of_matcher_spec.rb
    │   │           ├── validate_length_of_matcher_spec.rb
    │   │           ├── validate_numericality_of_matcher_spec.rb
    │   │           └── validate_presence_of_matcher_spec.rb
    │   │       ├── active_record
    │   │           ├── accept_nested_attributes_for_matcher_spec.rb
    │   │           ├── association_matcher_spec.rb
    │   │           ├── association_matchers
    │   │           │   └── model_reflection_spec.rb
    │   │           ├── define_enum_for_matcher_spec.rb
    │   │           ├── encrypt_matcher_spec.rb
    │   │           ├── have_attached_matcher_spec.rb
    │   │           ├── have_db_column_matcher_spec.rb
    │   │           ├── have_db_index_matcher_spec.rb
    │   │           ├── have_implicit_order_column_spec.rb
    │   │           ├── have_readonly_attributes_matcher_spec.rb
    │   │           ├── have_rich_text_matcher_spec.rb
    │   │           ├── have_secure_token_matcher_spec.rb
    │   │           ├── normalize_matcher_spec.rb
    │   │           ├── serialize_matcher_spec.rb
    │   │           └── validate_uniqueness_of_matcher_spec.rb
    │   │       ├── doublespeak
    │   │           ├── double_collection_spec.rb
    │   │           ├── double_implementation_registry_spec.rb
    │   │           ├── double_spec.rb
    │   │           ├── object_double_spec.rb
    │   │           ├── proxy_implementation_spec.rb
    │   │           ├── stub_implementation_spec.rb
    │   │           └── world_spec.rb
    │   │       ├── doublespeak_spec.rb
    │   │       ├── independent
    │   │           └── delegate_method_matcher_spec.rb
    │   │       ├── routing
    │   │           └── route_matcher_spec.rb
    │   │       └── util
    │   │           └── word_wrap_spec.rb
    └── unit_spec_helper.rb
├── tasks
    └── documentation.rb
└── zeus.json


/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: Create a report to help us improve
 4 | title: ''
 5 | labels: ''
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | <!-- By contributing to this project, you agree to abide by the thoughtbot Code
11 | of Conduct: https://thoughtbot.com/open-source-code-of-conduct -->
12 | 
13 | ### Description
14 | 
15 | <!-- A clear and concise description of what the bug is. -->
16 | 
17 | ### Reproduction Steps
18 | 
19 | <!-- Steps for others to reproduce the bug. Be as specific as possible. A
20 | reproduction script or link to a sample application that demonstrates the
21 | problem are especially helpful. -->
22 | 
23 | <!-- You can create a reproduction script by copying this sample reproduction
24 | script and adding whatever code is necessary to get a failing test case:
25 | https://github.com/thoughtbot/shoulda-matchers/blob/main/REPRODUCTION_SCRIPT.rb -->
26 | 
27 | ### Expected behavior
28 | 
29 | <!-- What you expected to happen. -->
30 | 
31 | ### Actual behavior
32 | 
33 | <!-- What happened instead. -->
34 | 
35 | ### System configuration
36 | **shoulda_matchers version**:
37 | **rails version**:
38 | **ruby version**:
39 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Feature request
 3 | about: Suggest an idea for this project
 4 | title: ''
 5 | labels: ''
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | <!-- By contributing to this project, you agree to abide by the thoughtbot Code
11 | of Conduct: https://thoughtbot.com/open-source-code-of-conduct -->
12 | 
13 | ### Problem this feature will solve
14 | 
15 | <!-- A clear and concise description of what the problem is. Ex. When doing
16 | [...] I find it difficult to [...] -->
17 | 
18 | ### Desired solution
19 | 
20 | <!-- The feature or change that would solve the problem -->
21 | 
22 | ## Alternatives considered
23 | 
24 | <!-- Any alternative solutions or features you've considered. -->
25 | 
26 | ## Additional context
27 | 
28 | <!-- Add any other context about this feature request. -->
29 | 


--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
 1 | name: Test
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |     paths-ignore:
 8 |       - '**.md'
 9 |   pull_request:
10 |     types:
11 |       - opened
12 |       - synchronize
13 |     paths-ignore:
14 |       - '**.md'
15 | 
16 | jobs:
17 |   build:
18 |     services:
19 |       postgres:
20 |         image: postgres
21 |         env:
22 |           POSTGRES_PASSWORD: postgres
23 |         ports: [ '5432:5432' ]
24 |         options: --health-cmd pg_isready --health-interval 2s --health-timeout 1s --health-retries 10
25 |     runs-on: ubuntu-latest
26 |     strategy:
27 |       fail-fast: false
28 |       matrix:
29 |         ruby:
30 |           - 3.3.0
31 |           - 3.2.2
32 |           - 3.1.4
33 |           - 3.0.6
34 |         appraisal:
35 |           - rails_7_2
36 |           - rails_7_1
37 |           - rails_7_0
38 |           - rails_6_1
39 |         adapter:
40 |           - sqlite3
41 |           - postgresql
42 |         exclude:
43 |           - { ruby: 3.3.0, appraisal: rails_6_1 }
44 |           - { ruby: 3.3.0, appraisal: rails_7_0 }
45 |           - { ruby: 3.2.2, appraisal: rails_6_1 }
46 |           - { ruby: 3.0.6, appraisal: rails_7_0 }
47 |           - { ruby: 3.0.6, appraisal: rails_7_1 }
48 |           - { ruby: 3.0.6, appraisal: rails_7_2 }
49 |     env:
50 |       DATABASE_ADAPTER: ${{ matrix.adapter }}
51 |       BUNDLE_GEMFILE: gemfiles/${{ matrix.appraisal }}.gemfile
52 |     steps:
53 |       - uses: actions/checkout@v3
54 |       - name: Set up Ruby
55 |         id: set-up-ruby
56 |         uses: ruby/setup-ruby@v1
57 |         with:
58 |           ruby-version: ${{ matrix.ruby }}
59 |       - uses: actions/cache@v3
60 |         with:
61 |           path: vendor/bundle
62 |           key: v1-rubygems-local-${{ runner.os }}-${{ matrix.ruby }}-${{ hashFiles(format('gemfiles/{0}.gemfile.lock', matrix.appraisal)) }}
63 |       - name: Install dependencies
64 |         run: bundle install --jobs=3 --retry=3
65 |       - name: Run Unit Tests
66 |         run: RUBYOPT='--enable=frozen-string-literal' bundle exec rake spec:unit --trace
67 |       - name: Run Acceptance Tests
68 |         run: RUBYOPT='--enable=frozen-string-literal' bundle exec rake spec:acceptance --trace
69 | 


--------------------------------------------------------------------------------
/.github/workflows/dynamic-readme.yml:
--------------------------------------------------------------------------------
 1 | name: update-templates
 2 | 
 3 | on: 
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |   workflow_dispatch:
 8 | 
 9 | jobs:
10 |   update-templates:
11 |     permissions:
12 |       contents: write
13 |       pull-requests: write
14 |       pages: write
15 |     uses: thoughtbot/templates/.github/workflows/dynamic-readme.yaml@main
16 |     secrets:
17 |       token: ${{ secrets.GITHUB_TOKEN }}
18 | 


--------------------------------------------------------------------------------
/.github/workflows/dynamic-security.yml:
--------------------------------------------------------------------------------
 1 | name: update-security
 2 | 
 3 | on:
 4 |   push:
 5 |     paths:
 6 |       - SECURITY.md
 7 |     branches:
 8 |       - main
 9 |   workflow_dispatch:
10 | 
11 | jobs:
12 |   update-security:
13 |     permissions:
14 |       contents: write
15 |       pull-requests: write
16 |       pages: write
17 |     uses: thoughtbot/templates/.github/workflows/dynamic-security.yaml@main
18 |     secrets:
19 |       token: ${{ secrets.GITHUB_TOKEN }}
20 | 


--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
 1 | name: RuboCop
 2 | 
 3 | on: [push, pull_request]
 4 | 
 5 | jobs:
 6 |   build:
 7 |     runs-on: ubuntu-latest
 8 | 
 9 |     steps:
10 |     - name: Checkout Repository
11 |       uses: actions/checkout@v3
12 | 
13 |     - name: Setup Ruby
14 |       uses: ruby/setup-ruby@v1
15 | 
16 |     - name: Cache gems
17 |       uses: actions/cache@v3
18 |       with:
19 |         path: ../vendor/bundle
20 |         key: ${{ runner.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
21 |         restore-keys: |
22 |           ${{ runner.os }}-rubocop-
23 | 
24 |     - name: Install gems
25 |       run: |
26 |         bundle config path ../vendor/bundle
27 |         bundle install --jobs 4 --retry 3
28 | 
29 |     - name: Run RuboCop
30 |       run: bundle exec rubocop --parallel
31 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | .bundle
 2 | .gh-pages
 3 | .thoughtbot-gh-pages
 4 | .mcmire-gh-pages
 5 | .yardoc
 6 | coverage
 7 | build
 8 | doc
 9 | pkg
10 | source
11 | spec/examples.txt
12 | tmp
13 | 


--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 2.7.15
2 | 


--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.3.0
2 | 


--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | ruby 3.3.0
2 | 


--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
 1 | --no-private
 2 | --protected
 3 | --readme README.md
 4 | --markup markdown
 5 | --hide-tag return
 6 | --hide-tag param
 7 | -e ./doc_config/yard/setup.rb
 8 | -
 9 | CHANGELOG.md
10 | docs/**/*.md
11 | 


--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
 1 | # Lines starting with '#' are comments.
 2 | # Each line is a file pattern followed by one or more owners.
 3 | 
 4 | # More details are here: https://help.github.com/articles/about-codeowners/
 5 | 
 6 | # The '*' pattern is global owners.
 7 | 
 8 | # Order is important. The last matching pattern has the most precedence.
 9 | # The folders are ordered as follows:
10 | 
11 | # In each subsection folders are ordered first by depth, then alphabetically.
12 | # This should make it easy to add new rules without breaking existing ones.
13 | 
14 | # Global rule:
15 | *           @matsales28
16 | 


--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
 1 | source 'https://rubygems.org'
 2 | 
 3 | gem 'appraisal', '2.5.0'
 4 | gem 'bundler', '~> 2.0'
 5 | gem 'pry'
 6 | gem 'pry-byebug'
 7 | gem 'rake', '13.0.1'
 8 | gem 'rspec', '~> 3.9'
 9 | gem 'rubocop', require: false
10 | gem 'rubocop-packaging', require: false
11 | gem 'rubocop-rails', require: false
12 | gem 'warnings_logger'
13 | gem 'zeus', require: false
14 | 
15 | # YARD
16 | gem 'fssm'
17 | gem 'redcarpet'
18 | gem 'rouge'
19 | gem 'yard'
20 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) Tammer Saleh and thoughtbot, inc.
 2 | 
 3 | Permission is hereby granted, free of charge, to any person
 4 | obtaining a copy of this software and associated documentation
 5 | files (the "Software"), to deal in the Software without
 6 | restriction, including without limitation the rights to use,
 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
 8 | copies of the Software, and to permit persons to whom the
 9 | Software is furnished to do so, subject to the following
10 | conditions:
11 | 
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 | 


--------------------------------------------------------------------------------
/REPRODUCTION_SCRIPT.rb:
--------------------------------------------------------------------------------
 1 | require 'bundler/inline'
 2 | 
 3 | gemfile(true) do
 4 |   source 'https://rubygems.org'
 5 |   gem 'shoulda-matchers'
 6 |   gem 'activerecord'
 7 |   gem 'sqlite3'
 8 |   gem 'rspec'
 9 | end
10 | 
11 | require 'active_record'
12 | require 'shoulda-matchers'
13 | require 'logger'
14 | 
15 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
16 | ActiveRecord::Base.logger = Logger.new(STDOUT)
17 | 
18 | # TODO: Update the schema to include the specific tables or columns necessary
19 | # to reproduce the bug
20 | ActiveRecord::Schema.define do
21 |   create_table :posts, force: true do |t|
22 |     t.string :body
23 |   end
24 | end
25 | 
26 | Shoulda::Matchers.configure do |config|
27 |   config.integrate do |with|
28 |     with.test_framework :rspec
29 | 
30 |     with.library :active_record
31 |     with.library :active_model
32 |   end
33 | end
34 | 
35 | RSpec.configure do |config|
36 |   config.include Shoulda::Matchers::ActiveRecord
37 |   config.include Shoulda::Matchers::ActiveModel
38 |   config.include Shoulda::Matchers::ActionController
39 | end
40 | 
41 | # TODO: Add any application specific code necessary to reproduce the bug
42 | class Post < ActiveRecord::Base
43 |   validates :body, uniqueness: true
44 | end
45 | 
46 | # TODO: Write a failing test case to demonstrate what isn't working as
47 | # expected
48 | RSpec.describe Post do
49 |   describe 'validations' do
50 |     it { is_expected.to validate_uniqueness_of(:body) }
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
 1 | require 'bundler/setup'
 2 | require 'bundler/gem_tasks'
 3 | require 'rspec/core/rake_task'
 4 | require 'appraisal'
 5 | require_relative 'tasks/documentation'
 6 | require_relative 'spec/support/tests/database'
 7 | require_relative 'spec/support/tests/current_bundle'
 8 | 
 9 | RSpec::Core::RakeTask.new('spec:unit') do |t|
10 |   t.ruby_opts = '-w -r ./spec/report_warnings'
11 |   t.pattern = 'spec/unit/**/*_spec.rb'
12 |   t.rspec_opts = '--color --format progress'
13 |   t.verbose = false
14 | end
15 | 
16 | RSpec::Core::RakeTask.new('spec:acceptance') do |t|
17 |   t.ruby_opts = '-w -r ./spec/report_warnings'
18 |   t.pattern = 'spec/acceptance/**/*_spec.rb'
19 |   t.rspec_opts = '--color --format progress'
20 |   t.verbose = false
21 | end
22 | 
23 | task :default do
24 |   if Tests::CurrentBundle.instance.appraisal_in_use?
25 |     sh 'rake spec:unit --trace'
26 |     sh 'rake spec:acceptance --trace'
27 |   elsif ENV['CI']
28 |     exec 'appraisal install && appraisal rake --trace'
29 |   else
30 |     appraisal = Tests::CurrentBundle.instance.latest_appraisal
31 |     exec "appraisal install && appraisal #{appraisal} rake --trace"
32 |   end
33 | end
34 | 
35 | namespace :appraisal do
36 |   task list: :environment do
37 |     appraisals = Tests::CurrentBundle.instance.available_appraisals
38 |     puts "Valid appraisals: #{appraisals.join(', ')}"
39 |   end
40 | end
41 | 
42 | Shoulda::Matchers::DocumentationTasks.create
43 | 
44 | task release: 'docs:publish_latest'
45 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | <!-- START /templates/security.md -->
 2 | # Security Policy
 3 | 
 4 | ## Supported Versions
 5 | 
 6 | Only the the latest version of this project is supported at a given time. If
 7 | you find a security issue with an older version, please try updating to the
 8 | latest version first.
 9 | 
10 | If for some reason you can't update to the latest version, please let us know
11 | your reasons so that we can have a better understanding of your situation.
12 | 
13 | ## Reporting a Vulnerability
14 | 
15 | For security inquiries or vulnerability reports, visit
16 | <https://thoughtbot.com/security>.
17 | 
18 | If you have any suggestions to improve this policy, visit <https://thoughtbot.com/security>.
19 | 
20 | <!-- END /templates/security.md -->
21 | 


--------------------------------------------------------------------------------
/doc_config/gh-pages/index.html.erb:
--------------------------------------------------------------------------------
 1 | <!doctype html>
 2 | <html>
 3 |   <head>
 4 |     <title>shoulda-matchers Documentation - latest</title>
 5 |     <meta http-equiv="refresh" content="0;URL=<%= locals[:ref] %>">
 6 |   </head>
 7 |   <body>
 8 |   </body>
 9 | </html>
10 | 


--------------------------------------------------------------------------------
/doc_config/yard/setup.rb:
--------------------------------------------------------------------------------
 1 | YARD::Templates::Engine.register_template_path(
 2 |   "#{File.dirname(__FILE__)}/templates",
 3 | )
 4 | 
 5 | require 'rouge'
 6 | 
 7 | module YARD
 8 |   module Templates
 9 |     module Helpers
10 |       module HtmlSyntaxHighlightHelper
11 |         def html_syntax_highlight_ruby(source)
12 |           highlight(:ruby, source)
13 |         end
14 | 
15 |         private
16 | 
17 |         def highlight(language, source)
18 |           lexer = Rouge::Lexers.const_get(language.capitalize)
19 |           Rouge::Formatters::HTML.new.format(lexer.new.lex(source))
20 |         end
21 |       end
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/css/full_list.css:
--------------------------------------------------------------------------------
 1 | body {
 2 |   font-size: 14px;
 3 |   padding: 20px;
 4 | }
 5 | 
 6 | h1 {
 7 |   font-size: 1.5em;
 8 | }
 9 | 
10 | .search_info, .toggle {
11 |   display: none;
12 | }
13 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/css/global.css:
--------------------------------------------------------------------------------
 1 | @import "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300italic,400,400italic,600,600italic,800|Droid+Sans+Mono";
 2 | 
 3 | body {
 4 |   font-size: 16px;
 5 |   line-height: 1.5;
 6 | }
 7 | 
 8 | a, a:hover {
 9 |   color: #136cc6;
10 | }
11 | 
12 | h1, h2, h3, h4, h5, h6, p, pre {
13 |   margin-bottom: 1em;
14 |   margin-top: 0;
15 | }
16 | 
17 | h1, h2, h3, h4, h5, h6, body {
18 |   font-family: "Source Sans Pro", sans-serif;
19 | }
20 | 
21 | h1, h2, h3, h4, h5, h6 {
22 |   font-weight: 800;
23 | }
24 | 
25 | pre, tt, code {
26 |   background: #FFFBF4;
27 |   border-radius: 3px;
28 |   border: 1px solid rgba(0,0,0,0.1);
29 |   font-family: "Droid Sans Mono", monospace;
30 |   font-size: 13px;
31 | }
32 | 
33 | pre code {
34 |   border: none;
35 | }
36 | 
37 | tt, code {
38 |   color: black;
39 |   padding: 0 4px;
40 | }
41 | 
42 | ul, ol {
43 |   margin-left: 1em;
44 |   padding-left: 1em;
45 | }
46 | 
47 | p, blockquote {
48 |   margin-bottom: 1.25em;
49 | }
50 | 
51 | blockquote {
52 |   font-style: italic;
53 |   padding-top: 0;
54 |   padding-bottom: 0;
55 |   padding-left: 1em;
56 | }
57 | 
58 | blockquote p {
59 |   font-size: inherit;
60 |   font-weight: inherit;
61 |   line-height: inherit;
62 | }
63 | 
64 | /*
65 | ul ul, ol ol, ul ol, ol ul {
66 |   margin-bottom: 1.25em;
67 | }
68 | */
69 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/full_list.erb:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2 |   "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 | <html>
 4 |   <head>
 5 |     <meta http-equiv="Content-Type" content="text/html; charset=<%= charset %>" />
 6 |     <% stylesheets_full_list.each do |stylesheet| %>
 7 |       <link rel="stylesheet" href="<%= url_for(stylesheet) %>" type="text/css" media="screen" charset="utf-8" />
 8 |     <% end %>
 9 | 
10 |     <% javascripts_full_list.each do |javascript| %>
11 |       <script type="text/javascript" charset="utf-8" src="<%= url_for(javascript) %>"></script>
12 |     <% end %>
13 | 
14 |     <title><%= @list_title %></title>
15 |     <base id="base_target" target="_parent" />
16 |   </head>
17 |   <body>
18 |     <div id="content">
19 |       <h1 id="full_list_header"><%= @list_title %></h1>
20 | 
21 |       <ul id="full_list" class="<%= @list_class || @list_type %>">
22 |         <%= erb "full_list_#{@list_type}" %>
23 |       </ul>
24 |     </div>
25 |   </body>
26 | </html>
27 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb:
--------------------------------------------------------------------------------
1 | <%= class_list %>
2 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb:
--------------------------------------------------------------------------------
1 | <% n = 1 %>
2 | <% @items.each do |item| %>
3 |   <li class="r<%= n %> <%= item.has_tag?(:deprecated) ? 'deprecated' : '' %>">
4 |     <%= linkify item, h(item.name(true)) %>
5 |     <small>(<%= item.namespace.title %>)</small>
6 |   </li>
7 |   <% n = n == 2 ? 1 : 2 %>
8 | <% end %>
9 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/js/full_list.js:
--------------------------------------------------------------------------------
1 | // Override with nothing
2 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/fulldoc/html/setup.rb:
--------------------------------------------------------------------------------
 1 | def stylesheets_full_list
 2 |   %w(css/solarized.css css/bootstrap.css css/global.css) + super
 3 | end
 4 | 
 5 | def javascripts
 6 |   javascripts = super
 7 |   javascripts.insert 1, 'js/jquery.stickyheaders.js'
 8 | end
 9 | 
10 | def class_list(root = Registry.root, tree = TreeContext.new)
11 |   out = String.new('')
12 |   children = run_verifier(root.children)
13 |   if root == Registry.root
14 |     children += @items.select {|o| o.namespace.is_a?(CodeObjects::Proxy) }
15 |   end
16 |   children.compact.sort_by(&:path).each do |child|
17 |     next unless child.is_a?(CodeObjects::NamespaceObject)
18 | 
19 |     name = child.namespace.is_a?(CodeObjects::Proxy) ? child.path : child.name
20 |     has_children = run_verifier(child.children).
21 |       any? {|o| o.is_a?(CodeObjects::NamespaceObject) }
22 |     out << "<li id='object_#{child.path}' class='#{tree.classes.join(' ')}'>"
23 |     out << "<div class='item'>"
24 |     out << "<a class='toggle'></a> " if has_children
25 |     out << linkify(child, name)
26 |     if child.is_a?(CodeObjects::ClassObject) && child.superclass
27 |       out << " &lt; #{child.superclass.name}"
28 |     end
29 |     out << "<small class='search_info'>"
30 |     out << child.namespace.title
31 |     out << '</small>'
32 |     out << '</div>'
33 |     tree.nest do
34 |       out << "<ul>#{class_list(child, tree)}</ul>" if has_children
35 |     end
36 |     out << '</li>'
37 |   end
38 |   out
39 | end
40 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/breadcrumb.erb:
--------------------------------------------------------------------------------
 1 | <div id="menu">
 2 |   <% unless @file && @file.filename == 'README.md' %>
 3 |     <span class="title">
 4 |       <%= linkify('file:README.md', 'Home') %>
 5 |     </span> &raquo;
 6 |   <% end %>
 7 |   <% if @contents || @file %>
 8 |     <span class="title"><%= @breadcrumb_title.sub(/\AFile: /, "") %></span>
 9 |   <% elsif object.is_a?(CodeObjects::Base) %>
10 |     <%= @breadcrumb.map {|obj| "<span class='title'>" + linkify(obj, obj.name) + "</span>" }.join(" &raquo; ") %>
11 |     <%= @breadcrumb.size > 0 ? " &raquo; " : "" %>
12 |     <span class="title"><%= object.root? ? "Top Level Namespace" : object.name(true) %></span>
13 |   <% end %>
14 | </div>
15 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/fonts.erb:
--------------------------------------------------------------------------------
1 | <!-- Fonts go here -->
2 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/footer.erb:
--------------------------------------------------------------------------------
1 | <div id="footer">
2 |   Generated on
3 |   <%= Time.now.strftime("%B %-d, %Y") %>
4 |   by
5 |   <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">YARD</a>.
6 | </div>
7 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/layout.erb:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 2 |   "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 3 | <html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 4 |   <head>
 5 |     <%= erb(:headers) %>
 6 |   </head>
 7 |   <body>
 8 |     <div id="header">
 9 |       <div class="header-row">
10 |         <%= erb(:breadcrumb) %>
11 |         <%= erb(:search) %>
12 |         <div class="clear"></div>
13 |       </div>
14 |     </div>
15 | 
16 |     <div id="main">
17 |       <div id="content"><%= yieldall %></div>
18 | 
19 |       <%= erb(:footer) %>
20 |     </div>
21 | 
22 |   </body>
23 | </html>
24 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/search.erb:
--------------------------------------------------------------------------------
 1 | <div id="search" class="js-search">
 2 |   <ul>
 3 |     <% menu_lists.each do |field| %>
 4 |       <li>
 5 |         <a href="<%= url_for_list(field[:type]) %>">
 6 |           <%= field[:search_title] %>
 7 |         </a>
 8 |       </li>
 9 |     <% end %>
10 |   </ul>
11 | 
12 |   <iframe id="search_frame" class="js-search-frame"></iframe>
13 | </div>
14 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/layout/html/setup.rb:
--------------------------------------------------------------------------------
 1 | def stylesheets
 2 |   %w(css/solarized.css css/bootstrap.css css/global.css) + super
 3 | end
 4 | 
 5 | def javascripts
 6 |   javascripts = super
 7 |   javascripts.insert 1, 'js/jquery.stickyheaders.js', 'js/underscore.min.js'
 8 | end
 9 | 
10 | def diskfile
11 |   @file.attributes[:markup] ||= markup_for_file('', @file.filename)
12 | 
13 |   contents =
14 |     if @file.filename == 'README.md'
15 |       preprocess_index(@file.contents)
16 |     else
17 |       @file.contents
18 |     end
19 | 
20 |   data = htmlify(contents, @file.attributes[:markup])
21 |   "<div id='filecontents'>#{data}</div>"
22 | end
23 | 
24 | def preprocess_index(contents)
25 |   regex = /\[ (\w+) \] \( lib \/ ([^()]+) \.rb (?:\#L\d+)? \)/x
26 | 
27 |   contents.gsub(regex) do
28 |     method_name = $1
29 |     file_path = $2
30 | 
31 |     module_name = file_path.split('/')[0..2].
32 |       map do |value|
33 |         value.
34 |           split('_').
35 |           map { |word| word[0].upcase + word[1..] }.
36 |           join
37 |       end.
38 |       join('::')
39 | 
40 |     "{#{module_name}##{method_name} #{method_name}}"
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/method_details/html/source.erb:
--------------------------------------------------------------------------------
 1 | <table class="source_code">
 2 |   <tr>
 3 |     <td class="lines">
 4 |       <pre><%= "\n\n\n" %><%= h format_lines(object) %></pre>
 5 |     </td>
 6 |     <td class="code">
 7 |       <pre><span class="info file"># File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %></span><%= "\n\n" %><%= html_syntax_highlight object.source %></pre>
 8 |     </td>
 9 |   </tr>
10 | </table>
11 | 


--------------------------------------------------------------------------------
/doc_config/yard/templates/default/module/html/box_info.erb:
--------------------------------------------------------------------------------
 1 | <% n = 1 %>
 2 | <dl class="box">
 3 |   <% if CodeObjects::ClassObject === object && object.superclass %>
 4 |     <dt class="r<%=n%>">Inherits:</dt>
 5 |     <dd class="r<%=n%>">
 6 |       <span class="inheritName"><%= linkify object.superclass %></span>
 7 |       <% if object.superclass.name != :BasicObject %>
 8 |         <ul class="fullTree">
 9 |           <li><%= linkify P(:Object) %></li>
10 |           <% object.inheritance_tree.reverse.each_with_index do |obj, i| %>
11 |             <li class="next"><%= obj == object ? obj.path : linkify(obj) %></li>
12 |           <% end %>
13 |         </ul>
14 |         <a href="#" class="inheritanceTree">show all</a>
15 |       <% end %>
16 |       </dd>
17 |     <% n = 2 %>
18 |   <% end %>
19 |   <% [[:class, "Extended by"], [:instance, "Includes"]].each do |scope, name| %>
20 |     <% if (mix = run_verifier(object.mixins(scope))).size > 0 %>
21 |       <dt class="r<%=n%>"><%= name %>:</dt>
22 |       <dd class="r<%=n%>"><%= mix.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
23 |       <% n = n == 2 ? 1 : 2 %>
24 |     <% end %>
25 |   <% end %>
26 |   <% if (mixed_into = mixed_into(object)).size > 0 %>
27 |     <dt class="r<%=n%>">Included in:</dt>
28 |     <dd class="r<%=n%>"><%= mixed_into.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
29 |     <% n = n == 2 ? 1 : 2 %>
30 |   <% end %>
31 | </dl>
32 | 


--------------------------------------------------------------------------------
/gemfiles/rails_6_1.gemfile:
--------------------------------------------------------------------------------
 1 | # This file was generated by Appraisal
 2 | 
 3 | source "https://rubygems.org"
 4 | 
 5 | gem "appraisal", "2.5.0"
 6 | gem "bundler", "~> 2.0"
 7 | gem "pry"
 8 | gem "pry-byebug"
 9 | gem "rake", "13.0.1"
10 | gem "rspec", "~> 3.9"
11 | gem "rubocop", require: false
12 | gem "rubocop-packaging", require: false
13 | gem "rubocop-rails", require: false
14 | gem "warnings_logger"
15 | gem "zeus", require: false
16 | gem "fssm"
17 | gem "redcarpet"
18 | gem "rouge"
19 | gem "yard"
20 | gem "spring"
21 | gem "spring-watcher-listen", "~> 2.0.0"
22 | gem "rails-controller-testing", ">= 1.0.1"
23 | gem "rails", "6.1.7.7"
24 | gem "puma", "~> 5.0"
25 | gem "sass-rails", ">= 6"
26 | gem "turbolinks", "~> 5"
27 | gem "jbuilder", "~> 2.7"
28 | gem "bcrypt", "~> 3.1.7"
29 | gem "bootsnap", ">= 1.4.4", require: false
30 | gem "rack-mini-profiler", "~> 2.0.0"
31 | gem "listen", "~> 3.3"
32 | gem "capybara", ">= 3.26"
33 | gem "selenium-webdriver", ">= 4.0.0.rc1"
34 | gem "webdrivers"
35 | gem "net-smtp", require: false
36 | gem "psych", "~> 3.0"
37 | gem "rspec-rails", "~> 6.0"
38 | gem "shoulda-context", "~> 2.0.0"
39 | gem "pg", ">= 0.18", "< 2.0"
40 | gem "sqlite3", "~> 1.4"
41 | 


--------------------------------------------------------------------------------
/gemfiles/rails_7_0.gemfile:
--------------------------------------------------------------------------------
 1 | # This file was generated by Appraisal
 2 | 
 3 | source "https://rubygems.org"
 4 | 
 5 | gem "appraisal", "2.5.0"
 6 | gem "bundler", "~> 2.0"
 7 | gem "pry"
 8 | gem "pry-byebug"
 9 | gem "rake", "13.0.1"
10 | gem "rspec", "~> 3.9"
11 | gem "rubocop", require: false
12 | gem "rubocop-packaging", require: false
13 | gem "rubocop-rails", require: false
14 | gem "warnings_logger"
15 | gem "zeus", require: false
16 | gem "fssm"
17 | gem "redcarpet"
18 | gem "rouge"
19 | gem "yard"
20 | gem "spring"
21 | gem "spring-watcher-listen", "~> 2.0.0"
22 | gem "rails-controller-testing", ">= 1.0.1"
23 | gem "rails", "7.0.8.1"
24 | gem "sprockets-rails"
25 | gem "puma", "~> 5.0"
26 | gem "importmap-rails"
27 | gem "turbo-rails"
28 | gem "stimulus-rails"
29 | gem "jbuilder"
30 | gem "bootsnap", require: false
31 | gem "capybara"
32 | gem "selenium-webdriver"
33 | gem "webdrivers"
34 | gem "rspec-rails", "~> 6.0"
35 | gem "shoulda-context", "~> 2.0.0"
36 | gem "bcrypt", "~> 3.1.7"
37 | gem "sqlite3", "~> 1.4"
38 | gem "pg", "~> 1.1"
39 | 


--------------------------------------------------------------------------------
/gemfiles/rails_7_1.gemfile:
--------------------------------------------------------------------------------
 1 | # This file was generated by Appraisal
 2 | 
 3 | source "https://rubygems.org"
 4 | 
 5 | gem "appraisal", "2.5.0"
 6 | gem "bundler", "~> 2.0"
 7 | gem "pry"
 8 | gem "pry-byebug"
 9 | gem "rake", "13.0.1"
10 | gem "rspec", "~> 3.9"
11 | gem "rubocop", require: false
12 | gem "rubocop-packaging", require: false
13 | gem "rubocop-rails", require: false
14 | gem "warnings_logger"
15 | gem "zeus", require: false
16 | gem "fssm"
17 | gem "redcarpet"
18 | gem "rouge"
19 | gem "yard"
20 | gem "spring"
21 | gem "spring-watcher-listen", "~> 2.0.0"
22 | gem "rails-controller-testing", ">= 1.0.1"
23 | gem "rails", "7.1.3.2"
24 | gem "sprockets-rails"
25 | gem "puma", "~> 6.0"
26 | gem "importmap-rails"
27 | gem "turbo-rails"
28 | gem "stimulus-rails"
29 | gem "jbuilder"
30 | gem "bootsnap", require: false
31 | gem "capybara"
32 | gem "selenium-webdriver"
33 | gem "webdrivers"
34 | gem "rspec-rails", "~> 6.0"
35 | gem "shoulda-context", "~> 2.0.0"
36 | gem "bcrypt", "~> 3.1.7"
37 | gem "sqlite3", "~> 1.4"
38 | gem "pg", "~> 1.1"
39 | 
40 | if RUBY_VERSION >= "3.1" && RUBY_VERSION < "3.2"
41 |   gem "error_highlight", ">= 0.4.0", platforms: [:ruby]
42 | end
43 | 
44 | 


--------------------------------------------------------------------------------
/gemfiles/rails_7_2.gemfile:
--------------------------------------------------------------------------------
 1 | # This file was generated by Appraisal
 2 | 
 3 | source "https://rubygems.org"
 4 | 
 5 | gem "appraisal", "2.5.0"
 6 | gem "bundler", "~> 2.0"
 7 | gem "pry"
 8 | gem "pry-byebug"
 9 | gem "rake", "13.0.1"
10 | gem "rspec", "~> 3.9"
11 | gem "rubocop", require: false
12 | gem "rubocop-packaging", require: false
13 | gem "rubocop-rails", require: false
14 | gem "warnings_logger"
15 | gem "zeus", require: false
16 | gem "fssm"
17 | gem "redcarpet"
18 | gem "rouge"
19 | gem "yard"
20 | gem "spring"
21 | gem "spring-watcher-listen", "~> 2.0.0"
22 | gem "rails-controller-testing", ">= 1.0.1"
23 | gem "rails", "~> 7.2.0"
24 | gem "brakeman", require: false
25 | gem "rubocop-rails-omakase", require: false
26 | gem "sprockets-rails"
27 | gem "puma", "~> 6.0"
28 | gem "importmap-rails"
29 | gem "turbo-rails"
30 | gem "stimulus-rails"
31 | gem "jbuilder"
32 | gem "bootsnap", require: false
33 | gem "capybara"
34 | gem "selenium-webdriver"
35 | gem "webdrivers"
36 | gem "rspec-rails", "~> 6.0"
37 | gem "shoulda-context", "~> 2.0.0"
38 | gem "bcrypt", "~> 3.1.7"
39 | gem "sqlite3", "~> 1.4"
40 | gem "pg", "~> 1.1"
41 | 
42 | if RUBY_VERSION >= "3.1" && RUBY_VERSION < "3.2"
43 |   gem "error_highlight", ">= 0.4.0", platforms: [:ruby]
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/shoulda-matchers.rb:
--------------------------------------------------------------------------------
1 | require 'shoulda/matchers'
2 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/configuration'
 2 | require 'shoulda/matchers/doublespeak'
 3 | require 'shoulda/matchers/error'
 4 | require 'shoulda/matchers/independent'
 5 | require 'shoulda/matchers/integrations'
 6 | require 'shoulda/matchers/matcher_context'
 7 | require 'shoulda/matchers/rails_shim'
 8 | require 'shoulda/matchers/util'
 9 | require 'shoulda/matchers/version'
10 | require 'shoulda/matchers/warn'
11 | 
12 | require 'shoulda/matchers/action_controller'
13 | require 'shoulda/matchers/active_model'
14 | require 'shoulda/matchers/active_record'
15 | require 'shoulda/matchers/routing'
16 | 
17 | module Shoulda # :nodoc:
18 |   module Matchers # :nodoc:
19 |     class << self
20 |       # @private
21 |       attr_accessor :assertion_exception_class
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/action_controller.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/action_controller/filter_param_matcher'
 2 | require 'shoulda/matchers/action_controller/route_params'
 3 | require 'shoulda/matchers/action_controller/set_flash_matcher'
 4 | require 'shoulda/matchers/action_controller/render_with_layout_matcher'
 5 | require 'shoulda/matchers/action_controller/respond_with_matcher'
 6 | require 'shoulda/matchers/action_controller/set_session_matcher'
 7 | require 'shoulda/matchers/action_controller/route_matcher'
 8 | require 'shoulda/matchers/action_controller/redirect_to_matcher'
 9 | require 'shoulda/matchers/action_controller/render_template_matcher'
10 | require 'shoulda/matchers/action_controller/rescue_from_matcher'
11 | require 'shoulda/matchers/action_controller/callback_matcher'
12 | require 'shoulda/matchers/action_controller/permit_matcher'
13 | require 'shoulda/matchers/action_controller/set_session_or_flash_matcher'
14 | require 'shoulda/matchers/action_controller/flash_store'
15 | require 'shoulda/matchers/action_controller/session_store'
16 | 
17 | module Shoulda
18 |   module Matchers
19 |     # This module provides matchers that are used to test behavior within
20 |     # controllers.
21 |     module ActionController
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/action_controller/filter_param_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActionController
 4 |       # The `filter_param` matcher is used to test parameter filtering
 5 |       # configuration. Specifically, it asserts that the given parameter is
 6 |       # present in `config.filter_parameters`.
 7 |       #
 8 |       #     class MyApplication < Rails::Application
 9 |       #       config.filter_parameters << :secret_key
10 |       #     end
11 |       #
12 |       #     # RSpec
13 |       #     RSpec.describe ApplicationController, type: :controller do
14 |       #       it { should filter_param(:secret_key) }
15 |       #     end
16 |       #
17 |       #     # Minitest (Shoulda)
18 |       #     class ApplicationControllerTest < ActionController::TestCase
19 |       #       should filter_param(:secret_key)
20 |       #     end
21 |       #
22 |       # @return [FilterParamMatcher]
23 |       #
24 |       def filter_param(key)
25 |         FilterParamMatcher.new(key)
26 |       end
27 | 
28 |       # @private
29 |       class FilterParamMatcher
30 |         def initialize(key)
31 |           @key = key
32 |         end
33 | 
34 |         def matches?(_controller)
35 |           filters_key?
36 |         end
37 | 
38 |         def failure_message
39 |           "Expected #{@key} to be filtered; filtered keys:"\
40 |             " #{filtered_keys.join(', ')}"
41 |         end
42 | 
43 |         def failure_message_when_negated
44 |           "Did not expect #{@key} to be filtered"
45 |         end
46 | 
47 |         def description
48 |           "filter #{@key}"
49 |         end
50 | 
51 |         private
52 | 
53 |         def filters_key?
54 |           filtered_keys.any? do |filter|
55 |             case filter
56 |             when Regexp
57 |               filter =~ @key
58 |             else
59 |               filter == @key
60 |             end
61 |           end
62 |         end
63 | 
64 |         def filtered_keys
65 |           Rails.application.config.filter_parameters
66 |         end
67 |       end
68 |     end
69 |   end
70 | end
71 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/action_controller/flash_store.rb:
--------------------------------------------------------------------------------
 1 | require 'active_support/core_ext/module/delegation'
 2 | module Shoulda
 3 |   module Matchers
 4 |     module ActionController
 5 |       # @private
 6 |       class FlashStore
 7 |         def self.future
 8 |           new
 9 |         end
10 | 
11 |         def self.now
12 |           new.use_now!
13 |         end
14 | 
15 |         attr_accessor :controller
16 | 
17 |         def initialize
18 |           @use_now = false
19 |         end
20 | 
21 |         def name
22 |           if @use_now
23 |             'flash.now'
24 |           else
25 |             'flash'
26 |           end
27 |         end
28 | 
29 |         def has_key?(key)
30 |           values_to_check.include?(key.to_s)
31 |         end
32 | 
33 |         def has_value?(expected_value)
34 |           values_to_check.values.any? do |actual_value|
35 |             expected_value === actual_value
36 |           end
37 |         end
38 |         delegate :empty?, to: :flash
39 | 
40 |         def use_now!
41 |           @use_now = true
42 |           self
43 |         end
44 | 
45 |         private
46 | 
47 |         def flash
48 |           @_flash ||= copy_of_flash_from_controller
49 |         end
50 | 
51 |         def copy_of_flash_from_controller
52 |           controller.flash.dup.tap do |flash|
53 |             copy_flashes(controller.flash, flash)
54 |             copy_discard_if_necessary(controller.flash, flash)
55 |           end
56 |         end
57 | 
58 |         def copy_flashes(original_flash, new_flash)
59 |           flashes = original_flash.instance_variable_get('@flashes').dup
60 |           new_flash.instance_variable_set('@flashes', flashes)
61 |         end
62 | 
63 |         def copy_discard_if_necessary(original_flash, new_flash)
64 |           discard = original_flash.instance_variable_get('@discard').dup
65 |           new_flash.instance_variable_set('@discard', discard)
66 |         end
67 | 
68 |         def set_values
69 |           flash.instance_variable_get('@flashes')
70 |         end
71 | 
72 |         def keys_to_discard
73 |           flash.instance_variable_get('@discard')
74 |         end
75 | 
76 |         def values_to_check
77 |           if @use_now
78 |             set_values.slice(*keys_to_discard.to_a)
79 |           else
80 |             set_values.except(*keys_to_discard.to_a)
81 |           end
82 |         end
83 |       end
84 |     end
85 |   end
86 | end
87 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/action_controller/route_params.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActionController
 4 |       # @private
 5 |       class RouteParams
 6 |         PARAMS_TO_SYMBOLIZE = %i{format}.freeze
 7 | 
 8 |         def initialize(args)
 9 |           @args = args
10 |         end
11 | 
12 |         def normalize
13 |           if controller_and_action_given_as_string?
14 |             extract_params_from_string
15 |           else
16 |             stringify_params
17 |           end
18 |         end
19 | 
20 |         protected
21 | 
22 |         attr_reader :args
23 | 
24 |         def controller_and_action_given_as_string?
25 |           args[0].is_a?(String)
26 |         end
27 | 
28 |         def extract_params_from_string
29 |           controller, action = args[0].split('#')
30 |           params = (args[1] || {}).merge!(controller: controller, action: action)
31 |           normalize_values(params)
32 |         end
33 | 
34 |         def stringify_params
35 |           normalize_values(args[0])
36 |         end
37 | 
38 |         def normalize_values(hash)
39 |           hash.each_with_object({}) do |(key, value), hash_copy|
40 |             hash_copy[key] = symbolize_or_stringify(key, value)
41 |           end
42 |         end
43 | 
44 |         def symbolize_or_stringify(key, value)
45 |           if PARAMS_TO_SYMBOLIZE.include?(key)
46 |             value.to_sym
47 |           else
48 |             stringify(value)
49 |           end
50 |         end
51 | 
52 |         def stringify(value)
53 |           if value.is_a?(Array)
54 |             value.map(&:to_param)
55 |           else
56 |             value.to_param
57 |           end
58 |         end
59 |       end
60 |     end
61 |   end
62 | end
63 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/action_controller/session_store.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActionController
 4 |       # @private
 5 |       class SessionStore
 6 |         attr_accessor :controller
 7 | 
 8 |         def name
 9 |           'session'
10 |         end
11 | 
12 |         def has_key?(key)
13 |           session.key?(key)
14 |         end
15 | 
16 |         def has_value?(expected_value)
17 |           session.values.any? do |actual_value|
18 |             expected_value === actual_value
19 |           end
20 |         end
21 | 
22 |         def empty?
23 |           session.empty?
24 |         end
25 | 
26 |         private
27 | 
28 |         def session
29 |           controller.session
30 |         end
31 |       end
32 |     end
33 |   end
34 | end
35 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class AttributeChangedValueError < Shoulda::Matchers::Error
 7 |           attr_accessor :matcher_name, :model, :attribute_name, :value_written,
 8 |             :value_read
 9 | 
10 |           def message
11 |             Shoulda::Matchers.word_wrap <<-MESSAGE
12 | The #{matcher_name} matcher attempted to set :#{attribute_name} on
13 | #{model.name} to #{value_written.inspect}, but when the attribute was
14 | read back, it had stored #{value_read.inspect} instead.
15 | 
16 | This creates a problem because it means that the model is behaving in a
17 | way that is interfering with the test -- there's a mismatch between the
18 | test that you wrote and test that we actually ran.
19 | 
20 | There are a couple of reasons why this could be happening:
21 | 
22 | * ActiveRecord is typecasting the incoming value.
23 | * The writer method for :#{attribute_name} has been overridden so that
24 |   incoming values are changed in some way.
25 | 
26 | If this exception makes sense to you and you wish to bypass it, try
27 | adding the `ignoring_interference_by_writer` qualifier onto the end of
28 | your matcher. If the test still does not pass after that, then you may
29 | need to do something different.
30 | 
31 | If you need help, feel free to ask a question on the shoulda-matchers
32 | issues list:
33 | 
34 | https://github.com/thoughtbot/shoulda-matchers/issues
35 |             MESSAGE
36 |           end
37 | 
38 |           def successful?
39 |             false
40 |           end
41 |         end
42 |       end
43 |     end
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class AttributeDoesNotExistError < Shoulda::Matchers::Error
 7 |           attr_accessor :model, :attribute_name, :value
 8 | 
 9 |           def message
10 |             Shoulda::Matchers.word_wrap <<-MESSAGE
11 | The matcher attempted to set :#{attribute_name} on the #{model.name} to
12 | #{value.inspect}, but that attribute does not exist.
13 |             MESSAGE
14 |           end
15 | 
16 |           def successful?
17 |             false
18 |           end
19 |         end
20 |       end
21 |     end
22 |   end
23 | end
24 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb:
--------------------------------------------------------------------------------
 1 | require 'forwardable'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     module ActiveModel
 6 |       class AllowValueMatcher
 7 |         # @private
 8 |         class AttributeSetterAndValidator
 9 |           extend Forwardable
10 | 
11 |           def_delegators(
12 |             :allow_value_matcher,
13 |             :after_setting_value_callback,
14 |             :attribute_to_check_message_against,
15 |             :context,
16 |             :expected_message,
17 |             :expects_strict?,
18 |             :ignore_interference_by_writer,
19 |             :instance,
20 |           )
21 | 
22 |           def initialize(allow_value_matcher, attribute_name, value)
23 |             @allow_value_matcher = allow_value_matcher
24 |             @attribute_name = attribute_name
25 |             @value = value
26 |             @_attribute_setter = nil
27 |             @_validator = nil
28 |           end
29 | 
30 |           def attribute_setter
31 |             @_attribute_setter ||= AttributeSetter.new(
32 |               matcher_name: :allow_value,
33 |               object: instance,
34 |               attribute_name: attribute_name,
35 |               value: value,
36 |               ignore_interference_by_writer: ignore_interference_by_writer,
37 |               after_set_callback: after_setting_value_callback,
38 |             )
39 |           end
40 | 
41 |           def attribute_setter_description
42 |             attribute_setter.description
43 |           end
44 | 
45 |           def validator
46 |             @_validator ||= Validator.new(
47 |               instance,
48 |               attribute_to_check_message_against,
49 |               context: context,
50 |               expects_strict: expects_strict?,
51 |               expected_message: expected_message,
52 |             )
53 |           end
54 | 
55 |           protected
56 | 
57 |           attr_reader :allow_value_matcher, :attribute_name, :value
58 |         end
59 |       end
60 |     end
61 |   end
62 | end
63 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class AttributeSetters
 7 |           include Enumerable
 8 | 
 9 |           def initialize(allow_value_matcher, values)
10 |             @tuples = values.map do |attribute_name, value|
11 |               AttributeSetterAndValidator.new(
12 |                 allow_value_matcher,
13 |                 attribute_name,
14 |                 value,
15 |               )
16 |             end
17 |           end
18 | 
19 |           def each(&block)
20 |             tuples.each(&block)
21 |           end
22 | 
23 |           def first_failing
24 |             tuples.detect(&method(:does_not_match?))
25 |           end
26 | 
27 |           protected
28 | 
29 |           attr_reader :tuples
30 | 
31 |           private
32 | 
33 |           def does_not_match?(tuple)
34 |             !tuple.attribute_setter.set!
35 |           end
36 |         end
37 |       end
38 |     end
39 |   end
40 | end
41 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class AttributeSettersAndValidators
 7 |           include Enumerable
 8 | 
 9 |           def initialize(allow_value_matcher, values)
10 |             @tuples = values.map do |attribute_name, value|
11 |               AttributeSetterAndValidator.new(
12 |                 allow_value_matcher,
13 |                 attribute_name,
14 |                 value,
15 |               )
16 |             end
17 |           end
18 | 
19 |           def each(&block)
20 |             tuples.each(&block)
21 |           end
22 | 
23 |           def first_passing
24 |             tuples.detect(&method(:matches?))
25 |           end
26 | 
27 |           def first_failing
28 |             tuples.detect(&method(:does_not_match?))
29 |           end
30 | 
31 |           protected
32 | 
33 |           attr_reader :tuples
34 | 
35 |           private
36 | 
37 |           def matches?(tuple)
38 |             tuple.attribute_setter.set! && tuple.validator.call
39 |           end
40 | 
41 |           def does_not_match?(tuple)
42 |             !matches?(tuple)
43 |           end
44 |         end
45 |       end
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class SuccessfulCheck
 7 |           def successful?
 8 |             true
 9 |           end
10 |         end
11 |       end
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class AllowValueMatcher
 5 |         # @private
 6 |         class SuccessfulSetting
 7 |           def successful?
 8 |             true
 9 |           end
10 |         end
11 |       end
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/disallow_value_matcher.rb:
--------------------------------------------------------------------------------
 1 | require 'forwardable'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     module ActiveModel
 6 |       # @private
 7 |       class DisallowValueMatcher
 8 |         extend Forwardable
 9 | 
10 |         def_delegators(
11 |           :allow_matcher,
12 |           :_after_setting_value,
13 |           :attribute_changed_value_message=,
14 |           :attribute_to_set,
15 |           :description,
16 |           :expects_strict?,
17 |           :failure_message_preface,
18 |           :failure_message_preface=,
19 |           :ignore_interference_by_writer,
20 |           :last_attribute_setter_used,
21 |           :last_value_set,
22 |           :model,
23 |           :simple_description,
24 |           :values_to_preset=,
25 |         )
26 | 
27 |         def initialize(value)
28 |           @allow_matcher = AllowValueMatcher.new(value)
29 |         end
30 | 
31 |         def matches?(subject)
32 |           allow_matcher.does_not_match?(subject)
33 |         end
34 | 
35 |         def does_not_match?(subject)
36 |           allow_matcher.matches?(subject)
37 |         end
38 | 
39 |         def for(attribute)
40 |           allow_matcher.for(attribute)
41 |           self
42 |         end
43 | 
44 |         def on(context)
45 |           allow_matcher.on(context)
46 |           self
47 |         end
48 | 
49 |         def with_message(message, options = {})
50 |           allow_matcher.with_message(message, options)
51 |           self
52 |         end
53 | 
54 |         def strict(strict = true)
55 |           allow_matcher.strict(strict)
56 |           self
57 |         end
58 | 
59 |         def ignoring_interference_by_writer(value = :always)
60 |           allow_matcher.ignoring_interference_by_writer(value)
61 |           self
62 |         end
63 | 
64 |         def failure_message
65 |           allow_matcher.failure_message_when_negated
66 |         end
67 | 
68 |         def failure_message_when_negated
69 |           allow_matcher.failure_message
70 |         end
71 | 
72 |         protected
73 | 
74 |         attr_reader :allow_matcher
75 |       end
76 |     end
77 |   end
78 | end
79 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/errors.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       # @private
 5 |       class CouldNotDetermineValueOutsideOfArray < RuntimeError; end
 6 | 
 7 |       # @private
 8 |       class NonNullableBooleanError < Shoulda::Matchers::Error
 9 |         def self.create(attribute)
10 |           super(attribute: attribute)
11 |         end
12 | 
13 |         attr_accessor :attribute
14 | 
15 |         def message
16 |           <<-EOT.strip
17 | You have specified that your model's #{attribute} should ensure inclusion of nil.
18 | However, #{attribute} is a boolean column which does not allow null values.
19 | Hence, this test will fail and there is no way to make it pass.
20 |           EOT
21 |         end
22 |       end
23 | 
24 |       # @private
25 |       class CouldNotSetPasswordError < Shoulda::Matchers::Error
26 |         def self.create(model)
27 |           super(model: model)
28 |         end
29 | 
30 |         attr_accessor :model
31 | 
32 |         def message
33 |           <<-EOT.strip
34 | The validation failed because your #{model_name} model declares `has_secure_password`, and
35 | `validate_presence_of` was called on a #{record_name} which has `password` already set to a value.
36 | Please use a #{record_name} with an empty `password` instead.
37 |           EOT
38 |         end
39 | 
40 |         private
41 | 
42 |         def model_name
43 |           model.name
44 |         end
45 | 
46 |         def record_name
47 |           model_name.humanize.downcase
48 |         end
49 |       end
50 |     end
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/helpers.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       # @private
 5 |       module Helpers
 6 |         def pretty_error_messages(object)
 7 |           format_validation_errors(object.errors)
 8 |         end
 9 | 
10 |         def format_validation_errors(errors)
11 |           list_items = errors.to_hash.keys.map do |attribute|
12 |             messages = errors[attribute]
13 |             "* #{attribute}: #{messages}"
14 |           end
15 | 
16 |           list_items.join("\n")
17 |         end
18 | 
19 |         def default_error_message(type, options = {})
20 |           model_name = options.delete(:model_name)
21 |           attribute = options.delete(:attribute)
22 |           instance = options.delete(:instance)
23 | 
24 |           RailsShim.generate_validation_message(
25 |             instance,
26 |             attribute.to_sym,
27 |             type,
28 |             model_name,
29 |             options,
30 |           )
31 |         end
32 |       end
33 |     end
34 |   end
35 | end
36 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       # @private
 5 |       module NumericalityMatchers
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module NumericalityMatchers
 5 |         # @private
 6 |         class EvenNumberMatcher < NumericTypeMatcher
 7 |           NON_EVEN_NUMBER_VALUE = 1
 8 | 
 9 |           def simple_description
10 |             description = ''
11 | 
12 |             if expects_strict?
13 |               description << 'strictly '
14 |             end
15 | 
16 |             description +
17 |               "disallow :#{attribute} from being an odd number"
18 |           end
19 | 
20 |           def allowed_type_adjective
21 |             'even'
22 |           end
23 | 
24 |           def diff_to_compare
25 |             2
26 |           end
27 | 
28 |           protected
29 | 
30 |           def wrap_disallow_value_matcher(matcher)
31 |             matcher.with_message(:even)
32 |           end
33 | 
34 |           def disallowed_value
35 |             if @numeric_type_matcher.given_numeric_column?
36 |               NON_EVEN_NUMBER_VALUE
37 |             else
38 |               NON_EVEN_NUMBER_VALUE.to_s
39 |             end
40 |           end
41 |         end
42 |       end
43 |     end
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb:
--------------------------------------------------------------------------------
 1 | require 'forwardable'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     module ActiveModel
 6 |       module NumericalityMatchers
 7 |         # @private
 8 |         class NumericTypeMatcher
 9 |           extend Forwardable
10 | 
11 |           def_delegators(
12 |             :disallow_value_matcher,
13 |             :expects_custom_validation_message?,
14 |             :expects_strict?,
15 |             :failure_message,
16 |             :failure_message_when_negated,
17 |             :ignore_interference_by_writer,
18 |             :ignoring_interference_by_writer,
19 |             :matches?,
20 |             :does_not_match?,
21 |             :on,
22 |             :strict,
23 |             :with_message,
24 |           )
25 | 
26 |           def initialize(numeric_type_matcher, attribute)
27 |             @numeric_type_matcher = numeric_type_matcher
28 |             @attribute = attribute
29 |           end
30 | 
31 |           def allowed_type_name
32 |             'number'
33 |           end
34 | 
35 |           def allowed_type_adjective
36 |             ''
37 |           end
38 | 
39 |           def diff_to_compare
40 |             raise NotImplementedError
41 |           end
42 | 
43 |           protected
44 | 
45 |           attr_reader :attribute
46 | 
47 |           def wrap_disallow_value_matcher(_matcher)
48 |             raise NotImplementedError
49 |           end
50 | 
51 |           def disallowed_value
52 |             raise NotImplementedError
53 |           end
54 | 
55 |           private
56 | 
57 |           def disallow_value_matcher
58 |             @_disallow_value_matcher ||= DisallowValueMatcher.new(disallowed_value).tap do |matcher|
59 |               matcher.for(attribute)
60 |               wrap_disallow_value_matcher(matcher)
61 |             end
62 |           end
63 |         end
64 |       end
65 |     end
66 |   end
67 | end
68 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module NumericalityMatchers
 5 |         # @private
 6 |         class OddNumberMatcher < NumericTypeMatcher
 7 |           NON_ODD_NUMBER_VALUE = 2
 8 | 
 9 |           def simple_description
10 |             description = ''
11 | 
12 |             if expects_strict?
13 |               description << 'strictly '
14 |             end
15 | 
16 |             description +
17 |               "disallow :#{attribute} from being an even number"
18 |           end
19 | 
20 |           def allowed_type_adjective
21 |             'odd'
22 |           end
23 | 
24 |           def diff_to_compare
25 |             2
26 |           end
27 | 
28 |           protected
29 | 
30 |           def wrap_disallow_value_matcher(matcher)
31 |             matcher.with_message(:odd)
32 |           end
33 | 
34 |           def disallowed_value
35 |             if @numeric_type_matcher.given_numeric_column?
36 |               NON_ODD_NUMBER_VALUE
37 |             else
38 |               NON_ODD_NUMBER_VALUE.to_s
39 |             end
40 |           end
41 |         end
42 |       end
43 |     end
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module NumericalityMatchers
 5 |         # @private
 6 |         class OnlyIntegerMatcher < NumericTypeMatcher
 7 |           NON_INTEGER_VALUE = 0.1
 8 | 
 9 |           def simple_description
10 |             description = ''
11 | 
12 |             if expects_strict?
13 |               description << ' strictly'
14 |             end
15 | 
16 |             description + "disallow :#{attribute} from being a decimal number"
17 |           end
18 | 
19 |           def allowed_type_name
20 |             'integer'
21 |           end
22 | 
23 |           def diff_to_compare
24 |             1
25 |           end
26 | 
27 |           protected
28 | 
29 |           def wrap_disallow_value_matcher(matcher)
30 |             matcher.with_message(:not_an_integer)
31 |           end
32 | 
33 |           def disallowed_value
34 |             if @numeric_type_matcher.given_numeric_column?
35 |               NON_INTEGER_VALUE
36 |             else
37 |               NON_INTEGER_VALUE.to_s
38 |             end
39 |           end
40 |         end
41 |       end
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb:
--------------------------------------------------------------------------------
 1 | require 'active_support/core_ext/module/delegation'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     module ActiveModel
 6 |       module NumericalityMatchers
 7 |         # @private
 8 |         class RangeMatcher < ValidationMatcher
 9 |           OPERATORS = [:>=, :<=].freeze
10 | 
11 |           delegate :failure_message, to: :submatchers
12 | 
13 |           def initialize(numericality_matcher, attribute, range)
14 |             super(attribute)
15 |             unless numericality_matcher.respond_to? :diff_to_compare
16 |               raise ArgumentError, 'numericality_matcher is invalid'
17 |             end
18 | 
19 |             @numericality_matcher = numericality_matcher
20 |             @range = range
21 |             @attribute = attribute
22 |           end
23 | 
24 |           def matches?(subject)
25 |             @subject = subject
26 |             submatchers.matches?(subject)
27 |           end
28 | 
29 |           def simple_description
30 |             description = ''
31 | 
32 |             if expects_strict?
33 |               description << ' strictly'
34 |             end
35 | 
36 |             description +
37 |               "disallow :#{attribute} from being a number that is not " +
38 |               range_description
39 |           end
40 | 
41 |           def range_description
42 |             "from #{Shoulda::Matchers::Util.inspect_range(@range)}"
43 |           end
44 | 
45 |           def submatchers
46 |             @_submatchers ||= NumericalityMatchers::Submatchers.new(build_submatchers)
47 |           end
48 | 
49 |           private
50 | 
51 |           def build_submatchers
52 |             submatcher_combos.map do |value, operator|
53 |               build_comparison_submatcher(value, operator)
54 |             end
55 |           end
56 | 
57 |           def submatcher_combos
58 |             @range.minmax.zip(OPERATORS)
59 |           end
60 | 
61 |           def build_comparison_submatcher(value, operator)
62 |             ComparisonMatcher.new(@numericality_matcher, value, operator).
63 |               for(@attribute).
64 |               with_message(@message).
65 |               on(@context)
66 |           end
67 |         end
68 |       end
69 |     end
70 |   end
71 | end
72 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module NumericalityMatchers
 5 |         # @private
 6 |         class Submatchers
 7 |           def initialize(submatchers)
 8 |             @submatchers = submatchers
 9 |           end
10 | 
11 |           def matches?(subject)
12 |             @subject = subject
13 |             failing_submatchers.empty?
14 |           end
15 | 
16 |           def failure_message
17 |             failing_submatcher.failure_message
18 |           end
19 | 
20 |           def failure_message_when_negated
21 |             non_failing_submatcher.failure_message_when_negated
22 |           end
23 | 
24 |           def add(submatcher)
25 |             @submatchers << submatcher
26 |           end
27 | 
28 |           private
29 | 
30 |           def failing_submatchers
31 |             @_failing_submatchers ||= @submatchers.reject do |submatcher|
32 |               submatcher.matches?(@subject)
33 |             end
34 |           end
35 | 
36 |           def non_failing_submatchers
37 |             @_non_failing_submatchers ||= @submatchers.reject do |submatcher|
38 |               submatcher.does_not_match?(@subject)
39 |             end
40 |           end
41 | 
42 |           def failing_submatcher
43 |             failing_submatchers.last
44 |           end
45 | 
46 |           def non_failing_submatcher
47 |             non_failing_submatchers.last
48 |           end
49 |         end
50 |       end
51 |     end
52 |   end
53 | end
54 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/qualifiers.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       # @private
 5 |       module Qualifiers
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 
11 | require_relative 'qualifiers/allow_nil'
12 | require_relative 'qualifiers/allow_blank'
13 | require_relative 'qualifiers/ignore_interference_by_writer'
14 | require_relative 'qualifiers/ignoring_interference_by_writer'
15 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module Qualifiers
 5 |         # @private
 6 |         module AllowBlank
 7 |           def initialize(*args)
 8 |             super
 9 |             @expects_to_allow_blank = false
10 |           end
11 | 
12 |           def allow_blank
13 |             @expects_to_allow_blank = true
14 |             self
15 |           end
16 | 
17 |           protected
18 | 
19 |           def expects_to_allow_blank?
20 |             @expects_to_allow_blank
21 |           end
22 |         end
23 |       end
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module Qualifiers
 5 |         # @private
 6 |         module AllowNil
 7 |           def initialize(*args)
 8 |             super
 9 |             @expects_to_allow_nil = false
10 |           end
11 | 
12 |           def allow_nil
13 |             @expects_to_allow_nil = true
14 |             self
15 |           end
16 | 
17 |           protected
18 | 
19 |           def expects_to_allow_nil?
20 |             @expects_to_allow_nil
21 |           end
22 |         end
23 |       end
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       module Qualifiers
 5 |         # @private
 6 |         module IgnoringInterferenceByWriter
 7 |           attr_reader :ignore_interference_by_writer
 8 | 
 9 |           def initialize(*)
10 |             @ignore_interference_by_writer = IgnoreInterferenceByWriter.new
11 |           end
12 | 
13 |           def ignoring_interference_by_writer(value = :always)
14 |             @ignore_interference_by_writer.set(value)
15 |             self
16 |           end
17 |         end
18 |       end
19 |     end
20 |   end
21 | end
22 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       class ValidationMatcher
 5 |         # @private
 6 |         class BuildDescription
 7 |           def self.call(matcher, main_description)
 8 |             new(matcher, main_description).call
 9 |           end
10 | 
11 |           def initialize(matcher, main_description)
12 |             @matcher = matcher
13 |             @main_description = main_description
14 |           end
15 | 
16 |           def call
17 |             if description_clauses_for_qualifiers.any?
18 |               "#{main_description}#{clause_for_allow_blank_or_nil},"\
19 |               " #{description_clauses_for_qualifiers.to_sentence}"
20 |             else
21 |               main_description + clause_for_allow_blank_or_nil
22 |             end
23 |           end
24 | 
25 |           protected
26 | 
27 |           attr_reader :matcher, :main_description
28 | 
29 |           private
30 | 
31 |           def clause_for_allow_blank_or_nil
32 |             if matcher.try(:expects_to_allow_blank?)
33 |               ' as long as it is not blank'
34 |             elsif matcher.try(:expects_to_allow_nil?)
35 |               ' as long as it is not nil'
36 |             else
37 |               ''
38 |             end
39 |           end
40 | 
41 |           def description_clauses_for_qualifiers
42 |             description_clauses = []
43 | 
44 |             if matcher.try(:expects_strict?)
45 |               description_clauses <<
46 |                 if matcher.try(:expects_custom_validation_message?)
47 |                   'raising a validation exception with a custom message on failure'
48 |                 else
49 |                   'raising a validation exception on failure'
50 |                 end
51 |             elsif matcher.try(:expects_custom_validation_message?)
52 |               description_clauses <<
53 |                 'producing a custom validation error on failure'
54 |             end
55 | 
56 |             description_clauses
57 |           end
58 |         end
59 |       end
60 |     end
61 |   end
62 | end
63 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_model/validation_message_finder.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveModel
 4 |       # @private
 5 |       class ValidationMessageFinder
 6 |         include Helpers
 7 | 
 8 |         def initialize(instance, attribute, context = nil)
 9 |           @instance = instance
10 |           @attribute = attribute
11 |           @context = context
12 |         end
13 | 
14 |         def allow_description(allowed_values)
15 |           "allow #{@attribute} to be set to #{allowed_values}"
16 |         end
17 | 
18 |         def expected_message_from(attribute_message)
19 |           attribute_message
20 |         end
21 | 
22 |         def has_messages?
23 |           errors.present?
24 |         end
25 | 
26 |         def source_description
27 |           'errors'
28 |         end
29 | 
30 |         def messages_description
31 |           if errors.empty?
32 |             ' no errors'
33 |           else
34 |             " errors:\n#{pretty_error_messages(validated_instance)}"
35 |           end
36 |         end
37 | 
38 |         def messages
39 |           Array(messages_for_attribute)
40 |         end
41 | 
42 |         private
43 | 
44 |         def messages_for_attribute
45 |           errors[@attribute]
46 |         end
47 | 
48 |         def errors
49 |           validated_instance.errors
50 |         end
51 | 
52 |         def validated_instance
53 |           @_validated_instance ||= validate_instance
54 |         end
55 | 
56 |         def validate_instance
57 |           @instance.valid?(*@context)
58 |           @instance
59 |         end
60 |       end
61 |     end
62 |   end
63 | end
64 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/active_record/association_matcher'
 2 | require 'shoulda/matchers/active_record/association_matchers'
 3 | require 'shoulda/matchers/active_record/association_matchers/counter_cache_matcher'
 4 | require 'shoulda/matchers/active_record/association_matchers/inverse_of_matcher'
 5 | require 'shoulda/matchers/active_record/association_matchers/join_table_matcher'
 6 | require 'shoulda/matchers/active_record/association_matchers/order_matcher'
 7 | require 'shoulda/matchers/active_record/association_matchers/through_matcher'
 8 | require 'shoulda/matchers/active_record/association_matchers/dependent_matcher'
 9 | require 'shoulda/matchers/active_record/association_matchers/required_matcher'
10 | require 'shoulda/matchers/active_record/association_matchers/optional_matcher'
11 | require 'shoulda/matchers/active_record/association_matchers/source_matcher'
12 | require 'shoulda/matchers/active_record/association_matchers/model_reflector'
13 | require 'shoulda/matchers/active_record/association_matchers/model_reflection'
14 | require 'shoulda/matchers/active_record/association_matchers/option_verifier'
15 | require 'shoulda/matchers/active_record/have_db_column_matcher'
16 | require 'shoulda/matchers/active_record/have_db_index_matcher'
17 | require 'shoulda/matchers/active_record/have_implicit_order_column'
18 | require 'shoulda/matchers/active_record/have_readonly_attribute_matcher'
19 | require 'shoulda/matchers/active_record/have_rich_text_matcher'
20 | require 'shoulda/matchers/active_record/have_secure_token_matcher'
21 | require 'shoulda/matchers/active_record/serialize_matcher'
22 | require 'shoulda/matchers/active_record/accept_nested_attributes_for_matcher'
23 | require 'shoulda/matchers/active_record/define_enum_for_matcher'
24 | require 'shoulda/matchers/active_record/uniqueness'
25 | require 'shoulda/matchers/active_record/validate_uniqueness_of_matcher'
26 | require 'shoulda/matchers/active_record/have_attached_matcher'
27 | require 'shoulda/matchers/active_record/normalize_matcher'
28 | require 'shoulda/matchers/active_record/encrypt_matcher'
29 | 
30 | module Shoulda
31 |   module Matchers
32 |     # This module provides matchers that are used to test behavior within
33 |     # ActiveRecord classes.
34 |     module ActiveRecord
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module AssociationMatchers
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class CounterCacheMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(counter_cache, name)
10 |             @counter_cache = counter_cache
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "counter_cache => #{counter_cache}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 | 
22 |             if correct_value?
23 |               true
24 |             else
25 |               self.missing_option = "#{name} should have #{description}"
26 |               false
27 |             end
28 |           end
29 | 
30 |           protected
31 | 
32 |           attr_accessor :subject, :counter_cache, :name
33 | 
34 |           def correct_value?
35 |             expected = normalize_value
36 | 
37 |             if expected.is_a?(Hash)
38 |               option_verifier.correct_for_hash?(
39 |                 :counter_cache,
40 |                 expected,
41 |               )
42 |             else
43 |               option_verifier.correct_for_string?(
44 |                 :counter_cache,
45 |                 expected,
46 |               )
47 |             end
48 |           end
49 | 
50 |           def option_verifier
51 |             @_option_verifier ||= OptionVerifier.new(subject)
52 |           end
53 | 
54 |           def normalize_value
55 |             if Rails::VERSION::STRING >= '7.2'
56 |               case counter_cache
57 |               when true
58 |                 { active: true, column: nil }
59 |               when String, Symbol
60 |                 { active: true, column: counter_cache.to_s }
61 |               when Hash
62 |                 { active: true, column: nil }.merge!(counter_cache)
63 |               else
64 |                 raise ArgumentError, 'Invalid counter_cache option'
65 |               end
66 |             else
67 |               counter_cache
68 |             end
69 |           end
70 |         end
71 |       end
72 |     end
73 |   end
74 | end
75 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class DependentMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(dependent, name)
10 |             @dependent = dependent
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "dependent => #{dependent}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 | 
22 |             if option_matches?
23 |               true
24 |             else
25 |               self.missing_option = generate_missing_option
26 |               false
27 |             end
28 |           end
29 | 
30 |           protected
31 | 
32 |           attr_accessor :subject, :dependent, :name
33 | 
34 |           private
35 | 
36 |           def option_verifier
37 |             @_option_verifier ||= OptionVerifier.new(subject)
38 |           end
39 | 
40 |           def option_matches?
41 |             option_verifier.correct_for?(option_type, :dependent, dependent)
42 |           end
43 | 
44 |           def option_type
45 |             case dependent
46 |             when true, false then :boolean
47 |             else :string
48 |             end
49 |           end
50 | 
51 |           def generate_missing_option
52 |             [
53 |               "#{name} should have",
54 |               (dependent == true ? 'a' : dependent),
55 |               'dependency',
56 |             ].join(' ')
57 |           end
58 |         end
59 |       end
60 |     end
61 |   end
62 | end
63 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class InverseOfMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(inverse_of, name)
10 |             @inverse_of = inverse_of
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "inverse_of => #{inverse_of}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 | 
22 |             if option_verifier.correct_for_string?(:inverse_of, inverse_of)
23 |               true
24 |             else
25 |               self.missing_option = "#{name} should have #{description}"
26 |               false
27 |             end
28 |           end
29 | 
30 |           protected
31 | 
32 |           attr_accessor :subject, :inverse_of, :name
33 | 
34 |           def option_verifier
35 |             @_option_verifier ||= OptionVerifier.new(subject)
36 |           end
37 |         end
38 |       end
39 |     end
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class OptionalMatcher
 7 |           attr_reader :missing_option
 8 | 
 9 |           def initialize(attribute_name, optional)
10 |             @attribute_name = attribute_name
11 |             @optional = optional
12 |             @submatcher = ActiveModel::AllowValueMatcher.new(nil).
13 |               for(attribute_name)
14 |             @missing_option = ''
15 |           end
16 | 
17 |           def description
18 |             "optional: #{optional}"
19 |           end
20 | 
21 |           def matches?(subject)
22 |             if submatcher_passes?(subject)
23 |               true
24 |             else
25 |               @missing_option = build_missing_option
26 | 
27 |               false
28 |             end
29 |           end
30 | 
31 |           private
32 | 
33 |           attr_reader :attribute_name, :optional, :submatcher
34 | 
35 |           def submatcher_passes?(subject)
36 |             if optional
37 |               submatcher.matches?(subject)
38 |             else
39 |               submatcher.does_not_match?(subject)
40 |             end
41 |           end
42 | 
43 |           def build_missing_option
44 |             String.new('and for the record ').tap do |missing_option_string|
45 |               missing_option_string <<
46 |                 if optional
47 |                   'not to '
48 |                 else
49 |                   'to '
50 |                 end
51 | 
52 |               missing_option_string << (
53 |                 'fail validation if '\
54 |                 ":#{attribute_name} is unset; i.e., either the association "\
55 |                 'should have been defined with `optional: '\
56 |                 "#{optional.inspect}`, or there "
57 |               )
58 | 
59 |               missing_option_string <<
60 |                 if optional
61 |                   'should not '
62 |                 else
63 |                   'should '
64 |                 end
65 | 
66 |               missing_option_string << "be a presence validation on :#{attribute_name}"
67 |             end
68 |           end
69 |         end
70 |       end
71 |     end
72 |   end
73 | end
74 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class OrderMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(order, name)
10 |             @order = order
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "order => #{order}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 | 
22 |             if option_verifier.correct_for_relation_clause?(:order, order)
23 |               true
24 |             else
25 |               self.missing_option = "#{name} should be ordered by #{order}"
26 |               false
27 |             end
28 |           end
29 | 
30 |           protected
31 | 
32 |           attr_accessor :subject, :order, :name
33 | 
34 |           def option_verifier
35 |             @_option_verifier ||= OptionVerifier.new(subject)
36 |           end
37 |         end
38 |       end
39 |     end
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class RequiredMatcher
 7 |           attr_reader :missing_option
 8 | 
 9 |           def initialize(attribute_name, required)
10 |             @attribute_name = attribute_name
11 |             @required = required
12 |             @submatcher = ActiveModel::DisallowValueMatcher.new(nil).
13 |               for(attribute_name).
14 |               with_message(validation_message_key)
15 |             @missing_option = ''
16 |           end
17 | 
18 |           def description
19 |             "required: #{required}"
20 |           end
21 | 
22 |           def matches?(subject)
23 |             if submatcher_passes?(subject)
24 |               true
25 |             else
26 |               @missing_option = build_missing_option
27 | 
28 |               false
29 |             end
30 |           end
31 | 
32 |           private
33 | 
34 |           attr_reader :attribute_name, :required, :submatcher
35 | 
36 |           def submatcher_passes?(subject)
37 |             if required
38 |               submatcher.matches?(subject)
39 |             else
40 |               submatcher.does_not_match?(subject)
41 |             end
42 |           end
43 | 
44 |           def validation_message_key
45 |             :required
46 |           end
47 | 
48 |           def build_missing_option
49 |             String.new('and for the record ').tap do |missing_option_string|
50 |               missing_option_string <<
51 |                 if required
52 |                   'to '
53 |                 else
54 |                   'not to '
55 |                 end
56 | 
57 |               missing_option_string << (
58 |                 'fail validation if '\
59 |                 ":#{attribute_name} is unset; i.e., either the association "\
60 |                 'should have been defined with `required: '\
61 |                 "#{required.inspect}`, or there "
62 |               )
63 | 
64 |               missing_option_string <<
65 |                 if required
66 |                   'should '
67 |                 else
68 |                   'should not '
69 |                 end
70 | 
71 |               missing_option_string << "be a presence validation on :#{attribute_name}"
72 |             end
73 |           end
74 |         end
75 |       end
76 |     end
77 |   end
78 | end
79 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class SourceMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(source, name)
10 |             @source = source
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "source => #{source}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 | 
22 |             if option_verifier.correct_for_string?(:source, source)
23 |               true
24 |             else
25 |               self.missing_option =
26 |                 "#{name} should have #{source} as source option"
27 |               false
28 |             end
29 |           end
30 | 
31 |           protected
32 | 
33 |           attr_accessor :subject, :source, :name
34 | 
35 |           def option_verifier
36 |             @_option_verifier ||= OptionVerifier.new(subject)
37 |           end
38 |         end
39 |       end
40 |     end
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       module AssociationMatchers
 5 |         # @private
 6 |         class ThroughMatcher
 7 |           attr_accessor :missing_option
 8 | 
 9 |           def initialize(through, name)
10 |             @through = through
11 |             @name = name
12 |             @missing_option = ''
13 |           end
14 | 
15 |           def description
16 |             "through #{through}"
17 |           end
18 | 
19 |           def matches?(subject)
20 |             self.subject = ModelReflector.new(subject, name)
21 |             through.nil? || association_set_properly?
22 |           end
23 | 
24 |           def association_set_properly?
25 |             through_association_exists? && through_association_correct?
26 |           end
27 | 
28 |           def through_association_exists?
29 |             if through_reflection.present?
30 |               true
31 |             else
32 |               self.missing_option =
33 |                 "#{name} does not have any relationship to #{through}"
34 |               false
35 |             end
36 |           end
37 | 
38 |           def through_reflection
39 |             @_through_reflection ||= subject.reflect_on_association(through)
40 |           end
41 | 
42 |           def through_association_correct?
43 |             if option_verifier.correct_for_string?(:through, through)
44 |               true
45 |             else
46 |               self.missing_option =
47 |                 "Expected #{name} to have #{name} through #{through}, "\
48 |                 'but got it through ' +
49 |                 option_verifier.actual_value_for(:through).to_s
50 |               false
51 |             end
52 |           end
53 | 
54 |           protected
55 | 
56 |           attr_accessor :through, :name, :subject
57 | 
58 |           def option_verifier
59 |             @_option_verifier ||= OptionVerifier.new(subject)
60 |           end
61 |         end
62 |       end
63 |     end
64 |   end
65 | end
66 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # The `have_readonly_attribute` matcher tests usage of the
 5 |       # `attr_readonly` macro.
 6 |       #
 7 |       #     class User < ActiveRecord::Base
 8 |       #       attr_readonly :password
 9 |       #     end
10 |       #
11 |       #     # RSpec
12 |       #     RSpec.describe User, type: :model do
13 |       #       it { should have_readonly_attribute(:password) }
14 |       #     end
15 |       #
16 |       #     # Minitest (Shoulda)
17 |       #     class UserTest < ActiveSupport::TestCase
18 |       #       should have_readonly_attribute(:password)
19 |       #     end
20 |       #
21 |       # @return [HaveReadonlyAttributeMatcher]
22 |       #
23 |       def have_readonly_attribute(value)
24 |         HaveReadonlyAttributeMatcher.new(value)
25 |       end
26 | 
27 |       # @private
28 |       class HaveReadonlyAttributeMatcher
29 |         def initialize(attribute)
30 |           @attribute = attribute.to_s
31 |         end
32 | 
33 |         attr_reader :failure_message, :failure_message_when_negated
34 | 
35 |         def matches?(subject)
36 |           @subject = subject
37 |           if readonly_attributes.include?(@attribute)
38 |             @failure_message_when_negated = "Did not expect #{@attribute}"\
39 |             ' to be read-only'
40 |             true
41 |           else
42 |             @failure_message =
43 |               if readonly_attributes.empty?
44 |                 "#{class_name} attribute #{@attribute} " <<
45 |                   'is not read-only'
46 |               else
47 |                 "#{class_name} is making " <<
48 |                   "#{readonly_attributes.to_a.to_sentence} " <<
49 |                   "read-only, but not #{@attribute}."
50 |               end
51 |             false
52 |           end
53 |         end
54 | 
55 |         def description
56 |           "make #{@attribute} read-only"
57 |         end
58 | 
59 |         private
60 | 
61 |         def readonly_attributes
62 |           @_readonly_attributes ||= @subject.class.readonly_attributes || []
63 |         end
64 | 
65 |         def class_name
66 |           @subject.class.name
67 |         end
68 |       end
69 |     end
70 |   end
71 | end
72 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/uniqueness.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module Uniqueness
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 
11 | require 'shoulda/matchers/active_record/uniqueness/model'
12 | require 'shoulda/matchers/active_record/uniqueness/namespace'
13 | require 'shoulda/matchers/active_record/uniqueness/test_model_creator'
14 | require 'shoulda/matchers/active_record/uniqueness/test_models'
15 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/uniqueness/model.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module Uniqueness
 6 |         # @private
 7 |         class Model
 8 |           def self.next_unique_copy_of(model_name, namespace)
 9 |             model = new(model_name, namespace)
10 | 
11 |             while model.already_exists?
12 |               model = model.next
13 |             end
14 | 
15 |             model
16 |           end
17 | 
18 |           def initialize(name, namespace)
19 |             @name = name
20 |             @namespace = namespace
21 |           end
22 | 
23 |           def already_exists?
24 |             namespace.has?(name)
25 |           end
26 | 
27 |           def next
28 |             Model.new(name.next, namespace)
29 |           end
30 | 
31 |           def symlink_to(parent)
32 |             table_name = parent.table_name
33 | 
34 |             new_class = Class.new(parent) do
35 |               define_singleton_method :table_name do
36 |                 table_name
37 |               end
38 | 
39 |               define_singleton_method :base_class do
40 |                 self
41 |               end
42 |             end
43 | 
44 |             namespace.set(name, new_class)
45 |           end
46 | 
47 |           def to_s
48 |             [namespace, name].join('::')
49 |           end
50 | 
51 |           protected
52 | 
53 |           attr_reader :name, :namespace
54 |         end
55 |       end
56 |     end
57 |   end
58 | end
59 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/uniqueness/namespace.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module Uniqueness
 6 |         # @private
 7 |         class Namespace
 8 |           def initialize(constant)
 9 |             @constant = constant
10 |           end
11 | 
12 |           def has?(name)
13 |             constant.const_defined?(name)
14 |           end
15 | 
16 |           def set(name, value)
17 |             constant.const_set(name, value)
18 |           end
19 | 
20 |           def clear
21 |             constant.constants.each do |child_constant|
22 |               constant.__send__(:remove_const, child_constant)
23 |             end
24 |           end
25 | 
26 |           def to_s
27 |             constant.to_s
28 |           end
29 | 
30 |           protected
31 | 
32 |           attr_reader :constant
33 |         end
34 |       end
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module Uniqueness
 6 |         # @private
 7 |         class TestModelCreator
 8 |           def self.create(model_name, namespace)
 9 |             Mutex.new.synchronize do
10 |               new(model_name, namespace).create
11 |             end
12 |           end
13 | 
14 |           def initialize(model_name, namespace)
15 |             @model_name = model_name
16 |             @namespace = namespace
17 |           end
18 | 
19 |           def create
20 |             new_model.tap do |new_model|
21 |               new_model.symlink_to(existing_model)
22 |             end
23 |           end
24 | 
25 |           protected
26 | 
27 |           attr_reader :model_name, :namespace
28 | 
29 |           private
30 | 
31 |           def model_name_without_namespace
32 |             model_name.demodulize
33 |           end
34 | 
35 |           def new_model
36 |             @_new_model ||= Model.next_unique_copy_of(
37 |               model_name_without_namespace,
38 |               namespace,
39 |             )
40 |           end
41 | 
42 |           def existing_model
43 |             @_existing_model ||= model_name.constantize
44 |           end
45 |         end
46 |       end
47 |     end
48 |   end
49 | end
50 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/active_record/uniqueness/test_models.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module ActiveRecord
 4 |       # @private
 5 |       module Uniqueness
 6 |         # @private
 7 |         module TestModels
 8 |           def self.create(model_name)
 9 |             TestModelCreator.create(model_name, root_namespace)
10 |           end
11 | 
12 |           def self.remove_all
13 |             root_namespace.clear
14 |           end
15 | 
16 |           def self.root_namespace
17 |             @_root_namespace ||= Namespace.new(self)
18 |           end
19 |         end
20 |       end
21 |     end
22 |   end
23 | end
24 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/configuration.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     def self.configure
 5 |       yield configuration
 6 |     end
 7 | 
 8 |     # @private
 9 |     def self.integrations
10 |       configuration.integrations
11 |     end
12 | 
13 |     # @private
14 |     def self.configuration
15 |       @_configuration ||= Configuration.new
16 |     end
17 | 
18 |     # @private
19 |     class Configuration
20 |       attr_reader :integrations
21 | 
22 |       def initialize
23 |         @integrations = nil
24 |       end
25 | 
26 |       def integrate(&block)
27 |         @integrations = Integrations::Configuration.apply(&block)
28 |       end
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak.rb:
--------------------------------------------------------------------------------
 1 | require 'forwardable'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     # @private
 6 |     module Doublespeak
 7 |       class << self
 8 |         extend Forwardable
 9 | 
10 |         def_delegators :world, :double_collection_for,
11 |           :with_doubles_activated
12 | 
13 |         def world
14 |           @_world ||= World.new
15 |         end
16 | 
17 |         def debugging_enabled?
18 |           ENV['DEBUG_DOUBLESPEAK'] == '1'
19 |         end
20 | 
21 |         def debug(&block)
22 |           if debugging_enabled?
23 |             puts block.call # rubocop:disable Rails/Output
24 |           end
25 |         end
26 |       end
27 |     end
28 |   end
29 | end
30 | 
31 | require 'shoulda/matchers/doublespeak/double'
32 | require 'shoulda/matchers/doublespeak/double_collection'
33 | require 'shoulda/matchers/doublespeak/double_implementation_registry'
34 | require 'shoulda/matchers/doublespeak/method_call'
35 | require 'shoulda/matchers/doublespeak/object_double'
36 | require 'shoulda/matchers/doublespeak/proxy_implementation'
37 | require 'shoulda/matchers/doublespeak/stub_implementation'
38 | require 'shoulda/matchers/doublespeak/world'
39 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/double_collection.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class DoubleCollection
 6 |         def initialize(world, klass)
 7 |           @world = world
 8 |           @klass = klass
 9 |           @doubles_by_method_name = {}
10 |         end
11 | 
12 |         def register_stub(method_name)
13 |           register_double(method_name, :stub)
14 |         end
15 | 
16 |         def register_proxy(method_name)
17 |           register_double(method_name, :proxy)
18 |         end
19 | 
20 |         def activate
21 |           doubles_by_method_name.each_value(&:activate)
22 |         end
23 | 
24 |         def deactivate
25 |           doubles_by_method_name.each_value(&:deactivate)
26 |         end
27 | 
28 |         def calls_by_method_name
29 |           doubles_by_method_name.inject({}) do |hash, (method_name, double)|
30 |             hash.merge! method_name => double.calls.map(&:args)
31 |           end
32 |         end
33 | 
34 |         def calls_to(method_name)
35 |           double = doubles_by_method_name[method_name]
36 | 
37 |           if double
38 |             double.calls
39 |           else
40 |             []
41 |           end
42 |         end
43 | 
44 |         protected
45 | 
46 |         attr_reader :world, :klass, :doubles_by_method_name
47 | 
48 |         def register_double(method_name, implementation_type)
49 |           doubles_by_method_name.fetch(method_name) do
50 |             implementation =
51 |               DoubleImplementationRegistry.find(implementation_type)
52 |             double = Double.new(world, klass, method_name, implementation)
53 |             doubles_by_method_name[method_name] = double
54 |             double
55 |           end
56 |         end
57 |       end
58 |     end
59 |   end
60 | end
61 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       module DoubleImplementationRegistry
 6 |         class << self
 7 |           def find(type)
 8 |             find_class!(type).create
 9 |           end
10 | 
11 |           def register(klass, type)
12 |             registry[type] = klass
13 |           end
14 | 
15 |           private
16 | 
17 |           def find_class!(type)
18 |             registry.fetch(type) do
19 |               raise ArgumentError, 'No double implementation class found for'\
20 |                 " '#{type}'"
21 |             end
22 |           end
23 | 
24 |           def registry
25 |             @_registry ||= {}
26 |           end
27 |         end
28 |       end
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/method_call.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class MethodCall
 6 |         attr_accessor :return_value
 7 |         attr_reader :method_name, :args, :caller, :block, :object, :double
 8 | 
 9 |         def initialize(args)
10 |           @method_name = args.fetch(:method_name)
11 |           @args = args.fetch(:args)
12 |           @caller = args.fetch(:caller)
13 |           @block = args[:block]
14 |           @double = args[:double]
15 |           @object = args[:object]
16 |           @return_value = nil
17 |         end
18 | 
19 |         def with_return_value(return_value)
20 |           dup.tap do |call|
21 |             call.return_value = return_value
22 |           end
23 |         end
24 | 
25 |         def ==(other)
26 |           other.is_a?(self.class) &&
27 |             method_name == other.method_name &&
28 |             args == other.args &&
29 |             block == other.block &&
30 |             double == other.double &&
31 |             object == other.object
32 |         end
33 | 
34 |         def to_hash
35 |           { method_name: method_name, args: args }
36 |         end
37 | 
38 |         def inspect
39 |           "#<#{self.class.name} #{to_hash.inspect}>"
40 |         end
41 |       end
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/object_double.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class ObjectDouble < BasicObject
 6 |         attr_reader :calls
 7 | 
 8 |         def initialize
 9 |           @calls = []
10 |           @calls_by_method_name = {}
11 |         end
12 | 
13 |         def calls_to(method_name)
14 |           @calls_by_method_name[method_name] || []
15 |         end
16 | 
17 |         def respond_to?(_name, _include_private = nil)
18 |           true
19 |         end
20 | 
21 |         def respond_to_missing?(_name, _include_all)
22 |           true
23 |         end
24 | 
25 |         def method_missing(method_name, *args, &block)
26 |           call = MethodCall.new(
27 |             method_name: method_name,
28 |             args: args,
29 |             block: block,
30 |             caller: ::Kernel.caller,
31 |           )
32 |           calls << call
33 |           (calls_by_method_name[method_name] ||= []) << call
34 |           nil
35 |         end
36 | 
37 |         protected
38 | 
39 |         attr_reader :calls_by_method_name
40 |       end
41 |     end
42 |   end
43 | end
44 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/proxy_implementation.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class ProxyImplementation
 6 |         extend Forwardable
 7 | 
 8 |         DoubleImplementationRegistry.register(self, :proxy)
 9 | 
10 |         def_delegators :stub_implementation, :returns
11 | 
12 |         def self.create
13 |           new(StubImplementation.new)
14 |         end
15 | 
16 |         def initialize(stub_implementation)
17 |           @stub_implementation = stub_implementation
18 |         end
19 | 
20 |         def call(call)
21 |           return_value = call.double.call_original_method(call)
22 |           stub_implementation.call(call.with_return_value(return_value))
23 |           return_value
24 |         end
25 | 
26 |         protected
27 | 
28 |         attr_reader :stub_implementation
29 |       end
30 |     end
31 |   end
32 | end
33 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/stub_implementation.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class StubImplementation
 6 |         DoubleImplementationRegistry.register(self, :stub)
 7 | 
 8 |         def self.create
 9 |           new
10 |         end
11 | 
12 |         def initialize
13 |           @implementation = proc { nil }
14 |         end
15 | 
16 |         def returns(value = nil, &block)
17 |           @implementation = block || proc { value }
18 |         end
19 | 
20 |         def call(call)
21 |           call.double.record_call(call)
22 |           implementation.call(call)
23 |         end
24 | 
25 |         protected
26 | 
27 |         attr_reader :implementation
28 |       end
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/doublespeak/world.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Doublespeak
 4 |       # @private
 5 |       class World
 6 |         def initialize
 7 |           @doubles_activated = false
 8 |         end
 9 | 
10 |         def double_collection_for(klass)
11 |           double_collections_by_class[klass] ||=
12 |             DoubleCollection.new(self, klass)
13 |         end
14 | 
15 |         def store_original_method_for(klass, method_name)
16 |           original_methods_for_class(klass)[method_name] ||=
17 |             klass.instance_method(method_name)
18 |         end
19 | 
20 |         def original_method_for(klass, method_name)
21 |           if original_methods_by_class.key?(klass)
22 |             original_methods_by_class[klass][method_name]
23 |           end
24 |         end
25 | 
26 |         def with_doubles_activated
27 |           @doubles_activated = true
28 |           activate
29 |           yield
30 |         ensure
31 |           @doubles_activated = false
32 |           deactivate
33 |         end
34 | 
35 |         def doubles_activated?
36 |           @doubles_activated
37 |         end
38 | 
39 |         private
40 | 
41 |         def activate
42 |           double_collections_by_class.each_value(&:activate)
43 |         end
44 | 
45 |         def deactivate
46 |           double_collections_by_class.each_value(&:deactivate)
47 |         end
48 | 
49 |         def double_collections_by_class
50 |           @_double_collections_by_class ||= {}
51 |         end
52 | 
53 |         def original_methods_by_class
54 |           @_original_methods_by_class ||= {}
55 |         end
56 | 
57 |         def original_methods_for_class(klass)
58 |           original_methods_by_class[klass] ||= {}
59 |         end
60 |       end
61 |     end
62 |   end
63 | end
64 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/error.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     class Error < StandardError
 5 |       def self.create(attributes)
 6 |         allocate.tap do |error|
 7 |           attributes.each do |name, value|
 8 |             error.__send__("#{name}=", value)
 9 |           end
10 | 
11 |           error.__send__(:initialize)
12 |         end
13 |       end
14 | 
15 |       def initialize(*args)
16 |         super
17 |         @message = message
18 |       end
19 | 
20 |       def message
21 |         ''
22 |       end
23 | 
24 |       def inspect
25 |         %(#<#{self.class}: #{message}>)
26 |       end
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/independent.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/independent/delegate_method_matcher'
 2 | require 'shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error'
 3 | 
 4 | module Shoulda
 5 |   module Matchers
 6 |     # This module provides matchers that are used to test behavior outside of
 7 |     # Rails-specific classes.
 8 |     module Independent
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Independent
 4 |       class DelegateMethodMatcher
 5 |         # @private
 6 |         class DelegateObjectNotSpecified < StandardError
 7 |           def message
 8 |             'Delegation needs a target. Use the #to method to define one, e.g.
 9 |             `post_office.should delegate(:deliver_mail).to(:mailman)`'.squish
10 |           end
11 |         end
12 |       end
13 |     end
14 |   end
15 | end
16 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     module Integrations
 5 |       class << self
 6 |         def register_library(klass, name)
 7 |           library_registry.register(klass, name)
 8 |         end
 9 | 
10 |         def find_library!(name)
11 |           library_registry.find!(name)
12 |         end
13 | 
14 |         def register_test_framework(klass, name)
15 |           test_framework_registry.register(klass, name)
16 |         end
17 | 
18 |         def find_test_framework!(name)
19 |           test_framework_registry.find!(name)
20 |         end
21 | 
22 |         private
23 | 
24 |         def library_registry
25 |           @_library_registry ||= Registry.new
26 |         end
27 | 
28 |         def test_framework_registry
29 |           @_test_framework_registry ||= Registry.new
30 |         end
31 |       end
32 |     end
33 |   end
34 | end
35 | 
36 | require 'shoulda/matchers/integrations/configuration'
37 | require 'shoulda/matchers/integrations/configuration_error'
38 | require 'shoulda/matchers/integrations/inclusion'
39 | require 'shoulda/matchers/integrations/rails'
40 | require 'shoulda/matchers/integrations/registry'
41 | 
42 | require 'shoulda/matchers/integrations/libraries'
43 | require 'shoulda/matchers/integrations/test_frameworks'
44 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/configuration.rb:
--------------------------------------------------------------------------------
 1 | require 'set'
 2 | 
 3 | module Shoulda
 4 |   module Matchers
 5 |     module Integrations
 6 |       # @private
 7 |       class Configuration
 8 |         def self.apply(&block)
 9 |           new(&block).apply
10 |         end
11 | 
12 |         attr_reader :test_frameworks
13 | 
14 |         def initialize(&block)
15 |           @test_frameworks = Set.new
16 |           @libraries = Set.new
17 | 
18 |           test_framework :missing_test_framework
19 |           library :missing_library
20 | 
21 |           block.call(self)
22 |         end
23 | 
24 |         def test_framework(name)
25 |           clear_default_test_framework
26 |           @test_frameworks << Integrations.find_test_framework!(name)
27 |         end
28 | 
29 |         def library(name)
30 |           @libraries << Integrations.find_library!(name)
31 |         end
32 | 
33 |         def apply
34 |           if no_test_frameworks_added? && no_libraries_added?
35 |             raise ConfigurationError, <<EOT
36 | shoulda-matchers is not configured correctly. You need to specify at least one
37 | test framework and/or library. For example:
38 | 
39 | Shoulda::Matchers.configure do |config|
40 |   config.integrate do |with|
41 |     with.test_framework :rspec
42 |     with.library :rails
43 |   end
44 | end
45 | EOT
46 |           end
47 | 
48 |           @test_frameworks.each do |test_framework|
49 |             test_framework.include(Shoulda::Matchers::Independent)
50 |             @libraries.each { |library| library.integrate_with(test_framework) }
51 |           end
52 | 
53 |           self
54 |         end
55 | 
56 |         private
57 | 
58 |         def clear_default_test_framework
59 |           @test_frameworks.select!(&:present?)
60 |         end
61 | 
62 |         def no_test_frameworks_added?
63 |           @test_frameworks.empty? || @test_frameworks.none?(&:present?)
64 |         end
65 | 
66 |         def no_libraries_added?
67 |           @libraries.empty?
68 |         end
69 |       end
70 |     end
71 |   end
72 | end
73 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/configuration_error.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       # @private
 5 |       class ConfigurationError < StandardError
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/inclusion.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       # @private
 5 |       module Inclusion
 6 |         def include_into(mod, *other_mods, &block)
 7 |           mods_to_include = other_mods.dup
 8 |           mods_to_extend = other_mods.dup
 9 | 
10 |           if block
11 |             mods_to_include << Module.new(&block)
12 |           end
13 | 
14 |           mod.__send__(:include, *mods_to_include)
15 |           mod.extend(*mods_to_extend)
16 |         end
17 |       end
18 |     end
19 |   end
20 | end
21 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/integrations/libraries/action_controller'
 2 | require 'shoulda/matchers/integrations/libraries/active_model'
 3 | require 'shoulda/matchers/integrations/libraries/active_record'
 4 | require 'shoulda/matchers/integrations/libraries/missing_library'
 5 | require 'shoulda/matchers/integrations/libraries/rails'
 6 | require 'shoulda/matchers/integrations/libraries/routing'
 7 | 
 8 | module Shoulda
 9 |   module Matchers
10 |     module Integrations
11 |       # @private
12 |       module Libraries
13 |       end
14 |     end
15 |   end
16 | end
17 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/action_controller.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class ActionController
 7 |           Integrations.register_library(self, :action_controller)
 8 | 
 9 |           include Integrations::Inclusion
10 |           include Integrations::Rails
11 | 
12 |           def integrate_with(test_framework)
13 |             test_framework.include(matchers_module, type: :controller)
14 | 
15 |             tap do |instance|
16 |               ActiveSupport.on_load(:action_controller_test_case, run_once: true) do
17 |                 instance.include_into(::ActionController::TestCase, instance.matchers_module) do
18 |                   def subject # rubocop:disable Lint/NestedMethodDefinition
19 |                     @controller
20 |                   end
21 |                 end
22 |               end
23 |             end
24 |           end
25 | 
26 |           def matchers_module
27 |             Shoulda::Matchers::ActionController
28 |           end
29 |         end
30 |       end
31 |     end
32 |   end
33 | end
34 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/active_model.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class ActiveModel
 7 |           Integrations.register_library(self, :active_model)
 8 | 
 9 |           include Integrations::Inclusion
10 |           include Integrations::Rails
11 | 
12 |           def integrate_with(test_framework)
13 |             test_framework.include(matchers_module, type: :model)
14 |             include_into(ActiveSupport::TestCase, matchers_module)
15 |           end
16 | 
17 |           private
18 | 
19 |           def matchers_module
20 |             Shoulda::Matchers::ActiveModel
21 |           end
22 |         end
23 |       end
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/active_record.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class ActiveRecord
 7 |           Integrations.register_library(self, :active_record)
 8 | 
 9 |           include Integrations::Inclusion
10 |           include Integrations::Rails
11 | 
12 |           def integrate_with(test_framework)
13 |             test_framework.include(matchers_module, type: :model)
14 |             include_into(ActiveSupport::TestCase, matchers_module)
15 |           end
16 | 
17 |           private
18 | 
19 |           def matchers_module
20 |             Shoulda::Matchers::ActiveRecord
21 |           end
22 |         end
23 |       end
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/missing_library.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class MissingLibrary
 7 |           Integrations.register_library(self, :missing_library)
 8 | 
 9 |           def integrate_with(test_framework)
10 |           end
11 | 
12 |           def rails?
13 |             false
14 |           end
15 |         end
16 |       end
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/rails.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class Rails
 7 |           Integrations.register_library(self, :rails)
 8 | 
 9 |           include Integrations::Rails
10 | 
11 |           SUB_LIBRARIES = [
12 |             :active_model,
13 |             :active_record,
14 |             :action_controller,
15 |             :routing,
16 |           ].freeze
17 | 
18 |           def integrate_with(test_framework)
19 |             Shoulda::Matchers.assertion_exception_class =
20 |               ActiveSupport::TestCase::Assertion
21 | 
22 |             SUB_LIBRARIES.each do |name|
23 |               library = Integrations.find_library!(name)
24 |               library.integrate_with(test_framework)
25 |             end
26 |           end
27 |         end
28 |       end
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/libraries/routing.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module Libraries
 5 |         # @private
 6 |         class Routing
 7 |           Integrations.register_library(self, :routing)
 8 | 
 9 |           include Integrations::Inclusion
10 |           include Integrations::Rails
11 | 
12 |           def integrate_with(test_framework)
13 |             test_framework.include(matchers_module, type: :routing)
14 | 
15 |             tap do |instance|
16 |               ActiveSupport.on_load(:action_controller_test_case, run_once: true) do
17 |                 instance.include_into(::ActionController::TestCase, instance.matchers_module)
18 |               end
19 |             end
20 |           end
21 | 
22 |           def matchers_module
23 |             Shoulda::Matchers::Routing
24 |           end
25 |         end
26 |       end
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/rails.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       # @private
 5 |       module Rails
 6 |         def rails?
 7 |           true
 8 |         end
 9 |       end
10 |     end
11 |   end
12 | end
13 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/registry.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       # @private
 5 |       class Registry
 6 |         def register(klass, name)
 7 |           registry[name] = klass
 8 |         end
 9 | 
10 |         def find!(name)
11 |           find_class!(name).new
12 |         end
13 | 
14 |         private
15 | 
16 |         def registry
17 |           @_registry ||= {}
18 |         end
19 | 
20 |         def find_class!(name)
21 |           registry.fetch(name) do
22 |             raise ArgumentError, "'#{name}' is not registered"
23 |           end
24 |         end
25 |       end
26 |     end
27 |   end
28 | end
29 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks.rb:
--------------------------------------------------------------------------------
 1 | require 'shoulda/matchers/integrations/test_frameworks/active_support_test_case'
 2 | require 'shoulda/matchers/integrations/test_frameworks/minitest_4'
 3 | require 'shoulda/matchers/integrations/test_frameworks/minitest_5'
 4 | require 'shoulda/matchers/integrations/test_frameworks/missing_test_framework'
 5 | require 'shoulda/matchers/integrations/test_frameworks/rspec'
 6 | require 'shoulda/matchers/integrations/test_frameworks/test_unit'
 7 | 
 8 | module Shoulda
 9 |   module Matchers
10 |     module Integrations
11 |       # @private
12 |       module TestFrameworks
13 |       end
14 |     end
15 |   end
16 | end
17 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class ActiveSupportTestCase
 7 |           Integrations.register_test_framework(self, :active_support_test_case)
 8 | 
 9 |           def validate!
10 |           end
11 | 
12 |           def include(*modules, **_options)
13 |             test_case_class.include(*modules)
14 |           end
15 | 
16 |           def n_unit?
17 |             true
18 |           end
19 | 
20 |           def present?
21 |             true
22 |           end
23 | 
24 |           protected
25 | 
26 |           attr_reader :configuration
27 | 
28 |           private
29 | 
30 |           def test_case_class
31 |             ActiveSupport::TestCase
32 |           end
33 |         end
34 |       end
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class Minitest4
 7 |           Integrations.register_test_framework(self, :minitest_4)
 8 | 
 9 |           def validate!
10 |           end
11 | 
12 |           def include(*modules, **_options)
13 |             test_case_class.class_eval do
14 |               include(*modules)
15 |               extend(*modules)
16 |             end
17 |           end
18 | 
19 |           def n_unit?
20 |             true
21 |           end
22 | 
23 |           def present?
24 |             true
25 |           end
26 | 
27 |           private
28 | 
29 |           def test_case_class
30 |             MiniTest::Unit::TestCase
31 |           end
32 |         end
33 |       end
34 |     end
35 |   end
36 | end
37 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class Minitest5
 7 |           Integrations.register_test_framework(self, :minitest_5)
 8 |           Integrations.register_test_framework(self, :minitest)
 9 | 
10 |           def validate!
11 |           end
12 | 
13 |           def include(*modules, **_options)
14 |             test_case_class.class_eval do
15 |               include(*modules)
16 |               extend(*modules)
17 |             end
18 |           end
19 | 
20 |           def n_unit?
21 |             true
22 |           end
23 | 
24 |           def present?
25 |             true
26 |           end
27 | 
28 |           private
29 | 
30 |           def test_case_class
31 |             Minitest::Test
32 |           end
33 |         end
34 |       end
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class MissingTestFramework
 7 |           Integrations.register_test_framework(self, :missing_test_framework)
 8 | 
 9 |           def validate!
10 |             raise TestFrameworkNotConfigured, <<-EOT
11 | You need to set a test framework. Please add the following to your
12 | test helper:
13 | 
14 | Shoulda::Matchers.configure do |config|
15 |   config.integrate do |with|
16 |     # Choose one:
17 |     with.test_framework :rspec
18 |     with.test_framework :minitest    # or, :minitest_5
19 |     with.test_framework :minitest_4
20 |     with.test_framework :test_unit
21 |   end
22 | end
23 |             EOT
24 |           end
25 | 
26 |           def include(*modules, **options)
27 |           end
28 | 
29 |           def n_unit?
30 |             false
31 |           end
32 | 
33 |           def present?
34 |             false
35 |           end
36 |         end
37 |       end
38 |     end
39 |   end
40 | end
41 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class Rspec
 7 |           Integrations.register_test_framework(self, :rspec)
 8 | 
 9 |           def validate!
10 |           end
11 | 
12 |           def include(*modules, **options)
13 |             ::RSpec.configure do |config|
14 |               config.include(*modules, **options)
15 |             end
16 |           end
17 | 
18 |           def n_unit?
19 |             false
20 |           end
21 | 
22 |           def present?
23 |             true
24 |           end
25 |         end
26 |       end
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     module Integrations
 4 |       module TestFrameworks
 5 |         # @private
 6 |         class TestUnit
 7 |           Integrations.register_test_framework(self, :test_unit)
 8 | 
 9 |           def validate!
10 |           end
11 | 
12 |           def include(*modules, **_options)
13 |             test_case_class.class_eval do
14 |               include(*modules)
15 |               extend(*modules)
16 |             end
17 |           end
18 | 
19 |           def n_unit?
20 |             true
21 |           end
22 | 
23 |           def present?
24 |             true
25 |           end
26 | 
27 |           private
28 | 
29 |           def test_case_class
30 |             ::Test::Unit::TestCase
31 |           end
32 |         end
33 |       end
34 |     end
35 |   end
36 | end
37 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/matcher_context.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     class MatcherContext
 5 |       def initialize(context)
 6 |         @context = context
 7 |       end
 8 | 
 9 |       def subject_is_a_class?
10 |         if inside_a_shoulda_context_project? && outside_a_should_block?
11 |           assume_that_subject_is_not_a_class
12 |         else
13 |           context.subject.is_a?(Class)
14 |         end
15 |       end
16 | 
17 |       protected
18 | 
19 |       attr_reader :context
20 | 
21 |       private
22 | 
23 |       def inside_a_shoulda_context_project?
24 |         defined?(Shoulda::Context)
25 |       end
26 | 
27 |       def outside_a_should_block?
28 |         context.is_a?(Class)
29 |       end
30 | 
31 |       def assume_that_subject_is_not_a_class
32 |         false
33 |       end
34 |     end
35 |   end
36 | end
37 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/routing.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     module Routing
 5 |       def route(method, path, port: nil)
 6 |         ActionController::RouteMatcher.new(self, method, path, port: port)
 7 |       end
 8 |     end
 9 |   end
10 | end
11 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/version.rb:
--------------------------------------------------------------------------------
1 | module Shoulda
2 |   module Matchers
3 |     # @private
4 |     VERSION = '6.5.0'.freeze
5 |   end
6 | end
7 | 


--------------------------------------------------------------------------------
/lib/shoulda/matchers/warn.rb:
--------------------------------------------------------------------------------
 1 | module Shoulda
 2 |   module Matchers
 3 |     # @private
 4 |     TERMINAL_MAX_WIDTH = 72
 5 | 
 6 |     # @private
 7 |     def self.warn(message)
 8 |       header = 'Warning from shoulda-matchers:'
 9 |       divider = '*' * TERMINAL_MAX_WIDTH
10 |       wrapped_message = word_wrap(message)
11 |       full_message = [
12 |         divider,
13 |         [header, wrapped_message.strip].join("\n\n"),
14 |         divider,
15 |       ].join("\n")
16 | 
17 |       Kernel.warn(full_message)
18 |     end
19 | 
20 |     # @private
21 |     def self.warn_about_deprecated_method(old_method, new_method)
22 |       warn <<EOT
23 | #{old_method} is deprecated and will be removed in the next major
24 | release. Please use #{new_method} instead.
25 | EOT
26 |     end
27 |   end
28 | end
29 | 


--------------------------------------------------------------------------------
/script/install_gems_in_all_appraisals:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -euo pipefail
 4 | 
 5 | SUPPORTED_VERSIONS=$(script/supported_ruby_versions)
 6 | 
 7 | install-gems-for-version() {
 8 |   local version="$1"
 9 |   (export RBENV_VERSION=$version; bundle && bundle exec appraisal install)
10 | }
11 | 
12 | for version in $SUPPORTED_VERSIONS; do
13 |   echo
14 |   echo "*** Installing gems for $version ***"
15 |   install-gems-for-version $version
16 | done
17 | 


--------------------------------------------------------------------------------
/script/run_all_tests:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -euo pipefail
 4 | 
 5 | SUPPORTED_VERSIONS=$(script/supported_ruby_versions)
 6 | 
 7 | run-tests-for-version() {
 8 |   local version="$1"
 9 |   (export RBENV_VERSION=$version; bundle exec rake)
10 | }
11 | 
12 | for version in $SUPPORTED_VERSIONS; do
13 |   echo
14 |   echo "*** Running tests for $version ***"
15 |   run-tests-for-version $version
16 | done
17 | 


--------------------------------------------------------------------------------
/script/supported_ruby_versions:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | 
3 | require 'yaml'
4 | 
5 | travis_config_path = File.expand_path('../.travis.yml', __dir__)
6 | travis_config = YAML.load_file(travis_config_path)
7 | puts travis_config.fetch('rvm').join(' ')
8 | 


--------------------------------------------------------------------------------
/script/update_gem_in_all_appraisals:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -euo pipefail
 4 | 
 5 | SUPPORTED_VERSIONS=$(script/supported_ruby_versions)
 6 | gem="$1"
 7 | 
 8 | update-gem-for-version() {
 9 |   local version="$1"
10 |   (export RBENV_VERSION=$version; bundle update "$gem"; bundle exec appraisal update "$gem")
11 | }
12 | 
13 | for version in $SUPPORTED_VERSIONS; do
14 |   echo
15 |   echo "*** Updating $gem for $version ***"
16 |   update-gem-for-version $version
17 | done
18 | 


--------------------------------------------------------------------------------
/script/update_gems_in_all_appraisals:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -euo pipefail
 4 | 
 5 | SUPPORTED_VERSIONS=$(script/supported_ruby_versions)
 6 | 
 7 | update-gems-for-version() {
 8 |   local version="$1"
 9 |   (export RBENV_VERSION=$version; bundle update "${@:2}"; bundle exec appraisal update "${@:2}")
10 | }
11 | 
12 | for version in $SUPPORTED_VERSIONS; do
13 |   echo
14 |   echo "*** Updating gems for $version ***"
15 |   update-gems-for-version "$version" "$@"
16 | done
17 | 


--------------------------------------------------------------------------------
/shoulda-matchers.gemspec:
--------------------------------------------------------------------------------
 1 | $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
 2 | require 'shoulda/matchers/version'
 3 | 
 4 | Gem::Specification.new do |s|
 5 |   s.name        = 'shoulda-matchers'
 6 |   s.version     = Shoulda::Matchers::VERSION.dup
 7 |   s.authors     = [
 8 |     'Tammer Saleh',
 9 |     'Joe Ferris',
10 |     'Ryan McGeary',
11 |     'Dan Croak',
12 |     'Matt Jankowski',
13 |     'Stafford Brunk',
14 |     'Elliot Winkler',
15 |   ]
16 |   s.date        = Time.now.strftime('%Y-%m-%d')
17 |   s.email       = 'support@thoughtbot.com'
18 |   s.homepage    = 'https://matchers.shoulda.io/'
19 |   s.summary     = 'Simple one-liner tests for common Rails functionality'
20 |   s.license     = 'MIT'
21 |   s.description = <<~DESC.tr("\n", ' ').squeeze(' ')
22 |     Shoulda Matchers provides RSpec- and Minitest-compatible one-liners to test
23 |     common Rails functionality that, if written by hand, would be much
24 |     longer, more complex, and error-prone.
25 |   DESC
26 | 
27 |   s.metadata = {
28 |     'bug_tracker_uri' => 'https://github.com/thoughtbot/shoulda-matchers/issues',
29 |     'changelog_uri' => 'https://github.com/thoughtbot/shoulda-matchers/blob/main/CHANGELOG.md',
30 |     'documentation_uri' => 'https://matchers.shoulda.io/docs',
31 |     'homepage_uri' => 'https://matchers.shoulda.io',
32 |     'source_code_uri' => 'https://github.com/thoughtbot/shoulda-matchers',
33 |   }
34 | 
35 |   s.files = Dir['{docs,lib}/**/*', 'README.md', 'LICENSE',
36 |     'shoulda-matchers.gemspec']
37 |   s.require_paths = ['lib']
38 | 
39 |   s.required_ruby_version = '>= 3.0.5'
40 |   s.add_dependency('activesupport', '>= 5.2.0')
41 | end
42 | 


--------------------------------------------------------------------------------
/spec/acceptance/active_model_integration_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'acceptance_spec_helper'
 2 | 
 3 | describe 'shoulda-matchers integrates with an ActiveModel project' do
 4 |   before do
 5 |     create_active_model_project
 6 | 
 7 |     write_file 'lib/user.rb', <<-FILE
 8 |       require 'active_model'
 9 | 
10 |       class User
11 |         include ActiveModel::Validations
12 |         attr_accessor :gender
13 | 
14 |         validates :gender, inclusion: { in: %w(male female) }
15 |       end
16 |     FILE
17 | 
18 |     write_file 'spec/user_spec.rb', <<-FILE
19 |       require 'spec_helper'
20 |       require 'user'
21 |       include Shoulda::Matchers::ActiveModel
22 | 
23 |       describe User do
24 |         context 'when gender is valid' do
25 |           it { is_expected.to validate_inclusion_of(:gender).in_array(%w(male female)) }
26 |         end
27 |         context 'when gender is invalid' do
28 |           it { is_expected.to validate_inclusion_of(:gender).in_array(%w(transgender female)) }
29 |         end
30 |       end
31 |     FILE
32 | 
33 |     write_file 'load_dependencies.rb', <<-FILE
34 |       require 'active_model'
35 |       require 'shoulda-matchers'
36 | 
37 |       puts ActiveModel::VERSION::STRING
38 |       puts "Loaded all dependencies without errors"
39 |     FILE
40 | 
41 |     updating_bundle do
42 |       add_rspec_to_project
43 |       add_shoulda_matchers_to_project(
44 |         manually: true,
45 |         with_configuration: false,
46 |       )
47 | 
48 |       write_file 'spec/spec_helper.rb', <<-FILE
49 |         require 'active_model'
50 |         require 'shoulda-matchers'
51 | 
52 |         Shoulda::Matchers.configure do |config|
53 |           config.integrate do |with|
54 |             with.test_framework :rspec
55 | 
56 |             with.library :active_model
57 |           end
58 |         end
59 |       FILE
60 |     end
61 |   end
62 | 
63 |   context 'when using active model library' do
64 |     it 'and loads without errors' do
65 |       result = run_command_within_bundle('ruby load_dependencies.rb')
66 | 
67 |       expect(result).to have_output('Loaded all dependencies without errors')
68 |     end
69 | 
70 |     it 'allows use of inclusion matcher from active model library' do
71 |       result = run_rspec_tests('spec/user_spec.rb')
72 | 
73 |       expect(result).to have_output('2 examples, 1 failure')
74 |       expect(result).to have_output(
75 |         'gender: ["is not included in the list"]',
76 |       )
77 |     end
78 |   end
79 | end
80 | 


--------------------------------------------------------------------------------
/spec/acceptance/multiple_libraries_integration_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'acceptance_spec_helper'
 2 | 
 3 | describe 'shoulda-matchers integrates with multiple libraries' do
 4 |   before do
 5 |     create_rails_application
 6 | 
 7 |     write_file 'db/migrate/1_create_users.rb', <<-FILE
 8 |       class CreateUsers < #{migration_class_name}
 9 |         def self.up
10 |           create_table :users do |t|
11 |             t.string :name
12 |           end
13 |         end
14 |       end
15 |     FILE
16 | 
17 |     run_rake_tasks!('db:drop', 'db:create', 'db:migrate')
18 | 
19 |     write_file 'app/models/user.rb', <<-FILE
20 |       class User < ActiveRecord::Base
21 |         validates_presence_of :name
22 |         validates_uniqueness_of :name
23 |       end
24 |     FILE
25 | 
26 |     add_rspec_file 'spec/models/user_spec.rb', <<-FILE
27 |       describe User do
28 |         subject { User.new(name: "John Smith") }
29 |         it { should validate_presence_of(:name) }
30 |         it { should validate_uniqueness_of(:name) }
31 |       end
32 |     FILE
33 | 
34 |     updating_bundle do
35 |       add_rspec_rails_to_project!
36 |       add_shoulda_matchers_to_project(
37 |         test_frameworks: [:rspec],
38 |         libraries: [:active_record, :active_model],
39 |       )
40 |     end
41 |   end
42 | 
43 |   context 'when using both active_record and active_model libraries' do
44 |     it 'allows the use of matchers from both libraries' do
45 |       result = run_rspec_suite
46 |       expect(result).to have_output('2 examples, 0 failures')
47 |       expect(result).to have_output(
48 |         'is expected to validate that :name cannot be empty/falsy',
49 |       )
50 |       expect(result).to have_output(
51 |         'is expected to validate that :name is case-sensitively unique',
52 |       )
53 |     end
54 |   end
55 | end
56 | 


--------------------------------------------------------------------------------
/spec/acceptance_spec_helper.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'support/tests/current_bundle'
 2 | 
 3 | Tests::CurrentBundle.instance.assert_appraisal!
 4 | 
 5 | #---
 6 | 
 7 | require 'rspec/core'
 8 | 
 9 | require 'spec_helper'
10 | 
11 | Dir[File.join(File.expand_path('support/acceptance/**/*.rb', __dir__))].sort.each do |file|
12 |   require file
13 | end
14 | 
15 | RSpec.configure do |config|
16 |   if config.respond_to?(:infer_spec_type_from_file_location!)
17 |     config.infer_spec_type_from_file_location!
18 |   end
19 | 
20 |   AcceptanceTests::Helpers.configure_example_group(config)
21 | 
22 |   config.include AcceptanceTests::Matchers
23 | end
24 | 


--------------------------------------------------------------------------------
/spec/doublespeak_spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'shoulda/matchers/doublespeak'
2 | require 'spec_helper'
3 | 


--------------------------------------------------------------------------------
/spec/report_warnings.rb:
--------------------------------------------------------------------------------
1 | require 'warnings_logger'
2 | 
3 | WarningsLogger.configure do |config|
4 |   config.project_name = 'shoulda-matchers'
5 |   config.project_directory = Pathname.new('..').expand_path(__dir__)
6 | end
7 | 
8 | WarningsLogger.enable
9 | 


--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
 1 | PROJECT_ROOT = File.expand_path('..', __dir__)
 2 | $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
 3 | 
 4 | require 'pry'
 5 | require 'pry-byebug' if RUBY_VERSION < '3.2'
 6 | 
 7 | require 'rspec'
 8 | 
 9 | RSpec.configure do |config|
10 |   config.expect_with :rspec do |c|
11 |     c.syntax = :expect
12 |   end
13 | 
14 |   config.order = :random
15 |   config.default_formatter = 'doc'
16 |   config.mock_with :rspec
17 |   config.example_status_persistence_file_path = 'spec/examples.txt'
18 | end
19 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'helpers/active_model_helpers'
 2 | require_relative 'helpers/active_record_helpers'
 3 | require_relative 'helpers/base_helpers'
 4 | require_relative 'helpers/command_helpers'
 5 | require_relative 'helpers/gem_helpers'
 6 | require_relative 'helpers/n_unit_helpers'
 7 | require_relative 'helpers/rails_migration_helpers'
 8 | require_relative 'helpers/rails_version_helpers'
 9 | require_relative 'helpers/rspec_helpers'
10 | require_relative 'helpers/ruby_version_helpers'
11 | require_relative 'helpers/step_helpers'
12 | 
13 | module AcceptanceTests
14 |   module Helpers
15 |     def self.configure_example_group(example_group)
16 |       example_group.include(self)
17 | 
18 |       example_group.before do
19 |         fs.clean
20 |       end
21 |     end
22 | 
23 |     include ActiveModelHelpers
24 |     include ActiveRecordHelpers
25 |     include BaseHelpers
26 |     include CommandHelpers
27 |     include GemHelpers
28 |     include NUnitHelpers
29 |     include RailsMigrationHelpers
30 |     include RailsVersionHelpers
31 |     include RspecHelpers
32 |     include RubyVersionHelpers
33 |     include StepHelpers
34 |   end
35 | end
36 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/active_model_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module ActiveModelHelpers
 5 |     include GemHelpers
 6 | 
 7 |     def active_model_version
 8 |       bundle_version_of('activemodel')
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/active_record_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module ActiveRecordHelpers
 5 |     include GemHelpers
 6 | 
 7 |     def active_record_version
 8 |       bundle_version_of('activerecord')
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/array_helpers.rb:
--------------------------------------------------------------------------------
 1 | module AcceptanceTests
 2 |   module ArrayHelpers
 3 |     def to_sentence(array)
 4 |       case array.size
 5 |       when 1
 6 |         array[0]
 7 |       when 2
 8 |         array.join(' and ')
 9 |       else
10 |         to_sentence(array[1..-2].join(', '), [array[-1]])
11 |       end
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/base_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../../tests/bundle'
 2 | require_relative '../../tests/database'
 3 | require_relative '../../tests/filesystem'
 4 | 
 5 | module AcceptanceTests
 6 |   module BaseHelpers
 7 |     def fs
 8 |       @_fs ||= Tests::Filesystem.new
 9 |     end
10 | 
11 |     def bundle
12 |       @_bundle ||= Tests::Bundle.new
13 |     end
14 | 
15 |     def database
16 |       @_database ||= Tests::Database.instance
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/command_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'base_helpers'
 2 | require_relative '../../tests/command_runner'
 3 | 
 4 | module AcceptanceTests
 5 |   module CommandHelpers
 6 |     include BaseHelpers
 7 |     extend RSpec::Matchers::DSL
 8 | 
 9 |     def run_command(*args)
10 |       Tests::CommandRunner.run(*args) do |runner|
11 |         runner.directory = fs.project_directory
12 |         yield runner if block_given?
13 |       end
14 |     end
15 | 
16 |     def run_command!(*args)
17 |       run_command(*args) do |runner|
18 |         runner.run_successfully = true
19 |         yield runner if block_given?
20 |       end
21 |     end
22 | 
23 |     def run_command_isolated_from_bundle(*args)
24 |       run_command(*args) do |runner|
25 |         runner.around_command do |run_command|
26 |           Bundler.with_original_env(&run_command)
27 |         end
28 | 
29 |         yield runner if block_given?
30 |       end
31 |     end
32 | 
33 |     def run_command_isolated_from_bundle!(*args)
34 |       run_command_isolated_from_bundle(*args) do |runner|
35 |         runner.run_successfully = true
36 |         yield runner if block_given?
37 |       end
38 |     end
39 | 
40 |     def run_command_within_bundle(*args)
41 |       run_command_isolated_from_bundle(*args) do |runner|
42 |         runner.command_prefix = "bundle _#{bundle.version}_ exec"
43 |         runner.env['BUNDLE_GEMFILE'] = fs.find_in_project('Gemfile').to_s
44 | 
45 |         yield runner if block_given?
46 |       end
47 |     end
48 | 
49 |     def run_command_within_bundle!(*args)
50 |       run_command_within_bundle(*args) do |runner|
51 |         runner.run_successfully = true
52 |         yield runner if block_given?
53 |       end
54 |     end
55 | 
56 |     def run_rake_tasks(*tasks)
57 |       options = tasks.last.is_a?(Hash) ? tasks.pop : {}
58 |       args = ['rake', *tasks, '--trace'] + [options]
59 |       run_command_within_bundle(*args)
60 |     end
61 | 
62 |     def run_rake_tasks!(*tasks)
63 |       options = tasks.last.is_a?(Hash) ? tasks.pop : {}
64 |       args = ['rake', *tasks, '--trace'] + [options]
65 |       run_command_within_bundle!(*args)
66 |     end
67 |   end
68 | end
69 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/file_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'base_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module FileHelpers
 5 |     include BaseHelpers
 6 | 
 7 |     def append_to_file(path, content, options = {})
 8 |       fs.append_to_file(path, content, options)
 9 |     end
10 | 
11 |     def remove_from_file(path, pattern)
12 |       fs.remove_from_file(path, pattern)
13 |     end
14 | 
15 |     def write_file(path, content)
16 |       fs.write(path, content)
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/gem_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'base_helpers'
 2 | require_relative 'command_helpers'
 3 | require_relative 'file_helpers'
 4 | 
 5 | module AcceptanceTests
 6 |   module GemHelpers
 7 |     include BaseHelpers
 8 |     include CommandHelpers
 9 |     include FileHelpers
10 | 
11 |     def add_gem(gem, *args)
12 |       bundle.add_gem(gem, *args)
13 |     end
14 | 
15 |     def install_gems
16 |       bundle.install_gems
17 |     end
18 | 
19 |     def updating_bundle(&block)
20 |       bundle.updating(&block)
21 |     end
22 | 
23 |     def bundle_version_of(gem)
24 |       bundle.version_of(gem)
25 |     end
26 | 
27 |     def bundle_includes?(gem)
28 |       bundle.includes?(gem)
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/minitest_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module MinitestHelpers
 5 |     include GemHelpers
 6 | 
 7 |     def minitest_version
 8 |       bundle_version_of('minitest')
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/n_unit_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'rails_version_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module NUnitHelpers
 5 |     include RailsVersionHelpers
 6 | 
 7 |     def n_unit_test_case_superclass
 8 |       case default_test_framework
 9 |       when :test_unit then 'Test::Unit::TestCase'
10 |       when :minitest_4 then 'MiniTest::Unit::TestCase'
11 |       else 'Minitest::Test'
12 |       end
13 |     end
14 | 
15 |     def default_test_framework
16 |       :minitest
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/pluralization_helpers.rb:
--------------------------------------------------------------------------------
 1 | module AcceptanceTests
 2 |   module PluralizationHelpers
 3 |     def pluralize(count, singular_version, plural_version = nil)
 4 |       plural_version ||= "#{singular_version}s"
 5 | 
 6 |       if count == 1
 7 |         "#{count} #{singular_version}"
 8 |       else
 9 |         "#{count} #{plural_version}"
10 |       end
11 |     end
12 |   end
13 | end
14 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/rails_migration_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module RailsMigrationHelpers
 5 |     include RailsVersionHelpers
 6 | 
 7 |     def migration_class_name
 8 |       "ActiveRecord::Migration[#{rails_version_for_migration}]"
 9 |     end
10 | 
11 |     private
12 | 
13 |     def rails_version_for_migration
14 |       rails_version.to_s.split('.')[0..1].join('.')
15 |     end
16 |   end
17 | end
18 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/rails_version_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module RailsVersionHelpers
 5 |     include GemHelpers
 6 | 
 7 |     def rails_version
 8 |       bundle_version_of('rails')
 9 |     end
10 | 
11 |     def rails_6_x?
12 |       rails_version =~ '~> 6.0'
13 |     end
14 |   end
15 | end
16 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/rspec_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'gem_helpers'
 2 | 
 3 | module AcceptanceTests
 4 |   module RspecHelpers
 5 |     include GemHelpers
 6 | 
 7 |     def rspec_core_version
 8 |       bundle_version_of('rspec-core')
 9 |     end
10 | 
11 |     def rspec_expectations_version
12 |       bundle_version_of('rspec-expectations')
13 |     end
14 | 
15 |     def rspec_rails_version
16 |       bundle_version_of('rspec-rails')
17 |     end
18 | 
19 |     def add_rspec_file(path, content)
20 |       content = "require 'rails_helper'\n#{content}"
21 |       write_file path, content
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/helpers/ruby_version_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../../tests/version'
 2 | 
 3 | module AcceptanceTests
 4 |   module RubyVersionHelpers
 5 |     def ruby_version
 6 |       Tests::Version.new(RUBY_VERSION)
 7 |     end
 8 | 
 9 |     def ruby_gt_3_1?
10 |       ruby_version >= '3.1'
11 |     end
12 |   end
13 | end
14 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/matchers/have_output.rb:
--------------------------------------------------------------------------------
 1 | module AcceptanceTests
 2 |   module Matchers
 3 |     def have_output(output)
 4 |       HaveOutputMatcher.new(output)
 5 |     end
 6 | 
 7 |     class HaveOutputMatcher
 8 |       def initialize(output)
 9 |         @output = output
10 |       end
11 | 
12 |       def matches?(runner)
13 |         @runner = runner
14 |         runner.has_output?(output)
15 |       end
16 | 
17 |       def failure_message
18 |         "Expected command to have output, but did not.\n\n"\
19 |           "Command: #{runner.formatted_command}\n\n"\
20 |           "Expected output:\n" +
21 |           output.inspect + "\n\n"\
22 |           "Actual output:\n" +
23 |           runner.output
24 |       end
25 | 
26 |       protected
27 | 
28 |       attr_reader :output, :runner
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../helpers/pluralization_helpers'
 2 | require_relative '../helpers/rails_version_helpers'
 3 | 
 4 | module AcceptanceTests
 5 |   module Matchers
 6 |     def indicate_number_of_tests_was_run(expected_output)
 7 |       IndicateNumberOfTestsWasRunMatcher.new(expected_output)
 8 |     end
 9 | 
10 |     class IndicateNumberOfTestsWasRunMatcher
11 |       include PluralizationHelpers
12 |       include RailsVersionHelpers
13 | 
14 |       def initialize(number)
15 |         @number = number
16 |       end
17 | 
18 |       def matches?(runner)
19 |         @runner = runner
20 |         expected_output === actual_output
21 |       end
22 | 
23 |       def failure_message
24 |         message = "Expected output to indicate that #{some_tests_were_run}.\n" +
25 |                   "Expected output: #{expected_output}\n"
26 | 
27 |         message <<
28 |           if actual_output.empty?
29 |             'Actual output: (empty)'
30 |           else
31 |             "Actual output:\n#{actual_output}"
32 |           end
33 | 
34 |         message
35 |       end
36 | 
37 |       protected
38 | 
39 |       attr_reader :number, :runner
40 | 
41 |       private
42 | 
43 |       def expected_output
44 |         /#{number} (?:tests?|runs?|examples?)(?:, #{number} assertions)?, 0 failures(?:, 0 errors(?:, 0 skips)?)?/
45 |       end
46 | 
47 |       def actual_output
48 |         runner.output
49 |       end
50 | 
51 |       def some_tests_were_run
52 |         "#{pluralize(number, 'test was', 'tests were')} run"
53 |       end
54 |     end
55 |   end
56 | end
57 | 


--------------------------------------------------------------------------------
/spec/support/tests/bundle.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'filesystem'
 2 | require_relative 'command_runner'
 3 | require_relative 'version'
 4 | 
 5 | module Tests
 6 |   class Bundle
 7 |     def initialize
 8 |       @already_updating = false
 9 |       @fs = Filesystem.new
10 |     end
11 | 
12 |     def updating
13 |       if already_updating?
14 |         yield self
15 |         return
16 |       end
17 | 
18 |       @already_updating = true
19 | 
20 |       yield self
21 | 
22 |       @already_updating = false
23 | 
24 |       install_gems
25 |     end
26 | 
27 |     def add_gem(gem, *args)
28 |       updating do
29 |         options = args.last.is_a?(Hash) ? args.pop : {}
30 |         version = args.shift
31 |         line = assemble_gem_line(gem, version, options)
32 |         fs.append_to_file('Gemfile', line)
33 |       end
34 |     end
35 | 
36 |     def remove_gem(gem)
37 |       updating do
38 |         fs.comment_lines_matching('Gemfile', /^ *gem ("|')#{gem}\1/)
39 |       end
40 |     end
41 | 
42 |     def install_gems
43 |       CommandRunner.run!('bundle install --local') do |runner|
44 |         runner.retries = 5
45 |       end
46 |     end
47 | 
48 |     def version_of(gem)
49 |       Version.new(Bundler.definition.specs[gem][0].version)
50 |     end
51 | 
52 |     def includes?(gem)
53 |       Bundler.definition.dependencies.any? do |dependency|
54 |         dependency.name == gem
55 |       end
56 |     end
57 | 
58 |     def version
59 |       Bundler::VERSION
60 |     end
61 | 
62 |     protected
63 | 
64 |     attr_reader :fs
65 | 
66 |     private
67 | 
68 |     def already_updating?
69 |       @already_updating
70 |     end
71 | 
72 |     def assemble_gem_line(gem, version, options)
73 |       formatted_options = options.
74 |         map { |key, value| "#{key}: #{formatted_value(value)}" }.
75 |         join(', ')
76 | 
77 |       line = %(gem '#{gem}')
78 | 
79 |       if version
80 |         line << %(, '#{version}')
81 |       end
82 | 
83 |       if options.any?
84 |         line << %(, #{formatted_options})
85 |       end
86 | 
87 |       line << "\n"
88 |     end
89 | 
90 |     def formatted_value(value)
91 |       if value.is_a?(Pathname)
92 |         value.to_s.inspect
93 |       else
94 |         value.inspect
95 |       end
96 |     end
97 |   end
98 | end
99 | 


--------------------------------------------------------------------------------
/spec/support/tests/current_bundle.rb:
--------------------------------------------------------------------------------
 1 | require 'bundler'
 2 | require 'appraisal'
 3 | 
 4 | module Tests
 5 |   class CurrentBundle
 6 |     AppraisalNotSpecified = Class.new(ArgumentError)
 7 | 
 8 |     include Singleton
 9 | 
10 |     def assert_appraisal!
11 |       unless appraisal_in_use?
12 |         message = <<EOT
13 | 
14 | 
15 | Please run tests starting with `appraisal <appraisal_name>`.
16 | Possible appraisals are: #{available_appraisals}
17 | 
18 | EOT
19 |         raise AppraisalNotSpecified, message
20 |       end
21 |     end
22 | 
23 |     def appraisal_in_use?
24 |       path.dirname == root.join('gemfiles')
25 |     end
26 | 
27 |     def current_or_latest_appraisal
28 |       current_appraisal || latest_appraisal
29 |     end
30 | 
31 |     def latest_appraisal
32 |       available_appraisals.max
33 |     end
34 | 
35 |     def available_appraisals
36 |       Appraisal::AppraisalFile.each.map(&:name)
37 |     end
38 | 
39 |     private
40 | 
41 |     def current_appraisal
42 |       if appraisal_in_use?
43 |         File.basename(path, '.gemfile')
44 |       end
45 |     end
46 | 
47 |     def path
48 |       Bundler.default_gemfile
49 |     end
50 | 
51 |     def root
52 |       Pathname.new('../../../..').expand_path(__FILE__)
53 |     end
54 |   end
55 | end
56 | 


--------------------------------------------------------------------------------
/spec/support/tests/database.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'database_configuration'
 2 | 
 3 | module Tests
 4 |   class Database
 5 |     NAME = 'shoulda-matchers-test'.freeze
 6 |     ADAPTER_NAME = ENV.fetch('DATABASE_ADAPTER', 'sqlite3').to_sym
 7 | 
 8 |     include Singleton
 9 | 
10 |     attr_reader :config
11 | 
12 |     def initialize
13 |       @config = Tests::DatabaseConfiguration.for(NAME, ADAPTER_NAME)
14 |     end
15 | 
16 |     def name
17 |       config.database
18 |     end
19 | 
20 |     def adapter_name
21 |       config.adapter
22 |     end
23 | 
24 |     def adapter_class
25 |       config.adapter_class
26 |     end
27 |   end
28 | end
29 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_adapters/config/postgresql.yml:
--------------------------------------------------------------------------------
 1 | default: &default
 2 |   adapter: postgresql
 3 |   encoding: unicode
 4 |   pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
 5 |   host: <%= ENV.fetch("DB_HOST", "localhost") %>
 6 |   username: <%= ENV.fetch("DB_USER", "postgres") %>
 7 |   password: <%= ENV.fetch("DB_USER_PASSWORD", "postgres") %>
 8 | 
 9 | development:
10 |   <<: *default
11 |   database: shoulda-matchers-test_development
12 | 
13 | test:
14 |   <<: *default
15 |   database: shoulda-matchers-test_test
16 | 
17 | production:
18 |   <<: *default
19 |   database: shoulda-matchers-test_production
20 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_adapters/config/sqlite3.yml:
--------------------------------------------------------------------------------
 1 | default: &default
 2 |   adapter: sqlite3
 3 |   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
 4 |   timeout: 5000
 5 | 
 6 | development:
 7 |   <<: *default
 8 |   database: db/development.sqlite3
 9 | 
10 | test:
11 |   <<: *default
12 |   database: db/test.sqlite3
13 | 
14 | production:
15 |   <<: *default
16 |   database: db/production.sqlite3
17 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_adapters/postgresql.rb:
--------------------------------------------------------------------------------
 1 | module Tests
 2 |   module DatabaseAdapters
 3 |     class PostgreSQL
 4 |       def self.name
 5 |         :postgresql
 6 |       end
 7 | 
 8 |       attr_reader :database
 9 | 
10 |       def initialize(database)
11 |         @database = database
12 |       end
13 | 
14 |       def adapter
15 |         self.class.name
16 |       end
17 | 
18 |       def require_dependencies
19 |         require 'pg'
20 |       end
21 |     end
22 | 
23 |     DatabaseConfigurationRegistry.instance.register(PostgreSQL)
24 |   end
25 | end
26 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_adapters/sqlite3.rb:
--------------------------------------------------------------------------------
 1 | module Tests
 2 |   module DatabaseAdapters
 3 |     class SQLite3
 4 |       def self.name
 5 |         :sqlite3
 6 |       end
 7 | 
 8 |       def initialize(_database)
 9 |       end
10 | 
11 |       def adapter
12 |         self.class.name
13 |       end
14 | 
15 |       def database
16 |         'db/db.sqlite3'
17 |       end
18 | 
19 |       def require_dependencies
20 |         require 'sqlite3'
21 |       end
22 |     end
23 | 
24 |     DatabaseConfigurationRegistry.instance.register(SQLite3)
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_configuration.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'database_configuration_registry'
 2 | require 'delegate'
 3 | 
 4 | module Tests
 5 |   class DatabaseConfiguration < SimpleDelegator
 6 |     attr_reader :adapter_class
 7 | 
 8 |     def self.for(database_name, adapter_name)
 9 |       config_class = DatabaseConfigurationRegistry.instance.get(adapter_name)
10 |       config = config_class.new(database_name)
11 |       new(config)
12 |     end
13 | 
14 |     def initialize(config)
15 |       @adapter_class = config.class.to_s.split('::').last
16 |       super(config)
17 |     end
18 | 
19 |     def load_file
20 |       YAML::load_file(File.join(__dir__, "database_adapters/config/#{adapter}.yml"), aliases: true)
21 |     rescue ArgumentError
22 |       YAML::load_file(File.join(__dir__, "database_adapters/config/#{adapter}.yml"))
23 |     end
24 |   end
25 | end
26 | 


--------------------------------------------------------------------------------
/spec/support/tests/database_configuration_registry.rb:
--------------------------------------------------------------------------------
 1 | require 'singleton'
 2 | 
 3 | module Tests
 4 |   class DatabaseConfigurationRegistry
 5 |     include Singleton
 6 | 
 7 |     def initialize
 8 |       @registry = {}
 9 |     end
10 | 
11 |     def register(config_class)
12 |       registry[config_class.name] = config_class
13 |     end
14 | 
15 |     def get(name)
16 |       registry.fetch(name) do
17 |         raise KeyError, "No such adapter registered: #{name}"
18 |       end
19 |     end
20 | 
21 |     protected
22 | 
23 |     attr_reader :registry
24 |   end
25 | end
26 | 
27 | require_relative 'database_adapters/postgresql'
28 | require_relative 'database_adapters/sqlite3'
29 | 


--------------------------------------------------------------------------------
/spec/support/tests/version.rb:
--------------------------------------------------------------------------------
 1 | module Tests
 2 |   class Version
 3 |     def initialize(version)
 4 |       @version = Gem::Version.new(version.to_s)
 5 |     end
 6 | 
 7 |     def <(other_version)
 8 |       compare?(:<, other_version)
 9 |     end
10 | 
11 |     def <=(other_version)
12 |       compare?(:<=, other_version)
13 |     end
14 | 
15 |     def ==(other_version)
16 |       compare?(:==, other_version)
17 |     end
18 | 
19 |     def >=(other_version)
20 |       compare?(:>=, other_version)
21 |     end
22 | 
23 |     def >(other_version)
24 |       compare?(:>, other_version)
25 |     end
26 | 
27 |     def =~(other_version)
28 |       Gem::Requirement.new(other_version).satisfied_by?(version)
29 |     end
30 | 
31 |     def to_s
32 |       version.to_s
33 |     end
34 | 
35 |     protected
36 | 
37 |     attr_reader :version
38 | 
39 |     private
40 | 
41 |     def compare?(operator, other_version)
42 |       Gem::Requirement.new("#{operator} #{other_version}").satisfied_by?(version)
43 |     end
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/spec/support/unit/attribute.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   class Attribute
 3 |     DEFAULT_COLUMN_TYPE = :string
 4 |     DEFAULT_COLUMN_OPTIONS = {
 5 |       null: false,
 6 |       array: false,
 7 |     }.freeze
 8 | 
 9 |     def initialize(args)
10 |       @args = args
11 |     end
12 | 
13 |     def name
14 |       args.fetch(:name)
15 |     end
16 | 
17 |     def column_type
18 |       args.fetch(:column_type, DEFAULT_COLUMN_TYPE)
19 |     end
20 | 
21 |     def column_options
22 |       {
23 |         type: column_type,
24 |         options: DEFAULT_COLUMN_OPTIONS.merge(args.fetch(:column_options, {})),
25 |       }
26 |     end
27 | 
28 |     def array?
29 |       column_options[:array]
30 |     end
31 | 
32 |     def default_value
33 |       args.fetch(:default_value) do
34 |         if column_options[:null]
35 |           nil
36 |         else
37 |           Shoulda::Matchers::Util.dummy_value_for(value_type, array: array?)
38 |         end
39 |       end
40 |     end
41 | 
42 |     protected
43 | 
44 |     attr_reader :args
45 |   end
46 | end
47 | 


--------------------------------------------------------------------------------
/spec/support/unit/capture.rb:
--------------------------------------------------------------------------------
 1 | module Kernel
 2 |   # #capture, #silence_stream, and #silence_stderr were removed in rails 5.0,
 3 |   # but we keep it them here
 4 | 
 5 |   if method_defined?(:capture)
 6 |     undef_method :capture
 7 |   end
 8 | 
 9 |   def capture(stream)
10 |     stream = stream.to_s
11 |     captured_stream = Tempfile.new(stream)
12 |     stream_io = eval("$#{stream}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
13 |     origin_stream = stream_io.dup
14 |     stream_io.reopen(captured_stream)
15 | 
16 |     yield
17 | 
18 |     stream_io.rewind
19 |     captured_stream.read
20 |   ensure
21 |     captured_stream.unlink
22 |     stream_io.reopen(origin_stream)
23 |   end
24 | 
25 |   if method_defined?(:silence_stream)
26 |     undef_method :silence_stream
27 |   end
28 | 
29 |   def silence_stream(stream)
30 |     old_stream = stream.dup
31 |     stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
32 |     stream.sync = true
33 |     yield
34 |   ensure
35 |     stream.reopen(old_stream)
36 |     old_stream.close
37 |   end
38 | 
39 |   if method_defined?(:silence_stderr)
40 |     undef_method :silence_stderr
41 |   end
42 | 
43 |   def silence_stderr
44 |     silence_stream($stderr) { yield if block_given? }
45 |   end
46 | end
47 | 


--------------------------------------------------------------------------------
/spec/support/unit/configuration.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   class Configuration
 3 |     CLASSES = %i[
 4 |       ActiveModelHelpers
 5 |       ActiveModelVersions
 6 |       ActiveRecordVersions
 7 |       ClassBuilder
 8 |       ColumnTypeHelpers
 9 |       ControllerBuilder
10 |       DatabaseHelpers
11 |       I18nFaker
12 |       MailerBuilder
13 |       MessageHelpers
14 |       ModelBuilder
15 |       RailsVersions
16 |       ValidationMatcherScenarioHelpers
17 |     ].freeze
18 | 
19 |     def self.configure_example_groups(config)
20 |       CLASSES.each do |class_name|
21 |         constantized_class = "UnitTests::#{class_name}"
22 |         Object.const_get(constantized_class).configure_example_group(config)
23 |       end
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/spec/support/unit/create_model_arguments/has_many.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module CreateModelArguments
 3 |     class HasMany < Basic
 4 |       def columns
 5 |         super.except(attribute_name)
 6 |       end
 7 | 
 8 |       private
 9 | 
10 |       def default_attribute_name
11 |         :children
12 |       end
13 |     end
14 |   end
15 | end
16 | 


--------------------------------------------------------------------------------
/spec/support/unit/create_model_arguments/uniqueness_matcher.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module CreateModelArguments
 3 |     class UniquenessMatcher < Basic
 4 |       def self.normalize_attribute(attribute)
 5 |         if attribute.is_a?(Hash)
 6 |           Attribute.new(attribute)
 7 |         else
 8 |           Attribute.new(name: attribute)
 9 |         end
10 |       end
11 | 
12 |       def self.normalize_attributes(attributes)
13 |         attributes.map do |attribute|
14 |           normalize_attribute(attribute)
15 |         end
16 |       end
17 | 
18 |       def columns
19 |         attributes.inject({}) do |options, attribute|
20 |           options.merge!(
21 |             attribute.name => {
22 |               type: attribute.column_type,
23 |               options: attribute.column_options,
24 |             },
25 |           )
26 |         end
27 |       end
28 | 
29 |       def validation_options
30 |         super.merge!(scope: scope_attribute_names)
31 |       end
32 | 
33 |       def attribute_default_values_by_name
34 |         attributes.inject({}) do |values, attribute|
35 |           values.merge!(attribute.name => attribute.default_value)
36 |         end
37 |       end
38 | 
39 |       protected
40 | 
41 |       def attribute_class
42 |         Attribute
43 |       end
44 | 
45 |       private
46 | 
47 |       def attributes
48 |         [attribute] + scope_attributes + additional_attributes
49 |       end
50 | 
51 |       def scope_attribute_names
52 |         scope_attributes.map(&:name)
53 |       end
54 | 
55 |       def scope_attributes
56 |         @_scope_attributes ||= self.class.normalize_attributes(
57 |           args.fetch(:scopes, []),
58 |         )
59 |       end
60 | 
61 |       def additional_attributes
62 |         @_additional_attributes ||= self.class.normalize_attributes(
63 |           args.fetch(:additional_attributes, []),
64 |         )
65 |       end
66 | 
67 |       class Attribute < UnitTests::Attribute
68 |         def value_type
69 |           args.fetch(:value_type) { column_type }
70 |         end
71 |       end
72 |     end
73 |   end
74 | end
75 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/active_model_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ActiveModelHelpers
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |     end
 6 | 
 7 |     def custom_validation(options = {}, &block)
 8 |       attribute_name = options.fetch(:attribute_name, :attr)
 9 |       attribute_type = options.fetch(:attribute_type, :integer)
10 |       column_options = options.fetch(:column_options, {})
11 |       attribute_options = { type: attribute_type, options: column_options }
12 | 
13 |       define_model(:example, attribute_name => attribute_options) do
14 |         validate :custom_validation
15 | 
16 |         define_method(:custom_validation, &block)
17 |       end.new
18 |     end
19 |     alias record_with_custom_validation custom_validation
20 | 
21 |     def validating_format(options)
22 |       define_model :example, attr: :string do
23 |         validates_format_of :attr, options
24 |       end.new
25 |     end
26 |   end
27 | end
28 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/active_model_versions.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ActiveModelVersions
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |       example_group.extend(self)
 6 |     end
 7 | 
 8 |     def active_model_version
 9 |       Tests::Version.new(::ActiveModel::VERSION::STRING)
10 |     end
11 |   end
12 | end
13 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/active_record_versions.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ActiveRecordVersions
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |       example_group.extend(self)
 6 |     end
 7 | 
 8 |     extend self
 9 | 
10 |     def active_record_version
11 |       Tests::Version.new(::ActiveRecord::VERSION::STRING)
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/active_resource_builder.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ActiveResourceBuilder
 3 |     def self.configure_example_group(example_group)
 4 |       require 'active_resource'
 5 | 
 6 |       example_group.include ActiveResourceBuilder
 7 | 
 8 |       example_group.after do
 9 |         ActiveSupport::Dependencies.clear
10 |       end
11 |     end
12 | 
13 |     def define_active_resource_class(class_name, attributes = {}, &block)
14 |       define_class(class_name, ActiveResource::Base) do
15 |         schema do
16 |           attributes.each do |attr, type|
17 |             attribute attr, type
18 |           end
19 |         end
20 | 
21 |         if block_given?
22 |           class_eval(&block)
23 |         end
24 |       end
25 |     end
26 |   end
27 | end
28 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/allow_value_matcher_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../record_with_different_error_attribute_builder'
 2 | require_relative '../record_builder_with_i18n_validation_message'
 3 | 
 4 | module UnitTests
 5 |   module AllowValueMatcherHelpers
 6 |     def builder_for_record_with_different_error_attribute(options = {})
 7 |       RecordWithDifferentErrorAttributeBuilder.new(options)
 8 |     end
 9 | 
10 |     def builder_for_record_with_unrelated_error(options = {})
11 |       RecordWithUnrelatedErrorBuilder.new(options)
12 |     end
13 | 
14 |     def builder_for_record_with_different_error_attribute_using_i18n(options = {})
15 |       builder = builder_for_record_with_different_error_attribute(options)
16 |       RecordBuilderWithI18nValidationMessage.new(builder)
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/application_configuration_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ApplicationConfigurationHelpers
 3 |     def with_belongs_to_as_required_by_default(&block)
 4 |       configuring_application(
 5 |         ::ActiveRecord::Base,
 6 |         :belongs_to_required_by_default,
 7 |         true,
 8 |         &block
 9 |       )
10 |     end
11 | 
12 |     def with_belongs_to_as_optional_by_default(&block)
13 |       configuring_application(
14 |         ::ActiveRecord::Base,
15 |         :belongs_to_required_by_default,
16 |         false,
17 |         &block
18 |       )
19 |     end
20 | 
21 |     def with_strict_loading_by_default_enabled(&block)
22 |       configuring_application(
23 |         ::ActiveRecord::Base,
24 |         :strict_loading_by_default,
25 |         true,
26 |         &block
27 |       )
28 |     end
29 | 
30 |     def with_strict_loading_by_default_disabled(&block)
31 |       configuring_application(
32 |         ::ActiveRecord::Base,
33 |         :strict_loading_by_default,
34 |         false,
35 |         &block
36 |       )
37 |     end
38 | 
39 |     private
40 | 
41 |     def configuring_application(config, name, value)
42 |       previous_value = config.send(name)
43 |       config.send("#{name}=", value)
44 |       yield
45 |     ensure
46 |       config.send("#{name}=", previous_value)
47 |     end
48 |   end
49 | end
50 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/column_type_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ColumnTypeHelpers
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |     end
 6 | 
 7 |     def column_type_class_namespace
 8 |       if database_adapter == :postgresql
 9 |         ActiveRecord::ConnectionAdapters::PostgreSQL
10 |       else
11 |         ActiveRecord::Type
12 |       end
13 |     end
14 | 
15 |     def column_type_class_for(type)
16 |       namespace =
17 |         if type == :integer && database_adapter == :postgresql
18 |           column_type_class_namespace::OID
19 |         else
20 |           column_type_class_namespace
21 |         end
22 | 
23 |       namespace.const_get(type.to_s.camelize)
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/confirmation_matcher_helpers.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../record_validating_confirmation_builder'
 2 | require_relative '../record_builder_with_i18n_validation_message'
 3 | 
 4 | module UnitTests
 5 |   module ConfirmationMatcherHelpers
 6 |     def builder_for_record_validating_confirmation(options = {})
 7 |       RecordValidatingConfirmationBuilder.new(options)
 8 |     end
 9 | 
10 |     def builder_for_record_validating_confirmation_with_18n_message(options = {})
11 |       builder = builder_for_record_validating_confirmation(options)
12 |       RecordBuilderWithI18nValidationMessage.new(
13 |         builder,
14 |         validation_message_key: :confirmation,
15 |       )
16 |     end
17 |   end
18 | end
19 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/controller_builder.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ControllerBuilder
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 | 
 6 |       example_group.after do
 7 |         delete_temporary_views
 8 |         restore_original_routes
 9 |       end
10 |     end
11 | 
12 |     def define_controller(class_name, &block)
13 |       new_class_name = if class_name.to_s =~ /Controller$/
14 |                          class_name.to_s
15 |                        else
16 |                          "#{class_name}Controller"
17 |                        end
18 | 
19 |       define_class(new_class_name, ActionController::Base, &block)
20 |     end
21 | 
22 |     def define_routes(&block)
23 |       self.routes = $test_app.draw_routes(&block)
24 |     end
25 | 
26 |     def build_fake_response(opts = {}, &block)
27 |       action = opts[:action] || 'example'
28 |       partial = opts[:partial] || '_partial'
29 |       block ||= lambda { head :ok }
30 |       controller_class = define_controller('Examples') do
31 |         layout false
32 |         define_method(action, &block)
33 |       end
34 |       controller_class.view_paths = [$test_app.temp_views_directory.to_s]
35 | 
36 |       define_routes do
37 |         get 'examples', to: "examples##{action}"
38 |       end
39 | 
40 |       create_view("examples/#{action}.html.erb", 'action')
41 |       create_view("examples/#{partial}.html.erb", 'partial')
42 | 
43 |       setup_rails_controller_test(controller_class)
44 |       self.class.render_views(true)
45 | 
46 |       get action
47 | 
48 |       controller
49 |     end
50 | 
51 |     def setup_rails_controller_test(controller_class)
52 |       @controller = controller_class.new
53 |     end
54 | 
55 |     def create_view(path, contents)
56 |       $test_app.create_temp_view(path, contents)
57 |     end
58 | 
59 |     def delete_temporary_views
60 |       $test_app.delete_temp_views
61 |     end
62 | 
63 |     def restore_original_routes
64 |       Rails.application.reload_routes!
65 |     end
66 |   end
67 | end
68 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/database_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module DatabaseHelpers
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |       example_group.extend(self)
 6 |     end
 7 | 
 8 |     extend self
 9 | 
10 |     def database_adapter
11 |       Tests::Database.instance.adapter_name
12 |     end
13 | 
14 |     def postgresql?
15 |       database_adapter == :postgresql
16 |     end
17 | 
18 |     alias :database_supports_array_columns? :postgresql?
19 |     alias :database_supports_uuid_columns? :postgresql?
20 |     alias :database_supports_money_columns? :postgresql?
21 |     alias :database_supports_expression_indexes? :postgresql?
22 |   end
23 | end
24 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/i18n_faker.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module I18nFaker
 3 |     extend self
 4 | 
 5 |     def self.configure_example_group(example_group)
 6 |       example_group.include(self)
 7 |     end
 8 | 
 9 |     def stubbing_translations(translations)
10 |       stub_translations(translations)
11 |       yield
12 |     ensure
13 |       I18n.backend.reload!
14 |       I18n.backend.send(:init_translations)
15 |     end
16 | 
17 |     def stub_translations(translations)
18 |       translations.each do |key, message|
19 |         stub_translation(key, message)
20 |       end
21 |     end
22 | 
23 |     def stub_translation(key_or_keys, message)
24 |       keys = [key_or_keys].flatten.join('.').split('.')
25 |       tree = keys.reverse.inject(message) { |data, key| { key => data } }
26 |       I18n.backend.store_translations(:en, tree)
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/mailer_builder.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module MailerBuilder
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |     end
 6 | 
 7 |     def define_mailer(name, _paths, &block)
 8 |       class_name = name.to_s.pluralize.classify
 9 |       define_class(class_name, ActionMailer::Base, &block)
10 |     end
11 |   end
12 | end
13 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/message_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module MessageHelpers
 3 |     include Shoulda::Matchers::WordWrap
 4 | 
 5 |     def self.configure_example_group(example_group)
 6 |       example_group.include(self)
 7 |     end
 8 | 
 9 |     def format_message(message, one_line: false)
10 |       stripped_message = message.strip_heredoc.strip
11 | 
12 |       if one_line
13 |         stripped_message.tr("\n", ' ').squeeze(' ')
14 |       else
15 |         word_wrap(stripped_message)
16 |       end
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/rails_versions.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module RailsVersions
 3 |     extend self
 4 | 
 5 |     def self.configure_example_group(example_group)
 6 |       example_group.include(self)
 7 |       example_group.extend(self)
 8 |     end
 9 | 
10 |     def rails_version
11 |       Tests::Version.new(Rails::VERSION::STRING)
12 |     end
13 | 
14 |     def rails_oldest_version_supported
15 |       6.1
16 |     end
17 |   end
18 | end
19 | 


--------------------------------------------------------------------------------
/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ValidationMatcherScenarioHelpers
 3 |     def self.configure_example_group(example_group)
 4 |       example_group.include(self)
 5 |     end
 6 | 
 7 |     def build_scenario_for_validation_matcher(args)
 8 |       UnitTests::ValidationMatcherScenario.new(
 9 |         build_validation_matcher_scenario_args(args),
10 |       )
11 |     end
12 | 
13 |     protected
14 | 
15 |     def validation_matcher_scenario_args
16 |       {}
17 |     end
18 | 
19 |     def configure_validation_matcher(matcher)
20 |       matcher
21 |     end
22 | 
23 |     private
24 | 
25 |     def build_validation_matcher_scenario_args(args)
26 |       args.
27 |         deep_merge(validation_matcher_scenario_args).
28 |         deep_merge(
29 |           matcher_name: matcher_name,
30 |           matcher_proc: method(matcher_name),
31 |         )
32 |     end
33 | 
34 |     def matcher_name
35 |       validation_matcher_scenario_args.fetch(:matcher_name) do
36 |         raise KeyNotFoundError.new(<<-MESSAGE)
37 | Please implement #validation_matcher_scenario_args in your example
38 | group, in such a way that it returns a hash that contains a
39 | :matcher_name key.
40 |         MESSAGE
41 |       end
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/spec/support/unit/i18n.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 |   config.after do
3 |     # Clear any translations added during tests by telling the backend to
4 |     # replace its translations with whatever is in the YAML files.
5 |     I18n.backend.reload!
6 |   end
7 | end
8 | 


--------------------------------------------------------------------------------
/spec/support/unit/load_environment.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../tests/current_bundle'
 2 | require_relative 'rails_application'
 3 | 
 4 | Tests::CurrentBundle.instance.assert_appraisal!
 5 | 
 6 | $test_app = UnitTests::RailsApplication.new
 7 | $test_app.create
 8 | $test_app.load
 9 | 
10 | require 'active_record/base'
11 | 
12 | ENV['RAILS_ENV'] = 'test'
13 | 


--------------------------------------------------------------------------------
/spec/support/unit/matchers/deprecate.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module Matchers
 3 |     def deprecate(old_method, new_method)
 4 |       DeprecateMatcher.new(old_method, new_method)
 5 |     end
 6 | 
 7 |     class DeprecateMatcher
 8 |       def initialize(old_method, new_method)
 9 |         @old_method = old_method
10 |         @new_method = new_method
11 |       end
12 | 
13 |       def matches?(block)
14 |         @captured_stderr = capture(:stderr, &block).gsub(/\n+/, ' ')
15 |         captured_stderr.include?(expected_message)
16 |       end
17 | 
18 |       def failure_message
19 |         "Expected block to #{expectation}, but it did not.\nActual warning: #{actual_warning}"
20 |       end
21 |       alias_method :failure_message_for_should, :failure_message
22 | 
23 |       def failure_message_when_negated
24 |         "Expected block not to #{expectation}, but it did."
25 |       end
26 |       alias_method :failure_message_for_should_not,
27 |         :failure_message_when_negated
28 | 
29 |       def description
30 |         "should #{expectation}"
31 |       end
32 | 
33 |       def supports_block_expectations?
34 |         true
35 |       end
36 | 
37 |       protected
38 | 
39 |       attr_reader :old_method, :new_method, :captured_stderr
40 | 
41 |       private
42 | 
43 |       def expected_message
44 |         "#{old_method} is deprecated and will be removed in the next major release. Please use #{new_method} instead."
45 |       end
46 | 
47 |       def expectation
48 |         "print a warning deprecating #{old_method} in favor of #{new_method}"
49 |       end
50 | 
51 |       def actual_warning
52 |         if captured_stderr.empty?
53 |           'nothing'
54 |         else
55 |           "\n  #{captured_stderr}"
56 |         end
57 |       end
58 |     end
59 |   end
60 | end
61 | 


--------------------------------------------------------------------------------
/spec/support/unit/matchers/fail_with_message_including_matcher.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module Matchers
 3 |     extend RSpec::Matchers::DSL
 4 | 
 5 |     matcher :fail_with_message_including do |expected|
 6 |       def supports_block_expectations?
 7 |         true
 8 |       end
 9 | 
10 |       match do |block|
11 |         @actual = nil
12 | 
13 |         begin
14 |           block.call
15 |         rescue RSpec::Expectations::ExpectationNotMetError => e
16 |           @actual = e.message
17 |         end
18 | 
19 |         @actual&.include?(expected)
20 |       end
21 | 
22 |       def failure_message
23 |         lines = ['Expectation should have failed with message including:']
24 |         lines << Shoulda::Matchers::Util.indent(expected, 2)
25 | 
26 |         if @actual
27 |           lines << 'The full message was:'
28 |           lines << Shoulda::Matchers::Util.indent(@actual, 2)
29 |         else
30 |           lines << 'However, the expectation did not fail at all.'
31 |         end
32 | 
33 |         lines.join("\n")
34 |       end
35 | 
36 |       def failure_message_for_should
37 |         failure_message
38 |       end
39 | 
40 |       def failure_message_when_negated
41 |         lines = ['Expectation should not have failed with message including:']
42 |         lines << Shoulda::Matchers::Util.indent(expected, 2)
43 |         lines.join("\n")
44 |       end
45 | 
46 |       def failure_message_for_should_not
47 |         failure_message_when_negated
48 |       end
49 |     end
50 |   end
51 | end
52 | 


--------------------------------------------------------------------------------
/spec/support/unit/matchers/fail_with_message_matcher.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module Matchers
 3 |     extend RSpec::Matchers::DSL
 4 | 
 5 |     matcher :fail_with_message do |raw_expected, wrap: false|
 6 |       expected =
 7 |         if wrap
 8 |           Shoulda::Matchers.word_wrap(raw_expected)
 9 |         else
10 |           raw_expected
11 |         end
12 | 
13 |       def supports_block_expectations?
14 |         true
15 |       end
16 | 
17 |       match do |block|
18 |         @actual = nil
19 | 
20 |         begin
21 |           block.call
22 |         rescue RSpec::Expectations::ExpectationNotMetError => e
23 |           @actual = e.message
24 |         end
25 | 
26 |         @actual && @actual == expected.sub(/\n\z/, '')
27 |       end
28 | 
29 |       define_method :failure_message do
30 |         lines = ['Expectation should have failed with message:']
31 |         lines << Shoulda::Matchers::Util.indent(expected, 2)
32 | 
33 |         if @actual
34 |           diff = differ.diff(@actual, expected)[1..]
35 | 
36 |           lines << 'Actually failed with:'
37 |           lines << Shoulda::Matchers::Util.indent(@actual, 2)
38 | 
39 |           if diff
40 |             lines << 'Diff:'
41 |             lines << Shoulda::Matchers::Util.indent(diff, 2)
42 |           end
43 |         else
44 |           lines << 'However, the expectation did not fail at all.'
45 |         end
46 | 
47 |         lines.join("\n")
48 |       end
49 | 
50 |       define_method :failure_message_when_negated do
51 |         lines = ['Expectation should not have failed with message:']
52 |         lines << Shoulda::Matchers::Util.indent(expected, 2)
53 |         lines.join("\n")
54 |       end
55 | 
56 |       private
57 | 
58 |       def differ
59 |         @_differ ||= RSpec::Support::Differ.new
60 |       end
61 |     end
62 |   end
63 | end
64 | 


--------------------------------------------------------------------------------
/spec/support/unit/matchers/print_warning_including.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module Matchers
 3 |     def print_warning_including(expected_warning)
 4 |       PrintWarningIncludingMatcher.new(expected_warning)
 5 |     end
 6 | 
 7 |     class PrintWarningIncludingMatcher
 8 |       def initialize(expected_warning)
 9 |         @expected_warning = collapse_whitespace(expected_warning)
10 |       end
11 | 
12 |       def matches?(block)
13 |         @captured_stderr = collapse_whitespace(capture(:stderr, &block))
14 |         @was_negated = false
15 |         captured_stderr.include?(expected_warning)
16 |       end
17 | 
18 |       def does_not_match?(block)
19 |         !matches?(block).tap do
20 |           @was_negated = true
21 |         end
22 |       end
23 | 
24 |       def failure_message
25 |         "Expected block to #{expectation}\n\nHowever, #{aberration}"
26 |       end
27 | 
28 |       def failure_message_when_negated
29 |         "Expected block not to #{expectation}\n\nHowever, #{aberration}"
30 |       end
31 | 
32 |       def description
33 |         "should print a warning containing #{expected_warning.inspect}"
34 |       end
35 | 
36 |       def supports_block_expectations?
37 |         true
38 |       end
39 | 
40 |       private
41 | 
42 |       attr_reader :expected_warning, :captured_stderr
43 | 
44 |       def was_negated?
45 |         @was_negated
46 |       end
47 | 
48 |       def expectation
49 |         "print a warning containing:\n\n  #{expected_warning}"
50 |       end
51 | 
52 |       def aberration
53 |         if was_negated?
54 |           'it did.'
55 |         elsif captured_stderr.empty?
56 |           'it actually printed nothing.'
57 |         else
58 |           "it actually printed:\n\n  #{captured_stderr}"
59 |         end
60 |       end
61 | 
62 |       def collapse_whitespace(string)
63 |         string.gsub(/\n+/, ' ').squeeze(' ')
64 |       end
65 |     end
66 |   end
67 | end
68 | 


--------------------------------------------------------------------------------
/spec/support/unit/model_creators.rb:
--------------------------------------------------------------------------------
 1 | module UnitTests
 2 |   module ModelCreators
 3 |     class << self
 4 |       def register(name, klass)
 5 |         registrations[name] = klass
 6 |       end
 7 | 
 8 |       def retrieve(name)
 9 |         registrations[name]
10 |       end
11 | 
12 |       private
13 | 
14 |       def registrations
15 |         @_registrations ||= {}
16 |       end
17 |     end
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/spec/support/unit/model_creators/active_model.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../model_creators'
 2 | require 'forwardable'
 3 | 
 4 | module UnitTests
 5 |   module ModelCreators
 6 |     class ActiveModel
 7 |       def self.call(args)
 8 |         new(args).call
 9 |       end
10 | 
11 |       extend Forwardable
12 | 
13 |       def_delegators(
14 |         :arguments,
15 |         :attribute_name,
16 |         :attribute_default_values_by_name,
17 |       )
18 | 
19 |       def initialize(args)
20 |         @arguments = CreateModelArguments::Basic.wrap(
21 |           args.merge!(
22 |             model_creation_strategy: UnitTests::ModelCreationStrategies::ActiveModel,
23 |           ),
24 |         )
25 |         @model_creator = Basic.new(arguments)
26 |       end
27 | 
28 |       def call
29 |         model_creator.call
30 |       end
31 | 
32 |       protected
33 | 
34 |       attr_reader :arguments, :model_creator
35 |     end
36 | 
37 |     register(:active_model, ActiveModel)
38 |   end
39 | end
40 | 


--------------------------------------------------------------------------------
/spec/support/unit/model_creators/active_record.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../model_creators'
 2 | require 'forwardable'
 3 | 
 4 | module UnitTests
 5 |   module ModelCreators
 6 |     class ActiveRecord
 7 |       def self.call(args)
 8 |         new(args).call
 9 |       end
10 | 
11 |       extend Forwardable
12 | 
13 |       def_delegators(
14 |         :arguments,
15 |         :attribute_default_values_by_name,
16 |         :attribute_name,
17 |         :model_name,
18 |       )
19 | 
20 |       def_delegators :model_creator, :customize_model
21 | 
22 |       def initialize(args)
23 |         @arguments = CreateModelArguments::Basic.wrap(
24 |           args.merge!(
25 |             model_creation_strategy: UnitTests::ModelCreationStrategies::ActiveRecord,
26 |           ),
27 |         )
28 |         @model_creator = Basic.new(arguments)
29 |       end
30 | 
31 |       def call
32 |         model_creator.call
33 |       end
34 | 
35 |       protected
36 | 
37 |       attr_reader :arguments, :model_creator
38 |     end
39 | 
40 |     register(:active_record, ActiveRecord)
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/spec/support/unit/model_creators/active_record/has_many.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../../model_creators'
 2 | require 'forwardable'
 3 | 
 4 | module UnitTests
 5 |   module ModelCreators
 6 |     class ActiveRecord
 7 |       class HasMany
 8 |         def self.call(args)
 9 |           new(args).call
10 |         end
11 | 
12 |         extend Forwardable
13 | 
14 |         def_delegators(
15 |           :arguments,
16 |           :attribute_name,
17 |           :attribute_default_values_by_name,
18 |         )
19 | 
20 |         def initialize(args)
21 |           @arguments = CreateModelArguments::HasMany.wrap(args)
22 |         end
23 | 
24 |         def call
25 |           child_model_creator.call
26 |           parent_model_creator.call
27 |         end
28 | 
29 |         protected
30 | 
31 |         attr_reader :arguments
32 | 
33 |         private
34 | 
35 |         alias_method :association_name, :attribute_name
36 |         alias_method :parent_model_creator_arguments, :arguments
37 | 
38 |         def child_model_creator
39 |           @_child_model_creator ||=
40 |             UnitTests::ModelCreationStrategies::ActiveRecord.new(
41 |               child_model_name,
42 |             )
43 |         end
44 | 
45 |         def parent_model_creator
46 |           @_parent_model_creator ||= begin
47 |             model_creator = UnitTests::ModelCreators::ActiveRecord.new(
48 |               parent_model_creator_arguments,
49 |             )
50 | 
51 |             model_creator.customize_model do |model|
52 |               model.has_many(association_name)
53 |             end
54 | 
55 |             model_creator
56 |           end
57 |         end
58 | 
59 |         def child_model_name
60 |           association_name.to_s.classify
61 |         end
62 |       end
63 |     end
64 | 
65 |     register(:"active_record/has_many", ActiveRecord::HasMany)
66 |   end
67 | end
68 | 


--------------------------------------------------------------------------------
/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb:
--------------------------------------------------------------------------------
 1 | require_relative '../../model_creators'
 2 | require 'forwardable'
 3 | 
 4 | module UnitTests
 5 |   module ModelCreators
 6 |     class ActiveRecord
 7 |       class UniquenessMatcher
 8 |         def self.call(args)
 9 |           new(args).call
10 |         end
11 | 
12 |         extend Forwardable
13 | 
14 |         def_delegators(
15 |           :arguments,
16 |           :attribute_name,
17 |           :attribute_default_values_by_name,
18 |         )
19 | 
20 |         def initialize(args)
21 |           @arguments = CreateModelArguments::UniquenessMatcher.wrap(args)
22 |           @model_creator = UnitTests::ModelCreators::ActiveRecord.new(
23 |             arguments,
24 |           )
25 |         end
26 | 
27 |         def call
28 |           model_creator.call
29 |         end
30 | 
31 |         protected
32 | 
33 |         attr_reader :arguments, :model_creator
34 |       end
35 |     end
36 | 
37 |     register(
38 |       :"active_record/uniqueness_matcher",
39 |       ActiveRecord::UniquenessMatcher,
40 |     )
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/spec/support/unit/record_builder_with_i18n_validation_message.rb:
--------------------------------------------------------------------------------
 1 | require 'delegate'
 2 | 
 3 | module UnitTests
 4 |   class RecordBuilderWithI18nValidationMessage < SimpleDelegator
 5 |     def initialize(builder, options = {})
 6 |       super(builder)
 7 |       @options = default_options.merge!(options)
 8 |       builder.message = validation_message_key
 9 |     end
10 | 
11 |     def validation_message_key
12 |       options[:validation_message_key]
13 |     end
14 | 
15 |     protected
16 | 
17 |     attr_reader :builder, :options
18 | 
19 |     private
20 | 
21 |     def model
22 |       @_model ||= super.tap do |_model|
23 |         stub_validation_messages
24 |       end
25 |     end
26 | 
27 |     def stub_validation_messages
28 |       stub_default_validation_message
29 |       stub_attribute_specific_validation_message
30 |     end
31 | 
32 |     def stub_default_validation_message
33 |       keys = [
34 |         'activerecord.errors.messages',
35 |         validation_message_key,
36 |       ]
37 | 
38 |       I18nFaker.stub_translation(keys, default_message)
39 |     end
40 | 
41 |     def stub_attribute_specific_validation_message
42 |       keys = [
43 |         'activerecord.errors',
44 |         "models.#{builder.model_name.to_s.underscore}",
45 |         "attributes.#{builder.attribute_that_receives_error}",
46 |         validation_message_key,
47 |       ]
48 | 
49 |       I18nFaker.stub_translation(
50 |         keys,
51 |         message_for_attribute_that_receives_error,
52 |       )
53 |     end
54 | 
55 |     def default_message
56 |       'the wrong message'
57 |     end
58 | 
59 |     def message_for_attribute_that_receives_error
60 |       'the right message'
61 |     end
62 | 
63 |     def default_options
64 |       {
65 |         validation_message_key: :validation_message_key,
66 |       }
67 |     end
68 |   end
69 | end
70 | 


--------------------------------------------------------------------------------
/spec/support/unit/record_validating_confirmation_builder.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'helpers/model_builder'
 2 | 
 3 | module UnitTests
 4 |   class RecordValidatingConfirmationBuilder
 5 |     include ModelBuilder
 6 | 
 7 |     def initialize(options)
 8 |       @options = options
 9 |     end
10 | 
11 |     def model
12 |       @_model ||= create_model
13 |     end
14 | 
15 |     def model_name
16 |       options.fetch(:model_name, 'Example')
17 |     end
18 | 
19 |     def record
20 |       model.new
21 |     end
22 | 
23 |     def message=(message)
24 |       options[:message] = message
25 |     end
26 | 
27 |     def attribute_to_confirm
28 |       options.fetch(:attribute, :attribute_to_confirm)
29 |     end
30 | 
31 |     def confirmation_attribute
32 |       options.fetch(
33 |         :confirmation_attribute,
34 |         :"#{attribute_to_confirm}_confirmation",
35 |       )
36 |     end
37 | 
38 |     def attribute_that_receives_error
39 |       confirmation_attribute
40 |     end
41 | 
42 |     protected
43 | 
44 |     attr_reader :options
45 | 
46 |     private
47 | 
48 |     def create_model
49 |       define_model(model_name, attribute_to_confirm => :string) do |model|
50 |         model.validates_confirmation_of(attribute_to_confirm, options)
51 |       end
52 |     end
53 |   end
54 | end
55 | 


--------------------------------------------------------------------------------
/spec/support/unit/record_with_different_error_attribute_builder.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'helpers/model_builder'
 2 | 
 3 | module UnitTests
 4 |   class RecordWithDifferentErrorAttributeBuilder
 5 |     include ModelBuilder
 6 | 
 7 |     def initialize(options)
 8 |       @options = options.reverse_merge(default_options)
 9 |     end
10 | 
11 |     def attribute_that_receives_error
12 |       options[:attribute_that_receives_error]
13 |     end
14 | 
15 |     def attribute_to_validate
16 |       options[:attribute_to_validate]
17 |     end
18 | 
19 |     def message
20 |       options[:message]
21 |     end
22 | 
23 |     def message=(message)
24 |       options[:message] = message
25 |     end
26 | 
27 |     def model
28 |       @_model ||= create_model
29 |     end
30 | 
31 |     def model_name
32 |       'Example'
33 |     end
34 | 
35 |     def record
36 |       model.new
37 |     end
38 | 
39 |     def valid_value
40 |       'some value'
41 |     end
42 | 
43 |     protected
44 | 
45 |     attr_reader :options
46 | 
47 |     private
48 | 
49 |     def context
50 |       {
51 |         validation_method_name: validation_method_name,
52 |         valid_value: valid_value,
53 |         attribute_to_validate: attribute_to_validate,
54 |         attribute_that_receives_error: attribute_that_receives_error,
55 |         message: message,
56 |       }
57 |     end
58 | 
59 |     def create_model
60 |       _context = context
61 | 
62 |       define_model model_name, model_columns do
63 |         validate _context[:validation_method_name]
64 | 
65 |         define_method(_context[:validation_method_name]) do
66 |           if self[_context[:attribute_to_validate]] != _context[:valid_value]
67 |             errors.add(_context[:attribute_that_receives_error], _context[:message])
68 |           end
69 |         end
70 |       end
71 |     end
72 | 
73 |     def validation_method_name
74 |       :custom_validation
75 |     end
76 | 
77 |     def model_columns
78 |       {
79 |         attribute_to_validate => :string,
80 |         attribute_that_receives_error => :string,
81 |       }
82 |     end
83 | 
84 |     def default_options
85 |       {
86 |         attribute_that_receives_error: :attribute_that_receives_error,
87 |         attribute_to_validate: :attribute_to_validate,
88 |         message: 'some message',
89 |       }
90 |     end
91 |   end
92 | end
93 | 


--------------------------------------------------------------------------------
/spec/support/unit/record_with_unrelated_error_builder.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'helpers/model_builder'
 2 | 
 3 | module UnitTests
 4 |   class RecordWithUnrelatedErrorBuilder
 5 |     include ModelBuilder
 6 | 
 7 |     def initialize(options)
 8 |       @options = options.reverse_merge(default_options)
 9 |     end
10 | 
11 |     def attribute_that_receives_error
12 |       options[:attribute_that_receives_error]
13 |     end
14 | 
15 |     def attribute_to_validate
16 |       options[:attribute_to_validate]
17 |     end
18 | 
19 |     def message
20 |       options[:message]
21 |     end
22 | 
23 |     def message=(message)
24 |       options[:message] = message
25 |     end
26 | 
27 |     def model
28 |       @_model ||= create_model
29 |     end
30 | 
31 |     def model_name
32 |       'Example'
33 |     end
34 | 
35 |     def record
36 |       model.new
37 |     end
38 | 
39 |     def valid_value
40 |       'some value'
41 |     end
42 | 
43 |     protected
44 | 
45 |     attr_reader :options
46 | 
47 |     private
48 | 
49 |     def context
50 |       {
51 |         validation_method_name: validation_method_name,
52 |         valid_value: valid_value,
53 |         attribute_to_validate: attribute_to_validate,
54 |         attribute_that_receives_error: attribute_that_receives_error,
55 |         message: message,
56 |       }
57 |     end
58 | 
59 |     def create_model
60 |       _context = context
61 | 
62 |       define_model model_name, model_columns do
63 |         validate _context[:validation_method_name]
64 | 
65 |         define_method(_context[:validation_method_name]) do
66 |           errors.add(_context[:attribute_that_receives_error], _context[:message])
67 |         end
68 |       end
69 |     end
70 | 
71 |     def validation_method_name
72 |       :custom_validation
73 |     end
74 | 
75 |     def model_columns
76 |       {
77 |         attribute_to_validate => :string,
78 |         attribute_that_receives_error => :string,
79 |       }
80 |     end
81 | 
82 |     def default_options
83 |       {
84 |         attribute_that_receives_error: :attribute_that_receives_error,
85 |         attribute_to_validate: :attribute_to_validate,
86 |         message: 'some message',
87 |       }
88 |     end
89 |   end
90 | end
91 | 


--------------------------------------------------------------------------------
/spec/support/unit/shared_examples/numerical_submatcher.rb:
--------------------------------------------------------------------------------
 1 | shared_examples 'a numerical submatcher' do
 2 |   it 'implements the with_message method' do
 3 |     expect(subject).to respond_to(:with_message).with(1).arguments
 4 |   end
 5 | 
 6 |   it 'implements the matches? method' do
 7 |     expect(subject).to respond_to(:matches?).with(1).arguments
 8 |   end
 9 | 
10 |   it 'implements the failure_message method' do
11 |     expect(subject).to respond_to(:failure_message).with(0).arguments
12 |   end
13 | 
14 |   it 'implements the failure_message_when_negated method' do
15 |     expect(subject).to respond_to(:failure_message_when_negated).with(0).arguments
16 |   end
17 | end
18 | 


--------------------------------------------------------------------------------
/spec/support/unit/validation_matcher_scenario.rb:
--------------------------------------------------------------------------------
 1 | require 'forwardable'
 2 | 
 3 | module UnitTests
 4 |   class ValidationMatcherScenario
 5 |     extend Forwardable
 6 | 
 7 |     def initialize(arguments)
 8 |       @arguments = arguments.dup
 9 |       @matcher_proc = @arguments.delete(:matcher_proc)
10 | 
11 |       @specified_model_creator = @arguments.delete(:model_creator) do
12 |         raise KeyError.new(<<-MESSAGE)
13 | :model_creator is missing. You can either provide it as an option or as
14 | a method.
15 |         MESSAGE
16 |       end
17 | 
18 |       @model_creator = model_creator_class.new(@arguments)
19 |     end
20 | 
21 |     def record
22 |       @_record ||= model.new.tap do |record|
23 |         attribute_default_values_by_name.each do |attribute_name, default_value|
24 |           record.public_send("#{attribute_name}=", default_value)
25 |         end
26 |       end
27 |     end
28 | 
29 |     def model
30 |       @_model ||= model_creator.call
31 |     end
32 | 
33 |     def matcher
34 |       @_matcher ||= matcher_proc.call(attribute_name)
35 |     end
36 | 
37 |     protected
38 | 
39 |     attr_reader(
40 |       :arguments,
41 |       :existing_value,
42 |       :matcher_proc,
43 |       :model_creator,
44 |       :specified_model_creator,
45 |     )
46 | 
47 |     private
48 | 
49 |     def_delegators(
50 |       :model_creator,
51 |       :attribute_name,
52 |       :attribute_default_values_by_name,
53 |     )
54 | 
55 |     def model_creator_class
56 |       UnitTests::ModelCreators.retrieve(specified_model_creator) ||
57 |         specified_model_creator
58 |     end
59 |   end
60 | end
61 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/filter_param_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::FilterParamMatcher, type: :controller do
 4 |   it 'accepts filtering a filtered parameter' do
 5 |     filter(:secret)
 6 | 
 7 |     expect(nil).to filter_param(:secret)
 8 |   end
 9 | 
10 |   it 'accepts filtering a parameter matching a filtered regex' do
11 |     filter(/(?!tip)pin(?!g)/)
12 | 
13 |     expect(nil).to filter_param(:pin)
14 |   end
15 | 
16 |   it 'rejects filtering an unfiltered parameter' do
17 |     filter(:secret)
18 |     matcher = filter_param(:other)
19 | 
20 |     expect(matcher.matches?(nil)).to eq false
21 | 
22 |     expect(matcher.failure_message).to match(/Expected other to be filtered.*secret/)
23 |   end
24 | 
25 |   def filter(param)
26 |     Rails.application.config.filter_parameters = [param]
27 |   end
28 | end
29 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::RedirectToMatcher, type: :controller do
 4 |   context 'a controller that redirects' do
 5 |     it 'accepts redirecting to that url' do
 6 |       expect(controller_redirecting_to('/some/url')).to redirect_to('/some/url')
 7 |     end
 8 | 
 9 |     it 'rejects redirecting to a different url' do
10 |       expect(controller_redirecting_to('/some/url')).
11 |         not_to redirect_to('/some/other/url')
12 |     end
13 | 
14 |     it 'accepts redirecting to that url in a block' do
15 |       expect(controller_redirecting_to('/some/url')).
16 |         to redirect_to('somewhere') { '/some/url' }
17 |     end
18 | 
19 |     it 'rejects redirecting to a different url in a block' do
20 |       expect(controller_redirecting_to('/some/url')).
21 |         not_to redirect_to('somewhere else') { '/some/other/url' }
22 |     end
23 | 
24 |     def controller_redirecting_to(url)
25 |       build_fake_response { redirect_to url }
26 |     end
27 |   end
28 | 
29 |   context 'a controller that does not redirect' do
30 |     it 'rejects redirecting to a url' do
31 |       controller = build_fake_response { render text: 'hello' }
32 | 
33 |       expect(controller).not_to redirect_to('/some/url')
34 |     end
35 |   end
36 | 
37 |   it 'provides the correct description when provided a block' do
38 |     matcher = redirect_to('somewhere else') { '/some/other/url' }
39 | 
40 |     expect(matcher.description).to eq 'redirect to "somewhere else"'
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/respond_with_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::RespondWithMatcher, type: :controller do
 4 |   statuses = { success: 200, redirect: 301, missing: 404, error: 500,
 5 |                not_implemented: 501, }
 6 | 
 7 |   statuses.each do |human_name, numeric_code|
 8 |     context "a controller responding with #{human_name}" do
 9 |       it 'accepts responding with a numeric response code' do
10 |         expect(controller_with_status(numeric_code)).to respond_with(numeric_code)
11 |       end
12 | 
13 |       it 'accepts responding with a symbol response code' do
14 |         expect(controller_with_status(numeric_code)).to respond_with(human_name)
15 |       end
16 | 
17 |       it 'rejects responding with another status' do
18 |         another_status = statuses.except(human_name).keys.first
19 | 
20 |         expect(controller_with_status(numeric_code)).
21 |           not_to respond_with(another_status)
22 |       end
23 |     end
24 |   end
25 | 
26 |   def controller_with_status(status)
27 |     build_fake_response do
28 |       render text: 'text', status: status
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/route_params_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::RouteParams, type: :controller do
 4 |   describe '#normalize' do
 5 |     context 'when the route parameters is a hash' do
 6 |       it 'stringifies the values in the hash' do
 7 |         expect(build_route_params(controller: :examples, action: 'example', id: '1').normalize).
 8 |           to eq({ controller: 'examples', action: 'example', id: '1' })
 9 |       end
10 |     end
11 | 
12 |     context 'when the route parameters is a string and a hash' do
13 |       it 'produces a hash of route parameters' do
14 |         expect(build_route_params('examples#example', id: '1').normalize).
15 |           to eq({ controller: 'examples', action: 'example', id: '1' })
16 |       end
17 |     end
18 | 
19 |     context 'when the route params is a string' do
20 |       it 'produces a hash of route params' do
21 |         expect(build_route_params('examples#index').normalize).
22 |           to eq({ controller: 'examples', action: 'index' })
23 |       end
24 |     end
25 |   end
26 | 
27 |   def build_route_params(*params)
28 |     Shoulda::Matchers::ActionController::RouteParams.new(params)
29 |   end
30 | end
31 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::SetFlashMatcher, type: :controller do
 4 |   it_behaves_like 'set session or flash matcher' do
 5 |     def store_name
 6 |       'flash'
 7 |     end
 8 | 
 9 |     def set_store
10 |       set_flash
11 |     end
12 | 
13 |     def store_within(controller)
14 |       controller.flash
15 |     end
16 |   end
17 | 
18 |   it_behaves_like 'set session or flash matcher' do
19 |     def store_name
20 |       'flash.now'
21 |     end
22 | 
23 |     def set_store
24 |       set_flash.now
25 |     end
26 | 
27 |     def store_within(controller)
28 |       controller.flash.now
29 |     end
30 |   end
31 | 
32 |   context 'when the controller sets both flash and flash.now' do
33 |     it 'does not mix flash and flash.now' do
34 |       controller = build_fake_response do
35 |         flash['key for flash'] = 'value for flash'
36 |         flash.now['key for flash.now'] = 'value for flash.now'
37 |       end
38 | 
39 |       expect(controller).not_to set_flash['key for flash.now']
40 |       expect(controller).not_to set_flash.now['key for flash']
41 |     end
42 |   end
43 | 
44 |   context 'when the now qualifier is called after the key is set' do
45 |     it 'raises a QualifierOrderError' do
46 |       controller = build_fake_response
47 | 
48 |       usage = lambda do
49 |         expect(controller).to set_flash['any key'].now
50 |       end
51 | 
52 |       expect(&usage).to raise_error(described_class::QualifierOrderError)
53 |     end
54 |   end
55 | 
56 |   context 'when the now qualifier is called after the to qualifier' do
57 |     it 'raises a QualifierOrderError' do
58 |       controller = build_fake_response
59 | 
60 |       usage = lambda do
61 |         expect(controller).to set_flash.to('any value').now
62 |       end
63 | 
64 |       expect(&usage).to raise_error(described_class::QualifierOrderError)
65 |     end
66 |   end
67 | end
68 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActionController::SetSessionMatcher, type: :controller do
 4 |   it_behaves_like 'set session or flash matcher' do
 5 |     def store_name
 6 |       'session'
 7 |     end
 8 | 
 9 |     def set_store
10 |       set_session
11 |     end
12 | 
13 |     def store_within(controller)
14 |       controller.session
15 |     end
16 |   end
17 | end
18 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActiveModel::HaveSecurePasswordMatcher, type: :model do
 4 |   context 'with no arguments passed to has_secure_password' do
 5 |     it 'matches when the subject configures has_secure_password with default options' do
 6 |       working_model = define_model(:example, password_digest: :string) { has_secure_password }
 7 |       expect(working_model.new).to have_secure_password
 8 |     end
 9 | 
10 |     it 'does not match when the subject does not authenticate a password' do
11 |       no_secure_password = define_model(:example)
12 |       expect(no_secure_password.new).not_to have_secure_password
13 |     end
14 | 
15 |     it 'does not match when the subject is missing the password_digest attribute' do
16 |       no_digest_column = define_model(:example) { has_secure_password }
17 |       expect(no_digest_column.new).not_to have_secure_password
18 |     end
19 |   end
20 | 
21 |   context 'when custom attribute is given to has_secure_password' do
22 |     it 'matches when the subject configures has_secure_password with correct options' do
23 |       working_model = define_model(:example, reset_password_digest: :string) { has_secure_password :reset_password }
24 |       expect(working_model.new).to have_secure_password :reset_password
25 |     end
26 | 
27 |     it 'does not match when the subject does not authenticate a password' do
28 |       no_secure_password = define_model(:example)
29 |       expect(no_secure_password.new).not_to have_secure_password :reset_password
30 |     end
31 | 
32 |     it 'does not match when the subject is missing the custom digest attribute' do
33 |       no_digest_column = define_model(:example) { has_secure_password :reset_password }
34 |       expect(no_digest_column.new).not_to have_secure_password :reset_password
35 |     end
36 | 
37 |     it 'rejects with an appropriate failure message' do
38 |       working_model = define_model(:example, reset_password_digest: :string) { has_secure_password :reset_password }
39 |       assertion = lambda do
40 |         expect(working_model.new).not_to have_secure_password :reset_password
41 |       end
42 | 
43 |       message = <<-MESSAGE
44 | expected Example to not have a secure password, defined on reset_password attribute!
45 |       MESSAGE
46 | 
47 |       expect(&assertion).to fail_with_message(message)
48 |     end
49 |   end
50 | end
51 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'unit_spec_helper'
 2 | 
 3 | describe Shoulda::Matchers::ActiveRecord::HaveReadonlyAttributeMatcher, type: :model do
 4 |   context 'a read-only attribute' do
 5 |     it 'accepts' do
 6 |       expect(with_readonly_attr).to have_readonly_attribute(:attr)
 7 |     end
 8 |   end
 9 | 
10 |   context 'an attribute that is not part of the read-only set' do
11 |     it 'rejects being read-only' do
12 |       model = define_model :example, attr: :string, other: :string do
13 |         attr_readonly :attr
14 |       end.new
15 | 
16 |       expect(model).not_to have_readonly_attribute(:other)
17 |     end
18 |   end
19 | 
20 |   context 'an attribute on a class with no readonly attributes' do
21 |     it 'rejects being read-only' do
22 |       expect(define_model(:example, attr: :string).new).
23 |         not_to have_readonly_attribute(:attr)
24 |     end
25 | 
26 |     it 'assigns a failure message' do
27 |       model = define_model(:example, attr: :string).new
28 |       matcher = have_readonly_attribute(:attr)
29 | 
30 |       matcher.matches?(model)
31 | 
32 |       expect(matcher.failure_message).not_to be_nil
33 |     end
34 |   end
35 | 
36 |   def with_readonly_attr
37 |     define_model :example, attr: :string do
38 |       attr_readonly :attr
39 |     end.new
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'doublespeak_spec_helper'
 2 | 
 3 | module Shoulda::Matchers::Doublespeak
 4 |   describe DoubleImplementationRegistry do
 5 |     describe '.find' do
 6 |       it 'returns an instance of StubImplementation if given :stub' do
 7 |         expect(described_class.find(:stub)).to be_a(StubImplementation)
 8 |       end
 9 | 
10 |       it 'returns ProxyImplementation if given :proxy' do
11 |         expect(described_class.find(:proxy)).to be_a(ProxyImplementation)
12 |       end
13 | 
14 |       it 'raises an ArgumentError if not given a registered implementation' do
15 |         expect {
16 |           expect(described_class.find(:something_else))
17 |         }.to raise_error(ArgumentError)
18 |       end
19 |     end
20 |   end
21 | end
22 | 


--------------------------------------------------------------------------------
/spec/unit/shoulda/matchers/doublespeak_spec.rb:
--------------------------------------------------------------------------------
 1 | require 'doublespeak_spec_helper'
 2 | 
 3 | module Shoulda::Matchers
 4 |   describe Doublespeak do
 5 |     describe '.double_collection_for' do
 6 |       it 'delegates to its world' do
 7 |         allow(Doublespeak.world).to receive(:double_collection_for)
 8 | 
 9 |         described_class.double_collection_for(:klass)
10 | 
11 |         expect(Doublespeak.world).
12 |           to have_received(:double_collection_for).
13 |           with(:klass)
14 |       end
15 |     end
16 | 
17 |     describe '.with_doubles_activated' do
18 |       it 'delegates to its world' do
19 |         allow(Doublespeak.world).to receive(:with_doubles_activated)
20 | 
21 |         described_class.with_doubles_activated
22 | 
23 |         expect(Doublespeak.world).to have_received(:with_doubles_activated)
24 |       end
25 |     end
26 |   end
27 | end
28 | 


--------------------------------------------------------------------------------
/spec/unit_spec_helper.rb:
--------------------------------------------------------------------------------
 1 | require_relative 'support/unit/load_environment'
 2 | 
 3 | require 'rspec/rails'
 4 | require 'rspec/matchers/fail_matchers'
 5 | require 'shoulda-matchers'
 6 | 
 7 | require 'spec_helper'
 8 | 
 9 | $VERBOSE = true
10 | 
11 | Dir[File.join(File.expand_path('support/unit/**/*.rb', __dir__))].sort.each do |file|
12 |   require file
13 | end
14 | 
15 | RSpec.configure do |config|
16 |   config.include RSpec::Matchers::FailMatchers
17 | 
18 |   UnitTests::Configuration.configure_example_groups(config)
19 | 
20 |   config.include UnitTests::Matchers
21 | 
22 |   config.infer_spec_type_from_file_location!
23 |   config.example_status_persistence_file_path = 'spec/examples.txt'
24 |   config.alias_it_behaves_like_to(:it_supports, 'it supports')
25 | 
26 |   config.before(:all, type: :controller) do
27 |     self.class.controller(ApplicationController) { }
28 |   end
29 | 
30 |   config.before(:suite) do
31 |     I18n.backend.send(:init_translations)
32 |   end
33 | end
34 | 
35 | if Rails::VERSION::STRING >= '7.2'
36 |   Rails.application.deprecators.behavior = :stderr
37 | else
38 |   ActiveSupport::Deprecation.behavior = :stderr
39 | end
40 | 
41 | Shoulda::Matchers.configure do |config|
42 |   config.integrate do |with|
43 |     with.test_framework :rspec
44 |     with.library :rails
45 |   end
46 | end
47 | 


--------------------------------------------------------------------------------
/zeus.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "command": "ruby -r rubygems -r ./custom_plan -e Zeus.go",
 3 | 
 4 |   "plan": {
 5 |     "boot": {
 6 |       "test_environment": {
 7 |         "rspec": []
 8 |       }
 9 |     }
10 |   }
11 | }
12 | 


--------------------------------------------------------------------------------