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.
--
Posted via http://www.ruby-forum.com/.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---