ahnbizcad
ahnbizcad

Reputation: 10797

Voting in voter controller for multiple models passing polymorphic/duck typed object variable

I am trying to implement voting for various models: Character, Universe models. I have some vote buttons on their respective show templates via a single DRY rendered partial. This means I need to make that partial agnostic to the model when I pass that model to the controller logic that creates the vote record.

So yes, I am wondering if it is possible to have this kind of duck typed variable passing to a votes controller, rather than repeating the vote creation logic in each of the Character and Universe controller.

How do I make this sort of duck typed variable passing using path helpers, votes partials, and votes controllers? Or should I just repeat the code in each votable model?

FYI: Naturally, the votes is polymorphic to the two models. I am using acts_as_votable gem https://github.com/ryanto/acts_as_votable

_vote.html.haml

.vote-comment-buttons
  = link_to image_tag("upvote.png"), votes_upvote_path(), method: :post, remote: true
  %span=# @comment.get_upvotes.sum(:vote_weight)
  = link_to image_tag("downvote.png"), votes_downvote_path(), method: :post, remote: true

^ Do I pass a duck type variable to the paths?, If so, how/where do I define the duck type variable?

(Note: i'm trying to vote via AJAX, but feel free to ignore the ajax related code like remote: true)

character.rb

class Character < ActiveRecord::Base
  acts_as_votable # creates a polymorphic association with votes
end

universe.rb

class Universe < ActiveRecord::Base
  acts_as_votable # creates a polymorphic association with votes
end

votes_controller.rb

class VotesController < ApplicationController

  before_action :check_if_voted(@votable)

  def upvote
    #votable.vote_up current_user

  end

  def downvote(votable)
    #votable.vote_down current_user
  end
end

Iis it valid to have parameters on actions? or do you just use params[]?

Upvotes: 0

Views: 524

Answers (1)

twonegatives
twonegatives

Reputation: 3438

Iis it valid to have parameters on actions? or do you just use params[]?

You can not have parameters at public actions, all the data should be parsed from params[]

How do I make this sort of duck typed variable passing using path helpers, votes partials, and votes controllers? Or should I just repeat the code in each votable model?

Assuming voting procedure is the same for both models, consider using concerns:

config/application.rb

 module YourApplicationName
   class Application < Rails::Application
     config.autoload_paths += %W(#{config.root}/app/controllers/concerns)
   end
 end

app/controllers/concerns/voting_controller.rb

module VotingController
  extend ActiveSupport::Concern

  included do
    before_filter :obtain_resources, only: [:upvote, :downvote]
  end

  def upvote
    # here goes your upvoting logics, something like
    # @object.liked_by user1, :vote_weight => 1
    # the @object variable has already been set in obtain_resource filter
    # other params should be catched up from the params hash
  end

  def downvote
    # here goes your downvoting logics
  end

  private
  def obtain_resources
    # here we retrieve the name of a particular controller which triggered this action
    model = controller_name.singularize.camelize.constantize
    @object = model.find(params[:id])
  end
end

In case you prefer using meaningful variable names instead of just @object, you can do this kind of thing:

# app/controllers/concerns/voting_controller.rb
def obtain_resources
  model = controller_name.singularize.camelize.constantize
  instance_name = controller_name.singularize
  # here you get @universe or @character
  instance_variable_set "@#{instance_name}", model.find(params[:id])
end

Then in your controllers you should include that concern:

class UniverseController < ApplicationController
  include VotingController
  # that's all, you should not implement voting actions here as they are included from VotingController
end

class CharactersController < ApplicationController
  include VotingController
end

You should also be sure to set appropriate routes in your config/routes.rb file, so that upvote and downvote actions exist in both of the controllers. As you could already understand, you should pass upvote_universe_path or upvote_characters_path to your partial to make this work (same for the downvoting). Each route should pass id of the object to controller's action.

Upvotes: 2

Related Questions