Tags to ruby interface: What I am missing? (repost)


#1

I offer my apologies in advance for the repost. On a list as busy as
this, it is sometimes easy to get missed in the shuffle.

I am trying to create, display and edit a referenced object’s text field
on the same view as my root object.

For example:

Quiz: <<quiz.name>>
Preamble: <<quiz.preamble_presentation.text>>
Postamble: <<quiz.postamble_presentation.text>>

[create][cancel]

quiz.preamble_presentation references a Presentation object instance.
The Presentation object contains a property called text, among others.
The generated form will display the ID of the presentation, but I cannot
figure out for the life of me how to display/edit the contents of the
text property of the presentation.

What (I think) I want to display/edit is
quiz.preamble_presentation.text. However, this is an illegal construct
according to the runtime environment.

I am missing something that will be patently obvious to someone who is
comfortable with HTML tags.

My approach to taglibs in the past, with JSP’s, has been to dig out the
generated servlet, throw away the JSP code and clean up the servlet code
into something that is maintainable. This does not appear to be an
(easy) option with rails, and I am open to becoming more comfortable
with tag libs.

I am running under WEBrick with rails 1.0 and Ruby 1.8.4 under Fedora
Core 3.

Thanks in advance,
David J.

The quiz/_form.rhtml contains:

<%= error_messages_for ‘quiz’ %>

Name
<%= text_field 'quiz', 'name' %>

Preamble presentation
<%= text_field 'quiz', 'preamble_presentation' %>

Postamble presentation
<%= text_field 'quiz', 'postamble_presentation' %>

The ddl for the tables is:

create table presentations
(
ID char(36) not null primary key,
text varchar (1024),
audio varchar (1024),
visual varchar (1024)
);

create table quizzes
(
ID char(36) not null primary key,
name varchar (255) not null,
preamble_presentation char(36),
postamble_presentation char(36),
foreign key (preamble_presentation) references presentations(ID),
foreign key (postamble_presentation) references presentations(ID)
);


#2

Is ‘text’ a reserved word? That would be my first guess. Try another
column
name instead of ‘text’.

Since you don’t post your code, it is impossible to tell if you are
using
invalid syntax.


#3

On Tue, 2006-03-14 at 18:57 -0800, HH wrote:

Is ‘text’ a reserved word? That would be my first guess. Try another column
name instead of ‘text’.

Since you don’t post your code, it is impossible to tell if you are using
invalid syntax.

I am re-testing using textval.

Which piece of code do you need? That is part of the problem.

Since I am not yet fully comfortable with taglibs, I can’t tell which
piece of code you might need. There’s a lot of generated classes so
far.

Thanks,


#4

On Tue, 2006-03-14 at 18:57 -0800, HH wrote:

Is ‘text’ a reserved word? That would be my first guess. Try another column
name instead of ‘text’.

Since you don’t post your code, it is impossible to tell if you are using
invalid syntax.

Does this error message help?

