Forum: Ruby on Rails Newb question about facade columns

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.
C59a0cd12b991d3b6266c730dbc4d81e?d=identicon&s=25 Rachel McConnell (Guest)
on 2006-03-02 22:11
(Received via mailing list)
Hello,

I am trying to deal with a database table that contains a date in the
form of the number of seconds since epoch.  Data is inserted in this
format by an existing script that I can't change, so a schema change
isn't realistic.

I would like to allow humans to view and edit this data in the form of
an actual human-readable date.  I figured a facade column would be the
way to go here, but I am stuck on the fact that the inputs from the
facade type are multiparameter.  This breaks the update_attributes call
in ActiveRecord::Base with a NoMethodError in my object's controller's
update method.

I have tried to add an additional attribute to the object's attributes
hash, but this code (which I put in the controller's update method)
fails silently - the attributes written out are the same before and
after:

     puts "the attributes currently are:"
     @event.attributes.each do |key, value|
       puts "#{key} = #{value}"
     end

     @event.attributes.store("occurred_at", nil)

     puts "after attempting to add an attribute, the attributes are:"
     @event.attributes.each do |key, value|
       puts "#{key} = #{value}"
     end


Any suggestions?  Has anyone used a facade column on a Date?  Since the
problem occurs in the *controller* update method, I can't see how to use
a callback to do the conversion, am I wrong?

(Is part of my problem the name of the column, which is "updated_at" ?
I tried renaming it for testing but got the same error.)

Thanks,
Rachel


Snips from the applicable files:

event.rb
-------------
class Event < ActiveRecord::Base
     # create a a field based on updated_at, that's an actual Date kind
     # of object
     def occurred_at
       Time.at( read_attribute("updated_at") )
     end
     def occurred_at=(date)
       write_attribute( "updated_at", date.to_i )
     end
end


event_controller.rb
--------------
...
   def update
     @event = Event.find(params[:id])
     if @event.update_attributes(params[:event])
       flash[:notice] = 'Event was successfully updated.'
       redirect_to :action => 'show', :id => @event
     else
       render :action => 'edit'
     end
   end
...


_form.rhtml
--------------
...
<p><label for="event_updated_at">Occurred at</label><br/>
<%= datetime_select 'event', 'occurred_at' %></p>
...


This displays the date in the dropdowns correctly.  However, if I try to
save a change (or even the same data), I get this:


  NoMethodError in Event#update

You have a nil object when you didn't expect it!
The error occured while evaluating nil.klass


c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1685:in
`execute_callstack_for_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1684:in
`each'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1684:in
`execute_callstack_for_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1677:in
`assign_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1339:in
`attributes='
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1266:in
`update_attributes'
#{RAILS_ROOT}/app/controllers/event_controller.rb:34:in `update'
97a7959f0d9e6c90ddae200520e93067?d=identicon&s=25 Damon Clinkscales (Guest)
on 2006-03-03 00:21
(Received via mailing list)
Rachel McConnell <rachel@...> writes:

> I am trying to deal with a database table that contains a date in the
> form of the number of seconds since epoch.  Data is inserted in this
> format by an existing script that I can't change, so a schema change
> isn't realistic.
>
[snip]
>
> (Is part of my problem the name of the column, which is "updated_at" ?
> I tried renaming it for testing but got the same error.)

updated_at is automatically updated for all ActiveRecord::Base
descendants.  So
if this column records something else, you should definitely name it
something
else.

Perhaps what you want to do is grab the values from the form in your
controller
and then have some logic in the model save it/convert it to the database
column
in the proper format.

like add an attribute to your model (but not your db) called "my_date"
and have
an attribute in your database called "epoch_thingy" or whatever you call
it:

class MyModel
  attr :my_date

  before_save :do_that_date_thing

  def do_that_date_thing
    #do some kind of crazy date conversion here
    self.epoch_thingy = my_conversion(self.my_date)
  end
end

Something like that...?

-damon
http://damonclinkscales.com/
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2006-03-03 00:51
(Received via mailing list)
Rachel McConnell wrote:

> update method.
This may work:

_form.html
----------

select_datetime @event.occurred_at, :prefix => 'event[occurred_at]'


event.rb
--------

class Event < ActiveRecord::Base
   def occurred_at
     Time.at( updated_at )
   end

   def occurred_at= (d)
     d.each_pair { |k,v| d[k] = v.to_i }
     self.updated_at =
Time.local(d['year'],d['month'],d['day'],d['hour'],d['minute']).to_i
   end
end


--
We develop, watch us RoR, in numbers too big to ignore.
C59a0cd12b991d3b6266c730dbc4d81e?d=identicon&s=25 Rachel McConnell (Guest)
on 2006-03-03 03:25
(Received via mailing list)
Mark Reginald James wrote:
>> facade type are multiparameter.  This breaks the update_attributes
>
>     d.each_pair { |k,v| d[k] = v.to_i }
>     self.updated_at =
> Time.local(d['year'],d['month'],d['day'],d['hour'],d['minute']).to_i
>   end
> end
>

I did think along those lines (and if that should work my problem is
elsewhere, but i don't know where).  My code actually fails *before* it
gets to call the occurred_at= method of the model object; or maybe that
would never get called.  The controller is using
@event.update_attributes which does... something different.  The stack
trace shows this:

c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1685:in
`execute_callstack_for_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1684:in
`each'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1684:in
`execute_callstack_for_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1677:in
`assign_multiparameter_attributes'
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1339:in
`attributes='
c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1266:in
`update_attributes'
#{RAILS_ROOT}/app/controllers/event_controller.rb:34:in `update'

Is there a different way to set the input parameters on the model, other
than

   @event.update_attributes(params[:event])

?

I mean, I could parse the params and attempt to set everything by hand
and that would probably work, but it kinda defeats the point of Rails'
infrastructure work.

Rachel
C59a0cd12b991d3b6266c730dbc4d81e?d=identicon&s=25 Rachel McConnell (Guest)
on 2006-03-03 03:28
(Received via mailing list)
Damon Clinkscales wrote:
>
>>(Is part of my problem the name of the column, which is "updated_at" ?
>>I tried renaming it for testing but got the same error.)
>
>
> updated_at is automatically updated for all ActiveRecord::Base descendants.  So
> if this column records something else, you should definitely name it something
> else.

MySQL allows auto-updated columns to be set explicitly; besides, I tried
renaming it just to see what would happen and that didn't help.  For
non-coding reasons a schema change really isn't do-able for me.

>
> Perhaps what you want to do is grab the values from the form in your controller
> and then have some logic in the model save it/convert it to the database column
> in the proper format.
>

As I understand it, that's exactly what a column facade does.  But (I
believe) since I'm trying to convert from a multipiece-data format to a
single-piece format, it isn't working :(

Rachel
C64e63b70be7dfed8b0742540b8b27e5?d=identicon&s=25 Mark Reginald James (Guest)
on 2006-03-03 03:34
(Received via mailing list)
Rachel McConnell wrote:
> @event.update_attributes which does... something different.  The stack
> trace shows this:
>
> c:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/base.rb:1685:in
> `execute_callstack_for_multiparameter_attributes'

Note that my suggestion uses select_datetime rather than
datetime_select.
This will prevent the multiparameter attribute assignment from trying to
occur.

--
We develop, watch us RoR, in numbers too big to ignore.
C59a0cd12b991d3b6266c730dbc4d81e?d=identicon&s=25 Rachel McConnell (Guest)
on 2006-03-03 04:37
(Received via mailing list)
Mark Reginald James wrote:
> Note that my suggestion uses select_datetime rather than datetime_select.
> This will prevent the multiparameter attribute assignment from trying to
> occur.
>

You're right, I missed that!  And your code works perfectly.  Looks like
it uses a 2 dimensional array instead of... a Hash, I think - whatever
datetime_select produces.  Must look into that more.

Thank you very much!

Rachel
This topic is locked and can not be replied to.