Restful api with rails - ruby-on-rails

I am learning how to write a restful API using RoR and have a question related to it. So, I will explain what I did along with the code.
This is how my controller looks like:
class EmployeesController < ApplicationController
require 'rest_client'
def index
uri = "localhost:3000/employees"
rest_resource = RestClient::Resource.new(uri)
users = rest_resource.get # will get back you all the detail in json format, but it will we wraped as string, so we will parse it in the next step.
#users = JSON.parse(users, :symbolize_names => true) # convert the return data into array
#users.each do |u|
logger.info(u.id)
end
# return based on the format type
respond_to do |format|
format.json {render json: #users}
format.xml {render xml: #users}
format.html
end
end
end
In my Gemfile, I have included rest-client as well.
gem 'rest-client'
My routes are :
root 'employees#index'
resources 'employees'
Hope everything is fine till now.
Now, when I send:
-> Curl request to 'http://localhost:3000/employees', it gets stuck.
-> Get request(by typing in the browser) to 'http://localhost:3000/', it get stuck here as well.
What is that which I am missing?

You don't need RestClient as you're writing a server here, not a client. The browser acts as the client. Remove the call to localhost as it's creating a loop.
The URL for this should already be set in your routes.rb, maybe using:
resources :users
Assuming this is a typical app, the show function should be reading from the database using ActiveRecord.
class EmployeesController < ApplicationController
def index
#users = User.all
respond_to do |format|
format.json {render json: #users}
format.xml {render xml: #users}
format.html
end
end
end

Do you have some other application running on localhost:3000? Because if not, then what your server does is calling himself again and again, causing a loop.
If you do have some other application, which fetches users from database, then be sure its running on some other port, other than this your rails app.
If you have only 1 app, then you don't need rest client.

You actually can do this without any additional gem. You just need to declare your routes according to what you want to expose to your API users and return the type (xml, json, ...) accordingly.

Related

Render different controller methods as JSON in Rails 5.2

I have a resource that renders as JSON perfectly fine at localhost:3000/gins.json from #gins = Gin.order(name: :desc).
Which will return ALL gins. However, I'd like to have a JSON response that only returns the last 4 gins, to use elsewhere. In the controller I also have:
#latestgins = Gin.order("created_at DESC").first(4)
The above would work in an index.html.erb view with <%= #latestgins.name %>, but how do I get the JSON for this? I have tried render json: #latestgins but navigating to localhost:3000/latestings.json, of course, gives a routing error.
I suspect I'm attacking this in completely the wrong way, but only just starting out with Rails API.
you can add respond to format json in your index method:
def index
#gins = Gin.order(name: :desc)
#latestgins = Gin.order("created_at DESC").first(4)
respond_to do |format|
format.html
format.json { render json: #latestgins }
end
end
your #latestgins is now available here : localhost:3000/gins.json
Edit
If you want a custom route to display your data, just add it in your routes:
defaults format: :json do
get 'last4gins', to: "gins#index"
end
Your data for the last 4 entries is available at http://localhost:3000/last4gins.json, at http://localhost:3000/last4gins but also at localhost:3000/gins.json
If you want to keep the gins index route clean, you can also create a custom method and remove the #latestgins from your index:
# routes
get 'last4gins', to: "gins#last4gins"
#controller
def index
#gins = Gin.order(name: :desc)
end
def last4gins
#latestgins = Gin.order("created_at DESC").first(4)
render json: #latestgins
end
Now the data is no more available at /gins.json

JSON format not displaying all model attributes (Rails 4)

I've run into a bizarre issue running Rails 4.2.7: When I look at the HTML index view for a particular model, everything seems fine. When I request the same data via JSON, I only get a subset of the attributes.
My index action is just:
def index
#coverages = Coverage.all
end
If I call it with
http://localhost:3000/coverages
everything is there. If I call it with
http://localhost:3000/coverages.json
I only get 7 of the attributes returned, in addition to the :id, :created_at, :updated_at, and a :url to the instance (e.g. http://localhost:3005/coverages/1.json).
Is the JSON method somehow truncating the attributes? I never recall running into this before.
POSTSCRIPT:
I modified the index action to:
def index
#coverages = Coverage.all
respond_to do |format|
format.html {render :index}
format.json {render json: #coverages}
end
end
and now I get all the attributes. Probably I was violating Rails best practices by not including the explicit format statements, but I am still curious about my original result.
I'm posting the solution I found (in the Postscript of the question) here in case it is useful. Still an open question as to why the original configuration didn't work.
Modifying the index action to:
def index
#coverages = Coverage.all
respond_to do |format|
format.html {render :index}
format.json {render json: #coverages}
end
end
solves the issue.

Rails REST API and Views

I am creating a Rails app to which its users have two ways of interaction.
Through a web interface and through an API (mobile app and other software).
The functions for the web and the api access are the same, for example a user can write a comment via the web interface (views) or through the API.
What I would do now is create all the controllers with views, and then create a namespace /API/ with its own controllers. The problem now is of course that I have to write the function to write write a comment twice. Once in my PostController and once in my API/PostController.
I learned that Rails = DRY, so I guess I am doing something wrong.
How would I make the same functions available for my views and at the same time for my API (JSON response).
And how would the routes and namespaces look like? I think even if I find a way to not repeat myself it would be nice to have API routes like api/v1/...
I've done something similar, both using an API namespaced controller and using a single controller.
Since you seem convinced that you can do everything equally within your Web interface as your API interface it would make sense to merge the two, though note that this is not always true.
If it makes more sense for you to use a single controller, then what you need to do is play with the repond_to format of requests coming to a single controller action.
Ex:
class ArticlesController < ApplicationController
def index
#articles = Article.all
respond_to do |format|
format.html # will render a view by default
format.json { render json: #articles }
end
end
def create
article = current_user.articles.build(article_params)
respond_to do |format|
if article.save
flash[:notice] = "success!"
else
flash[:error] = "uhoh!"
end
format.html # renders a view by default
format.json { render json: { errors: #articles.errors }
end
end
private
def article_params
params.require(:article).permit(:title, :content)
end
end

Render JSON in Rails

I want to see the following code in json format. When I do so I get a blank page with no content. In the rubymine debugger I have the correct information in json format.
include Java
require 'net/http'
class PatientRecordController < ApplicationController
def index
url = 'http://localhost:8080/patient/record/v2/'
data_raw = Net::HTTP.get_response(URI.parse(url)).body
#data_json = ActiveSupport::JSON.decode(data_raw)
respond_to do |format|
format.json {render json: #data_json}
end
end
end
You must add .json at the end of the path you enter into the browser.
What is the path to this particular index action? This code should indeed render JSON, which your debugger is indicating that it indeed is.
The URL you should be visiting is something akin to http://your_host/patient_records/index.json.
If you don't want to change the request to include .json, you can just drop the respond_to block render json: #data_json on its own will work.

Abbreviating respond_to

I am currently working on a web application built on Rails 3 that heavily uses Ajax/REST for the client side. Thus, I often find myself writing controller actions like this:
def create
if !params[:name]
respond_to do |format|
format.html { render json: {}, status: :not_found }
format.json { render json: {}, status: :not_found }
end
return
end
account = ...
respond_to do |format|
format.html { render json: account }
format.json { render json: account }
end
end
Nearly all of my actions are returning a json object in a success case or an error code. However, I always have to write this verbose respond_to block and a return, if I want the action to return earlier.
Instead I would like to use something like this instead, or a similar alternative:
def create
if !params[:name]
throw :not_found
end
account = ...
return account
end
How can this be done with Rails 3+ ?
Have a look into inherited_resources. This will allow you to rewrite your controller as:
class SomeController < ApplicationController
inherit_resources
respond_to :html, :js, :json
end
That is it. All of your create/read/update/delete methods will be accessible as usual. You can, as I have in the past, inherit from a master resources controller which uses inherited_resources, and then you can tweak the responses in a more general way.
class ResourcesController < ApplicationController
inherit_resources
respond_to :html, :js
def create
create! do |format|
format.js do
# generic code here for managing all create methods initiated via js
# current model is avialbe via 'resource'
# e.g 'resource.errors'
end
end
end
Then simply inherit from that controller:
class SomeController < ResourcesController
end
This abstraction can be overkill for most purposes, but it has come in extremely handy when working 30 or 40 models which all require similar controllers.
Inherited_resources offers many helpers for accessing the current model (referred to as resource) to facilitate dynamic references, so you can, for example, return relevant forms, or partials based on resource/model name.
To give you an idea of how to use this, you could return forms for the current controller by using the controller name in the parameters. Should be noted that malformed controller names will not reach this method (as it will return 404), so it is safe to use:
format.js do
render "#{params[:controller]}/form"
end
Best of all, you can override any of the methods yourself by defining them in a particular controller.
If your are always returning json, you can ommit the respond_to block and write it like :
def create
if !params[:name]
render json: {}, status: :not_found
return
end
account = ...
render json: account
end

Resources