Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

silently skip add with before_add association callback instead of raising an exception?

I'm trying to do this

 has_many :roles, :before_add => :enforce_unique

 def enforce_unique(assoc)
   false if exists? assoc
 end

From the docs: "If a before_add callback throws an exception, the object does not get added to the collection". The using false above does not prevent the add, so I'm forced to do this:

 def enforce_unique(assoc)
   raise if exists? assoc
 end

This way, it's true that it doesn't get added, but it also raises an exception that has to be handled. Not very useful to me here. I would prefer this to behave more like regular AR callback before_save, where returning FALSE also prevents the save (or add) but doesn't raise an exception.

In this case above, I would prefer this to just not add the assoc silently. Is there a way to do this? I missing something? Or is raising an exception the only option here?

like image 256
pixelearth Avatar asked Sep 01 '11 17:09

pixelearth


2 Answers

The way to solve it I think is to use throw and catch, which in Ruby are meant for flow control. Raising an exception is not a good fit, since this isn't an exceptional circumstance.

I ended up doing:

catch(:duplicate) do
  association.create({})
end

And then in the before_add callback, I did:

if(Class.where({}).first)
  throw :duplicate
end

More on throw/catch here:

http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/

like image 120
RandallB Avatar answered Nov 15 '22 10:11

RandallB


If the association isn't polymorphic you can do something like:

validates_uniqueness_of :name_of_model

inside of Role where name_of_model us what you are associating with

like image 33
Kyle Patterson Avatar answered Nov 15 '22 11:11

Kyle Patterson