Passing Parameters From Rails Routes to Controller Actions

Say you have an index action renders a collection of posts. Your posts also have different kinds that have special conditions to render as a collection and you’ve created scopes to handle this.

class Post < ActiveRecord::Base
  scope :development, -> { where(kind: 'development').recently_published }
  scope :climbing, -> { where(kind: 'climbing').unarchived }

  ...
end

You want to use the same index template to render the collection rather than duplicating the code. One way to do this is with a case statement and a parameter.

def index
  case params[:post_kind]
    when 'development'
      @posts = Post.development
    when 'climbing'
      @posts = Post.climbing
    else
      @posts = Post.all
  end
end

A problem with this is that when you add a new scope you’ll have to add an additional when block. One way around this is to use send.

  @posts = params[:post_kind].present? ? Post.send(:post_kind) :
Post.all 

One obvious problem with this is that now our users can send any method they like to the Post object (destroy_all for example). In order to prevent this we should sanitize the input.


def index
  @posts = Post.send(sanitized_kind)
end

private

def sanitized_kind
  %w( climbing development ).include?(params[:post_kind]) ? params[:post_kind] : 'all'
end

Lastly, instead of having the user pass the parameter as a get variable we can set up additional routes which will provide cleaner URLs.

get 'posts/development' => 'posts#index', post_kind: 'development'
get 'posts/climbing' => 'posts#index', post_kind: 'climbing'
Written on June 4, 2016