ruby on rails: suddenly failing method - ruby-on-rails

I am not sure how to present my issue best without posting the whole framework.
I have a method duplicate! which should duplicate an object (channel). Usually it works but there is one channel where the method fails and I just don't understand why:
def duplicate!
channel = Channel.new do |c|
c.title = title << ' (copy)'
c.description = description
end
channel.nodes += nodes
playlist.nodes.each { |n| channel.playlist.playlist_items.create(node: n) }
channel
end
As said nearly all channels duplicate without a problem, but now I have one channel which fails to get duplicated:
2.3.0 :002 > channel.duplicate!
NoMethodError: undefined method `playlist_items' for nil:NilClass
from /var/www/app/models/channel.rb:110:in `block in duplicate!'
from /var/www/app/models/channel.rb:110:in `each'
from /var/www/app/models/channel.rb:110:in `duplicate!'
Every Channel has Nodes and a Playlist, the error producing Channel has too.
I don't really understand the error; how can this method fail depended on the object to duplicate?

The reason this is failing is because one of your channels dont have a playlist record. So this line is failing
playlist.nodes.each { |n| channel.playlist.playlist_items.create(node: n) }
for the channel that does not have a playlist record channel.playlist returns nil so when you do channel.playlist.playlist_items you are calling nil.playlist_items, and since nil does not have the method you get an error.
You could just do this instead of making your on duplicate method fyi
copy_of_channel5 = channel5.dup
This will duplicate the object
You could also do this
copy_of_channel5 = channel5.clone
Look here for a details explanation of the difference between the 2
Trouble Shooting*
Go to your gem file and add this line gem 'pry'
Run bundle
Replace this line playlist.nodes.each { |n| channel.playlist.playlist_items.create(node: n) } with the below code.
playlist.nodes.each {|n| binding.pry}
once the execution console pauses type in the console channel.playlist then press enter and post the output.

Related

Contentful Rails Test if Object is Empty

p entry # <Contentful::Entry[item] id='3oXocy0iJX8ksdfe435RjWjE'>
i = entry.image
p i.present? # true
p i.nil? # false
p i.respond_to?(:url) # true
p i.url # undefined method `url' for nil:NilClass
p i # undefined method `url' for nil:NilClass
I'm pulling an item from Contenful CMS and trying to render it in Rails. My content admin managed to delete the image (the field is actually called "image") on the entry, thereby causing the render to break. The code above is me trying to test for a missing image so I can skip rendering the section and not break my website.
As you can see, this is mindnumbingly insane. Rails says the entry is there (it is), it says it's not nil, then immediately below it throws an error for nil:NilClass.
The last line is probably the most interesting - even if I just try to print or inspect "i" I still get the error as if I'm trying to access the url attribute. I feel like I'm taking crazy pills.

