Helper method within each do loop not working - ruby-on-rails

I have a loop that looks like this
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= collection.stories.count %>
<% end %>
It works perfectly to show the Collections that belongs to a User, and then show how many Stories are in each Collection.
However, I want to use a helper that does this.
in the view
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= number_of_stories_in_collection %>
<% end %>
in the helper
module CollectionsHelper
def number_of_stories_in_collection
collection.stories.count
end
def render_stories_count
if number_of_stories_in_collection.zero?
'No stories in this collection yet'
else
"#{number_of_stories_in_collection} #{'story'.pluralize(number_of_stories_in_collection)}"
end
end
end
I get an error that says
undefined method `stories' for #<Collection::ActiveRecord_Relation:0x007f510f504af8>
Any help is appreciated, thanks!

The 'collection' variable isn't an instance variable, so the helper can't see it.
Change your view to this:
<% #user.collections.each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= number_of_stories_in(collection) %>
<% end %>
And your helper method to:
def number_of_stories_in(collection)
collection.stories.count
end
This way you are passing the variable to the helper correctly.

extending #Richard's answer and little bit of optimisation to avoid n+1 queries..
<% #user.collections.includes(:stories).each do |collection| %>
<h1 class="impact"> <%= collection.name %><br></h1>
<%= render_stories_count(collection) %>
<% end %>
helper:
module CollectionsHelper
def number_of_stories_in(collection)
collection.stories.length
end
def render_stories_count(collection)
if (count = number_of_stories_in(collection)).zero?
'No stories in this collection yet'
else
"#{count} #{'story'.pluralize(count)}"
end
end
end

Related

Rails 6 how to decorate application.html.erb

I've got an application.html.erb where I want move calculation of unread user messages into some decorator/helper, basically because it looks like this:
<% if current_user %>
<%= link_to 'Messages', conversations_path %>
<% counter = #conversations.map do |conversation| %>
<% unless conversation.unread_message_count(current_user).zero? %>
<% conversation.unread_message_count(current_user) %>
<% end %>
<% end %>
(<%= counter.sum %>)
I know the basic concept of decorators but I'm wondering if I have ConversationDecorator in app/decorators/conversation_decorator.rb with defined counter method with #conversations.map block there, how to use this decorator inside of application.html.erb ?
Example ConversationDecorator:
class ConversationDecorator < ApplicationDecorator
def unread_counter
#conversations.map do |conversation|
conversation.unread_message_count(current_user) unless conversation.unread_message_count(current_user).zero?
end.sum
end
end

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 %>

Ruby on rails create a new variable to make an each

i'm new into ruby on rails and i want to assign a variable to make an each like this
<% 3.times do |calendar| %>
<% test = #lessons_calendar %>
<% test.each do |lesson| %>
display html here
<% end %>
<% end %>
The thing is that in my controller i have assigned 3 variables like this #lessons_1 #lessons_2 and #lessons_3 but when i run the code it says undefined method `each' for nil:NilClass, how can i join the number created by calendar to the new variable ? Thanks
Instead of below
<% 3.times do |calendar| %>
<% test = #lessons_calendar %>
<% test.each do |lesson| %>
display html here
<% end %>
<% end %>
Make changes in your controller as well as in view as below
Controller Code
# take new variable
#lessons = []
#lessons << #lessons_1
#lessons << #lessons_2
#lessons << #lessons_3
Now do code in view file as below
<% #lessons.each do |lesson| %>
<% lesson.each do |ls| %>
your code here
<%end%>
<%end%>
Hope this will help you.
<% 3.times do |calender| %>
<%= #lessons_calendar.collect{ |lesson| Write Your Code Here }.join("").html_safe rescue 'No Record' %>
<% end %>
With in the collect iterator you can assign it to instance var if you want to. Thanks

undefined method `value' for #<CategoryItemValue::ActiveRecord_Associations_CollectionProxy:0x007ff9706d24c0>

This is so simple but isnt working. What am I missing?
controlelr
#guide = Guide.friendly.find(params[:guide_id])
#category = #guide.categories.friendly.find params[:id]
#items = #category.category_items
view
<% #items.each do |item| %>
<%= item.category_item_values.value %>
<% end %>
gives the no method error of
undefined method 'value' for #<CategoryItemValue::ActiveRecord_Associations_CollectionProxy:0x007ff9706d24c0>
There is a values column in the category_item_values table so I'm not sure what the problem is.
item.category_item_values is the CollectionProxy instance (one might think of it as of an kinda array.)
Each category_item has [likely, you did not provide sufficiently enough info to guess more precisely] many values. If the assumption above is correct, here you go:
<% #items.each do |item| %>
<% item.category_item_values.each do |value| %>
<%= value %> # or maybe (depending on your model) <%= value.value %>
<% end %>
<% end %>
You will have to loop over each of the category_item_values to get the result as this suggests <CategoryItemValue::ActiveRecord_Associations_CollectionProxy:0x007ff9706d24c0> that your category_item_value is a association.
So you could do something like
<% item.category_item_values.each do |category_item_value| %>
<%= category_item_value.value %>
<% end %>

Am I supposed to do this in helper? or does this make slower?

In this case which pattern will be faster?
Obviously Pattern1 with helper looks much more sophisticated and looks clean.
But it send SQL every time when user_link method is called.
Here it calls up to 100times at one page loading.
Which way would be better for benchmark performance?
Pattern1. With helper
application_helper
def user_link(username)
link_to User.find_by_username(username).user_profile.nickname, show_user_path(username)
end
view
<% #topics.order("updated_at DESC").limit(100).each do |topic| %>
<%= user_link(topic.comment_threads.order("id").last.user.username) if topic.comment_threads.present? %>
<% end %>
Pattern2. Without helper. Just only view
<% #topics.order("updated_at DESC").limit(100).each do |topic| %>
<%= link_to(topic.comment_threads.order("id").last.user.nickname, show_user_path(topic.comment_threads.order("id").last.user.username) ) if topic.comment_threads.present? %>
<% end %>
try
# Topics model
#via scope
scope :get_topic_list, lambda do
order("updated_at DESC").joins(:comment_threads => :user).limit(100)
end
#via method
def self.get_topic_list
Topic.order("updated_at DESC").joins(:comment_threads => :user).limit(100)
end
# in your controller or move to model itself (recommened)
#topics = Topic.get_topic_list
# in you view
<% #topics.each do |topic| %>
<%= link_to(topic.comment_threads.order("id").last.user.nickname, show_user_path(topic.comment_threads.order("id").last.user.username) ) if topic.comment_threads.present? %>
<% end %>

Resources