RE: Should controllers be "smart"?


#1

I’d keep them separate

User.register(…) is not called
User.register_and_deliver_welcome_email(…)

I try and factor each method so it has one simple task (laziness tends
to get in the way). If your find that your repeating yourself then I’d
create a higher level helper method such as
User.register_and_deliver_welcome_email(…)

Decoupling the email action from the registration action would provide
greater flexibility in the model

as Koz mentions it does all depend on individual requirements and/or
tastes.

Ross


#2

Ok so how would the register method look in the following:

User
has_many :addresses
validates_presence_of_one_address # <- Say some custom validation to
require one address

Address
belongs_to :user
validates_presence_of :address, :city, :state, :zip
validate_zip_code # <- Say some custom validation to lookup zipcode

Business RULE:
User must have at least one address.
Address must have valid zip code plus address, city, state, zip.

My web form:
Fields for User Information, including TWO addresses (one for shipping,
and mailing).

So according to the idea the model should have all business rules and
logic…

User.register should save the user info AND adresses, right?

So what if an address validation fails. How does controller know which
address failed, and what the errors. Can you show me how the
User.register method would look in this case?

Chris

Ross D. wrote:

I’d keep them separate

User.register(…) is not called
User.register_and_deliver_welcome_email(…)

I try and factor each method so it has one simple task (laziness tends
to get in the way). If your find that your repeating yourself then I’d
create a higher level helper method such as
User.register_and_deliver_welcome_email(…)

Decoupling the email action from the registration action would provide
greater flexibility in the model

as Koz mentions it does all depend on individual requirements and/or
tastes.

Ross


#3

On 5/5/06, Chris B. removed_email_address@domain.invalid wrote:

Ok so how would the register method look in the following:
validate_zip_code # <- Say some custom validation to lookup zipcode

To me, things like zip code validation, email validation, etc. would
be a Helper.


#4

Your register could save everything if you want it to. Just depends
on how course-grained you want it to be.

The real point is that even if you don’t write a register method to
handle everything, the business logic is still available entirely from
the model.

u = logged_in_user
u.shipping_address = Address.new(params[:shipping])
u.billing_address = Address.new(params[:billing])
u.save

Now obviously this snippet requires some code from the controller (it
uses our helper method logged_in_user and params), but it’s not
coupled to the controller. You could open the console and set a
billing address. You could write a standalone app that uses the model
to do this. The controller is handling application logic - figuring
out what objects to use, methods to call, order in which to call them

  • but the business logic belongs to the model.

Finally, to answer your question about how to handle errors, it seems
pretty simple to me.

u = User.register(User.new(params[:user]),
Address.new(params[:shipping]), Address.new(params[:billing])
if u.save
redirect_to success_url
else
@shipping = u.shipping_address
@billing = u.billing_address
end

class User
belongs_to :shipping_address, :foreign_key => ‘shipping_address’,
:class => :address
belongs_to :billing_address, :foreign_key => ‘billing_address’,
:class => :address
validates_associated :shipping_address
validates_associated :billing_address

def self.register(u, shipping, billing)
u.shipping = shipping
u.billing = billing
u
end
end

Throw a validates_associated in your User model, which will validate
the two addresses and add errors if validation fails. Then assign
those to the instance variables, which will populate your forms and
show any errors. My example SUCKS of course, to be passing in a user
and then returning it, but I’m just trying to show you that you can
write a coarse-grained register method and still use the nice rails
error handling.

Pat