Aggregation/composed model attribute


#1

First off, apologies about my previous empty post.

Also, apologies for the newbie nature of this post. I am new to rails
and I’m working on a simple relational database to be administered
and accessible to the public through rails. I have a question about
aggregation as outlined in the pragprog “agile web development” book,
around page 258 to 260. My example is nearly identical to the one
used in the book. I have basically followed the example there to the
letter, the only difference is that I want to aggregate fields from a
model called Author rather than Customer, and one of the field names
is different.

My question is simply this, once the Author model refers to the new
Name model, how can I access the new :name attribute of instances of
Author? I tried to do that in a view with the following code:

<%=
author[:name] %> It doesn’t return an error, just doesn’t output
anything. The book shows how to created a new instance of Customer
with the aggregate Name object, but what about instances from a
database record? Shouldn’t they have a :name attribute if they have
all the attributes that it’s composed of?

The code I’ve used for Author and Name models is below. Thanks in
advance for any info on where I’m going wrong.

*** app/models/author.rb ***
class Author < ActiveRecord::Base

has_and_belongs_to_many :books
validates_presence_of :first_name, :last_name

composed_of declaration maps Author attributes to the Name model

object
composed_of :name,
:class_name => Name,
:mapping =>
[ [ :first_name, :first ],
[ :other_name, :other],
[ :last_name, :last]
]
end


*** app/models/name.rb ***

Class defines the Name object, aggregation of attirbutes of Author

object.

class Name
attr_reader :first, :other, :last

def initialize(first, other, last)
@first = first
@other = other
@last = last
end

def to_s
[ @first, @other, @last ].compact.join(" ")
end
end


#2

Use:

<%= author.name %> then check out page 203.

Cody

On 12/6/05, Alien8 Recordings removed_email_address@domain.invalid wrote:

is different.
The code I’ve used for Author and Name models is below. Thanks in
composed_of :name,

Class defines the Name object, aggregation of attirbutes of Author

def to_s
[ @first, @other, @last ].compact.join(" ")
end
end


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails


http://www.codyfauser.com


#3

Hey Cody,

Sorry, just one more question. I see that you can’t use symbols to
access model attributes that are aggregated, but what does one do in
the case when one wants to pass them to a method that requires
symbols, such as:
auto_complete_for :post, :title

Thanks,
Sean

On 6-Dec-05, at 3:47 PM, Cody F. wrote:

and I’m working on a simple relational database to be administered
Author? I tried to do that in a view with the following code:

<%=
class Author < ActiveRecord::Base
[ :other_name, :other],

Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails


ALIEN8 RECORDINGS
P.O. BOX 666, STATION R
MONTREAL, QC
CANADA, H2S 3L1

http://www.alien8recordings.com


#4

Sean,

It all depends on what you’re doing. The reason auto_complete_for
doesn’t work on the aggregate is because it builds the sql where
conditions based on the symbol you pass in. Since there isn’t a
‘name’ column in your table the query will fail. Click the ‘show
source’ link on the rdocs for auto_complete_for for more details:
http://api.rubyonrails.com/classes/ActionController/Macros/AutoComplete/ClassMethods.html#M000032

Depending on your circumstances you could still access the attributes
author.first_name author.last_name. Otherwise, in the case of
autocomplete you can check out Thomas F.’ fantastic demos at
http://demo.script.aculo.us/, which show a customized autocomplete in
action.

Cody

On 12/6/05, Alien8 Recordings removed_email_address@domain.invalid wrote:

My question is simply this, once the Author model refers to the new

           :mapping =>

class Name
end

P.O. BOX 666, STATION R
MONTREAL, QC
CANADA, H2S 3L1

http://www.alien8recordings.com


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails


http://www.codyfauser.com


#5

Sean,

For autocompleting names I usually cheat and create a full_name
attribute for the object. Then I create a before_save callback that
creates the full name from the first and last name. I am sacrificing
the extra storage space for convenience. I find it is usually easier
to bend your ideals to match Rails than the other way around.

class Person < ActiveRecord::Base
validates_presence_of :first_name, :last_name
before_save :generate_full_name

composed_of :name,
:mapping => [ [ :first_name, :first], [ :last_name, :last
] ]

def self.find_by_name(name)
self.find_all_by_first_name_and_last_name(name.first, name.last)
end

private
def generate_full_name
self.full_name = self.first_name + " " + self.last_name
end
end

class Name
attr_reader :first, :last

def initialize(first, last)
@first, @last = first, last
end

def last_first
“#@last, #@first
end
end

This way you can keep all of your methods that are specific to names,
like last_first, in the Name class and use them for display purposes,
but you can still conveniently autocomplete forms. There is some
duplication, but I don’t think it really matters in the long run.

There are also dynamically generated finders for your attributes.
These methods are automatically available for the model I’ve created
above (and more):

Person.find_by_first_name_and_last_name(‘Cody’, ‘Fauser’)
Person.find_all_by_full_name(‘Cody F.’)

And using the method defined above:
Person.find_by_name(Name.new(‘Cody’, ‘Fauser’))

Hope this helps.

Cody

On 12/9/05, Alien8 Recordings removed_email_address@domain.invalid wrote:

editing form with the resulting record. Since I have now learned that

http://api.rubyonrails.com/classes/ActionController/Macros/
Cody

  auto_complete_for :post, :title

Cody

around page 258 to 260. My example is nearly identical to the one
instances of
advance for any info on where I’m going wrong.
composed_of :name,

Class defines the Name object, aggregation of attirbutes of

removed_email_address@domain.invalid
ALIEN8 RECORDINGS
P.O. BOX 666, STATION R
MONTREAL, QC
CANADA, H2S 3L1

http://www.alien8recordings.com


http://www.codyfauser.com


#6

Hey Cody,

Thanks for yet more help on this issue (I’ve been working on other
things in the last few days).

I was able to get a custom auto_complete for function working, but
now I am starting to wonder if creating an attribute by aggregation
was really the way to go. When I submit the search form that I am
using the auto_complete_for text field in I am calling a find() on
the author model with a conditional statement and rendering an
editing form with the resulting record. Since I have now learned that
apparently all finds on columns other than id require sql (the
conditional statements) I guess I’m in the same position that led me
to try the custom auto_complete_for function. So I guess now my
question is: do you know a way to do a conditional find on an active
record object, so that I can match to the aggregated Name attribute?
If not, I wonder if there is any way to get the auto_complete_for
text field to pass an id even while the author name is being displayed.

Thanks again,
Sean

On 6-Dec-05, at 6:15 PM, Cody F. wrote:

Depending on your circumstances you could still access the attributes

Sean

First off, apologies about my previous empty post.
letter, the only difference is that I want to aggregate fields
author[:name] %> It doesn’t return an error, just doesn’t
class Author < ActiveRecord::Base
[ [ :first_name, :first ],
class Name
end

P.O. BOX 666, STATION R


http://www.codyfauser.com


ALIEN8 RECORDINGS
P.O. BOX 666, STATION R
MONTREAL, QC
CANADA, H2S 3L1

http://www.alien8recordings.com