Ed_ Ed_ - 21 days ago 5
Ruby Question

Dry repeated methods and routes in Rails

I'm new to ruby/rails and I have this situation with 3 resources which uses comments provided by the acts_as_votable gem. The thing is, I have added the necessary methods and routes in all of the resources, resulting in a complete non-dry aberration.

My routes are:

Rails.application.routes.draw do
root to: 'home#index'

get 'home/index', to: 'home#index'

resources :users

get 'thinga/tagged/:tag', to: 'thinga#tags', as: 'tagged_thingas'
get 'thingb/tagged/:tag', to: 'thingb#tags', as: 'tagged_thingbs'

resources :thinga do
member do
get :like
get :dislike
get :unvote
end
end

resources :thingb do
member do
get :like
get :dislike
get :unvote
end
end

resources :thingc, only: [:create, :update, :destroy] do
member do
get :like
get :dislike
get :unvote
end
end

devise_for :users, path: 'auth',
:controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
end


In the controllers the repeated code is

def like
@thinga = Thinga.find(params[:id])
@thinga.liked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end

def dislike
@thinga = Thinga.find(params[:id])
@thinga.disliked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end

def unvote
@thinga = Thinga.find(params[:id])
@thinga.unvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end


Can someone help me to learn how to dry off this cases please?. Thank you.

Answer

Try rails routes concerns

http://edgeguides.rubyonrails.org/routing.html#routing-concerns

And I think you'll be able to do this

concern :votable do
  member do
    get :like
    get :dislike
    get :unvote
  end
end

resources :thinga, :thingb, :concerns => :votable
resources :thingc, :concerns => :votable, only: [:create, :update, :destroy]

For the controllers - Make a votables_controller and have the thingas_controller or thingbs_controller subclass it. You can call a before action to set the @votable instance variable and have the subclasses set the variable. The shared methods will be in one place.

class votables_controller < ApplicationController

  before_filter :set_votable

  def like
    @votable.liked_by current_user
    respond_to do |format|
      format.html { redirect_to :back }
      format.js { render layout: false }
    end
  end

  def dislike
    @votable.disliked_by current_user
    respond_to do |format|
      format.html { redirect_to :back }
      format.js { render layout: false }
    end
  end

  def unvote
    @votable.unvote_by current_user
    respond_to do |format|
      format.html { redirect_to :back }
      format.js { render layout: false }
    end
  end

end

in the subclasses (this is an example for thinga)

class thingas_controller < votables_controller

  def set_votable
    @votable = Thinga.find(params[:id])
  end

end
Comments