Habtm and date_select problem


#1

I have an habtm problem, which I just can’t figure out for the life of
me. This is a continuation of another problem I had (on this thread:
http://www.ruby-forum.com/topic/44286#9200), which someone was kind
enough to help me with.)

To recap, I have tables Projects and Stages, with a habtm relationship.
I have a table Projects_Stages contains project_id, stages_id and an
additional date_done field (containing the date the stage of the project
was completed).

In addition to letting the user select the next stage of the project, I
want them to be able to select the date that stage was completed, so I
need a date select field on the page.

My models:

class Stages < AR
has_and_belongs_to_many :projects
end

class Projects < AR
has_and_belongs_to_many :stages
attr_accessor :newly_completed_stage_id
attr_accessor :stage_date_done

def before_save
stages.push_with_attributes(Stage.find(newly_completed_stage_id.to_i),
:date_done => stage_date_done) unless newly_completed_stage_id.blank?
end

end

My edit.rhtml page contains (as part of the form_tag):

<%= collection_select(:project, :newly_completed_stage_id,
Step.find(:all) - @project.stages, :id, :description, {:include_blank =>
true}) %>

<%= date_select (:project, :stage_date_done, :order => [:day, :month,
:year]) %>

The collection_select works just fine on its own. However, adding the
date_select to the edit.rhtml page as shown above, returns the following
error:

undefined method `klass’ for nil:NilClass

Trace shows parameters are being passed (some data omitted):

Parameters: {“commit”=>“Save”, “id”=>“1”,
“project”=>{“stage_date_done(3i)”=>“13”, “newly_completed_step_id”=>“5”,
“stage_date_done(1i)”=>“2005”, “stage_date_done(2i)”=>“12”}}

If I omit the date_select command in edit.rhtml and change my
before_save method to “:date_done => Date.today”, then everything works
properly. So the problem is with the date_select command or the
stage_date_done attribute. But I can’t find anything wrong with it, and
I can’t find any information on what the ‘klass’ method is. So I’m
stuck! Is this just an habtm limitation or am I missing something?

The only thing I can think of is that date_select returns an array (as
evidenced by the parameters passed), and that maybe I need to somehow
define stage_date_done as an array using attr_accessor? However, I can’t
find any way to do this (I have both the Programming Ruby and Agile
books).

Thanks!


#2

zero halo wrote:

undefined method `klass’ for nil:NilClass

The only thing I can think of is that date_select returns an array (as
evidenced by the parameters passed), and that maybe I need to somehow
define stage_date_done as an array using attr_accessor? However, I can’t
find any way to do this (I have both the Programming Ruby and Agile
books).

There is no way for Rails to know that stage_date_done is of class Date,
so multi-parameter assignments from posted parameters only work with
database columns where the type can be determined.

Possible solutions:

  1. Extract the parameters manually, perhaps making use of AR’s
    extract_callstack_for_multiparameter_attributes method.
  2. Somehow instantiate stage_date_done as a column in a way that
    Rails knows it’s not database-backed.
  3. Instantiate stage_date_done as Date.new, and patch Rails around
    http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/base.rb#L1690
    to look for the class of non-column attributes.


We develop, watch us RoR, in numbers too big to ignore.


#3

Mark Reginald J. wrote:

  1. Extract the parameters manually, perhaps making use of AR’s
    extract_callstack_for_multiparameter_attributes method.

Best way to do this would probably be to use select_year,
select_month, and select_day, use their field_name options,
and three attr_accessors.


We develop, watch us RoR, in numbers too big to ignore.


#4

Mark Reginald J. wrote:

Best way to do this would probably be to use select_year,
select_month, and select_day, use their field_name options,
and three attr_accessors.

Yep, tried that and it worked! I set each select_xxx with
:prefix=>:project, :field_name=>:stage_xxx
then created attr_accessor :stage_day, :stage_month, :stage_year

and finally changes before_save to:

def before_save
stagedate = (stage_year + stage_month + stage_day).to_date
stages.push_with_attributes(Stage.find(newly_completed_stage_id.to_i),
:date_done => (stagedate)) unless newly_completed_stage_id.blank?
end

Interesting is that :date_done => stagedate returns a strange error
(‘unknown method < in Nil’). Only :date_done => (stagedate) works.

Sure would be nice though if habtm had a way of verifying the attribute
type in this sort of situation. Maybe Rails could be patched so that
attr_accessor would accept a variable type?

Thanks a lot, Mark, for your help. It’s almost taken me a long to try to
solve this one little problem as to write my whole app!


#5

zero halo wrote:

def before_save
stagedate = (stage_year + stage_month + stage_day).to_date
stages.push_with_attributes(Stage.find(newly_completed_stage_id.to_i),
:date_done => (stagedate)) unless newly_completed_stage_id.blank?
end

Interesting is that :date_done => stagedate returns a strange error
(‘unknown method < in Nil’). Only :date_done => (stagedate) works.

stagedate = Date.new( stage_year, stage_month, stage_day ) would be
better.

Sure would be nice though if habtm had a way of verifying the attribute
type in this sort of situation. Maybe Rails could be patched so that
attr_accessor would accept a variable type?

Yes, I think Rails should allow models to define both DB-backed
and non-DB-backed columns, so that both types can make use of
validations and multi-parameter assignments.


We develop, watch us RoR, in numbers too big to ignore.