undefined method `[]' for nil:NilClass Rails 4 - ruby-on-rails

I am working on a weather app using the Weather Underground API. Everything almost appears to be working fine in my web app. Except for one thing. When ever I start up my server and head to my index page, in this case, the main page. I get the following error message: undefined method []' for nil:NilClass. Checking my logs, I see the following message: NoMethodError (undefined method '[]' for nil:NilClass):app/controllers/welcome_controller.rb:14 in index.
now my controller looks like this:
class WelcomeController < ApplicationController
def index
#states will need to be defined and then #states.sort will sort all of them on the form.
#states = %w(HI AK CA OR WA ID UT NV AZ NM CO WY MT ND SD NE KS OK TX LA AR
MO IA MN WI IL IN MI OH KY TN MS AL GA FL SC NC VA WV DE MD PA NY NJ CT RI
MA VT NH ME DC PR)
#states.sort!
#Here is the call to the API
response = HTTParty.get("http://api.wunderground.com/api/#
{ENV['wunderground_api_key']}/geolookup/conditions/q/#{params[:state]}/#
{params[:city]}.json")
#location = response['location']['city']
#temp_f = response['current_observation']['temp_f']
#temp_c = response['current_observation']['temp_c']
#weather_icon = response['current_observation']['icon_url']
#weather_words = response['current_observation']['weather']
#forecast_link = response['current_observation']['forecast_url']
#real_feel = response['current_observation']['feelslike_f']
#This part of the code will change the background depending on what
#weather_words is.
#Head over to the views/layouts/application.html.erb file to see more.
if #weather_words == "Partly Cloudy" || #weather_words == "Mostly Cloudy"
#body_class = "partly-cloudy"
elsif #weather_words == "Cloudy" || #weather_words == "Scattered Clouds" || #weather_words == "Overcast"
#body_class = "partly-cloudy"
elsif #weather_words == "Clear"
#body_class = "sunny"
elsif #weather_words == "snow"
#body_class = "snow"
elsif #weather_words == "Rain"
#body_class = "rain"
elsif #weather_words == "Fog"
#body_class = "fog"
elsif #weather_words == "Thunderstorms and Rain" || #weather_words == "Thunderstorms"
#body_class = "thunder"
end
end
Now, I have tracked down the problem, I believe, to the params :state and :city not being filled in when I load the page. If I delete this part of the code:
#location = response['location']['city']
#temp_f = response['current_observation']['temp_f']
#temp_c = response['current_observation']['temp_c']
#weather_icon = response['current_observation']['icon_url']
#weather_words = response['current_observation']['weather']
#forecast_link = response['current_observation']['forecast_url']
#real_feel = response['current_observation']['feelslike_f']
Then load the page, everything will work fine if I select a state and city, then add the above deleted code-it will pull it up. Except I cannot start my server and go directly to my index page or else it will crash. I also tried placing the following in:
params[:state] = "MA"
params[:city] = "Boston"
and that will load the page just fine except I am stuck on Boston! Finally, Here are my routes:
#The index page gets two routes:
#The get route for when we initially come to the page
get 'index' => 'welcome#index'
#And then a post route for when we come back to the index page after
# submitting the form
post 'index' => 'welcome#index'
Any help will be great! I also have all of my code posted at github, username is ravenusmc. Again, thank you for the help.

One or more fields of response are probably nil. This is a very common mistake; you should always check if the variable is nil or empty before trying to access nested hash keys or array positions. e.g., insted of #location = response['location']['city'], use something like:
#location = response['location'] ? response['location']['city'] : nil
Do the same for the rest of the #location = response... attributions.
If you're using ruby 2.3.0, you can use the dig method:
#location = response.dig('location', 'city')
Which handles nil values for you. See the difference:
2.3.0 :004 > response = {}
# => {}
2.3.0 :005 > response.dig('location', 'city')
# => nil
2.3.0 :006 > response['location']['city']
NoMethodError: undefined method `[]' for nil:NilClass
from (irb):6
from /home/lbrito/.rvm/rubies/ruby-2.3.0/bin/irb:11:in `<main>'

Related

Ruby rails object is nil, yet controller logic acting as if object is not nil

I have an an object Search with a method listings that returns an array of hashes if there are search results, otherwise it returns an empty array. In the event there is any empty array, I need to skip some code and go straight to the show page. I've tried object.nil? object.empty? object.present? all with the same outcome....the object which is supposed to be nil is treated as non-nil.
Controller code:
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.last.present?
if #results.last[0] == "p" || #results.last[0] == "s" || #results.last[0] == "d"
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end
end
show action results in
undefined method `[]' for nil:NilClass
Extracted source (around line #21):
19 p "#results.pop is either p, s, or d"
20 #sort_column = #results.pop
21 #grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
22 end
23 end
24 end
but, the server interface verifies that #results.last is nil:
>> #results
=> []
>> #results.last
=> nil
>> #results.last.present?
=> false
>> #results.last[0]
NoMethodError: undefined method `[]' for nil:NilClass
from /Users/tomb/Projects/schoolsparrow/app/controllers/searches_controller.rb:21:in `show'
>>
I'm at a loss as to how to logic is getting past the results.last.present? when results.last is nil.
If you're testing to see whether or not your array has any values in it, try:
#results.any?
An empty array is not nil, which is why your checks are failing.
EDIT:
A concise explanation of nil v. empty v. blank in Ruby on Rails
Why don't you check your condition on #results.present? and not #results.last.present?.
#results.last would throw a NoMethodError if #result is nil
To check if an array has elements use .any? or its opposite twin .empty?:
irb(main):006:0> [].empty?
=> true
irb(main):007:0> [].any?
=> false
.present? and .presence work on arrays but they are really more idiomatically correct for hashes like the params.
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.any? && ['p', 's', 'd'].include?(#results.last[0])
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end

Error Calling Initialized Variables In Rails

Building a Rails 3.2 app with Ruby 1.9.
I am trying to write a helper method that initializes 3 variables and when i try to call the initialized variables from my view i get an "undefined method" error.
Method In Helper File
module StoreHelper
class Status
def initialize(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
#avoid nil class errors for vol2 and 3. volume 1 can never be nil
if product.volume2.nil?
product.volume2 = 0
end
if product.volume3.nil?
product.volume3 = 0
end
#Promo status logic
if (product_sales >= product.volume2) && (product_sales < product.volume3)
#level3_status = "Active"
#level2_status = "On!"
#level1_status = "On!"
elsif (product_sales >= product.volume3)
#level3_status = "On!"
#level2_status = "On!"
#level1_status = "On!"
else #level3_status = "Pending"
end
end
I then attempt to call the the initialized variable #level3_status like so
<%=level3_status (product)%>
Not sure what i'm doing wrong any help would be appreciated.
How long are you programming with ruby? You have to create a new instance of your class to access the instance outside. Take a look at these basics: http://www.tutorialspoint.com/ruby/ruby_variables.htm
UPDATE
From the link above..
Ruby Instance Variables:
Instance variables begin with #. Uninitialized instance variables have the value nil and produce warnings with the -w option.
Here is an example showing usage of Instance Variables.
class Customer
def initialize(id, name, addr)
#cust_id=id
#cust_name=name
#cust_addr=addr
end
def display_details()
puts "Customer id ##cust_id"
puts "Customer name ##cust_name"
puts "Customer address ##cust_addr"
end
end
# Create Objects
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
# Call Methods
cust1.display_details()
cust2.display_details()
That´s how you can work with ruby and instance variables. More details are in the link.
In your case I think you have another "error", you mixed a few things.. where is your helper class? Under the app/helpers/store_helper.rb? In this file you should just add view helpers. If I am right with my intuitions I would solve your problem like following:
app/helpers/store_helper.rb
module StoreHelper
def get_level_states(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
product.volume2 = 0 if product.volume2.nil?
product.volume3 = 0 if product.volume3.nil?
levels = {}
if (product_sales >= product.volume2) && (product_sales < product.volume3)
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "Active!"
elsif product_sales >= product.volume3
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "On!"
else
levels[:3] = "Pending"
end
levels
end
end
app/views/your_views_folder/your_view.html.erb
to get the different level state:
<% levels = get_level_states(product) %>
<%= levels[:1] %> # will print the level 1
<%= levels[:2] %> # will print the level 2
<%= levels[:3] %> # will print the level 3

Cant found model with out an ID in rails 3.2.12

i ve this method. I m not at all able to understand the error which is
Couldn't find Company without an ID
in ActiveRecord::RecordNotFound in CustomersController#bulk_create
This method is written to create customers for a company in bulk by taking their name and numbers in format name:number.
The method is as follows:
def bulk_create
res = ""
comp_id = params[:customer][:selected_companies].delete_if{|a| a.blank?}.first
comp = Company.find(comp_id)
s = SentSmsMessage.new
s.set_defaults
s.data = tmpl("command_signup_ok", customer, comp) unless params[:customer][:email].length > 0
s.data = params[:customer][:email] if params[:customer][:email].length > 0
s.company = comp if !comp.nil?
s.save
unless comp_id.blank?
params[:customer][:name].lines.each do |line|
(name, phone) = line.split(/\t/) unless line.include?(":")
(name, phone) = line.split(":") if line.include?(":")
phone = phone.gsub("\"", "")
phone = phone.strip if phone.strip.to_i > 0
name = name.gsub("\"", "")
name = name.gsub("+", "")
phone = "47#{phone}" if params[:customer][:active].to_i == 1
customer = Customer.first(:conditions => ["phone_number = ?", phone])
if customer.nil?
customer = Customer.new
customer.name = name
# customer.email
# customer.login
# customer.password
customer.accepted_agreement = DateTime.now
customer.phone_number = phone
customer.active = true
customer.accepted_agreement = DateTime.now
customer.max_msg_week = params[:customer][:max_msg_week]
customer.max_msg_day = params[:customer][:max_msg_day]
customer.selected_companies = params[:customer][:selected_companies].delete_if{|a| a.blank?}
res += "#{name} - #{phone}: Create OK<br />" if customer.save
res += "#{name} - #{phone}: Create failed<br />" unless customer.save
else
params[:customer][:selected_companies].each do |cid|
new_company = Company.find(cid) unless cid.blank?
if !new_company.nil?
if !customer.companies.include?(new_company)
customer.companies << new_company
if customer.save
res += "#{name} - #{phone}: Customer exists and the customer was added to the firm #{new_company.name}<br />"
else
res += "#{name} - #{phone}: Customer exist, but something went wrong during storage. Check if the client is in the firm.<br />"
end
else
res += "#{name} - #{phone}: Customer exists and is already on firm #{new_company.name}<br />"
end
end
end
end
s.sms_recipients.create(:phone_number => customer.phone_number)
end
s.save
s.send_as_sms
#result = res
respond_to do |format|
format.html { render "bulk_create"}
end
else
#result = "You have not selected any firm to add these users. Press the back button and try again."
respond_to do |format|
format.html { render "bulk_create"}
end
end
end
I want to update one situation here. That when i submit the form blank then it gives this error. Also if i filled the form with the values then its show the situation which the method is returning in case of fail.
res += "#{name} - #{phone}: Create failed <br />"
The tmpl method
private
def tmpl(setting_name, customer, company = nil)
text = ""
if customer.companies.count > 0
sn = "#{setting_name}_#{#customer.companies.first.company_category.suffix}".downcase rescue setting_name
text = Setting.value_by(sn) rescue ""
end
textlenth = text.length rescue 0
if textlenth < 3
text = Setting.value_by(setting_name) rescue Setting.value_by("command_error")
end
return fill_template(text, customer, company)
end
From the model customer.rb
def selected_companies=(cmps)
cmps.delete("")
# Check the old ones. Make a note if they are not in the list. If the existing ones are not in the new list, just remove them
self.companies.each do |c|
self.offer_subscriptions.find(:first, ["customer_id = ?", c]).destroy unless cmps.include? c.id.to_s
cmps.delete c.id.to_s if cmps.include? c.id.to_s
end
# Then create the new ones
cmps.each do |c2|
cmp = Company.find(:first, ["id = ?", c2])
if cmp && !c2.blank?
offerSubs = offer_subscriptions.new
offerSubs.company_id = c2
offerSubs.save
end
end
end
def selected_companies
return self.companies.collect{|c| c.id}
end
The association of customer is as follows:
has_many :offer_subscriptions
has_many :companies, :through => :offer_subscriptions
This code is written by the some one else. I m trying to understand this method but so far not being able to understand this code.
Please help.
Thanks in advance.
You are getting 'Couldn't find Company without an ID' error because your Company table doesn't contain record with id = comp_id
Change comp = Company.find(comp_id) to comp = Company.find_by_id(comp_id).
This will return nil instead of an error.
Add comp is not nil condition is already handled in your code.
Your comp_id line is returning nil.
comp_id = params[:customer][:selected_companies].delete_if{|a| a.blank?}.first
Post the params that get passed to this function and we could hopefully find out why. In the meantime you could enclose the block in a begin - rescue block to catch these errors:
begin
<all your code>
rescue ActiveRecord::RecordNotFound
return 'Unable to find a matching record'
end
try this:
comp = ""
comp = Company.find(comp_id) unless comp_id.nil?
instead of comp = Company.find(comp_id)
further nil checking present in your code.
Reason being
params[:customer][:selected_companies].delete_if{|a| a.blank?} = []
so [].first = nil
therefor, params[:customer][:selected_companies].delete_if{|a| a.blank?}.first = nil
and comp_id is nil
So check the log file and check what is coming in the parameter "selected_companies"
when you will find the parameter, everything will be understood well....

Rspec instance variables and controller testing

I'm writing a bowling score calculator, and I'm trying to set up RSpec tests, but for some reason I can't get my tests to work correctly.
players_controller_spec.rb:
require 'spec_helper'
describe PlayersController do
let(:player_names) { ["player1",
"player2",
"player3",
"player4"] }
describe "POST bowl" do
before(:each) do
#game = Game.create!
player_names.each do |name|
Player.create!(:name => name)
end
#game.players = Player.all
Player.all.each do |player|
(0..9).each do |number|
player.frames << Frame.create(:number => number)
end
end
end
describe "for the player's third bowl" do
before(:each) do
#game.players[#game.current_player].frames[9].update_attributes({:number => 9, :first_bowl => "X", :second_bowl => "X", :score => 20})
#game.update_attributes({:current_player => 0, :current_frame => 9})
end
describe "if the bowl is a number score" do
before(:each) do
post :bowl, {:score => "5", :id => #game.id}
end
it "should update the player's score" do
#game.players[#game.current_player].frames[#game.current_frame].score.should == 25
end
end
end
end
end
players_controller.rb
def bowl
#game = Game.find(params[:id])
#score = params[:score]
#current_player = #game.current_player
#current_frame = #game.current_frame
#player = #game.players[#current_player]
#frame = #player.frames[#current_frame]
if #frame.first_bowl.nil?
#frame.first_bowl = #score
if #score == "/"
raise "Error"
end
if #score == "X" && #frame.number == 9
#frame.bonus = 2
end
#frame.score = (/\A[0-9]\z/ === #score ? #score.to_i : 10)
elsif #frame.second_bowl.nil?
#frame.second_bowl = #score
if #frame.score + #score.to_i > 10
raise "Error"
end
if #score == "X"
if #frame.number != 9 || (#frame.number == 9 && #frame.first_bowl != "X") # can't be a spare has to be number or strike
raise "Error"
end
end
if #score == "/" && #frame.number == 9
#frame.bonus = 1
end
if /\A[0-9]\z/ === #score
#frame.score += #score.to_i
elsif #score == "/"
#frame.score = 10
elsif #score == "X"
#frame.score = 20
end
elsif #frame.third_bowl.nil?
#frame.third_bowl = #score
if #frame.number != 9
raise "Error"
end
#frame.bonus = nil
#frame.update_attributes({:score => (/\A[0-9]\z/ === #score ? #frame.score + #score.to_i : #frame.score + 10)})
else
raise "Error"
end
#frame.save
if #game.current_frame > 0
#prev_frame = #player.frames[#frame.number-1]
if #prev_frame.nil?
#prev_frame = Frame.create(:number => #game.current_frame-1)
#player.frames << #prev_frame
#player.frames = #player.frames.sort_by { |f| f.number }
end
update_scores
end
The spec in question is players_controller_spec.rb and at the start of the tests I'm creating a new game with 4 players and each player with 10 frames. Before each test, I'm setting a certain frame's values to be fit what I'm trying to test. The test above is an example where I want to make sure that bowling a score of 5 on the third bowl on the last frame correctly updates the score. But, even though in the debugger I see that the score is updated in the frame (when I debug in the controller method), once I return to the Rspec test, it doesn't work. It expects 25 but gets nil. Is there something I'm missing about how instance variables are transferred between specs and controllers?
So first off there is no 'transferring'. The controller and the example are 2 completely independent objects, each with their own instance variables (You can use the assigns spec helper to retrieve the value of a controller instance variable though).
That's not the root cause of your issue. You do, even before the controller executes, have an #game instance variable that is the game you are interested in. However with activerecord, every time you do Game.find you'll receive separate ruby objects (corresponding to the same database row). Once the row has been loaded from the database it doesn't notice changes made to the database behind its back.
You can reload the object with #game.reload
As a side note this sort of stuff is easier to work with if most of that logic was pushed down into one of your models rather than sitting in the controller.

Gem Resque Error - Undefined "method perform" after Overriding it form the super class

First of all Thanks for you all for helping programmers like me with your valuable inputs in solving day to day issues.
This is my first question in stack overflow as I am experiencing this problems from almost one week.
WE are building a crawler which crawls the specific websites and extract the contents from it, we are using mechanize to acheive this , as it was taking loads of time we decided to run the crawling process as a background task using resque with redis gem , but while sending the process to background I am experiencing the error as the title saying,
my code in lib/parsers/home.rb
require 'resque'
require File.dirname(__FILE__)+"/../index"
class Home < Index
Resque.enqueue(Index , :page )
def self.perform(page)
super (page)
search_form = page.form_with :name=>"frmAgent"
resuts_page = search_form.submit
total_entries = resuts_page.parser.xpath('//*[#id="PagingTable"]/tr[2]/td[2]').text
if total_entries =~ /(\d+)\s*$/
total_entries = $1
else
total_entries = "unknown"
end
start_res_idx = 1
while true
puts "Found #{total_entries} entries"
detail_links = resuts_page.parser.xpath('//*[#id="MainTable"]/tr/td/a')
detail_links.each do |d_link|
if d_link.attribute("class")
next
else
data_page = #agent.get d_link.attribute("href")
fields = get_fields_from_page data_page
save_result_page page.uri.to_s, fields
#break
end
end
site_done
rescue Exception => e
puts "error: #{e}"
end
end
and the superclass in lib/index.rb is
require 'resque'
require 'mechanize'
require 'mechanize/form'
class Index
#queue = :Index_queue
def initialize(site)
#site = site
#agent = Mechanize.new
#agent.user_agent = Mechanize::AGENT_ALIASES['Windows Mozilla']
#agent.follow_meta_refresh = true
#rows_parsed = 0
#rows_total = 0
rescue Exception => e
log "Unable to login: #{e.message}"
end
def run
log "Parsing..."
url = "unknown"
if #site.url
url = #site.url
log "Opening #{url} as a data page"
#page = #agent.get(url)
#perform method should be override in subclasses
#data = self.perform(#page)
else
#some sites do not have "datapage" URL
#for example after login you're already on your very own datapage
#this is to be addressed in 'perform' method of subclass
#data = self.perform(nil)
end
rescue Exception=>e
puts "Failed to parse URL '#{url}', exception=>"+e.message
set_site_status("error "+e.message)
end
#overriding method
def self.perform(page)
end
def save_result_page(url, result_params)
result = Result.find_by_sql(["select * from results where site_id = ? AND ref_code = ?", #site.id, utf8(result_params[:ref_code])]).first
if result.nil?
result_params[:site_id] = #site.id
result_params[:time_crawled] = DateTime.now().strftime "%Y-%m-%d %H:%M:%S"
result_params[:link] = url
result = Result.create result_params
else
result.result_fields.each do |f|
f.delete
end
result.link = url
result.time_crawled = DateTime.now().strftime "%Y-%m-%d %H:%M:%S"
result.html = result_params[:html]
fields = []
result_params[:result_fields_attributes].each do |f|
fields.push ResultField.new(f)
end
result.result_fields = fields
result.save
end
#rows_parsed +=1
msg = "Saved #{#rows_parsed}"
msg +=" of #{#rows_total}" if #rows_total.to_i > 0
log msg
return result
end
end
What's Wrong with this code?
Thanks

Resources