My Article model has_many instances of my Comment model, which has a text attribute.
However, in my view, when I call through iteration article.comments.last.text I get undefined method error.
I must say that when I call it through the console, it does return the text attribute.
The log's only relevant response to this error is:
ActionView::Template::Error (undefined method `text' for nil:NilClass):
View code:
- #articles.each do |article|
.article
%comment= article.comments.last.text
Update your view code to following. I hope it works for you.
- #articles.each do |article|
.article
%comment= article.comments.last.try(:text)
You should do some defensive coding when trying something like this. article.comments.last.text. There will always be a possibility when article.comments is blank. It returns an empty array [].So when you execute something like .last.text. It will break the code throwing error.
You can check something like article.comments.present? and then access the last comment.
To add to the accepted answer, the problem is defined with the error:
--
undefined method `text' for nil:NilClass
It means you're calling text on a class / variable / data-set which isn't populated (nil).
As mentioned, the way to do this is to evaluate whether an article has any .comments. Whilst .try(:x) is the best way to do it, the more verbose way is to use conditional logic:
%comment= article.comments.last.text if article.comments.any?
--
it does return the text attribute
Maybe the comment exists, but it isn't associated to article.
Calling article.comments only calls the comments associated to article (through their respective foreign keys). If the comment is not associated to article, it won't appear in the collection.
Thus, if you're checking whether text exists for a comment, you also need to make sure the comment is associated with article. A simple way to do this is through the Rails Console:
$ rails c
$ article = Article.first
$ comment = Comment.first
$ article.comments << comment
First thing to mention is that you have N+1 problem here. You are querying your database on each article to get all it's comments. It's may slowdown your system.
I suggest the next approach for this solution.
Define new relation in Article
class Article < ActiveRecord::Base
has_one :last_comment, -> { where(created_at: :desc) }, class_name: 'Article'
end
Load this relation in your controller
def your_action
# you can continue querying as I show you with 'where' and 'your_scope', the 'includes' is a must to
#articles = Article.includes(:last_comment).where(...).your_scope
...
end
Then in your view just use the next code
- #articles.each do |article|
.article
- if article.last_comment.present?
%comment= article.last_comment.text
Related
I'm using tabulatr2 to handle table alike data. However I can't wrap my head around how to include pundit's policy checks in tabulatr2's data definition.
So I have something akin to
class MystuffTabulatrData < Tabulatr::Data
column :blah
buttons do |b,r|
if policy(r).destroy? # XXX: NoMethodError - undefined method `policy' for #<Data::Proxy:0x83f84bb0>
...
end
end
end
One usually uses something like <%= table_for Mystuff %> in a view.
Since we work with Data::Proxy there, the source suggests that #controller should be available. So #controller.policy(r) works just fine.
in my listing model i setup a method
def set_listing_number
listing_number="TM#{created_at.year}#{id}"
end
i have a couple of records that was created before i ran the migration to create the listing_number column. i wanted to update all the records at once but keep receiving this error.
here's the code that i ran that produce the error in console verifying if the method . it works i cannot save the assignment.
listing_number=listing.set_listing_number
=> "TM2014574"
2.0.0-p247 :003 > listing_number.save
NoMethodError: undefined method `save' for "TM2014574":String
i tried a couple with no success , that i also found here like this one
Listing.all.each{|n| n.update_attributes(:listing_number =>"TM#{n.created_at.year})}
the question how do i update the previous record at once. probably it's pretty basic but can't figure it out. thanks
# This method in Listing Model
def set_listing_number
listing_number="TM#{created_at.year}#{id}"
end
# In controller
Listing.all.each do |listing|
listing.set_listing_number
listing.save
end
or you can refactor this as
Listing.all.each do |listing|
listing.update listing_number: "TM#{listing.created_at.year}"
end
You're calling save on a string. You need listing.save in your code.
I am querying my booking model to get details, including a list of has_many appointments.
To do this I'm using a scope:
scope :current_cart,
Booking.includes(:appointments).where(:parent_id => 1).where(:complete =>nil).order("created_at DESC").limit(1)
Then in the view:
<% #booking.appointments.each do |appointment| %>
# info output
<% end %>
To get this working, in the controller, I have to do this:
#booking = Booking.current_cart[0]
It's the [0] bit i'm worried about. I guess I'm using a method that wants to return a collection, and that means i have to state that i want the first (only) record. How can i state a similar scope that is more appropriate to fetch a member?
Try tacking ".first" onto the end of the scope. Scopes are just regular AREL queries, and so you can use any standard methods as you normally would.
Adding .first or [0] to the scope gives error:
undefined method `default_scoped?' for
googling that gave this:
undefined method `default_scoped?' while accessing scope
So apparently adding .first or [0] stops it being chainable, so it gives an error. using that answer, i did:
scope :open_carts,
Booking.includes(:appointments).where(:parent_id => 1)
.where(:complete =>nil).order("created_at DESC")
def self.current_cart
open_carts.first
end
Little bit messy, but i'd prefer mess in my model, and it isn't nonsensical to look at.
Based on the Rails 3 API, the difference between a scope and a class method is almost non-existent.
class Shipment < ActiveRecord::Base
def self.unshipped
where(:shipped => false)
end
end
is the same as
scope :unshipped, where(:shipped => false)
However, I'm finding that I'm sometimes getting different results using them.
While they both generate the same, correct SQL query, the scope doesn't always seem to return the correct values when called. It looks like this problem only occurs when its called the same way twice, albeit on a different shipment, in the method. The second time it's called, when using scope it returns the same thing it did the first time. Whereas if I use the class method it works correctly.
Is there some sort of query caching that occurs when using scope?
Edit:
order.line_items.unshipped
The line above is how the scope is being called. Orders have many line_items.
The generate_multiple_shipments method is being called twice because the test creates an order and generates the shipments to see how many there are. It then makes a change to the order and regenerates the shipments. However, group_by_ship_date returns the same results it did from the first iteration of the order.
def generate_multiple_shipments(order)
line_items_by_date = group_by_ship_date(order.line_items.unshipped)
line_items_by_date.keys.sort.map do |date|
shipment = clone_from_order(order)
shipment.ship_date = date
line_items_by_date[date].each { |line_item| shipment.line_items << line_item }
shipment
end
end
def group_by_ship_date(line_items)
hash = {}
line_items.each do |line_item|
hash[line_item.ship_date] ||= []
hash[line_item.ship_date] << line_item
end
hash
end
I think your invocation is incorrect. You should add so-called query method to execute the scope, such as all, first, last, i.e.:
order.line_items.unshipped.all
I've observed some inconsistencies, especially in rspec, that are avoided by adding the query method.
You didn't post your test code, so it's hard to say precisely, but my exeprience has been that after you modify associated records, you have to force a reload, as the query cache isn't always smart enough to detect a change. By passing true to the association, you can force the association to reload and the query to re-run:
order.line_items(true).unshipped.all
Assuming that you are referencing Rails 3.1, a scope can be affected by the default scope that may be defined on your model whereas a class method will not be.
I used the nested model gem to create a Picture that can take tags. Now I have added an attribute to my model Picture so it has an attribute taglist. When I create a new tag, I want this to happen
class TagsController < ApplicationController
def create
#tag = Tag.new(params[:id])
if #tag.save
taglist = picture.taglist
taglist+=#tag.tagcontent
#tag.picture.update_attributes(:taglist => taglist)
end
end
end
and in my routes
resources :pictures do
resources :tags
end
When i make a new tag, nothing happens in the taglist attribute, like nothing happened, why?
It's hard to help due to lack of information, but I see two possible issues:
Tag.new(params[:id]) doesn't make sense. Assuming Tag inherits from ActiveRecord::Base, you need to pass it a hash of attributes (e.g. Tag.new(:name => 'mytag')) You are likely not getting into the if #tag.save block at all due to validation errors. Also, you don't need to provide an id to an object you want to create. The database chooses the id.
Inside the block, picture is undefined on the first line.
Why not try debugging with something like:
if #tag.save
taglist = picture.taglist
taglist+=#tag.tagcontent
#tag.picture.update_attributes(:taglist => taglist)
else
p "ERRORS:"
p #tag.errors.full_messages
end
See what errors that prints out into your console.
I definitely think that picture is probably undefined in the create method of the controller. Can you show us the view, the form you're using to create a new tag? Is there a form field through which you're choosing which photo gets the tag?
Please show us the association and your view for creating the new tag.
Actually, what I'd really recommend instead of cooking up your own is to use:
Agile Web Development's acts_as_taggable_on_steroids
It's an excellent plugin to make tagging easy; it has quite a few nifty features built in, including the searches, tag clouds, etc. We use it on our projects.