Check if user voted acts as votable - ruby-on-rails

I want to make some styling changes if user has voted for a photo, and I used this code (acts_as_votable docs):
<% if current_user.voted_for? #photo %>
<%= link_to like_photo_path(#photo), method: :put do %>
<button>
¡Liked!
</button>
<% end %>
<% else %>
You dont like it yet
<% end %>
But this wont work because it will show "Liked" all the time, even if I didn't click the Like button.
photos controller
def upvote
#photo = Photo.friendly.find(params[:id])
#photo.liked_by current_user
redirect_to user_photo_path(#photo.user,#photo)
end
What can it be wrong?

Add an additional condition in your if statement
<% if current_user.voted_for? #photo && #photo.liked_by current_user %>
# different text
<% elsif current_user.voted_for? #photo %>
<%= link_to like_photo_path(#photo), method: :put do %>
<button>
¡Liked!
</button>
<% end %>
<% else %>
You dont like it yet
<% end %>
This is a pretty common design pattern of basically falling through to the next logical default.
Note that if you find yourself nesting "if" statements, like so
if condition_one
if condition_two
if condition_three
# do something
else
# do something else
end
This is the same as
if condition_one && condition_two && condition_three
# do something
else
# do something else
end
If you find yourself falling into the nested ifs pattern then rethink what you're doing. You may need to decompose the code into a helper method, etc.

Related

Acts_As_Votable Gem Automatically Liking Everything

I'm using the acts_as_votable gem to like and unlike "Deals" in my Ruby on Rails project. My user is set to act_as_voter and my deal is set to acts_as_votable, but for some reason everything is set to like as soon as a new user is created, and they can't unlike the deal. For some reason my list of deals all have an unlike button and it doesn't actually do anything but refresh the page. Here's some of my code.
app/views/catalog/index.html.erb
<ul class="deals_list">
<% #deals.each do |deal| %>
<li>
<div>
...
<div class="favorite">
<% if account_signed_in? and current_account.accountable_type == "Personnel" %>
<%= image_tag("dark-favorite.png") %>
<% if deal.liked_by current_account %>
<%= link_to unlike_deal_path(deal), method: :put do %>
Unlike
<% end %>
<% else %>
<%= link_to like_deal_path(deal), method: :put do %>
Like
<% end %>
<% end %>
<% end %>
</div>
</li>
<% end %>
</ul>
app/controllers/deals_controller.rb
def like
#deal = Deal.find(params[:id])
#deal.liked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
def unlike
#deal = Deal.find(params[:id])
#deal.unliked_by current_account
redirect_back(fallback_location: catalog_index_url)
end
config/routes.rb
resources :deals do
member do
put 'like', to: "deals#like"
put 'unlike', to: "deals#unlike"
end
end
Be sure and read the entire Readme because you're using the library wrong.
To check if a voter has voted on a model, you can use voted_for?. You can check how the voter voted by using voted_as_when_voted_for.
I zeroed in on your problem because I was expecting to see a "?" after the deal.liked_by call, which would indicate a boolean result (by convention, not always the case).
So use this instead:
<% if current_account.voted_for? deal %>

Creating a fallback if current_user is nil for Devise

I have instances in my app that perform actions based on the current_user courtesy of Devise. For instance:
Controller
class PostsController < ApplicationController
def like
#post.liked_by current_user
end
end
View
...
<% if current_user.liked? post %>
<%= link_to "Unlike", unlike_post_path(current_user.to_param, post), method: :put, remote: true %>
<% else %>
<%= link_to "Like", like_post_path(current_user.to_param, post), method: :put, remote: true %>
<% end %>
My problem, cross-site is if there is no current_user then I'll get a NilClass error. I'm aware of the callback before_filter :authenticate_user! but was wondering what would be the best solution where the actions are still visible in the view for both logged in/non-logged in users.
Devise has a helper for this:
<% if user_signed_in? && current_user.liked?(post) %>
...
<% else %>
...
<% end %>
If they aren't signed in, the second part of the condition will never be evaluated so no Nil error.
You can also try the helper current_user like so:
<% if !current_user.nil? && current_user.liked?(post) %>
...
<% else %>
...
<% end %>
We are basically telling rails that if the current user is NOT nil and the current user has liked the post then we should see the unlike button, else we see the like button.

check if user has attribute with certain value in views

I want to show the user a part of the page if he has an attribute with a certain value.
something like this
<% if user.st == "Completed" %>
<p>just for him</p>
<% end %>
I get undefined method for nil class, how can I select the class in my views directly so I can access the attribute. I cannot use any params
edit: If I do this it works, but I want to check the attribute not if he is signed in
<% if user_signed_in? %>
<% end %>
thanks
In place of user use current_user, I'm assuming that it is for signed in user only
<% if current_user.st == "Completed" %>
<p>just for him</p>
<% end %>
You can do something like this:
In your controller:
#user = User.find(params[:id]) #or whatever user you want
In your view:
<% if #user.st == "Completed" %>
<p>just for him</p>
<% end %>

Test ownership in a helper

I have this block in my views:
<% video.members.each do |p| %>
<% if p.id == current_user.id %>
<%= "paid" %>
<% end %>
<% end %>
Basically I'm trying to work out if a member has paid for a video based on whether the id's match.
Maybe this a really bad way of doing it, which case I'd be happy to try and different method.
Assuming it is an ok way of checking this, how could I write a similar statement but as a helper method? I've tried, but it seems you can't write the same logic in helpers as the block just spits out the full array and not the id, meaning it doesn't work.
You should do this instead:
<% if video.members.exists?(id: current_user.id) %>
<%= 'Paid' %>
<% end %>
This will generate a single query to test if the video has been paid by the current_user ;-)
In a helper:
# application_helper.rb
def display_paid_or_not(video)
return '' if video.blank? # similar to .nil?
video.members.exists?(id: current_user.id) ? 'Paid' : ''
end
# in view
<%= display_paid_or_not(video) %>
Hope this helps!

Exit rails gracefully if conditions are met?

I have a code where I would need to exit (php version of die), if certain events occur
Snippet in trace_controller.rb
def show
rule=Rule.new
#order,#order_error=rule.get_order(#order_external_id)
#order_items, #order_items_error=rule.get_order_items(#order)
#order_item_units, #order_item_units_error=rule.get_order_item_units(#order_items)
#outbound_messages, #outbound_messages_error = rule.check_outbound_messages(#order_external_id)
#inbound_messages, #inbound_messages_error = rule.check_inbound_messages(#outbound_message)
......
In show.html.erb
<% unless #order_error.blank? %>
<%= #order_error.html_safe %>
<% else %>
<%= render "trace/display_tabular_data", :data => #order %>
<% end %>
.....
.....
<% unless #order_items_error.blank? %>
<%= #order_items_error.html_safe %>
<% else %>
<% #order_items.each do |order_item| %>
<h5>Order Item</h5>
<%= render "trace/display_tabular_data", :data => order_item %>
<% end %>
<% end %>
......
Most of my functions are dependent on outcome of previous functions. Now take get_order_items function which is dependent on orders. If the order does not exist, there is no need to calculate get_order function as it won't exist either. Additionally, it fires up an error, as it says I am passing it a NIL object when I perform operations on orders inside get_order_item.
Additionally in the show.html.erb - #order_items, and #order_items_error should not even exist if the order does not exist. I just wanna render the function till the order_error, and then stop.
Now, coming from PHP background, I forgot that rails does not have die. So is there an alternate of die? Abort isn't it. I need it to exit disgracefully. Or is my best shot using conditionals if, unless etc...But it will look ugly as the page will become full of them. How would you about it?
To cut off the current action and render the view, you can use return. This will crash with errors if you try to use an unitialized instance variable in the view though.
You can also use render nothing: true which stops the current action and renders nothing.
Perhaps consider redirecting back as well:
flash[:error] = 'There was no order!'
redirect_to :back
I am not sure what exactly you want to do. But this should meet with what you are asking for.
def show
rule=Rule.new
#order,#order_error=rule.get_order(#order_external_id)
unless #order.blank?
#order_items, #order_items_error=rule.get_order_items(#order)
#order_item_units, #order_item_units_error=rule.get_order_item_units(#order_items) unless #order_items.blank?
end
#outbound_messages, #outbound_messages_error = rule.check_outbound_messages(#order_external_id)
#inbound_messages, #inbound_messages_error = rule.check_inbound_messages(#outbound_message) unless #outbound_messages.blank?
end
Similarly, you can add conditions in your view file:
<% unless #order.blank? %>
<% unless #order_error.blank? %>
<%= #order_error.html_safe %>
<% else %>
<%= render "trace/display_tabular_data", :data => #order %>
<% end %>
.....
.....
<% unless #order_items.blank? %>
<% unless #order_items_error.blank? %>
<%= #order_items_error.html_safe %>
<% else %>
<% #order_items.each do |order_item| %>
<h5>Order Item</h5>
<%= render "trace/display_tabular_data", :data => order_item %>
<% end %>
<% end %>
<% end %>
.......
.......
<% end %>
It is just a basic idea what I get from your example, though it was not clear enough to show what you want to achieve. So, whole idea is to check the variable before using it!
One more point I would like to mention, avoid using unless - else, unless should be used alone, if you need to put some logic in else block, why not use if - else. That probably makes more sense than unless - else.

Resources