Shoulda-matchers: validate_uniqueness_of matcher raises exception when scope_to attribute is string

Created on 18 Feb 2019  路  7Comments  路  Source: thoughtbot/shoulda-matchers

Version: shoulda-matchers (4.0.0.rc1)
Given an ActiveRecord model with a uniqueness validation where the scoped_to parameter is a string (which is otherwise appears to be perfectly valid):

class Post < ApplicationRecord
  validates :name, uniqueness: { scope: "account_id" }
end

And a spec:

it { is_expected.to validate_uniqueness_of(:name).scoped_to("account_id") }

I expect the validation to run, but it errors out:

 Failure/Error: it { is_expected.to validate_uniqueness_of(:name).scoped_to("account_id") }
     TypeError:
wrong argument type String (expected Proc)
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.0.0.rc1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb:843:in `block in all_scopes_are_booleans?'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.0.0.rc1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb:842:in `all?'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.0.0.rc1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb:842:in `all_scopes_are_booleans?'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.0.0.rc1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb:762:in `matches_uniqueness_with_scopes?'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.0.0.rc1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb:332:in `matches?'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rspec-expectations-3.7.0/lib/rspec/expectations/handler.rb:50:in `block in handle_matcher'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rspec-expectations-3.7.0/lib/rspec/expectations/handler.rb:27:in `with_matcher'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rspec-expectations-3.7.0/lib/rspec/expectations/handler.rb:48:in `handle_matcher'
     # /Users/philcoggins/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rspec-expectations-3.7.0/lib/rspec/expectations/expectation_target.rb:65:in `to'

This appears to stem from the fact that arguments are expected to be symbols.

I can fix with the following monkey-patch and changing the matcher to .scoped_to(:account_id)

module Shoulda
  module Matchers
    module ActiveRecord
      class ValidateUniquenessOfMatcher < ActiveModel::ValidationMatcher
        def actual_sets_of_scopes
          validations.map do |validation|
            Array.wrap(validation.options[:scope]).map(&:to_sym)
          end.reject(&:empty?)
        end
      end
    end
  end
end

I could also fix by simply changing the scoped_to attribute to use a symbol, but this uniqueness validation is generated in a separate library that I don't have as much control over.

Bug PR Welcome

All 7 comments

Thanks for the report! We'll see if we can fix this in the next version, although a PR is welcome!

I tried this solution and getting following error
NameError: uninitialized constant ActiveModel::ValidationMatcher
Rails Version: Rails 5.2.2.1
Ruby Version: 2.5.1
shoulda-matchers: 3.1.3

@zeeshangulzar where did you add the code?

I added code in a new file under config/initializers/.

@zeeshangulzar I have mine in rails_helper.rb, underneath all my require statements. Also, I've only tested this patch with latest shoulda version 4.x, so without more info I'm not sure how much more help I can be.

@PhilCoggins Thanks for guidance. I have also moved it into rails_helper and it worked.

Closing this since the fix for this is now in master. ^

Was this page helpful?
0 / 5 - 0 ratings