[ROR]Single Table Inheritance with Rails 4 (Part 2)

来源:互联网 发布:windows下nvidia smi 编辑:程序博客网 时间:2024/06/06 10:52

Time to continue learning about Single Table Inheritance with Ruby on Rails.

Learn how to implement STI with Rails with this free ebook

In this article, we are going to see how to create a common controller for our models (Animal, Lion, etc). We will also need to create the corresponding routes and add some helpers to generate the right paths. If you don’t have the code from the first part, you can get it here.

The common controller

Time to start working ! First, we have to generate the common controller for our models. The name is going to be AnimalsController which matches our parent model.

rails g controller AnimalsController index show new edit create update destroy

Define the routes

Let’s move to the routes file and define our routes. First, remove the lines generated by rails since we are going to use resources :

# Remove these lines from config/routes.rbget "animals/index"# Code hidden for brevityget "animals/destroy"

Define the root of our app and the routes for Animals :

# config/routes.rbSti::Application.routes.draw do resources :animals root 'animals#index'end

Setup the index action and the corresponding view

Fire up your server and access localhost:3000 and you will see, well nothing. We should add some content to our index view, maybe the list of all the animals in the database. To provide this list to the view, we need to add some simple code to our controller :

# app/controllers/animals_controller.rbdef index  @animals = Animal.allend

And copy/paste (or retype it, totally up to you) the following in animals/index.html.erb.

# app/views/animals/index.html.erb<h1>The Lion Tribe</h1><table>  <thead>    <tr>      <th>Name</th>      <th>Race</th>      <th>Age</th>      <th></th>      <th></th>      <th></th>    </tr></thead>  <tbody>    <% @animals.each do |animal| %>      <tr>        <td><%= animal.name %></td>        <td><%= animal.race %></td>        <td><%= animal.age %></td>        <td><%= link_to 'Show', "" %></td>        <td><%= link_to 'Edit', "" %></td>        <td><%= link_to 'Destroy', "", method: :delete, data: { confirm: 'Are you sure?' } %></td>      </tr>    <% end %>  </tbody></table>

The view still looks pretty bad huh, but whatever, it’s not a design tutorial. You are free to add some css (and you can even pull request on [the repo][2], I will definitely merge it if it looks good).

Now that we have a basic way to show our data, we can start playing with Single Table Inheritance. In the first part, we created one table animals and one model Animal. We also created 3 other models (Lion, Meerkat, WildBoars) that inherit from Animal and all live happily in the same table. Thanks to that, we avoided repeating code at the model level. We can follow the DRY rule at the controller level by using a unique controller for all our STI models.

Routes and STI

After a bit of code, the index action of the Animals controller will be able to show either all the animals or only the animals of a specific race. To do this, the controller need to know which race is requested. Thanks to Rails, we can define a default parameter in our routes.

# config/routes.rbresources :animalsresources :lions, controller: 'animals', type: 'Lion' resources :meerkats, controller: 'animals', type: 'Meerkat' resources :wild_boars, controller: 'animals', type: 'WildBoar'root 'animals#index'

See the type key ? That’s the trick. Everytime we access /lions/*, Rails will add a parameter in paramswith the key type. So, for each model that inherit from Animal, we define resource routes and specify that they have to use the animals controller. We also add the corresponding type.

We can now extract the type value in the controller with the following methods :

class AnimalsController < ApplicationController     before_action :set_race    def index         @animals = Animal.all     end    # Code hidden for brivety    private    def set_race        @race = race     end    def race         Animal.races.include?(params[:type]) ? params[:type] : "Animal"    end    def race_class         race.constantize     endend

Basically, before any action we assign the value of the race params, or Animal if no race is passed, to the variable @race so our views can access it. We can now change the Animal.all to race_class.all. The method race_class is going to send back the constantized parameter type like Lion for example.

def index  @animals = race_class.allend

Warm Up

If you access http://localhost:3000, you will have the list of all animals. But if you access http://localhost:3000/lions or http://localhost:3000/meerkats, you only get the list of the specified animal. Wonderful, isn’t it ?!

That’s it for this article. In the next part, we are going to add dynamic paths and the missing views !

原创粉丝点击