wiring a dependent update

Rails 2.0.2
Ruby 1.8.5
SQLite3
CentOS-5.1

models/

Entity
entity has_one client

Client
client belongs_to entity

validates_associated

view/

clients/new.html.erb

<h1>New client</h1>

<%= error_messages_for :client %>
<%= error_messages_for :entity %>

<% form_for(@client) do |f| %>

  <% fields_for(@entity) do |e| %>
    <p>
      <b>Client Name</b><br />
      <%= e.text_field :entity_name %>
    </p>

    <p>
      <b>Client Legal Name</b><br />
      <%= e.text_field :entity_legal_name %>
    </p>

    <p>
      <b>Client Legal Form</b><br />
      <%= e.text_field :entity_legal_form %>
    </p>

  <% end %>

  <p>
    <b>Client status</b><br />
    <%= f.text_field :client_status %>
  </p>
...

controllers/
clients_controller.rb

  # POST /clients
  # POST /clients.xml
  def create
    @entity = Entity.new(params[:entity])
    @client = Client.new(params[:client])

    respond_to do |format|
      if @entity.save && @client.save
        flash[:notice] = 'Client was successfully created.'
        format.html { redirect_to(@client) }
        format.xml { render :xml => @client, :status => :created,
:location => @client }
      else
        format.html { render :action => "new" }
        format.xml { render :xml => @client.errors, :status =>
:unprocessable_entity }
      end
    end
  end

What I want to do is to have an entity created if one does not exist and
the existing entity.id returned to client otherwise. The above code
almost works when there is no existing entity but the client.entity_id
is not set to the id of the newly created entity throwing an SQL error.

Question 1. Must I create a method in clients.rb, say entity_build, that
checks for the existence of an entity (find_by_entity_name) or creates a
new entity otherwise? What would this look like?

Question 2. What do I pass the method from the controller? @entity,
@client?

Question 3. How do I get the fields from the view into the entity from
within the client model? See Q.2

Question 4. Is there a simpler way do do this?

I am twisting in the breeze her trying to figure out how to make this
work. I have read any number of tutorials and code examples but I am
just not getting it.

Any help is most welcome.

Okay - I've never understood the has_one principle. If :entity has_one
:client, then why wouldn't you just put the fields for :client in the
:entity model?

If it was :entity has_many :clients, then the way you would get
client.entity_id populated would be as follows:

    @entity = Entity.new(params[:entity])
    @client = Client.new(params[:client])
    @entity.clients << @client

The result of that last line is that the new client is updated with the
appropriate entity_id and then saved. So, in a has_one maybe it's like
this...

    @entity = Entity.new(params[:entity])
    @client = Client.new(params[:client])
    @entity.client = @client

But I'm just not sure - the has_one thing is confusing to me, and from
what I've read it actually works backwards from what you would expect if
you're used to has_many.

Can you fill me in a little more on what you're doing - I'm almost
certain there's a better design out there just waiting for you to find
it. :slight_smile:

Cayce Balara wrote:

Okay - I've never understood the has_one principle. If :entity has_one
:client, then why wouldn't you just put the fields for :client in the
:entity model?

My background is data design and I am a newcomer to ruby and rails. I
have examined both the language and framework on and off for the past
few years but I am now beginning a real project. Currently, my main
problem is getting enough traction with the API to know where to go to
find answers. This is coming, albeit slowly.

Now, with respect to the has_one / belongs_to relationship or, as rails
puts it, association; consider the situation at hand. What is a client?
Well, that depends upon your point of view but essentially a client is
someone that consumes your output and sends you money. Now, what is a
vendor? Well, similarly a vendor is someone whose output you consume and
whom you send money to. The salient question here is: are these two
concepts, clients and vendors, separate things or are they separate
roles of a single thing?

In many system designs these two concepts are treated as separate
things. They possess state like account balance and physical attributes
like addresses. They are related to transactions like invoices and
cheques. However, considered another way, client and vendor can be
profitably considered as just roles played by a distinct entity, whose
existence is independent of whether or not they buy from or sell to us.

