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.
Related
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 %>
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.
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 building a ruby on rails app that lets the user pick a file from their hard drive, and then writes specific lines from the text file into a database. I've built the form, but now I don't know how to get the content of the file into a variable, format it and then write it into the database.
I have a database table named drafts with colums set_1, set_2, set_3. I want to write line 13(from the text file) into set_1, line 165 into set_2 and line 317 into set_3. I also want to format the lines before writing them. In the file they look like this ------ FRF ------ and I only want FRF.
I've spent a lot of time searching stackoverflow and rubyonrails guides but have a hard time figuring this out. I'm very new to ruby and rails in general, so any help is appreciated.
Here's my controller (drafts_controller.rb):
class DraftsController < ApplicationController
def new
#page_title = "Upload MTGO Draft"
#draft = Draft.new
end
def create
#draft = Draft.new(draft_params)
if #draft.save
flash[:notice] = "Draft Saved!"
redirect_to drafts_show_path
else
render "new"
end
end
def index
#page_title = "MTGO Draft"
end
def show
#page_title = "MTGO Draft Replayer"
end
def search
#page_title = "Search MTGO Draft"
end
def destroy
end
def draft_params
params.require(:draft).permit(:name, :set_1, :set_2, :set3, :file_setter)
end
end
and my view: (new.html.erb):
<%= form_for #draft, :multipart => true do |f| %>
<div class="form-group">
Draft Log: <%= f.file_field :file_setter %>
<%= f.submit 'Save' %>
</div>
<% end %>
here's the model (draft.rb)
class Draft < ActiveRecord::Base
def file_setter=(file)
path = file.tempfile.to_path.to_s
lines = File.read(path).split("\r\n")
self.set_1 = lines[12]
self.set_2 = lines[164]
self.set_3 = lines[316]
end
attr_accessor :file_setter
end
If you need more info or want to get into the whole app more he's a gitHub repository.
Thanks for the help.
Here's the log excerp:
Started POST "/drafts" for 127.0.0.1 at 2015-04-10 10:52:57 +0200
Processing by DraftsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8IjtOXuXm3fswWTslDTq5ijjo0dpCWHlu9X7UFb5prvawrZfkLC46v7PbqTgrSrvukLxAAoBIBftsGqiXv79JA==", "draft"=>{"file_setter"=>#<ActionDispatch::Http::UploadedFile:0x00000005ee4a08 #tempfile=#<Tempfile:/tmp/RackMultipart20150410-3067-1fssm13>, #original_filename="1", #content_type="application/octet-stream", #headers="Content-Disposition: form-data; name=\"draft[file_setter]\"; filename=\"1\"\r\nContent-Type: application/octet-stream\r\n">}, "commit"=>"Save"}
[1m[36m (0.2ms)[0m [1mBEGIN[0m
[1m[35mSQL (1.8ms)[0m INSERT INTO `drafts` (`created_at`, `updated_at`) VALUES ('2015-04-10 08:52:57', '2015-04-10 08:52:57')
[1m[36m (41.6ms)[0m [1mCOMMIT[0m
Redirected to http://localhost:3000/drafts/show
Completed 302 Found in 57ms (ActiveRecord: 43.6ms)
Add this method to your model:
def file_setter=(file)
path = file.tempfile.to_path.to_s
lines = File.read(path).split("\r\n")
self.set_1 = lines[12]
self.set_2 = lines[164]
self.set_3 = lines[316]
end
In your form change the file_field value from :file to :file_setter like this:
<%= form_for #draft, :multipart => true do |f| %>
<div class="form-group">
Draft Log: <%= f.file_field :file_setter %>
</div>
<% end %>
// Its very important that you add :multipart => true to the form_for method. Otherwise file uploads wont work.
// Also add this to your model:
attr_accessor :file_setter
// In your controller you have to change this:
def draft_params
params.require(:draft).permit(:name, :set_1, :set_2, :set3)
end
To this:
def draft_params
params.require(:draft).permit(:name, :set_1, :set_2, :set3, :file_setter)
end
I have a Word model with one column: word. I have a form that creates an #word object when submitted.
words/_form.html.erb
<%= form_for(#word, :remote => (params[:action] == 'new' ? true : false)) do |f| %>
<fieldset>
<div class="field">
<%= f.text_field :word, :required => true %>
</div>
</fieldset>
<div class="actions">
<%= f.submit :disable_with => 'Submitting...' %>
</div>
<% end %>
words/create.js.erb
$('#words').prepend( '<%= escape_javascript(render #word) %>' );
$('#new-word-form-container').find('input:not(:submit),select,textarea').val('');
I'd like to shorthand the creation of multiple words simontaniously on one form submission (i.e. instead of having to resubmit to create each individual word).
I have a method in my Word model that splits the string into an array of words (separated by a comma or space).
class Word < ActiveRecord::Base
attr_accessible :word
# Split Words method splits words seperated by a comma or space
def self.split_words(word)
# Determine if multiple words
if word.match(/[\s,]+/)
word = word.split(/[\s,]+/) # Split and return array of words
else
word = word.split # String => array
end
end
end
I'm trying to use a for loop within my create action to step through each array element, and create an #word object for the element.
class WordsController < ApplicationController
respond_to :js, :json, :html
def create
split = Word.split_words(params[:word])
split.each do |w|
#word = Word.create(params[:w])
respond_with(#word)
end
end
I'm currently getting a HashWithIndifferentAccess error, as listed below.
Started POST "/words" for 127.0.0.1 at 2014-06-10 13:09:26 -0400
Processing by WordsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0hOmyrQfFWHRkBt8hYs7zKuHjCwYhYdv444Zl+GWzEA=", "word"=>{"word"=>"stack, overflow"}, "commit"=>"Create Word"}
Completed 500 Internal Server Error in 0ms
NoMethodError (undefined method `match' for {"word"=>"stack, overflow"}:ActiveSupport::HashWithIndifferentAccess):
app/models/word.rb:9:in `split_words'
app/controllers/words_controller.rb:36:in `create'
Any help is greatly appreciated.
In your create action in the words controller, you fetch the words from the params which gives you back a parameter object. The parameter object is a hash like object that inherits from ActiveSupport::HashWithIndifferentAccess. Then you try and call the match method on your parameter object and it does not know how to respond to it, so you get a NoMethodError.
Checkout http://api.rubyonrails.org/classes/ActionController/Parameters.html
The first thing you need to do is pass params[:word][:word] instead of params[:word], this should give you back a string object and this method should now work.
It also looks like you might run into another problem in the each loop in create as params[:w] might return nil. You should instead just pass in w because that will be each word in the array you are iterating over and if am not mistaken you want to create a word object for each word.
def create
split = Word.split_words(params[:word][:word])
#words = split.map do |w|
Word.create(word: w)
end
respond_with(#words)
end
class WordsController < ApplicationController
respond_to :js, :json, :html
def create
#words = params[:word].split.map { |word| Word.create(word) }
respond_with(#words)
end
end