

Put Callback Logic Into Callback Objects [Rails] - nagnatron
http://miloshadzic.com/2012/03/08/Put-Callback-Logic-Into-A-Callback-Object/

======
JonWood
This strikes me as being similar to extracting bits of a model out into
modules that then get included back into that model. It looks like you're
reducing the complexity of the model, but in fact you're just hiding it
elsewhere, without making it any easier to stub things out.

~~~
nagnatron
You're right that it's similar to extracting it to a module since (in this
example) HashGenerator.new is evaluated with the class and in my Request spec
I can't exactly stub HashGenerator.new to return a double.

How would you do it?

~~~
camwest
Create a RequestFactory which is responsible for creating valid requests, put
the hash generator in there. Then use the factory in the controller instead of
directly creating the model.

That way you don't have the indirection of an ActiveRecord callback and you
have your process of creating a request and assigning it a unique hash in a
straightforward single process.

~~~
nagnatron
This makes sense. Thanks.

~~~
camwest
I've learned this the hard way. I have an app which has callback chains
through 5+ models and it's basically impossible to figure out what's going on.

-C

------
gcao
Shameless plug here. If you use the Aspector gem I created, you could move
validation logic into an aspect and test it. And apply aspect is very easy.
Here is what the code will look like.

class Request < ActiveRecord::Base

end

class RequestValidation < Aspector::Base

    
    
      target do
        def has_unique_hash_id?
          exists?(:hash_id => hash_id)
        end
      end
    
      before :create do
        hash_id = SecureRandom.hex(12) until has_unique_hash_id?
      end

end

RequestValidation.apply(Request)

------
soveran
You may run into race conditions when two processes end up saving an object
with the same hash_id. The check and the object creation should both happen
inside a transaction.

~~~
nagnatron
Good point. Thanks.

------
bhousel
I'm kind of curious why you use SecureRandom.hex and then check for
uniqueness, rather than just using SecureRandom.uuid?

Edit: Nevermind, I realize now that SecureRandom's uuid isn't really a uuid,
in that sense. It's just random.

