2.16 Addendum: Custom Controller Files

We don’t have to put all of our actions within the default ApplicationController file that comes included with any Rails app; we can add our own controllers, if we want to organize things a bit more. With an app of any non-trivial size, you’ll end up with hundreds of actions, and it can get unwieldy to put them all in one gigantic application_controller.rb.

Instead, we can change our route for /rock to this:

get("/rock", { :controller => "game", :action => "play_rock" })

Now when a user visits /rock, they will see an error uninitialized constant GameController.

As we know, when Ruby says “uninitialized constant” it means “I can’t find that class”.

So, what’s going on here? When we said :controller => "game" in the route, we told Rails to look for a class called GameController when someone visits /rock.

  • All of the controller class names will end in ...Controller, and they will begin with whatever value we provided for the key :controller in the route.

  • Like all Ruby classes, the name must be CamelCase (not snake_case or Some_Hybrid). So in this case, it will be GameController.

  • The class must be defined in a Ruby file that is the snake_cased version of its name. Rails will itself use the .underscore method to figure out the name; we can try it ourselves in rails console:

    [2] pry(main)> "GameController".underscore
    => "game_controller"
  • The Ruby file must be placed within the app/controllers/ folder. So, in this case, we create a file called app/controllers/game_controller.rb (don’t forget the .rb file extension).

  • Finally, within this file, we define the class:

    class GameController < ApplicationController
    end
  • We inherit from ApplicationController, which in turn inherits from ActionController::Base; much like our models inherited from ActiveRecord::Base via ApplicationRecord.

    Our models inherited .save, .where, and a bunch of other awesome database-related methods from ActiveRecord::Base; whereas our controllers are going to inherit a bunch of methods like render, redirect_to, and a bunch of other awesome interface-related methods from ActionController::Base.

  • Don’t forget the end that goes with the Class; type it before you forget it.

  • Move your play_rock action over from application_controller.rb into this new class.

  • Now, when a user visits the path /rock, the “uninitialized constant” error should go away and you should see a response as before.

    If you still see the “unitialized constant” error, then:

    • You named your class wrong; it must exactly match the value in routes.rb, followed by Controller (singular), and CamelCase.
    • You named the file wrong. Try doing .underscore on a string containing the class name in rails console to figure out the correct filename.
    • You put the file in the wrong folder. It has to be within app/controllers/. Not within, for example, app/ or app/controllers/concerns/.
    • You forgot the .rb file extension.
    • If you can’t find which of the above it is, try deleting what you did and paving over your work again from scratch. Sometimes you just can’t spot your own typos, and paving over is the best approach.

You can make as many controllers as you like; in general, a rule of thumb is to have one controller per database table.