undefined method `preamble_presentation.textvalue’ for
#Quiz:0xb79261f0

Extracted source (around line #8):

5: <%= text_field ‘quiz’, ‘name’ %>


6:
7:

Preamble
presentation

8: <%= text_field ‘quiz’, ‘preamble_presentation.textvalue’ %>


9:
10:

Postamble
presentation

11: <%= text_field ‘quiz’, ‘postamble_presentation’ %>

Related code snip from the controller:

def new
@quiz = Quiz.new
@quiz.preamble_presentation = Presentations.new

@quiz.postamble_presentation = Presentations.new

end

def create
@quiz = Quiz.new(params[:quiz])
@quiz.preamble_presentation =
Presentations.new(params[:preamble_presentation])

@quiz.postamble_presentation =

Presentation.new(params[:postamble_presentation])
if @quiz.preamble_presentation.save and @quiz.save

and @quiz.postamble_presentation.save

  flash[:notice] = 'Quiz was successfully created.'
  redirect_to :action => 'list'
else
  render :action => 'new'
end

end


#5

David,

A couple of things I would suggest.

First, how badly do you need that normalization? Can you put the
preamble/postable directly in the quizzes table? If so, your problem is
pretty much solved.

However, if that isn’t an option you have a couple of things to address.
First, in your DDL you have both the premable_presentaiton column AND a
foreign key called preamble_presentation. Rails isn’t going to like
that.
Rename or remove those non foreign_key columns. Also rename your foreign
key
columns to something that rails expects: preamble_presentation_id and
postamble_presentation_id

Next, you have to tell the model how these objects relate to each other.
Assuming Quiz is your model class, you want to tell it how to find the
preamble/postamble. The below assumes your presentatino table is
represented
as a Presentation active record, too:

class Quiz < ActiveRecord::Base
belongs_to :preamble_presentation, :class => “Presentation”
belongs_to :postamble_presentation, :class => “Presentation”
end

What this allows is direct access to the pre and postamble presentations
associated with each quiz. ‘belongs_to’ might seem odd, but you have to
use
it as the foreign key for your presentations actually resides in the
quizzes
table. If you had a foreign key to the quiz in the presentations table,
you’d use the ‘has_one’ keyword. Anyways, you can load a quiz and access
those properties like so:

@quiz = Quiz.find_by_id(1)
@quiz.preamble_presentation.text = “foo”
@quiz.postamble_presentation.text = “bar”

Next, I wouldn’t bother with the text_field methods in the view. I think
you
have stretched them farther than they want to go. Instead, use this
pattern
in your view:

<%= text_field_tag ‘quiz[preamble_presentation][text]’, @
quiz.preamble_presentation.text %>

<%= text_field_tag ‘quiz[postamble_presentation][text]’, @
quiz.postamble_presentation.text %>

Rails will interpret the ‘[]’ notation as nested properties within the
object. In your your controller you can access these as nested hashes
(assuming the form submit action is ‘update’):

def update
@quiz = Quiz.find_by_id(:id, :include => [:preamble_presentation,
:postamble_presentation])
@quiz.preamble_presentation.text =
@params[:quiz][:preamble_presentation][:text]
@quiz.postamble_presentation.text =
@params[:quiz][:postamble_presentation][:text]
if @quiz.save && @quiz.preamble_presentation.save && @
quiz.postamble_presentation.save
flash[:message] = “Success!”
redirect_to :action => “show”, :id => @params[:id]
else
flash[:message] = “Unable to save.”
end
end

You have to call save 3 times because Rails will not automatically save
objects in “belongs_to” associations.

Hope that helps!


#6

Thanks! This is exactly the sort of information I was looking for.

On Wed, 2006-03-15 at 11:41 -0800, Justin B. wrote:

David,

A couple of things I would suggest.

First, how badly do you need that normalization? Can you put the
preamble/postable directly in the quizzes table? If so, your problem
is pretty much solved.

For the quizzer, it could be handled differently. For real projects
involving financially sensitive data, it is very necessary. Since the
quizzer is primarily a way for me to learn Rails with something that is
immediately useful but not mission critical, I choose to lean towards
learning rather than expediency.

However, if that isn’t an option you have a couple of things to
address. First, in your DDL you have both the premable_presentaiton
column AND a foreign key called preamble_presentation. Rails isn’t
going to like that. Rename or remove those non foreign_key columns.
Also rename your foreign key columns to something that rails expects:
preamble_presentation_id and postamble_presentation_id

The columns are the same. The Firebird understanding of this DDL is
“the preamble_presentation field is a foreign key from the presentations
table’s ID field”, and it exists in this case primarily for the
referential integrity during testing. I will rename the columns as you
suggest.

Presentations is actually referenced by many more tables (Questions,
Answers, HistoricalQuestions, and HistoricalAnswers for starters), so it
cannot reflect ownership.

Next, you have to tell the model how these objects relate to each
other. Assuming Quiz is your model class, you want to tell it how to
find the preamble/postamble. The below assumes your presentatino table
is represented as a Presentation active record, too:

It is …

class Quiz < ActiveRecord::Base
belongs_to :preamble_presentation, :class => “Presentation”
belongs_to :postamble_presentation, :class => “Presentation”
end

This looks sensible. If I am reading this correctly, it says “the
instance property preamble_presentation contains a reference to an
instance of class Presentation”. The “belongs_to” operator is used
because the most common usage of this reference pattern is to indicate
ownership in a hierarchy. My data model inverts some of the assumptions
made by the rails designers, hence the backwards appearing construct.

It might be appropriate at some point in the future (after I am
comfortable with the environment) for me to suggest that a “references”
operator be added, which could be implemented as a synonym to
belongs_to, although its semantic meaning is subtly different.

Next, I wouldn’t bother with the text_field methods in the view. I
think you have stretched them farther than they want to go.

I figured as much. Since I am mostly unfamiliar with HTML tag
programming, this is where I thought I was lost. Can you explain in
more detail the semantics of this tag to the rhtml processing? I might
understand it better then.

(assuming the form submit action is ‘update’):
flash[:message] = “Success!”
redirect_to :action => “show”, :id => @params[:id]
else
flash[:message] = “Unable to save.”
end
end

You have to call save 3 times because Rails will not automatically
save objects in “belongs_to” associations.

Makes sense - Rails does not pretend to understand my business, which is
why it should be much easier to use than EJB’s.

Hope that helps!
It sure does!

Thanks again!
David J.


#7

My bad - I ran into that too and actually removed it from the code I
was using to test my post. Just remove the :include statement from
that find and it should work. Those two foreign keys that point to the
same object confuse Rails somehow and it constructs a bad SQL
statement.


#8

Almost success …

The lookup is failing:

code:
def edit

This failed - it generated some really funky and wierd SQL.

@quiz = Quiz.find_by_id(:id, :include =>

[:preamble_presentation, :postamble_presentation])
end

error trace:

ActiveRecord::StatementInvalid in Admin#edit
FireRuby::FireRubyException: Error preparing a SQL statement.
Dynamic SQL Error
SQL error code = -206
Column unknown
QUIZZES.PRESENTATION_ID
At line 1, column 480.
Column does not belong to referenced table
SQL Code = -206
Firebird Code = 335544569
: SELECT presentations.“TEXTVALUE” AS t2_r1, presentations.“AUDIO” AS
t2_r2, presentations.“VISUAL” AS t2_r3, presentations.“ID” AS t1_r0,
presentations.“TEXTVALUE” AS t1_r1, quizzes.“ID” AS t0_r0,
presentations.“AUDIO” AS t1_r2, quizzes.“NAME” AS t0_r1,
presentations.“VISUAL” AS t1_r3, quizzes.“PREAMBLE_PRESENTATION_ID” AS
t0_r2, quizzes.“POSTAMBLE_PRESENTATION_ID” AS t0_r3, presentations.“ID”
AS t2_r0 FROM quizzes LEFT OUTER JOIN presentations ON presentations.id
= quizzes.presentation_id LEFT OUTER JOIN presentations ON
presentations.id = quizzes.presentation_id WHERE (quizzes.“ID” = '—
:id
’ )

RAILS_ROOT: script/…/config/…

Application Trace | Framework Trace | Full Trace
/usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/connection_adapters/abstract_adapter.rb:88:in
log' /usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/connection_adapters/firebird_adapter.rb:315:inexecute’
/usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/connection_adapters/firebird_adapter.rb:391:in
select' /usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/connection_adapters/firebird_adapter.rb:306:inselect_all’
/usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/associations.rb:937:in
select_all_rows' /usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/associations.rb:851:infind_with_associations’
/usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:395:in
find' /usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:393:infind’
/usr/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:980:in
method_missing' ./script/../config/../app/controllers/admin_controller.rb:52:inedit’

Here’s the code examples you gave me, corrected so that they compile and
are starting to work:

On Wed, 2006-03-15 at 11:41 -0800, Justin B. wrote:

is represented as a Presentation active record, too:
class Quiz < ActiveRecord::Base
require ‘presentations’
require ‘question’

include UUIDHelper

validates_presence_of :name
has_many :questions, :order => :position
belongs_to :preamble_presentation, :class_name => “Presentations”
belongs_to :postamble_presentation, :class_name => “Presentations”
end

Next, I wouldn’t bother with the text_field methods in the view. I
think you have stretched them farther than they want to go. Instead,
use this pattern in your view:

Preamble presentation
<%= text_field_tag 'quiz[preamble_presentation][textvalue]', @quiz.preamble_presentation.textvalue %>

Postamble presentation
<%= text_field_tag 'quiz[postamble_presentation][textvalue]', @quiz.postamble_presentation.textvalue %>

Rails will interpret the ‘[]’ notation as nested properties within the
object. In your your controller you can access these as nested hashes
(assuming the form submit action is ‘update’):

class AdminController < ApplicationController

def index
list
render :action => ‘list’
end

def list_questions
redirect_to(:controller => “/questions”, :parent_quiz => params
[:id])
end

def list
@quiz_pages, @quizzes = paginate :quizzes, :per_page => 10
end

def show
@quiz = Quiz.find_by_id(:id, :include =>
[:preamble_presentation, :postamble_presentation])
end

def new
@quiz = Quiz.new
@quiz.preamble_presentation = Presentations.new
@quiz.postamble_presentation = Presentations.new
@quiz.preamble_presentation.textvalue = ‘’
@quiz.postamble_presentation.textvalue = ‘’
end

def create

changed because we are instantiating the presentations here

may not be needed if we instantiate presentations in the initialize,

but that will require thinking because we already have a use for

the initialize method.

@quiz = Quiz.new (@params[:quiz])

@quiz = Quiz.new
@quiz.name = @params[:quiz][:name]
@quiz.preamble_presentation = Presentations.new
@quiz.postamble_presentation = Presentations.new
@quiz.preamble_presentation.textvalue = @params

[:quiz][:preamble_presentation][:textvalue]
@quiz.postamble_presentation.textvalue = @params
[:quiz][:postamble_presentation][:textvalue]

if @quiz.preamble_presentation.save &&

@quiz.postamble_presentation.save && @quiz.save

  flash[:notice] = 'Quiz was successfully created.'
  redirect_to :action => 'list'
else
  render :action => 'new'
end

end

def edit
@quiz = Quiz.find(params[:id])
end

def update
@quiz = Quiz.find_by_id(:id, :include =>
[:preamble_presentation, :postamble_presentation])
@quiz.preamble_presentation.textvalue = @params
[:quiz][:preamble_presentation[:textvalue]]
@quiz.postamble_presentation.textvalue = @params
[:quiz][:postamble_presentation[:textvalue]]
if @quiz.preamble_presentation.save &&
@quiz.postamble_presentation.save && @quiz.save
flash[:notice] = ‘Quiz was successfully updated.’
redirect_to :action => “show”, :id => @params[:id]
else
flash[:message] = “Unable to save.”
render :action => ‘edit’
end
end

def destroy
@quiz = Quiz.find_by_id(:id, :include =>
[:preamble_presentation, :postamble_presentation])
@quiz.preamble_presentation.destroy
@quiz.postamble_presentation.destroy
@quiz.destroy
redirect_to :action => ‘list’
end
end


#9

Here’s how to make it work - add the :foreign_key directive

class Quiz < ActiveRecord::Base
require ‘presentations’
require ‘question’

include UUIDHelper

validates_presence_of :name
has_many :questions, :order => :position
belongs_to :preamble_presentation, :class_name =>
“Presentations”, :foreign_key => :preamble_presentation_id
belongs_to :postamble_presentation, :class_name =>
“Presentations”, :foreign_key => :postamble_presentation_id
end