MVC Design Question - dependent objects

Given

entity has_one, :client

client belongs_to :entity

When creating a new client and an instance of entity.name does not exist
then I want to add an entity and a client. (This is done and works)

If entity.name exists then I want to check if entity.client also exists.

If entity.client exists then I want to return both the entity and client
instances from the database to the user and ask if this is the client
they wished to create.

If entity.client does not exist then I want to return the entity to the
user and ask if this is the entity that they wish to add as a client and
allow them to add the client role to that entity.

So, does the code for this sort of checking and shuffling of data belong
mainly in the model or mainly in the controller? Are there Rails
specific considerations that bear upon the answer?

The trend seems to be to keep as much logic on the Model as possible,
and only use the controller for grabbing the data that the View needs.
In this case, your controller needs to know if it should tell the view
to ask a question. You should let the model take care of the how and
why. It's not necessarily a Rails specific design pattern, more than it
is considered best practice.

On a side note, I'm a fan of using the before_filter to do a little
pre-processing before hitting the code in the action. For example in
your Entity controller:

  before_filter :check_for_client, :only => :edit

  def edit
    #stuff related to editing an entity
  end

  private
  def check_for_client
     @entity = Entity.find(params[:id])
     redirect_to(new_client_path) unless @entity.has_client?
  end

In addition to performing you check, your entity record is already
loaded for your edit action.

Bryan M. wrote:

On a side note, I'm a fan of using the before_filter to do a little
pre-processing before hitting the code in the action. For example in
your Entity controller:

  before_filter :check_for_client, :only => :edit

  def edit
    #stuff related to editing an entity
  end

  private
  def check_for_client
     @entity = Entity.find(params[:id])
     redirect_to(new_client_path) unless @entity.has_client?
  end

I just want to clarify something. When one references the entity through
the client new action then this code must go into the
clients_controller.rb correct? I understand that controllers are never
implicitly invoked by reference to the model. Am I correct?

If this code is used in multiple controllers then is it best to extract
it as a helper method or is there a different preferred way of doing
that?

If this code is used in multiple controllers then is it best to

extract

it as a helper method or is there a different preferred way of doing
that?

Helpers are really meant to help automate view components. If you want
to share some common functionality to your controllers, I would put that
code into a module, and save it in the "lib" folder. You can then
include that in your controllers (or models, or views) by just adding
"include MyModule" to the controllers you want it accessible to. If you
think it should be accessible to all your controllers, you can add the
include statement to your ApplicationController.

I would say only use a module if it's really necessary to share code.
Typically, you'll want to keep your CRUD operations inside of the
controller. You're right that models shouldn't invoke the controller,
but I think you're forgetting that the Entity model and the Client model
are already connected through their relationship.

In your Client Controller, you should have no trouble accessing it's
Entity through @client.entity, and vice versa in the Entity Controller
(@entity.client).

So from the previous example, if you go to edit an Entity with no
client, it redirects to the Client's new action, where you ask a user if
he'd like to add a client. Your client controller might look something
like this:

  before_filter :load_entity

  def new
    @client = Client.new
  end

def create
    @client = Client.new(params[:client])
    @client.entity = @entity
    @client.save!
  end

  private
  def load_entity
    #since this is a has_one, we can load the entity in for the whole
controller
    @entity = Entity.find(params[:entity_id])
  end

You'll have to be passing the :entity_id in from somewhere. You can do
this in your re-directs:

redirect_to(new_client_path, :entity_id => @entity.id)

or if you're using nested routes:

redirect_to(new_entity_client_path(@entity))

Hope this helps.