Search code examples
ruby-on-railsrubyruby-on-rails-4sti

Rails STI class auto initialize


I'm trying to make a STI Base model which changes automatically to inherited class like that:

#models/source/base.rb
class Source::Base < ActiveRecord::Base
  after_initialize :detect_type

  private
  def detect_type
    if (/(rss)$/ ~= self.url)
      self.type = 'Source::RSS'
    end
  end
end

#models/source/rss.rb
class Source::RSS < Source::Base
  def get_content
    puts 'Got content from RSS'
  end
end

And i want such behavior:

s = Source::Base.new(:url => 'http://stackoverflow.com/rss')
s.get_content #=> Got content from RSS

s2 = Source::Base.first # url is also ending rss
s2.get_content #=> Got content from RSS

Solution

  • If I were you I would add a class method that returns the right instance.

    class Source::Base < ActiveRecord::Base
      def self.new_by_url(params)
        type = if (/(rss)$/ ~= params[:url])
          'Source::RSS'
        end
        raise 'invalid type' unless type
        type.constantize.new(params)
      end
    end
    

    Then you will get the behavior needed:

    s = Source::Base.new_by_url(:url => 'http://stackoverflow.com/rss')
    s.get_content #=> Got content from RSS
    

    And s will be an instance of Source::RSS.

    Note: after read your comment about becomes: its code uses klass.new. And new is a class method. After initialize, your object is done and it is a Source::Base, and there are no way to change it.