How do I get and display JSON with HTTParty? - ruby-on-rails

I'm new to Ruby on Rails and programming in general. So far I've successfully used my console to return JSON data from an API but I can't seem to make it work on my local server.
In the console I entered:
httparty "http://rubygems.org/api/v1/versions/httparty.json"
and it returned the JSON after I managed to install/configure everything.
So then in the console I wrote:
rails new Catalog
rails generate controller new index
I followed the tutorial to get <h1>Hello, Rails!</h1> to display on my page when I go to:
http://localhost:3000/
Now I am trying to get the JSON data returned and display on that same page where it says "Hello, Rails".
In index.html.erb I have:
<h1>Hello, Rails</h1>
<%= #result %>
In home_controller.rb I have:
class HomeController < ApplicationController
def get_catalog
include HTTParty
#result = HTTParty.get("http://rubygems.org/api/v1/versions/httparty.json")
end
end
In routes.rb I have:
Catalog::Application.routes.draw do
get "home/index"
root :to => 'home#index'
end
Nothing appears under "Hello, Rails" when I go to the page.
I'm still trying to wrap my head around how all of this stuff interacts with each other. Can anyone see what I'm doing wrong here perhaps?
--- Update
I'm trying to output just the product names into li elements. My files now are:
index.html.erb:
<ul>
<% #http_party_json.each do |event| %>
<% event.each do |e| %>
<li><%=h e['Products']['Name'] %></li>
<% end %>
<% end %>
</ul>
controller:
class HomeController < ApplicationController
def index
#response = HTTParty.get("myURL")
#http_party_json = JSON.parse(#response.body)
end
end
The error I am getting is:
undefined method `[]' for nil:NilClass
Extracted source (around line #5):
2: <ul>
3: <% #http_party_json.each do |event| %>
4: <% event.each do |e| %>
5: <li><%=h e['Products']['Name'] %></li>
6: <% end %>
7: <% end %>
8: </ul>
When I take off the ['Name'] I get JSON starting with:
[{"Name"=>"3x4 Vinyl Magnet", "Description"=>"Made of durable high-gloss vinyl. Measures 3x4 inches and has rounded corners. Waterproof and scratch resistant."
Why can't I target ['Name'] to get just the product names?

HTTParty parses XML/JSON responses for you. Simply use parsed_response to get it:
#response = HTTParty.get("URL").parsed_response

HTTParty.get returns an object of the type HTTParty::Response, which is documented in HTTParty (Check out the examples.)
There is a nice helper method on HTTParty::Response called `#parsed_response. This returns the JSON string parsed into a Hash or Array:
HTTParty.get("URL").parsed_response
Alternatively, in order to get the body of this message, you can call #result.body. This returns a string, which you can safely output to your page as you did. However, I'm guessing you want to do more with it than just output it to a page.
In order to parse this into a hash/Array that you can use/manipulate/return as true JSON you would do something like:
http_party_json = JSON.parse(#response.body)
So with this, you could do something like the following in your controller if you wanted to be able to return JSON:
#response = HTTParty.get("http://rubygems.org/api/v1/versions/httparty.json").parsed_response
respond_to do |format|
format.json { render :json => JSON.parse(#result) }
format.html { render "index.html.erb" }
end
Updated answer based on your updated information:
It appears as though e['Products'] returns an array (notice the square brackets on the outside.) So, if you are only expecting one you could do:
e['Products'][0]['Name']
If it might return multiple products, you might, instead, need to account for the possibility of many products and use an each block:
e['Products'].each do |product|
#do something with product['Name']
end
It just depends on your needs for the app.

Related

Ruby on Rails - Show method for a parsed JSON result

I am building a seemingly simple website for a visitor to get a status update on a selected data piece. The data comes from an external API. The gist of it is this: the visitor sees a list of the data names, clicks one, and is redirected to a partial with status update, either True or False.
My index method works very well, and iterates through the data names perfectly. I believe my routing (using the friendly_id gem) should work fine. However, I cannot figure out how to properly set up the show method.
Here is my code thus far:
Controller:
class DataController < ApplicationController
include HTTParty
attr_accessor :name, :status
def index
#response = HTTParty.get("api_url").parsed_response
respond_to do |format|
format.json { render :json => JSON.parse(#result, :include => { :data => { :only => [:name, :status]}}) }
format.html { render "index.html.erb" }
end
end
def show
#response = HTTParty.get('api_url').parsed_response
respond_to do |format|
format.json { render :json => JSON.parse(#result, :include => { :data => { :only => [:name, :status]}}) }
format.html { render "show.html.erb" }
end
#name = #response.find(params[:name])
end
end
View:
<% #response.each do |data| %>
<ul>
<li>
<%= link_to data['name'].upcase, name_path(#name) %>
</li>
</ul>
<% end %>
Routes:
Rails.application.routes.draw do
root 'data#index'
get '/:name' => 'data#show', as: "name"
end
All of this together brings up the following error:
ActionController::UrlGenerationError in Data#index
Showing /app/views/layouts/_header.html.erb where line #11 raised:
No route matches {:action=>"show", :controller=>"data", :name=>nil} missing required keys: [:name]
What I am trying to accomplish is to be able to route each iterated link as 'root/{data name}', pulling {data name} from the JSON result as a parameter, and then rendering a show page with the status update information. Clearly, I have no idea how to actually capture the "name" key from the parsed JSON result as a param for the show method. I have tried creating a data_params method to contain it, but it did not work. I have tried implicitly calling the param on the find method in show (as above), to no avail. I have even tried calling a new api scrape in a params method and trying to parse the results into that, and nothing.
I'm guessing it is either some very simple mistake I'm not seeing (is my link_to actually pointing the right direction? are my api calls and JSON parsing done correctly [yes, I know I should create a separate method or helper for the api call, but that hasn't been working out for me so far--I keep breaking the app]? is this link actually supposed to call from the index method still?), or else something a bit more out of my depth.
Any ideas on where to go from here?
In this view:
<% #response.each do |data| %>
<ul>
<li>
<%= link_to data['name'].upcase, name_path(#name) %>
</li>
</ul>
<% end %>
You're using #name but you never actually assign a value to it in your index controller method. As a result - it gets passed to the name_path function with a value of nil. That throws an error because your route definition requires a name.
I think you want something like name_path(data['name']) or something along those lines.

Empty Array in Controller ...but works in Rails Console

I'm baffled...
I have a model called Distributor and when I do Distributor.all in the console I get the array of all the records as I should (there are currently 657 records in there according to this query).
However, I created a controller action to show this distributors in an index view. And an empty array is returned from the same query Distributor.all
I've restarted the server
I've checked for spelling mistakes (literally copied and pasted the Distributor.all from controller to
console to make sure and it worked in console)
I've used binding.pry to debug and no problems are showing there
I've checked the logs and no errors are showing up
I've checked the logs vs the console and the same SQL query is being run Distributor Load (10.9ms) SELECT "distributors".* FROM "distributors" for both, but returning different results
generating the CSV or HTML isn't a problem because both load without errors
Any suggestions?
Distributor Model:
class Distributor < ActiveRecord::Base
belongs_to :brand
def self.to_csv
attributes = %w{distributor_id mfg_brand_id company_name address_one address_two city state postcode country address_type primary_address users_count created_at}
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |d|
csv << d.attributes.values_at(*attributes)
end
end
end
end
Distributors Controller
class DistributorsController < ApplicationController
before_action :authenticate_user!
def index
#user = current_user
if #user.admin?
#distributors = Distributor.all
respond_to do |format|
format.html
format.csv { send_data #distributors.to_csv }
end
else
flash["danger"] = "You must be a logged in Admin to access this route"
redirect_to root_path
end
end
end
Distributors index.html.erb
<h1>Distributors#index</h1>
<p>Find me in app/views/distributors/index.html.erb</p>
<%= #distributors.each do |d| %>
<%= d %>
<% end %>
In your controller, if you replace #distributors = RheemruudDistributor.all with #distributors = Distributor.all it should work, right? Or am I missing something?
Finally figured it out...and I figured it was something simple.
This is a multi-tenant app using the apartment gem and I had to put this newly created model in the excluded models part of the apartment.rb initializer.
Figured I'd leave this hear in case anyone else is in this situation.
use
<% #distributors.each do |d| %>
<%= d %>
<% end %>
instead of
<%= #distributors.each do |d| %>
<%= d %>
<% end %>
on your view

Cant find model with id error

My screenshot
I have messages speaker models.
I want to display number of messages each speaker posted on pages index page. When i insert #speaker = Speaker.find(params[:id]) it dispalys error Couldn't find Speaker with 'id'=. I dont understand what am i doing wrong.
my pages controller
class PagesController < ApplicationController
def home
#messages = Message.all.order("created_at DESC")
#speakers = Speaker.all
#speaker = Speaker.find(params[:id])
#count = #speaker.messages.count
#listeners = Listener.all
end
def about
end
def show
#speaker = Speaker.find(params[:id])
end
end
and my home view
<%= #count %> Messages
I think I need to see your routes.rb and the whole home view to better understand. My guess is that you are not actually passing the parameter for it to find.
For example, if your route looks something like this example route:
# Example of regular route:
# get 'products/:id' => 'catalog#view'
When you go to the URL products/1, it knows to pass the parameter id of 1 (params[:id] = 1) to the controller. Otherwise, you need to pass it into the GET request somehow like through a link. Since it is your home page, I doubt that is the intended set up of your app.
My guess is you are attempting to loop through all speakers on the home page. In that case, you can achieve the same effect but with simpler code. This is just a guess, but you may be able to do something like this in your controller and views:
class PagesController < ApplicationController
def home
#speakers = Speaker.all
end
end
<% #speakers.each do |speaker| %>
<%= speaker.name %>: <%= speaker.messages.count %> messages
<ul>
<% speaker.messages.each do |message| %>
<li><%= message.body %></li>
<% end %>
</ul>
<% end %>
This will print out the speaker's name and message count and then all their messages, assuming you set up the relationship between them in your model.

send a object in the rails form_tag method

I am not able to send a object using the rails form_tag form helper. I have tried the following
Download:<%=form_tag({controller: "orders", action: "csv_downloader",format: "csv"}, method: "post") do%>
<%= hidden_field_tag(:data, #orders_customs_display) %>
<%= submit_tag ("Download CSV"),:class => "btn btn-success download" %>
<% end %>
In the view and in the controller I have the following
def csv_downloader
data=params[:data]
respond_to do |format|
format.csv { send_data data.to_csv }
end
end
In the routes I have the following:
match "csv_downloader", to: "orders#csv_downloader", via: [:post]
As you see the .to_csv is a custom function written in the order.rb model
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |order|
csv << order.attributes.values_at(*column_names)
end
end
end
When I try downloading I get the following error.
undefined method `to_csv' for #<String:0x0000000716f8a8>
This is essentially the argument is coming as a string and not as an object? How do I rectify this?
You can send strings or files via html forms but you cannot send complex object.
What you can do: Serialize the object (Marshal) into a hidden string field and deserialize the string into an object later on the controller again. But this may be insecure because the user might be able to change the serialized data.
The preferred way would be to recreate the data in the same way like it was done for the html view, but just not to render the view but to send a csv file.

undefined method 'each' Ruby on Rails

Apologies in advance, I am a newbie trying to get my head around rails.
My View at the bottom works when I use:
def show
#posts = Post.all
end
However in my controller I now have:
def show
#posts = Post.find_by_category_id params[:id];
end
In my view I have
<%= #posts.each do |post| %>
<%= post.title %>
<% end %>
Some please explain why I get this error. What should I use. category_id is a foreign key on the Post table.
Look at http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find_by
Finds the first record matching the specified conditions
find_by_ will return only one post, not a collection. So you are not able to use each.
try
def show
#posts = Post.all.find_by_category_id params[:id];
end

Resources