Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Single Table Inheritance and models subdirectories

I have a card-game application which makes use of Single Table Inheritance. I have a class Card, and a database table cards with column type, and a number of subclasses of Card (including class Foo < Card and class Bar < Card, for the sake of argument).

As it happens, Foo is a card from the original printing of the game, while Bar is a card from an expansion. In an attempt to rationalise my models, I have created a directory structure like so:

app/
+ models/
  + card.rb
  + base_game/
    + foo.rb
  + expansion/
    + bar.rb

And modified environment.rb to contain:

Rails::Initializer.run do |config|
  config.load_paths += Dir["#{RAILS_ROOT}/app/models/**"]
end

However, when my app reads a card from the database, Rails throws the following exception:

ActiveRecord::SubclassNotFound (The single-table inheritance mechanism failed to locate the subclass: 'Foo'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Card.inheritance_column to use another column for that information.)

Is it possible to make this work, or am I doomed to a flat directory structure?

like image 530
Chowlett Avatar asked Sep 06 '25 04:09

Chowlett


1 Answers

Probably the best way to do this would be to nest the Foo class inside a BaseGame module.

A ruby module is roughly similar to a package structure in other languages, it's a mechanism for partitioning related bits of code into logical groups. It has other functions such as mixins (which you can find explained here: http://www.rubyfleebie.com/an-introduction-to-modules-part-1/) but in this instance they're not relevant.

You'll need to reference and instantiate the class slightly differently. For example you'd query it like this:

BaseGame::Foo.find(:all,:conditons => :here)

Or create an instance of it like this:

BaseGame::Foo.new(:height => 1)

Rails supports modularized code for Active Record models. You just need to make a few changes to where the class is stored. say for example you move the class Foo into a module BaseGame (as in your example), you need to move apps/models/foo.rb to apps/models/base_game/foo.rb.So you're file tree will look like:

app/
 + models/
  + card.rb #The superclass
   + base_game/
      + foo.rb

To declare this on the class define like so:

module BaseGame
  class Foo < Card
  end
end
like image 87
Ceilingfish Avatar answered Sep 09 '25 04:09

Ceilingfish