1

I would like to know if there's a way to when doing STI the update_attributes, validate the attributes based on the new class type?

For e.g. suppose i have:

class A < ActiveRecord::Base
end
class B < A
    validates :attribute_z, :presence => true
end 
class C < A
    validates :attribute_x, :presence => true
    validates :attribute_y, :presence => true 
end

If i run (the way rails is implemented):

b = A.find('b-id')
b.update_attributes({ 'type' => 'C', :attribute_x => 'present', :attribute_y => 'present', :attribute_z => nil }) # will return false with errors on 'attribute_z must be present'

I've tried with #becomes:

b = A.find('b-id')
b = b.becomes(C)
b.update_attributes({ 'type' => 'C', :attribute_x => 'present', :attribute_y => 'present', :attribute_z => nil })
# this works partially, because the validations are ok but when i look to console i get something like: 
UPDATE "as" SET "type" = 'c', "attribute_z" = NULL, "attribute_y' = 'present', 'attribute_x' = 'present' WHERE "as"."type" IN ('C') AND "as"."id" = 'b-id' 
# which is terrible because it's looking for a record of B type on the C types.
daniloisr
  • 1,367
  • 11
  • 20
Guilherme
  • 1,126
  • 9
  • 17
  • i could put :if => proc { |record| record.type == 'C' } on the validations and put the validations at A class. But it wouldn't make sense to have the subclasses. The difference basically of B and C is only in the validation behavior. (I have many validations on both types) – Guilherme Jan 14 '13 at 17:49

2 Answers2

0

Allied to this topic Callback for changed ActiveRecord attributes?, you can catch any assignments done to type attribute and make "self" to be of different class (A, B or C) by using the becomes method. So whenever you use find method, it'll populate a fresh new model instance with the data that comes from the DB (identified by 'b-id') and it'll automatically forced-cast the model instance to another type if necessary.

Does that help you?

Community
  • 1
  • 1
leandroico
  • 1,207
  • 13
  • 17
  • thank you, i didn't knew about the attribute_changed. But it don't solve my problem because the problem is, when doing STI, the UPDATE SQL clausule is: UPDATE table SET attributes WHERE (FORCED BY STI STUFF, table.type IN ('ObjectType')) AND table.id = 'updated-id'. So if i have a object that is from type B, and is changed to type C using the becomes, the 'update' will be called in the object of type C and then the update will use the 'C' as the FORCED ARGUMENTS FOR STI, but the record with id is on DB with the old type 'B', so the UPDATE will not work. – Guilherme Jan 14 '13 at 18:20
0

i've made a solution: https://gist.github.com/4532583, since the condition is added internally by rails (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/inheritance.rb#L15) i've created a new "update_attributes" method that changes the type if the type is given. :)

Guilherme
  • 1,126
  • 9
  • 17