I guess you can consider this a continuation from my previous question. Basically I'm rendering a js.erb partial which enables ajax functionality to like/dislike a restaurant dish. I have four actions that render this partial:
class DishesController < ApplicationController
def like
#dish.liked_by current_user
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
def unlike
#dish.unliked_by current_user
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
...
end
To DRY this up I planned to put the respond_to method inside of a before_action callback:
class DishesController < ApplicationController
before_action :render_vote_partial
...
private
...
def render_vote_partial
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
end
end
Unfortunately this doesn't render the partial at all. What am I doing wrong?
If you want to dry up the respond_to, just stick it in a method (like you have already) and call that method after each action.
Instead of
before_action :render_vote_partial
just do do:
def like
# do your work here ...
render_vote_partial
end
def unlike
# do your work here ...
render_vote_partial
end
Related
Trying to implement a tree-like comments on the site via a gem - acts-as-commentable-with-threading.
Comments are excellent and are displayed on the site when I visit a site under the user (implemented via the gem devise).
But when trying to view pages anonymously, naturally, I receive an error that id is not may be due to the elements onto a blank.
This is my controller recipes_controller.rb:
class RecipesController < ApplicationController
before_action :authenticate_chef!, except: [:index, :show]
def show
#recipe = Recipe.find(params[:id])
#comments = #recipe.comment_threads.order('created_at desc')
#user_who_commented = current_chef
#new_comment = Comment.build_from(#recipe, #user_who_commented.id, "")
end
...
comments_controller.rb:
class CommentsController < ApplicationController
before_action :authenticate_chef!
def create
commentable = commentable_type.constantize.find(commentable_id)
#user_who_commented = current_chef
#comment = Comment.build_from(commentable, #user_who_commented.id, body)
respond_to do |format|
if #comment.save
make_child_comment
format.html { redirect_to(:back, :notice => 'Comment was successfully added.') }
else
format.html { render :action => "new" }
end
end
end
...
recipe.rb:
class Recipe < ActiveRecord::Base
acts_as_commentable
...
In views (recipes/show.html.erb) I put this render :
<%= render partial: "comments/template", locals: {commentable: #recipe, new_comment: #comment} %>
I think that you may need in the controller to create something like a design if ... else for those who just browse the site, because the default at this point in the show method is set current_chef, because of what and error.
You need to handle the special case in view(probably comment template) for anonymous visit. Cause then current_chef would be nil. So where you're using it in view and controller, handle that properly.
A tip: You don't need to assign current_chef to any instance variable actually. It's already a helper method. You can call it directly from view.
I have a feature where users like/unlike and dislike/undislike dishes. This is how I initially have my actions setup:
Dishes Controller
class DishesController < ApplicationController
def like
#dish.liked_by current_user
end
def dislike
#dish.disliked_by current_user
end
...
end
Considering the js.erb templates for each template are completely identical I wanted to create just one shared partial that each could use:
Original Setup
# like.js.erb, unlike.js.erb,dislike.js.erb,undislike.js.erb
$(".restaurant__dish-vote--<%= #dish.modifier_class %>").html('<%= escape_javascript(render "restaurants/menu_partials/dish_votes", dish: #dish) %>');
New Setup
# shared/_vote.js.erb
$(".restaurant__dish-vote--<%= #dish.modifier_class %>").html('<%= escape_javascript(render "restaurants/menu_partials/dish_votes", dish: #dish) %>');
Now I'm trying to refactor my controller to reflect these changes but the ajax functionality doesn't seem to work with the following:
class DishesController < ApplicationController
def like
respond_to do |format|
format.js { render partial: "dishes/shared/vote.js.erb" }
end
#dish.liked_by current_user
end
end
The action completes but the changes on the user-facing side are only reflected upon refresh. What am I doing wrong?
I think you should call #dish.liked_by current_user before render partial: "dishes/shared/vote.js.erb". The problem is that #dish is not updated yet.
I have several actions (lets call them action_a, action_b, etc.)
In each action I want to check if the user is logged in and if not to have a respond_to block as follows
format.js {
if current_user.nil?
render partial: 'some_partial', handler: [:erb], formats: [:js]
end
}
For one action this is fine, but not for many actions, as there will be many duplications of this code which will do exactly the same thing, it is not very pretty or maintainable
Is there a way to put this somewhere so I can reuse this code and not rewrite it in every needed action?
Use before_filter (rails <4.0) or before_action (rails 4.0)
class YourController < ApplicationController
before_filter :check_user
...
your actions
...
private
def check_user
redirect_to sign_in_path if current_user.nil?
end
end
or if you want specific actions and respond use around_action (filter):
class YourController < ApplicationController
around_action :check_user
...
your actions
def show
#variable = Variable.last
end
...
private
def check_user
yield #(you normal action without js respond)
format.js {
if current_user.nil?
render partial: 'some_partial', handler: [:erb], formats: [:js]
end
}
end
end
Read up on Responders. These are meant to help with that.
Your specific problem is what filters are generally used for. See this Section on Filters in the Action Controller Guide
More generally, this is just ruby code, so you can refactor everything out into methods:
def do_stuff_with(partial_name, format)
if current_user.nil?
render partial: partial_name, handler: [:erb], formats: [format]
end
end
format_js { do_stuff_with('some_partial', :js) }
How to make redirect_to works in those filters?
I'm trying to change
def start
....
redirect_to index
end
def end
...
redirect_to index
end
to
around_filter :around
def around
...
yield
redirect_to index
end
def start
..
end
def stop
...
end
After the action is complete it renders the template automatically, thus you cannot render / redirect after the request is complete. You could solve this by putting the redirect_to at the end of the actions that you need it for. This is not what around_filters were designed to do.
Presumably, your actions already have a redirect_to or render call. You cannot call these methods twice per request.
You can change response.location, which has the same effect as calling redirect_to. An example with an after_filter (the same could be done with around):
after_filter :different_redirect, only:[:create]
def different_redirect
if self.status == 302
response.location = other_thing_path
end
end
def create
#my_thing = MyThing.new(params[:my_thing])
respond_to do |format|
if #my_thing.save
format.html { redirect_to(my_things_path) }
else
format.html { render :action => "new" }
end
end
end
In my application i need to set some deafult actions for all format.js and format.htm responses. At this moment I have something like this in all controllers:
def index
#users = User.all
respond_to do |format|
format.html {html_response}
format.js {js_response}
end
end
But I think that it isn't a good solution. What can I do?
Make a private method in your ApplicationController and call it from wherever required
class ApplicationController < ActionController::Base
…
private
def default_responses
respond_to do |format|
format.html {html_response}
format.js {js_response}
end
end
end
class SomethingsController < ApplicationController
def index
#somethings = Something.all
default_responses
end
end