1

Exam is a parent of SatTest and ActTest like so

class Exam < ActiveRecord::Base
     self.inheritance_column = :test_type

       def self.find_sti_class(type_name)
           type_name = self.name
           super
       end
end

class ActTest < Exam
    def self.sti_name
        'ACT'
    end

    def some_method
    end
end

class SatTest < Exam
    def self.sti_name
        'SAT'
    end

    def some_method
    end
end

When I query Exam.find(1) it returns an instance of Exam. As a result, when I call some_method on this object, it calls returns undefined method some_method instead of calling the method on it's subclass.

What is the best way to send the method down to its subclass without having to requery again? I know I could do this, but it seems pretty hacky

class Exam < ActiveRecord::Base
    def some_method
        if self.type == "SAT"
            SatTest.find(self.id).some_method
        elsif self.type == "ACT"
            ActTest.find(self.id).some_method
        end
    end
end

UPDATE In regards to the type field, I have edited the models above (it is renamed to test_type in my app). I'm using rails 4.2.1.

Derrick Mar
  • 1,021
  • 1
  • 12
  • 15

1 Answers1

2

Rails will initialize the correct object if have STI properly setup (i.e. your exams table has a type:string column or you've defined self.inheritance_column on the exam model)

If your query is returning an instance of Exam, it means that either the test_type column is blank/nil or you don't have STI setup properly.

class Exam < ActiveRecord::Base
  self.inheritance_column = :test_type

  def some_method
    "called from Exam"
  end
end

class ActTest < Exam
  def some_method
    "called from ActTest"
  end
end

class SatTest < Exam
  def some_method
    "called from SatTest"
  end
end

Try it in console

Exam.create name:'SAT', test_type:'SatTest'
 => #<SatTest id: 1, name: "SAT", test_type: "SatTest", created_at: "2015-04-20 01:52:43", updated_at: "2015-04-20 01:52:43"> 

exam = Exam.find 1
 => #<SatTest id: 1, name: "SAT", test_type: "SatTest", created_at: "2015-04-20 01:52:43", updated_at: "2015-04-20 01:52:43">
exam.some_method
 => "called from SatTest"

Reference

Bart Jedrocha
  • 11,450
  • 5
  • 43
  • 53
  • Yeah that's interesting... I started a clean app and the behavior is just like the console. But with this legacy app although I feel like I have defined a type column I still get back an `Exam` object instead of the child instance – Derrick Mar Apr 20 '15 at 03:50
  • Ahh I think it has to do something with me overriding the value of the ```inheritance column``` with the method ```self.find_sti_class(type_name)```. Currently looking into it more – Derrick Mar Apr 20 '15 at 04:00
  • You don't need any additional methods such as `self.find_sti_class`. Also, the string you store within the `inheritence_column` must match the name of class (e.g. `SatTest`) in order for any of this to work as you expect. – Bart Jedrocha Apr 20 '15 at 13:52
  • Well I'm doing that because I would like to use "SAT" and "ACT" as the type identifier, instead of the model name "SatTest" and "ActTest". E.g. http://stackoverflow.com/questions/23293177/rails-sti-how-to-change-mapping-between-class-name-value-of-the-type-column But I don't think I'm overriding the method correctly. – Derrick Mar Apr 20 '15 at 18:35
  • Then you'd better off dropping the suffix "test" from their class names. – Omar Ali Apr 20 '15 at 18:38
  • @DerrickMar out of curiosity, for what purpose? Unless you're working with a legacy database, I see very little reason to modify the semantics of how the inheritance column is stored in the database. If you still insist, take a look at https://gist.github.com/bjedrocha/f82d63187773d096491b. Note that while that will work for retrieval, you'll need to add additional logic to deal with record creation. Not worth it in my opinion. – Bart Jedrocha Apr 20 '15 at 19:48
  • Hey @BartJedrocha. Thanks for the gist. Yeah there's good reason for me to make it "SAT" (it's a legacy database). When client receives json, they would like to call response.test_type to get "SAT" to be displayed in front end. – Derrick Mar Apr 23 '15 at 00:19