Ruby rails: controllers
Controllers manipulate the model/views. They should be short/specific. This is how users interact with the site.
In code, they are represented as ActionController::Base
and generally subclasses of your application's ApplicationController
.
Handles sessions, logins/authorization, filters, redirection, and errors.
Documentation
official overview https://guides.rubyonrails.org/v2.3.11/action_controller_overview.html
Locations
{project}/app/controllers/application_controller
application superclass for all controllers {project}/app/controllers/{controller}_controller.rb
controllers
Basics
Controllers interpret requests
- Controllers should be skinny, Models should contain most of the logic.
- Controller-Name should be in plural. ex:
UsersController
- methods are referred to as
Actions
- Controller routes generally refers to urls the same name.
UsersController
-->https://example.com/users
(defined inroutes.rb
)- Request object accessible via
request
method.
Introspection
Module::ThingController.action_methods # list all actions on controller Module::ThingController.controller_path #
Methods
Some methods are resolved automatically
TODO:
expand on this, document properly. Possibly reference to url helpers
GET domain.com/path # index GET domain.com/path/$ID # show
Query Params
Query params ex:
https://example.com?status=unread&before=2020-01-01
are processed in theindex
method.Simple Params
# ex: https://example.com/users?status=unread&before=2020-01-01 class UsersController < ActionController::Base def index @status = params[:status] # nil if not assigned @before = params[:before] # nil if not assigned end endCollection Params
lists# each `{variable}[]=` indicates an entry within the list # # https://example.com/users?ids[]=1&ids[]=2&ids[]=3 class UsersController < ActionController::Base def index params['ids] == ['1', '2', '3'] end end
- hashes are also possible, but confusing
JSON Params
Params can be json-encoded, if in your HTML header yourContent-Type: application/json
# https://example.com/users?{"foo": "bar"} class UsersController < ActionController::Base def index params["foo"] == "bar" end end
Request Object
- the attr
ActionController.request
contains the instance ofActionDispatch::Request
See https://guides.rubyonrails.org/action_controller_overview.html#the-request-object
# curl -X POST -d '{"json": true}' https://example.com/foo class MyController < ActionController::Base def foo payload = request.body.read() # '{"json": true}' request.post? # determine HTTP method end end
Response Object
Generally the response object is not used directly, but instead returned by ??.
You can set an http status (error code) by ??.render plain: "404 Not Found", status: 404 # render status: 500 # HTTP-500 render status: :forbidden # rails symbol for HTTP-500flash[:notice] = "bar" flash[:error] = "bar"redirect_to photos_url # url method for route /photos redirect_to post_url(@post), flash: { error: "error msg" } # redirect_to({ action: 'atom' }, alert: "error msg") # redirect_back(fallback_location: photos_url) #
- https://guides.rubyonrails.org/layouts_and_rendering.html#options-for-render
- https://api.rubyonrails.org/classes/ActionController/Redirecting.html#method-i-redirect_to
Forms
- HTML form data is made accessible on the
params()
method,
which returns a dict-likeActionController::Parameters
- If the param is not assigned, no error is raised, instead the field will be empty
<!-- sample html form --> <html> <body> <form action="/create.html" method="post"> <label for="title">Title:</label> <input id="title" type="text"> <label for="description">Description:</label> <input id="description" type="text"> <input type="submit"> </form> </body> </html># sample controller, receives form data class BlogPost < ActionController::Base def create @title = params[:title] @description = params[:description] end end
Output Formats
A single endpoint may request different returned formats.
The requested format is determined by:
- A file-extension at the end of the url
https://foo.com/users/1.json
- ?
Within
rails routes
these format-able routes will show like this:users GET /users(.:format) users#index
- You handle request types with
format.${TYPE}
- You can change return types with
render(${TYPE}: ...)
def update # .. work .. respond_to do |format| format.html { redirect_to @url } # request to ../update.html, redirect format.json { render(json: @foo.errors) } # request to ../update.json, return JSON payload format.js # request to ../update.js, evaluate ../views/../update.js.erb end end
Hooks
Like models, controllers have several hooks that you may use to alter controller behaviours.
before_action :set_user, only: [:show, :edit, :update, :destroy] # call set_user() before invocations of show/edit/update/destroy
Overriding Layout
Instead of using the default (
app/views/layouts/application.html.erb
orapp/views/layouts/{controller}.html.erb
), you can override it. This is useful if you want to use a particular layout for a group of views.Determined by a
"string"
class WelcomeController < ApplicationController layout "forest_layout" # {project}/app/views/layouts/forest_layout.html.erb endDetermined by a
:method
class WelcomeController < ApplicationController layout :choose_layout def choose_layout if ENV["USE_OCEAN_LAYOUT"] == "true" return "ocean_layout" end return "forest_layout" end endConditional based on Request Format (ex: RSS, HTML, XML, ...)
class WelcomeController < ApplicationController # conditionally set layout layout "forest_layout", except: :rss layout "forest_layout", except: [:rss, :xml] # restrict layout type layout "forest_layout", only: :html end
Render different view
render/render_to_string
will render a view (and format as a response).def index render :user # render view for action 'user' render "products/user" # render a different controller's action end