This is obviously a common error. However, I am unable to resolve this issue when going over my code. I am trying to access ProPublica's API for congress. My model, view, and controller is pretty straightforward and this exact code has worked with me when accessing the Google News API.
I keep getting an undefined method error when I try and use the ".each" method in my view to iterate through the JSON response. I believe that I am passing the proper headers to the API as it requests.
My models:
class CongressTracker < ApplicationRecord
include HTTParty
def self.response
#congress = "most recent congress"
#chamber = "one each for congress and senate"
#type = "introduced, passed, etc."
congress_url = "https://api.propublica.org/congress/v1/115/senate/bills/passed.json"
HTTParty.get(congress_url,
:headers => {
"X-API-KEY" => "api-key-here"
})
end
end
class Bill < ApplicationRecord
include HTTParty
end
My controller:
class BillsController < ApplicationController
def index
#response = CongressTracker.response
end
end
My view:
<% #response["results"].each do |bill| %>
<p><%= bill["title"]%></p>
<p><%= bill["summary"]%></p>
<% end %>
My route:
resources :bills
The error in detail:
Rendering bills/index.html.erb within layouts/application
Rendered bills/index.html.erb within layouts/application (2.0ms)
Completed 500 Internal Server Error in 312ms (ActiveRecord: 0.0ms)
ActionView::Template::Error (undefined method `each' for nil:NilClass):
1: <% #response["results"].each do |bill| %>
2: <p><%= bill["title"]%></p>
3: <p><%= bill["summary"]%></p>
4: <% end %>
app/views/bills/index.html.erb:1:in `_app_views_bills_index_html_erb__2110131784793159686_70138696839360'
Example of expected JSON response (which I can get to work in the terminal):
{
"status":"OK",
"copyright":"Copyright (c) 2017 Pro Publica Inc. All Rights Reserved.",
"results":[
{
"congress": "115",
"chamber": "Senate",
"num_results": "20",
"offset": "0",
"bills": [
{
"bill_id": "hr2825-115",
"bill_type": "hr",
"number": "H.R.2825",
"bill_uri": "https://api.propublica.org/congress/v1/115/bills/hr2825.json",
"title": "DHS Authorization Act of 2017",
"summary": "",
},
The reason you are getting the undefined method 'each' for nil:NilClass error, is most likely because the response is {"message"=>"Forbidden"}, because your API key is incorrect.
I tested your code, and everything works correctly as long as you have the correct API key.
You have some mistakes with your view, most likely because you don't have the results yet.
To get the title and the summary of the bills you will need something like this:
<% #response["results"].each do |result| %>
<% result["bills"].each do |bill| %>
<p><%= bill["title"]%></p>
<p><%= bill["summary"]%></p>
<% end %>
<% end %>
Related
I keep getting this error: "undefined method `[]' for nil:NilClass" while trying to parse JSON using HTTParty in Ruby on Rails.
I want to be able to consume an API, but am unable to get anything to work.
The URL works fine and returns JSON elements with no problem; I am just unable to access them and present them for some strange reason.
I am using Rails 5 and Ruby 2.4. All API keys are hidden, but are properly input into my Rails app.
Here is my lib file:
require 'httparty'
class Wunderground
include HTTParty
format :json
base_uri 'api.wunderground.com'
attr_accessor :temp, :location, :icon, :desc, :url, :feel_like
def initialize(response)
#temp = response['current_observation']['temp_f']
#location = response['current_observation']['display_location']['full']
#icon = response['current_observation']['icon_url']
#desc = response['current_observation']['weather']
#url = response['current_observation']['forecast_url']
#feel_like = response['current_observation']['feelslike_f']
end
def self.get_weather(state, city)
response = get("/api/#{ENV["wunderground_key"]}/conditions/q/#{state}/#{city}.json")
if response.success?
new(response)
else
raise response.response
end
end
end
I've entered my api key into my application.yml file like so:
wunderground_key: "YOUR_API_KEY"
My controller:
class HomeController < ApplicationController
require 'Wunderground'
def wunderground
#weather = Wunderground.get_weather(params[:state], params[:city])
end
def index
end
end
My routes:
root 'home#index'
get 'wunderground', to: 'home#wunderground'
My view:
<div>
<%= form_tag wunderground_path, method: "get", class: "form-inline" do %>
<%= text_field_tag :city, nil, class: "form-control", placeholder: "City Name" %>
<%= select_tag :state, options_for_select(#states), :prompt => "Please select", class: "form-control" %>
<%= submit_tag "Check Weather", name: nil, class: "btn btn-primary" %>
<% end %>
</div>
<div>
<% if #weather.present? %>
<h3><%= #weather.location %></h3>
<p>The temperature is:
<%= #weather.temp %></p>
<p>Feels like:
<%= #weather.feel_like %></p>
<p>
<%= #weather.desc %>
<%= image_tag #weather.icon %>
</p>
<p>
<%=link_to "Full Forecast", #weather.url, target: "_blank" %>
</p>
</div>
<% end %>
Edit:
This is the error that I am getting as shown in the development log:
NoMethodError (undefined method `[]' for nil:NilClass):
lib/Wunderground.rb:12:in `initialize'
lib/Wunderground.rb:23:in `new'
lib/Wunderground.rb:23:in `get_weather'
app/controllers/home_controller.rb:6:in `wunderground'
Source of error
You're getting this error, because response['current_observation'] is nil.
Debugging strategy
To arrive at this conclusion, we typically look at the error message, and the line of code the error is originating from.
From your question, it's clear that you already know what the error is: undefined method '[]' for nil:NilClass, and if you look at the stack trace, your error is coming from lib/Wunderground.rb:12:in initialize'.
The relevant line of code is this:
#temp = response['current_observation']['temp_f']
In Ruby, object['foo'] is a syntax sugar for the "call the [] method on object with argument 'foo'. It can also be described as "send [] message to object with an argument 'foo'.
Chained [][] calls are just calling the same method on the returned object.
In effect, you are calling [] with 'current_observation' on response, and then calling [] on the returned object with 'temp_f'. What this means, is that you're calling [] on 2 objects:
response
Object returned by response['current_observation']
Looking at the error, undefined method '[]' for nil:NilClass, tells us that one of the above two objects is nil.
Since in a previous call response.success? returned true, we can conclude response['current_observation'] is indeed returning nil.
Further debugging and finding ways to fix this
There are a few options.
Pretty print the response: pp response.parsed_response.
Use the debug_output setting from HTTParty to look at the HTTP request response log.
class Wunderground
include HTTParty
format :json
debug_output $stdout
# ...
# rest of the code
# ...
end
With debug mode on, watch the server log so you can get an idea about what response is the API returning.
So I'm trying to create a new "transaction" each time I click the button "Purchase", but doesn't seem to work. I get the error "undefined method `[]' for nil:NilClass".
<% #comics.each do |comic|%>
<ul>
<li><%= comic.title %> </li>
<li><%= comic.author %> </li>
<li><%= comic.year %> </li>
<li><%= comic.publisher %> </li>
<% if user_signed_in? %>
<%= button_to 'Purchase', {:controller => "transactions", :action => "create", :seller_id => comic.user_id, :buyer_id=> current_user.id, :comic_id => comic.id} , {:method=>:post} %>
<% end %>
</ul>
This is what you can find in the transactions controller:
def create
#my_transaction = Transaction.new(
buyer_id: params[:transaction][:buyer_id],
seller_id: params[:transaction][:seller_id],
comic_id: params[:transaction][:comic_id]
)
#my_transaction.save
redirect_to "/transactions/"
end
Do you have any idea why this might be happening?
Thanks!
There are a couple of ways you can debug this:
Look in the console logs to see what is being posted in the params hash.
add a puts statement at the top of the create statement to view what is in the params variable e.g.
controller
def create
puts params.inspect
end
I suspect you'll find that the params hash does not have a transaction key and the create method should be
def create
#my_transaction = Transaction.new(
buyer_id: params[:buyer_id],
seller_id: params[:seller_id],
comic_id: params[:comic_id]
)
end
The params[:transaction] is nil, you can see the sent parameters in the log (tail -f log/development.log if the server doesn't log). In your case you access the required data like params[:comic_id]
A few tips:
Never trust the input coming from the client:
:buyer_id=> current_user.id here an attacker could send any ID since the button_to helper will create a html form which is easily accessible using the devtool. Instead check it on the server side. Same goes for the seller_id, you can just fetch the related comic comic = Comic.find params[:comic_id].
You might want to consider an another API approach like POST /comics/1/buy this is a bit more restfull, and you could use the built in path helpers for that url like buy_comic_path(comic)
This probably is a nooby one. I'm building a search form.
In the model document.rb, I have this :
pg_search_scope :search_full_text,
:against => :full_text,
:using => {
:tsearch => {
:prefix => true
}
}
and in documents_controller.rb, I have :
def find
$results = Document.search_full_text(params[:ch_acte][:text])
end
But NOTHING gets send to the database. The server log only says:
Started POST "/documents/find" for ::1 at 2017-01-19 08:48:07 +0100
Processing by DocumentsController#find as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZkqVYMuqMqnUjLer/FVdBdtv4cycp71dXqPQw6j0mfHKX5ptin7p7YiYFj8bNtjciQDmHzbKtBnZoILpGGvl8Q==", "ch_acte"=>{"text"=>"complet", "words"=>"", #[cut for brievity]}, "commit"=>"Cherche"}
Rendering documents/find.html.erb within layouts/messources
Rendered documents/find.html.erb within layouts/messources (0.4ms)
Completed 200 OK in 216ms (Views: 210.2ms | ActiveRecord: 0.0ms)
Other than the method pg_search_scope in the model and calling that method in the controller, what must I do to get this sent to the database?
When I run Document.search_full_text("esp") in rails console, it works fine.
UPDATE
I added this in documents/find.html.erb :
<% $results.each do |m| %>
<p>The id is <%= m.id %>.</p>
<% end %>
I get an page that displays my menu, and only white after that...
You should understand that Rails tries to be as performant as possible. When you build a search it does NOT execute until you attempt to access the results of the search.
So you would do...
def find
#documents = Document.search_full_text(params[:ch_acte][:text])
end
Then in your find.html.erb you might do...
<% #documents.each do |document| %>
<%= document.name %>
<% end %>
The query is only executed when the #documents.each line is executed to retrieve the documents... i.e. only when it needs to be executed.
I'm following along with this tutorial (http://www.yoniweisbrod.com/autocomplete-magic-with-rails/) using jQuery-ui's autocomplete, but when I attempt to search using the text field, it routes to the controller's show method instead of the autocomplete_ingredient_name method.
Here's the code for my form:
<%= form_tag(cocktail_path(1), :method => 'get', :class => "search_form", :remote => true) do %>
<%= label_tag(:query, "Choose ingredients:") %>
<%= autocomplete_field_tag(:query, params[:query], autocomplete_ingredient_name_cocktails_path, {class: "search-query", placeholder: "", type: "search"}) %>
<% #ingredients.each do |ingredient| %>
<%= hidden_field_tag "ingredients[]", ingredient.name %>
<% end %>
<%= submit_tag("Search") %>
<% end %>
And my controller.
class CocktailsController < ApplicationController
autocomplete :ingredient, :name
def index
#cocktails = []
#ingredients = []
end
def autocomplete_ingredient_name
#ingredients = Ingredient.order(:name).where("name LIKE ?", "'%#{params[:query]}%'")
respond_to do |format|
format.html
format.json {
render json: #ingredients.map(&:name)
}
end
end
def show
hash = {}
#cocktails = []
#ingredients = Ingredient.all.map {|ingredient| ingredient}
#ingredients.select! {|ingredient| ingredient.name.downcase.include?(params[:query])}
if params[:ingredients]
old_ingredients = []
params[:ingredients].each do |ing|
old_ingredients << Ingredient.find_by(name: ing)
end
cocktails = #ingredients.map {|ingredient| ingredient.cocktails }.flatten
old_cocktails = old_ingredients.map {|ingredient| #cocktails << ingredient.cocktails }.flatten!
old_cocktails.each do |cocktail|
hash[cocktail] = 1
end
cocktails.each do |cocktail|
if hash.has_key?(cocktail)
#cocktails << cocktail
end
end
#cocktails = #cocktails.uniq.flatten
else
#cocktails = #ingredients.map {|ingredient| ingredient.cocktails }.flatten
end
end
end
And here is the message from my server, going to the CocktailsController#show method, instead of the autocomplete method.
Started GET "/cocktails/autocomplete_ingredient_name?term=mi" for ::1 at 2015-10-12 15:32:21 -0500
Started GET "/cocktails/autocomplete_ingredient_name?term=mi" for ::1 at 2015-10-12 15:32:21 -0500
Processing by CocktailsController#show as JSON
Processing by CocktailsController#show as JSON
Parameters: {"term"=>"mi", "id"=>"autocomplete_ingredient_name"}
Parameters: {"term"=>"mi", "id"=>"autocomplete_ingredient_name"}
Ingredient Load (8.6ms) SELECT "ingredients".* FROM "ingredients"
Ingredient Load (8.6ms) SELECT "ingredients".* FROM "ingredients"
Completed 500 Internal Server Error in 38ms (ActiveRecord: 8.6ms)
Completed 500 Internal Server Error in 38ms (ActiveRecord: 8.6ms)
TypeError (no implicit conversion of nil into String):
app/controllers/cocktails_controller.rb:25:in `include?'
app/controllers/cocktails_controller.rb:25:in `block in show'
app/controllers/cocktails_controller.rb:25:in `select!'
app/controllers/cocktails_controller.rb:25:in `show'
TypeError (no implicit conversion of nil into String):
app/controllers/cocktails_controller.rb:25:in `include?'
app/controllers/cocktails_controller.rb:25:in `block in show'
app/controllers/cocktails_controller.rb:25:in `select!'
app/controllers/cocktails_controller.rb:25:in `show'
The code is supposed to create a jQuery-ui dropdown that predicts what you're searching, but the dropdown never shows up and it immediately returns a 500 error.
Any thoughts on why this isn't routing to the right method would be extremely appreciated!
This is likely because of a routing error, i.e. your GET "/cocktails/autocomplete_ingredient_name?term=mi" directive is handled by the wrong entry in your /config/routes.rb file.
Make sure the route that handles your autocomplete process is defined prior to the route that handles the show action of your cocktails controller.
Since the latter usually takes the form get 'cocktails/:id', the 'autocomplete_ingredient_name' part of the URI is affected to the :id component and the processing is delegated to the show action of your controller with said id.
The autocomplete route is defined, since the autocomplete_ingredient_name_cocktails_path directive in your form generates a properly formatted URI ; so I believe this is merely an issue of precedence.
You have another potential issue, however: your autocomplete query parameter is 'term' in your request, but it is 'query' in your controller action. They should have the one and same name.
Ive been trying to call a helper method from my controller on a rails object and i continue to get this error. Here is all my code.
class AuctionsController < ApplicationController
helper_method :bid
def bid
#auction = #auction.update_attribute(:current_price == :current_price + 1.00)
end
view
<%= link_to("Bid", #auction.bid(auction) )%>
stack trace
Started GET "/auctions" for 127.0.0.1 at 2014-11-11 05:46:16 -0600
Processing by AuctionsController#index as HTML
Auction Load (1.7ms) SELECT "auctions".* FROM "auctions"
Rendered auctions/index.html.erb within layouts/spacelab (199.7ms)
Completed 500 Internal Server Error in 234ms
ActionView::Template::Error (undefined method `bid' for nil:NilClass):
26: <h3, class="textcolor"><%= auction.description %></h3><br />
27: <h3, class="textcolor"><%= auction.start_time.strftime("Opens on %B %d on %I:%M %p") %></h3><br />
28: <h3, class="textcolor"><%= auction.end_time.strftime("Closes on %B %d on %I:%M %p") %></h3><br />
29: <%= link_to("Bid", #auction.bid(auction) )%>
30:
31: <%= link_to 'Show', auction, class: "btn btn-primary btn-lg btn-block" %>
32:
app/views/auctions/index.html.erb:29:in `block in _02d262c45abda05ea87ddc9c2c9ec185'
app/views/auctions/index.html.erb:16:in `_02d262c45abda05ea87ddc9c2c9ec185'
Rendered /Users/claymccullough/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.1.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.0ms)
Rendered /Users/claymccullough/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.1.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (0.8ms)
Rendered /Users/claymccullough/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.1.6/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb within rescues/layout (92.3ms)
Could anyone tell me if my code is wrong or methodology is incorrect? thanks
edit please see my answer below, that is my real problem...
You have a missundertanding about methods on the controller, you're trying to call a Controller Method on an object, you can't do that. The methods on AuctionsController are part of Controllers no part of the Class, if you want to add operations to a Model class you have to write them in Auction Model
Correct call to your Controller, passing #auction as a parameter
<%= link_to("Bid", #auction )%>
First of all, the error message ActionView::Template::Error (undefined method bid for nil:NilClass) means that you are trying to call a method (bid) on an object that doesn't exist (#auction). Furthermore, the #auction.bid(auction) bit doesn't look good to me either - from a semantics and code-reading point of view but I don't know what you are trying to do exactly.
If you show us the rest of your AuctionsController we will be able to tell you more about what's wrong.