Ruby on Rails 3 Controller

Generate a RoR controller

Command to create a controller "Home" with actions "index" and "register"

% rails generate controller Home index register
      create  app/controllers/home_controller.rb
       route  get "home/register"
       route  get "home/index"
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      create    app/views/home/register.html.erb
      invoke  test_unit
      create    test/functional/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      create      test/unit/helpers/home_helper_test.rb

Files generated

In directory File Created Description
app/controllers home_controller.rb Controller "Home"
app/views/home index.html.erb View for action "index"
app/views/home register.html.erb View for action "register"
app/helpers home_helper.rb Helper method for "Home"
test/unit/helpers home_helper_test.rb Unit test helper
test/functional home_controller_test.rb Functional test

Controller Class

Instantiate an empty user and to be edited by a View

Controller
class HomeController < ApplicationController
  def index
  end

  def register
      @user = User.new
  end
end
View
<%= form_for(@user) do |f| %>
  <div class="field">
    <%= f.label :user_name %><br />
    <%= f.text_field :user_name %>
  </div>
  ...
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Set a Default Home Page for a RoR application

Change the default home page

http://localhost:3000/
  • Use "my_action" in "my_controller" to process home page
  1. Un-comment and re-define the "root" in the route configuration
    config/routes.rb
    root :to => "my_controller#my_action"
    
  2. Delete the original home page
    rm public/index.html
  3. Add business logic to "my_action"
  4. Add Html content to the view
    app/views/my_controller/my_action.html.erb
    
  5. Verify the result by hitting the home page

Submit & Read HTTP parameters in Rails

A Html Form
<form action="/accounts" method="post">
  <input type="text" name="account[user_name]" value="john" />
  <input type="text" name="account[address][city]" value="San Francisco" />
</form>

To read the Html form parameters

params[:account]                  #=> {"user_name" => "john", "address" => {"city" => "San Francisco"}}
params[:account][:address]        #=> {"city" => "San Francisco"}
params[:account][:address][:city] #=> "San Francisco"



To read query parameters

http://127.0.0.1:3000/home/index?reg=john&arr[]=1&arr[]=2
params[:reg] #=> "john"
params[:arr] #=> ["1", "2"]
  • All types are returned as string

Routing Parameter

match 'products/:coupon' => 'catalog#view'

map.connect "/register/:status",
  :controller => "register",
  :action => "verify",
  :coupon => "disc10"
# For URL /products/aa34

params[:coupon] #=> "aa34"

Using Filter in Rails

Filters are methods invoke before, after or around an action

  • after_filter
  • around_filter
  • before_filter
  • Controllers also inherits filters from parents & the global application controller (ApplicationController)

Inject code before calling an action in a Controller: before_filter

class HomeController < ApplicationController
  # Call prepare before calling an action
  before_filter :prepare

  def prepare
    ...
  end

Apply the filter on selective Rails Controller Action

  # Call the filters only for the actions listed in :only
  before_filter :prepare, :only => [:show, :edit, :update, :destroy]

  # Call the filters for all actions except those listed in :except
  before_filter :prepare, :except => [:show, :destroy]

Use code block as filter

before_filter do |controller|
    controller.send(:intruder?)  # call intruder? method in the controller
    ...
end

def intruder?
  ...
end

Redirect user if it is not login for all controllers of the application

class ApplicationController < ActionController::Base
  before_filter do |controller|
    redirect_to login_url unless controller.send(:login?)
  end
end

Use class as filter

class IntruderFilter
  def self.filter(controller)
    if controller.send(:intruder?)
       ...
    end
  end
end
before_filter IntruderFilter

Skipping inherited filters

# Skip the filters for actions listed in :only
# Useful for skipping inherited filters
skip_before_filter :prepare, :only => [:browse]

Before filter can be used to redirect a request (like redirect user if the user does not have the necessary privilege)

  before_filter :check

  def check
    if intruder?
      redirect_to "/"
    end
  end

If you have defined the "root" route

root :to => "my_controller#my_action"       # Rails will define root_url automatically
# Replace redirect_to "/" with
redirect_to root_url

Be careful of infinite loop on redirect in filter

  before_filter :check

  def check
    if intruder?
      # When redirect to "bye", Rails calls the before_filter again
      # It invokes the filter again which results in infinite loop
      redirect_to action "bye"
    end
  end

  def bye
    ...
  end

Around Filter

class HomeController < ApplicationController::Base
  around_filter :log_global_exception

  def log_global_exception
    logger.debug "Before calling action"
    yield                                     # Yield to the action
  rescue => exception                         # If the action raise any exception, catch it
    logger.debug "Exception: #{exception}"    # Log it and then raise it again
    raise
  end

  def index
  end
end

Input Data Verification with Ruby on Rails

Verification verify a pre-defined condition before an action is invoked

Controller
class AccountsController < ApplicationController
  verify :params => [:p1, :p2],              # Verify if params[:p1] & params[:p2] are not nil
         :render => {:action => "register"}, # If not, render "register" instead
         :add_flash => {                     # And set the Flash message
           :error => "Requirement not meet"
         }

Apply the verification on selected actions only

class AccountsController < ApplicationController
  verify :params => [:p1, :p2],
         :render => {:action => "register"},
         :add_flash => {
           :error => "Requirement not meet"
         },
         :only => :show                     # Verify only those actions listed on :only

  def show
      ...
  end

Controller Methods

Controller methods in accessing controller meta information

controller_name   # The name of the current controller
action_name       # The name of the current action invoked
Controller methods in accessing Http Objects Information
request Info on host, domain, format, method, headers, port, protocol, query_string, remote_ip, url
response Info on body, status, location. content type, headers
path_parameters Path information
query_parameters Query string
request_parameters Parameters in the Post

Global Exception Handler

To handle "SomeError" globally

raise SomeError
app/controllers/accounts_controller.rb
class ApplicationController < ActionController::Base
  # Call handle_some_error when SomeError is not handled
  rescue_from SomeError, :with => :handle_some_error

private
  def handle_some_error
    flash[:error] = "Some error happened"
    redirect_to :back
  end
end

Initializer

Create application wide configuration constant

  1. Add a new file (or reuse initializer) under config/initializers
    LmsParser::Application.config.username = 'user'
    LmsParser::Application.config.password = 'password'
    
  • Use LmsParser::Application.config.username to access the variable
  • Reboot rails for any changes

Avoid Logging Sensitive Data

Use filter_parameter_logging to avoid logging of sensitive parameters

class ApplicationController < ActionController::Base
  filter_parameter_logging :password
end
  • params[:password] indicates all password fields will be replaced before logging into a file
  • By default, this is already enabled in config/application.rb