If I have...
class Bunny < ActiveRecord::Base
has_many :carrots
end
...how can I check in the View if #bunny has any carrots? I want to do something like this:
<% if #bunny.carrots? %>
<strong>Yay! Carrots!</strong>
<% for carrot in #bunny.carrots %>
You got a <%=h carrot.color %> carrot!<br />
<% end %>
<% end %>
I know #bunny.carrots? doesn't work -- what would?
<% if #bunny.carrots.any? %>
<strong>Yay! Carrots!</strong>
<% for carrot in #bunny.carrots %>
You got a <%=h carrot.color %> carrot!<br />
<% end %>
<% end %>
unless #bunny.carrots.empty?
would work as well
either:
if #bunny.carrots.length>0
or
unless #bunny.carrots.nil? || #bunny.carrots.length>0
or
if #bunny.carrots.any?
by the way, you will find more operations on collections if you use irb or script/console with require 'irb/completion'
#bunny.carrots is an array, so you can treat it as such by calling array methods on it, e.g. unless #bunny.carrots.empty?
Related
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
I am facing a weird bug and unfortunately I don't know how to investigate about it.
I am rendering certain pins on my homepage when the integer => pinoftheday is set to true. I am manually setting some pins to true.
For some pins its working well and they are appearing on the homepage, some others just don't. Btw, I am checking in my console and they are correctly set to true.
Here is a bit of code:
<% #pins.each do |pin| %>
<% if pin.pinoftheday %>
(...) some informations about the pin
<% end %>
<% end %>
Any ideas how I could check why some pins are not rendering? I am not writting any tests for now... I know this is stupid but I just did not learnt testing for rails.
Thank you.
EDIT: Yes, in my code it's a pin model. I wanted to used post to make it clearer. Figured it was not :) - Edited it to the correct model: pin.
Try the below code.
<% #postss.each do |post| %>
<% if post.pinoftheday %>
(...) some informations about the pin
<% end %>
<% end %>
Your problem is that you've defined a local variable in your block, and are referencing another:
<% #postss.each do |post| %>
<% if post.pinoftheday %>
...
<% end %>
<% end %>
--
You'd be better using a scope:
#app/models/post.rb
class Post < ActiveRecord::Base
scope :pin_of_the_day, -> { where pinoftheday: true }
end
You'll also do well to make your pinoftheday column boolean. If you're referencing 1 = true; 0 = false, Rails handles that with a tinyint in your db, calling true/false as boolean logic. Instead of referencing the integer as a number, you can call true etc.
The above will allow you to call:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def index
#postss = Post.pin_of_the_day
end
end
This will remove the inefficient conditional logic (<% if ...):
<% #postss.each do |post| %>
...
<% end %>
If I understood your code then will below:
<% #postss.each do |pin| %>
<% if pin.pinoftheday.nil? %>
(...) some informations about the pin
<% else %>
(...) some informations about the pin
<% end %>
<% end %>
Hope will help you
I'm new to rails and I'm trying to build a view that will list the parents and related children
Ex:
Passport has many Visas
I want to list information about the passport and the visas that the passport has.
So I have
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<% end %>
<% end %>
I'm getting the error
undefined method `visa_type' for #Passport:0x000000091b8b28
It looks like rails is trying to find the property visa_type for passport, instead of in visa. How does the scope work within each? Can I force it to access visa_type from visa?
I think you're looking for the fields_for form helper. This will allow you to create fields for the relevant visa attributes. Replace your code sample with the following, and you should be all set.
<% #passport_list.each do |passport| %>
# passportFields
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
You can also iterate over the list as follows:
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
<% end %>
For more information on fields_for, check out the link I added above, and to customize further for your use case, check out the "One-to-many" section.
IMO you should always handle the null case of an object.
Something like this if you use rails (present? is a Rails function)...
<% if #passport_list.present? %>
<% #passport_list.each do |passport| %>
passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<%end%>
<%end%>
<% else %>
<p>Nothing to see here</p>
<% end %>
However if your #passport_list is backed by an ActiveRecord Query, you can handle this in the model/helper/controller by returning the .none query on the model. Note that this differs from an empty array because it is an ActiveRecord Scope, so you can chain AR queries onto it
# scope on AR model
def self.awesomeville
where(country_of_origin: "awesomeville")
end
# method queried in controller
#passport_list = Passport.all
if #passport_list.present?
#passport_list
else
Passport.none
end
# additional filtering in view is now possible without fear of NoMethodError
#passport_list.awesomeville
Whereas a ruby Array would raise an error as it would respond to the Array methods.
I would like the #comment1 to change to #comment2 by using the i in the 1..5 loop. I have the following code that is pretty repetitive. I am hoping to dry it up.
Hi,I am using acts_as_commentable_with_threading. I am basically looping through all comments and checking to see if that comment has children. If so, print out the children while checking to see if those children have children. So I plan on going a few levels deep, hence the #comment1,2,3, etc...How can I DRY this? Recursion some how? If not, I could maybe go a few levels deep and end the comment indentation at #comment5 for example.
EDIT!
Thank you Samiron!
Here is the updated helper function...
def show_comments_with_children(comments)
comments.each do |comment|
yield comment
if comment.children.any?
concat <<-EOF.html_safe
<div class="span7 offset1-1 pcomment">
EOF
show_comments_with_children(comment.children) { |x| yield x } #Dont worry, this will not run another query :)
concat <<-EOF.html_safe
</div>
EOF
end
end
end
<div class="span7 offset1-1 pcomment">
<% #comment1 = comment.children%>
<% for comment in #comment1 %>
<%= render "comment_replies", :comment => comment %>
<div class="span7 offset1-1 pcomment">
<% #comment2 = comment.children%>
<% for comment in #comment2 %>
<%= render "comment_replies", :comment => comment %>
<div class="span7 offset1-1 pcomment">
<% #comment3 = comment.children%>
<% for comment in #comment3 %>
<%= render "comment_replies", :comment => comment %>
<% end %>
</div>
....
<%(1..5).each do |i| %>
<% #comment1 = comment.children%>
<% for comment in #comment1 %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
Probably you are looking for instance_variable_set.
# Following snippet is not TESTED. It is here to just demonstrate "instance_variable_set"
<%(1..5).each do |i| %>
<% instance_variable_set("#comment#{i}", comment.children) %>
<% for comment in instance_variable_get("#comment#{i}") %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
But definitely this is not a recommendable approach. You can share your controller code and what you want to achieve in your view. There must be some way to make it properly DRY. In your post you are always getting comment.children. is it really?
Actual Solution:
Your view code will be like this
#0th level is the top level
<% show_comments_with_children(#comments, 0) do |comment, level|%>
<!-- #Use level to differ the design for different depth-->
<%= render "comment_replies", :comment => comment %>
<%end%>
and add this helper function show_comments_with_children in your helper function. Which will be.
def show_comments_with_children(comments, level)
comments.each do |comment|
yield comment, level
if comment.children.any?
show_comments_with_children(comment.children, level+1) {|x, l| yield x, l} #Dont worry, this will not run another query :)
end
end
end
The manor in which you are defining this code is rather poor, and you should consider defining #comment as an array rather than as independent variables for each #comment1, #comment2, etc..
That said, what you are looking for is the instance_variable_get() method
<(1..5).each do |i| %>
<% instance_variable_set("#comment#{i}", comment.children) %>
<% for comment in instance_variable_get("#comment#{i}") %>
<%= render "comment_replies", :comment => comment %>
<% end %>
<% end %>
This is definitely something good to know but in this case I highly recommend converting you comment instance variables to an array!
I am trying to display the output of this find -
#test = User.joins(:plans => [:categories => [:project => :presentations]]).where(current_user.id)
Here is my output loop
<% #test.each do |p| %>
<%= p.plans %>
<% p.plans.each do |d| %>
<%= debug(d) %>
<% d.categories.each do |e| %>
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
<% end %>
<% end %>
<% end %>
The loop works until it gets to project when it throws this error
undefined method `each' for "#<Project:0x000001033d91c8>":Project
If I change it to projects in the loop it gives this error
undefined method `projects' for #<Plan:0x000001033da320>
The debug at categories level shows this
--- !ruby/object:Category
attributes:
id: 2
name: test
short_name: tst
created_at:
updated_at:
category_id: 2
plan_id: 5
My relationships look like this
User
has_many :user_plans
Plan
has_many :user_plans
has_and_belongs_to_many :categories
Category
has_one :project
has_and_belongs_to_many :plans
Project
has_many :presentations, :dependent => :delete_all
Presentation
belongs_to :project
Do I need to changed my find ?
Thanks, Alex
Category has_one :project
so it is single object not collection thus no each method.
According to your relationship definitions, Category only has_one project, so why do you want to iterate over e.project? If you just want to show debugging output, replace
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
<%= debug(e.project) %>
But if you want to go deeper, into presentations, do:
<%= debug(e.project) %>
<% e.project.presentations.each do |presentation| %>
<%= debug(presentation) %>
<% end %>
Your problem is that you are calling the array method .each on a single object.
category.project will give you a single Project object right? That's not an array, so you can't call each on it.
Replace this:
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
debug(e.project)
While you're at it, here's some other advice: use descriptive variable names. Why does 'p' represent a test, 'd' represent a plan, 'e' represent a category, etc? Variable names should tell you what the object is. Similarly, i'd expect the variable #test to hold a Test object. In your code it seems to be an array. Use plural variable names for a variable that holds a collection of that type of object - eg #plans would be an array of Plan objects.
eg
<% #tests.each do |test| %>
<% test.plans.each do |plan| %>
<%= debug(plan) %>
<% plan.categories.each do |category| %>
<%= debug(category.project) %>
<% end %>
<% end %>
<% end %>
Isn't that more readable?