Rails and Conditional STI Routes

96 views Asked by At

I have a STI system something like this:

Model: Fruit

Various Types (via a type column): Apple, Orange, Banana, Grape

So currently I would have routes like this:

url_for(Apple.first) = /apples/1
url_for(Orange.first) = /oranges/2
url_for(Banana.first) = /bananas/3
etc...

After implementing this I have a situation where I would want a select set of Fruit types to default back to just plain Fruit i.e. no STI BUT still keep the type. For example lets say I wanted Apples and Oranges to just be plain old fruit:

url_for(Apple.first) = /fruit/1
url_for(Orange.first) = /fruit/2
url_for(Banana.first) = /bananas/3

I know that I can do this:

fruit_path(Apple.first) = /fruit/1
fruit_path(Orange.first) = /fruit/2

The issue I have with the workaround above is that I have a pile of logic, helpers, etc. that now breaks if I do this.

I know I can clear the type column but that means I lose that type data. I guess I could set a second column (fruit_type etc.) to save this. Seems less ideal but may be my best option.

For reference this question / answer is close to what I am looking for:

Rails STI routing

The question I am asking: is there a way (in the STI models perhaps) to 'disable' STI on a per-type basis while retaining the type field?

1

There are 1 answers

0
max On
class Fruit
  # just for the sake of the example
  include ActiveModel::Naming
  include ActiveModel::Model

  def self.uses_parent_name
    define_singleton_method :model_name do
      ActiveModel::Name.new(self.name, nil, Fruit.name)
    end
  end
end
class Apple < Fruit
  uses_parent_name
end
class Orange < Fruit
  uses_parent_name
end
class Banana < Fruit
end 

uses_parent_name is just a simple "macro-method" that defines a model_name class method. .model_name is how ActiveModel translates model instances into routes, i18n keys and parameter keys.

irb(main):024:0> Apple.model_name.route_key
=> "fruits"
irb(main):026:0> Orange.model_name.route_key
=> "fruits"
irb(main):025:0> Banana.model_name.route_key
=> "bananas"