Newb question about facade columns


#1

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

Occurred at
<%= datetime_select 'event', 'occurred_at' %>

...

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:ineach’
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:inassign_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:inupdate_attributes’
#{RAILS_ROOT}/app/controllers/event_controller.rb:34:in `update’


#2

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/


#3

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.


#4

Mark Reginald J. 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:ineach’
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:inassign_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:inupdate_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


#5

Damon C. 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 :frowning:

Rachel


#6

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.


#7

Mark Reginald J. 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