Forum: Ruby on Rails Tags to ruby interface: What I am missing? (repost)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
David J. (Guest)
on 2006-03-15 04:13
(Received via mailing list)
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' %>

<!--[form:quiz]-->
<p><label for="quiz_name">Name</label><br/>
<%= text_field 'quiz', 'name'  %></p>

<p><label for="quiz_preamble_presentation">Preamble
presentation</label><br/>
<%= text_field 'quiz', 'preamble_presentation'  %></p>

<p><label for="quiz_postamble_presentation">Postamble
presentation</label><br/>
<%= text_field 'quiz', 'postamble_presentation'  %></p>

<!--[eoform:quiz]-->


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)
);
HH (Guest)
on 2006-03-15 04:59
(Received via mailing list)
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.
David J. (Guest)
on 2006-03-15 05:08
(Received via mailing list)
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,
David J. (Guest)
on 2006-03-15 05:11
(Received via mailing list)
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'  %></p>
6:
7: <p><label for="quiz_preamble_presentation">Preamble
presentation</label><br/>
8: <%= text_field 'quiz', 'preamble_presentation.textvalue'  %></p>
9:
10: <p><label for="quiz_postamble_presentation">Postamble
presentation</label><br/>
11: <%= text_field 'quiz', 'postamble_presentation'  %></p>


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
Justin B. (Guest)
on 2006-03-15 21:44
(Received via mailing list)
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!
David J. (Guest)
on 2006-03-16 01:52
(Received via mailing list)
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.
David J. (Guest)
on 2006-03-16 05:12
(Received via mailing list)
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:in
`execute'
/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:in
`select_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:in
`find_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:in
`find'
/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:in `edit'




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:
>
<p><label for="quiz_preamble_presentation">Preamble
presentation</label><br/>
<%= text_field_tag 'quiz[preamble_presentation][textvalue]',
@quiz.preamble_presentation.textvalue  %></p>

<p><label for="quiz_postamble_presentation">Postamble
presentation</label><br/>
<%= text_field_tag 'quiz[postamble_presentation][textvalue]',
@quiz.postamble_presentation.textvalue  %></p>

> 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
Justin B. (Guest)
on 2006-03-16 06:59
(Received via mailing list)
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.
David J. (Guest)
on 2006-03-16 14:31
(Received via mailing list)
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
This topic is locked and can not be replied to.