If one considers a client and a vendor simply as roles within a
transaction based system then entity may be factored out and become a
separate concept. A single entity may then possess the role of client
(has_one :client) or vendor (has_one :vendor) (or both or neither) and
also possess multiple attributes (has_many :locations, has_many
:contacts). The role of client and vendor may not persist (client.status
= "inactive") even when the other role does (vendor.status =
"preferred"). An entity may be something else than a client or a
vendor, perhaps an employee, a consignee for a drop shipment, an agent
for the firm, or a person working at a client. Perhaps it is
significant to know whether two or more entities are related (has_many
:relationships).

Extracting the entity from its roles permits entity.contacts where
contacts:

belongs_to :entity, :foreign_key => :entity_id
belongs_to :entity, :foreign_key => :contact_entity_id

You can now reflect into a single representation of entity properties
located in a group of consistently named and structured tables whether
the entity is considered a client or a vendor in context.

Given this you also can have things like shipments:

  has_many :orders, :through => :order_shipments
  has_one :entity, :foreign_key => :consignee_entity_id,
                          :include => [:locations, :contacts]
  has_one :entity, :foreign_key => :carrier_entity_id,
  ...

You can also handle situations like issuing cheques to non-vendors
(refunds to clients) and receiving funds from non-clients (employee
reimbursement for disallowed expenses) in a much more natural manner
since you are not required by the data design to set up a vendor in the
first or a client in the second instance.

If it was :entity has_many :clients, then the way you would get
client.entity_id populated would be as follows:

    @entity = Entity.new(params[:entity])
    @client = Client.new(params[:client])
    @entity.clients << @client

In my design an entity either has a role or it does not. A single entity
is not treated as being multiple clients. In certain circumstances one
might wish to treat divisions of a very large corporate entity as
separate clients and this design could handle that eventuality, but that
is not the case at present. On the other hand, if these division were
structured as distinct legal forms then the current approach remains
valid and a relationship model accommodates the reality.

The result of that last line is that the new client is updated with the
appropriate entity_id and then saved. So, in a has_one maybe it's like
this...

    @entity = Entity.new(params[:entity])
    @client = Client.new(params[:client])
    @entity.client = @client

This will fail with an SQL error as client.entity_id will be null. I
know, I've tried ;-).

But I'm just not sure - the has_one thing is confusing to me, and from
what I've read it actually works backwards from what you would expect if
you're used to has_many.

What is bedeviling me at the moment is my state of profound ignorance
with the Rails APi. There is nothing else for it but to read the damn
thing again but, as with many things, what you read at leisure seldom
returns when you need it. I have since rediscovered the API for
ActiveRecord::Associations::ClassMethods and have therein learned that
the way to do what I want is eventually going to look somewhat like
this:

In clients_controllers.rb

  # GET /clients/new
  # GET /clients/new.xml
  def new
    @entity = Entity.new
    @client = @entities.build_client
...

  # POST /clients
  # POST /clients.xml
  def create
    @entity = Entity.new(params[:entity])
    @client = @entity.build_client(params[:client])

Based on my very preliminary and presently shallow understanding, the
build_client method is generated by AR in consequence of the has_one /
belongs_to association specification in the respective models. This
then wires the underlying dependencies wherein client.entity_id is set
to entity.id. In this case the AR association also enforces the
foreign_key constraint so that if @entity.save fails then too does the
dependent @client.save.

If I were to wrap this in a transaction
(ActiveRecord::Transactions::ClassMethods) then it appears that I need
to employ entity.save! and client.save! to force a transactional roll
back in the event of an error in either. However, subject to further
study and a deeper apprehension on my part, a transactional approach
does not seem to provide any measurable benefit over the basic behaviour
in this simple case.

Thank you for presenting the opportunity to clarify this to myself. It
is amazing how having to explain oneself to someone else straightens
things out in your own mind.