views:

54

answers:

1

Hi, I've got something like this

class Reply < AR::Base
end

class VideoReply < Reply
  def hello
    p 'not ok'
  end
end

class PostReply < Reply
  def hello
    p 'ok'
  end
end

...

So when I am creating object:

# params[:reply][:type] = "VideoReply"
@reply = Reply.new(params[:reply])

How can I invoke child method (in this case VideoReply::hello)?

UPD: I can imagine only very stupid solution:

@reply = Reply.new(params[:reply])
eval(@reply.type).find(@reply.id).hello

But it is not cool, I think :)

+1  A: 

When you're dealing with STI-based models, you'll have problems creating them if you're not careful. Retrieving them should be done automatically so long as you use the base class to find.

What you need is to create the proper kind of model in the first place and the rest will be fine. In your model or controller define a list of valid classes:

REPLY_CLASSES = %w[ Reply VideoReply PostReply ]

Then you can use this to verify the type before creating an object:

# Find the type in the list of valid classes, or default to the first
# entry if not found.
reply_class = REPLY_CLASSES[REPLY_CLASSES.index(params[:reply][:type]).to_i]

# Convert this string into a class and build a new record
@reply = reply_class.constantize.new(params[:reply])

This should create a reply with the proper class. Methods should work as desired at this point.

tadman
it looks hacky, but why not? thx! By the way to create REPLY_CLASSES array we can use `Reply.subclasses.map{|s| s.to_s} << "Reply"`
fl00r
The temptation is there to use `Reply.subclasses` but the catch is that only indicates which classes are loaded. When your app spins up that will be empty. Rails is able to auto-load subclasses on-demand, but it can't predict which subclasses can be loaded in advance without actually trying to load them. If you want to get fancy, you can manually load all of `app/models/reply/*.rb` to force them in if you want, but you'll have models called `Reply::Video` and `Reply::Post` instead. You could also try force-loading `app/models/*reply*.rb` as well.
tadman
In production Rails will load all classes. In development we can use static REPLY_CLASSES array
fl00r
I think things are auto-loaded even in production, so you might want to validate that your sucblasses array will be fully populated.
tadman