`block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)

I'm trying to load a remote JSON URL to eventually store it in an SQLite3 database.
I am loading the JSON alright (I think) and then looping through it to assign each JSON value to a variable which will then be built into a db.execute statement to store it into the database.
I am getting an error 'block in <main>': undefined method[]' for nil:NilClass (NoMethodError) when I run the code.
The link Undefined method '>' for nil:NilClass <NoMethodError> is full of information but I don't understand what's going on to be honest. I'm wondering if someone can explain to me where I'm going wrong in my understanding.
My code is:
require 'json'
require 'open-uri'
tournament_url = "http://www.pgatour.com/data/r/033/leaderboard-v2.json"
puts tournament_url
leaderboard = JSON.load(open(tournament_url))
no_of_players = leaderboard['leaderboard']['players'].length
puts "The number of players to be loaded is: #{no_of_players}"
data_array = Array.new
for i in 0..no_of_players
current_position = leaderboard['leaderboard']['players'][i]['current_position']
end
If I write any code after the end loop it won't execute.
If had also tried to do a check on the value returned by saying:
if (leaderboard['leaderboard']['players'][i]['current_position'].nil?)
current_position = ""
else
current_position = leaderboard['leaderboard']['players'][i]['current_position']
end
Range 0..no_of_players is inclusive.
You probably want to use exclusive version (note three dots):
0...no_of_players
or:
0..no_of_players-1
.lenth returns the number of array items - so the array position of the last item would be no_of_players.length - 1. Basically you get the error because on the last iteration of the loop you get nil.
Instead I might suggest using .each:
leaderboard['leaderboard']['players'].each do |player|
current_position = player['current_position']
end

Rails 3 - NoMethodError: undefined method for nil:NilClass in each Iteration

I'm iterating over an array of instances of a Rails model. Here is my code:
product_details.each do |product_detail|
product_detail.label = Backend::ProductGroup.where(product_group_number: product_detail.product_group).first.label
end
The attribute 'label' from 'product_detail' isn't an attribute from my Rails ActiveRecord model. I added it with attr_accessor in my class definition. I did this, because I wanted to add this attribute dynamically, only when I need to do this. When I ran the code without the 'each' iteration in my rails console it works just fine. But when I execute the above code I get the following error message:
NoMethodError: undefined method 'label' for nil:NilClass
Did I do something obviously wrong?
Many thanks in advance.
You likely have several product_detail items that have no matching product_group. So calling .first on the empty collection returns nil. To get around the error, you can test if the product_group was found before proceeding:
product_details.each do |product_detail|
product_group = Backend::ProductGroup.where(product_group_number: product_detail.product_group).first
product_detail.label = product_group.label if product_group
end
You can also do this more efficiently like so:
group_labels = BackEnd::ProductGroup.
where(product_group_number: product_details.map(&:product_group)).
inject({}){|m, g| m[g.product_group_number] = g.label; m}
product_details.each do |product_detail|
product_detail.label = group_labels[product_detail.product_group]
end
This will result in a single database call to grab all related groups, and put the labels in a keyed hash for easy discovery and assignment.

Protecting against an undefined chained method

I have a long loop that results in this:
csv_code = CSV.generate do |csv|
csv << ["Product ID","Name", "Url"]
#all_products.each do |product|
if product.page_url("en_US") != nil
turl = product.page_url("en_US")
end
csv << [product.name,product.d_id, turl]
end
end
The method uses products 1-17 works great resulting in a url printed. When I get to my 18th record I have problems
Product.find(18) // product found!
product.find(18).page_url("en_US")
NoMethodError: undefined method `page_url' for nil:NilClass
How can I protect against these undefined events?
url = product.page_url("en_US")
The issue is that a product is nil:
undefined method 'page_url' for nil:NilClass". Solution:
(It has nothing to do with page_url maybe returning nil.)
Make sure product can't be nil: but be wary that this may be a deeper issue. In any case, "fixing" this issue is easy to deal with.
Consider either using a collection restriction (such as Enumerable#reject):
#all_products.reject(&:nil?).each do {
...
}
The above uses the Symbol#to_proc "Rails magic", but could just as easily have been {|x| x.nil?} as the restriction. The downside is it's not practical to use this for a "no URL" condition per-product although Enumerable#partition could help with that: use the right tool for the job.
Another solution is to expand the conditional check itself:
if product && product.page_url("en_US")
# yay
else
# uhm
end
The short-circuit nature of && will ensure page_url is only invoked upon a truthy value (which excludes nil).
I also took the liberty of assuming page_url can't return false as I find this makes the intent more clear.
Happy coding.
Try this:
product.find(18).try(:page_url, "en_US")
But it's a perf killer.
Are you sure Product.find(18) doesn't return nil ?
Anyway, you could do:
url = product.nil? ? "no_url" : product.page_url("en_US")

debugging a ruby on rails error

I'm some what new with ruby on rails, so I'm attempting to debug this error that I'm getting but, from my understanding, is working on the prod code.
The error:
NoMethodError in JoinController#index
undefined method `join_template' for nil:NilClass
/app/controllers/join_controller.rb:5:in `index'
Okay, so line 5 in index is:
elsif current_brand.join_template == 'tms'
So clearly current_brand is nil. The controller is a child class of AppController, so checking that out I see that current_brand is a method:
def current_brand
return #current_brand if defined?(#current_brand)
url_array = request.env['HTTP_HOST'].split('.').reverse
url = url_array[1] << "." << url_array[0]
#current_brand = Brand.find(:first, :conditions => ["url LIKE ?", "%" << url << "%"])
end
It seems that #current_brand is always returned, yet it's continuing to be Nil. What could the problem be?
It may be your query is not returning anything. You can use a debugger, but it's pretty easy to just output #current_brand and see what it evaluates to.
logger.debug(#current_brand)
You must check two things:
Does Rails build the SQL query properly with the url passed in on the last line of that method?
Does the record exist in the brands table? you're not actually checking for that.
Also, passing in the url like that opens you up to a potential SQL injection attack.
You need to rewrite your definition with a rescue or if/else so if you do get a nil element then it won't be a fatal error.
This is your problem:
#current_brand = Brand.find(:first, :conditions => ["url LIKE ?", "%" << url << "%"])
#current_brand is not finding anything so make sure you find something.
#current_brand = Brand.find(:first)
If this fixes the problem then you know it is not finding anything and you will need to change your code that if it returns nil then it doesn't provide the function or it finds a default brand such as what I suggested.

Resources