Pushing business logic into the model

I have a question re: pushing your business logic into your models, with the intention that the controllers are much more leaner.

Say I have a model object Email. I am tempted to write static methods as part of the Email which do all the business of creating, validating, doing some other plumbing, etc.

Out of the box, Rails suggest your controller look like this

(heavily shortened for brevity)

class EmailController

def add @email = Email.new end

def save    @email = Email.create_with_params(params[:email], session[:foobar])    if @email.save      redirect_to :action => "success"   else      redirect_to :action => "add" end end

Email.create_with_params() is a custom method that does more than just a basic Email.new, it does that, but also takes some session data and does other stuff with it (related to the creation of the Email record, its not completely willy-nilly).

The problem with this approach is this: if you go to the save() and the model has failed validation, whether it be via the native validation or custom stuff, once you do the redirect you lost your @email instance variable, so an "error_message_for" on the add.rhtml template doesnt contain anything. AND you cannot do a render :action => "add" because lets say you need to hit the DB to do other stuff to get the form ready, etc. Basically you need to do other stuff before you can just render the template.

So now I think you need some kind of a data-bus to hold your @email instance var across the request.

Either session or flash comes to mind. flash is nice because it will auto-clean up after itself. Whereas with session you would need to remember to purge it (say, after a successful request).

Am I approaching this from the wrong angle? To clear up my controllers, I am placing more logic into the model, which makes sense, but when you are dealing with requests that span methods you need some way to keep track of it all.

I am tempted to have my custom model objects return two things, a boolean result flag and the instance itself, which I can directly place into my data-bus (session/flash?)

e.g.

def save     result, @email = Email.create_with_params(params[:email], session[:foobar])     if result       redirect_to :action => "success"    else      flash[:email] = @email      redirect_to :action => "add"   end end

And then you modify add() to have a look at the flash first.... Am I totallly crazy here?

Thanks -william

Why can't you simply do the following (which I did in one of my apps):

def save   ...   if ...   .   .   else     add # The DB stuff you need to get the form ready should be in add.     [initialise @email with values from params]     render :action=>"add"   end end

Thushan

Maybe I’m not following this right… but it sounds like you’re doing a “swiss army knife” method. I am a huge fan of of having methods call other methods instead of doing tons of stuff in one step. That’s what I love about the callbacks in ActiveRecord.

Email object would create the email. It should save it to the database.

The after_create callback would send it out. Your controller only needs to know if the record saved or not. You could put your own message into the error messages if it didn’t work.

That allows the pieces to be tested individually.

You could also look into using an Observer to do this.

Just my thoughts.