2

I'm adding objects into a collection with <<

current_user.followers<<users

I'd like to catch an exception when a user in users already exists in the followers collection. How can I do that?

Thank you.

PS. It's really hard to find results on Google by typing "<<", is there a name to call this method?

Matthew
  • 837
  • 7
  • 20

3 Answers3

7

You can override the << method on followers association. Note also that :push and :concat are also aliases to :<< so you have to override them too. This is the definition you should put in the overridden association method in order to raise error in case of follower already exists.

class User < ActiveRecord::Base
  has_many :followers do
    def <<(*records)
      result = true
      load_target if @owner.new_record?
      transaction do
        flatten_deeper(records).each do |record|
          raise_on_type_mismatch(record)
          raise 'Record Already Exists' if @owner.send(@reflection.name).exists? record
          add_record_to_target_with_callbacks(record) do |r|
            result &&= insert_record(record) unless @owner.new_record?
          end
        end
      end

      result && self
    end
    alias_method :push, :<<
    alias_method :concat, :<<
  end
end
badawym
  • 1,499
  • 1
  • 13
  • 19
2

You can use the before_add callback:

has_many :followers,
         :through => :whatever,
         :before_add => Proc.new { |user, follower|
           raise "Duplicate follower" if user.followers.include?(follower)
         }

If you are simply trying to ignore duplicates, you can use the :uniq option instead:

has_many :followers,
         :through => :whatever,
         :uniq => true

(the join model table will contain duplicates but they will be ignored).

juanrpozo
  • 697
  • 4
  • 8
1

<< is called left shift operator, but in the context of collection it is used for inserting an object into the collection.

You could achieve the solution by making insertion through an instance method of user class,

class User < ActiveRecord::Base
  def add_follower(follower)
    raise "Duplicate follower" if followers.include?(follower)
    followers << follower
  end
end
nkm
  • 5,844
  • 2
  • 24
  • 38