Search code examples
ruby-on-railsrubyvalidationactiverecordmodels

Cross uniqueness model validation in rails


I have a model where I want columns to contain different ids. So a user may follow another user, but not follow themselves.

Migration

class CreateFollows < ActiveRecord::Migration
  def change
    create_table :follows do |t|
      t.integer :follower_id
      t.integer :followee_id

      t.timestamps
    end
  end
end

Model

class Follow < ActiveRecord::Base
  belongs_to :follower, class_name: 'User'
  belongs_to :followee, class_name: 'User'

  validates :follower_id, uniqueness: { scope: :followee_id }
end

But my test seems to fail

Test

it 'cannot follow itself' do
  user = FactoryGirl.create(:user)
  follow = FactoryGirl.create(:follow, follower: user, followee: user)
  expect(follow).to be_invalid
end

Output

Failures:

1) Follow cannot follow itself
 Failure/Error: expect(follow).to be_invalid
   expected `#<Follow id: 27, follower_id: 78, followee_id: 78, created_at: "2014-07-04 02:20:59", updated_at: "2014-07-04 02:20:59">.invalid?` to return true, got false
 # ./spec/models/follow_spec.rb:23:in `block (2 levels) in <top (required)>'

From everything I've read, this looks write. Anyone have any pointers?

Thanks


Solution

  • This validation:

    validates :follower_id, uniqueness: { scope: :followee_id }
    

    simply says that the set of follower_ids for each followee_id cannot contain duplicates (i.e. you can't follow one person twice), it says nothing about followee_id being different from follower_id.

    If you want to disallow following yourself then you have to say so:

      validate :cant_follow_yourself
    
    private
    
      def cant_follow_yourself
        return if followee_id != follower_id
        # add an appropriate error message here...
      end