Creating web APIs with Rails - ruby-on-rails

Trying to implement API calls. What goes in the controller? I think I should create API an API view. Should I use a one of the API gems?
I'm trying to use my first app to write to the database. My 2nd App will use a web API to GET/POST from first app's uri for fields that would share same data.

Take a look at rabl gem it will assist you to generate JSON views using your current controllers, From this you can get the idea of how JSON APIs work and proceed to namespacing your controllers and routes for your API. For http requests between your two apps, you can use either httparty or typhoeus gems

I have done several Api's in RoR, and these are the steps I usually take:
Create a restfull Api:
I usually create a controller for the api, lets call it ApiController, all the requests i do on my app go through that controller
Authenticate the api controller:
for the sake of simplicity here is a non secure way to authenticate the users who use your api:
class ApiController < ApplicationController
before_filter :apiauth
private
def apiauth
if request.headers["ApiAuth"]!='IguD7ITC203'
raise "error"
end
end
end
create some mothods and responses for your api:
here is a simple method you can implement on the controller:
def successOrder
#order=Order.find_by_id(params['id'])
if #order
#order.status=params['status']
if #order.save
render :json => {:status => "true", :order => {:id => #order.id, :status => #order.status}}
else
render :json => {:status =>"false", :errors => #order.errors.full_messages }
end
else
render :json => {:status => "false", :errors => ["Invalid Order Id"]}
end
end
update your routes:
sometimes I like to wildcard my routes for my api, like this:
match '/:action', :to => 'api#%{action}', :constraints => lambda {|r| r.subdomain.present? && r.subdomain == 'api'} , :via => [:post]
Finnaly take a look at gems like Jbuilder that can build you awsome responses for your jsons. hope it helps

I recently created an API which served mobile and web application. I used RABL gem templating system and i was able to customize my API views as i wanted. You can use RABL to generate JSON and XML based APIs from any ruby object.

Related

Rails Webservice--How to pass in search parameters

I have built a rails app that I wish to adapt to a restful webservice that returns results based upon queries requested from a mobile app. The functionality of the app requires that a search is enabled and I have employed sunspot solr within the rails app. The code for the search (within the respective controller) is as such:
def search(code, dow, period)
# performing the search as I want it
#search = Sunspot.search(Camps) do
fulltext code
with(dow).equal_to(true)
with(period).equal_to(true)
with(:is_active).equal_to(true)
order_by :price, :desc
end
#search.results
end
The major problem is that every rails webservice tutorial only lists very inflexible HTTP requests like: GET /user/{#}, and to execute you simply input the user #. But for the app, I will need to pass in varying parameters noting the method head as: search(code, dow, period).
What do I need to change to make the app respond and return the requested information when using a GET and POST requests to some URI
ie. /search...param...param...etc that can allow for the method head input parameters I have designated?
As mentioned in the comments, you'll want to look at the routes to sort this out
Non Resourceful Routes
The problem you have is you're trying to handle variables in the routes, which aren't the typical use case
Rails allows you to send any type of data through the routes - it's just a case of defining them correctly
Here's how we've implemented basic search routing:
#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]
#app/controllers/products_controller.rb
def search
#products = Product.search(params[:search])
respond_to do |format| --> handles different mime-types (HTML / JSON)
format.js { render :partial => "elements/livesearch", :locals => {:search => #products, :query => params[:search]} }
format.html { render :index }
end
end
#app/models/product.rb --> put your Solr stuff in here
def self.search(search)
basic_search(name: search, description: search).take(5)
end

No response available from custom Devise rails auth api, but my RABL api works fine

I'm using Restangular in AngularJS to connect to my RAILS API backend. I can grab data no problem using Restangular from my standard RABL api service, but when I try to POST to my custom Devise login/logout api controller O don't seem to get a resonse.
I followed this code ehere:
http://lucatironi.github.io/tutorial/2012/10/15/ruby_rails_android_app_authentication_devise_tutorial_part_one/
def create
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
render :status => 200,
:json => { :success => true,
:info => "Logged in",
:data => { :auth_token => current_user.authentication_token } }
end
My first guess is that RABL is doing something right, where the render in the create method above isn't working for Restangular. I don't have a RABL template for the create method, do I need one?
Any help on this is much appreciated.
You can just add a before filter at the top of your controller to authenticate all methods by adding: before_filter :authenticate_user!.
If you'd rather do the authorisation in the method itself, just do authenticate_user!. I tried calling warden for authentication but I ended up running into many other problems.
For a RABL template, yes you need one.

Ruby on rails User registration using rest API call and Devise

Can anyone guide me how to register a user from mobile device (rest API) in ruby on rails. I'm using Devise with Rails 3.0.
it is giving me this following error
NameError in Devise::CustomRegistrationsController#create
I've override the functionality of devise registration controller with the following.
def create
respond_to do |format|
format.html {
super
}
format.json {
build_resource
if resource.save
render :status => 200, :json => resource
else
render :json => resource.errors, :status => :unprocessable_entity
end
}
end
end
this solved the problem and I've added
skip_before_filter :verify_authenticity_token, :only => :create
to avoid authenticity check.
Wouldn't it be easier to make views for mobile than make an app on android/iOS? If you need API, then go with POST requests at /users/sign_up (and similar), for example,
browse localhost:3000/users/sign_up and change form's action parameter to action="/users.json", then click submit and you will receive the API's response, for me (on vanilla setup):
{"email":["has already been taken"],"password":["doesn't match confirmation","is too short (minimum is 6 characters)"]}
This way you can debug API (which follows standard conventions) with your browser. Notice that only :format parameter changes on rails routes (you can choose .json or .xml for APIs response)
POST info sent by my browser:
"utf8=✓&authenticity_token=n5vXMnlzrXefnKQEV4SmVM8cFdHDCUxMYWEBMHp9fDw%3D&user[email]=asd%40fasd.org&user[password]=321&user[password_confirmation]=1233&commit=Sign+up"

Organizing the Controller Directory in Rails (3)

I'm developing an application that is primarily an API gateway. In the expectation that we'll be developing multiple versions of the API over time and with the interest of having backward compatibility, I am looking to have something along the lines of:
http://host.domain.com/apiv1/:token/:module(/:id(:method))
Given this, what I am looking to do is to have a sub-routing system of my own within each API. What I'd like to have in terms of a file structure in the Controller directory is something akin to the following:
/app/controllers/apiv1_controller.rb
/app/controllers/apiv1/module_controller.rb
/app/controllers/apiv1/response_controller.rb
and eventually also have:
/app/controllers/apiv2_controller.rb
/app/controllers/apiv2/module_controller.rb
/app/controllers/apiv2/response_controller.rb
What this breaks down to is that I am unsure how to call methods within the controllers in the subdirectories, using something like:
return Apiv1::ResponseController.index
gives me:
undefined method `index' for Apiv1::ResponseController:Class
Any leads? Does this setup require that I explicitly "require" the requisite file manually?
Pasted Here in response to the question:
routes.rb
AppName::Application.routes.draw do
resources :users
match 'api-v1/:token/:module(/:id(/:method))' => 'apiv1#route'
root :to => "welcome#index"
end
apiv1_controller.rb
class Apiv1Controller < ApplicationController
protect_from_forgery
respond_to :json
def route
Rails.logger.level = 0
logger.info("ROUTE ACTION")
logger.info("params: #{params}")
Apiv1::ResponseController.index(params)
end
end
apiv1/response_controller.rb
class Apiv1::ResponseController < ApplicationController
protect_from_forgery
respond_to :json
def index(params)
Rails.logger.level = 0
logger.info("INDEX ACTION")
result = {
'success' => true,
'controller' => 'response',
'api' => 'v1'
}
render :json => result
end
end
If you are looking for versioning your REST APIs, you can use the restful_route_version gem. It also takes care of inheriting one version from another so that you don't rewrite the same REST APIs for every version.

Best way to send information from a client-side Ruby script to a Rails app?

I would like to create entries in a Track (as in music) database in a Rails application by sending the track data information from a client-side Ruby script. I only need to create and destroy tracks from the script, I don't need to have any web interface, and I'm not worrying about authentication/authorization at the moment. Could someone please walk me through (a) how to properly set up the Rails app (using Rails 2.3.8) and (b) how to send the data from a Ruby script?
Here's the approach I have taken so far:
I have created a Track model and Tracks controller. Here is the Track controller code:
class TracksController < ApplicationController
def create
#track = Track.new(params[:track])
respond_to do |format|
if track.save
format.any(:xml, :json) { head :ok }
else
format.xml { render :xml => #track.errors, :status => :unprocessable_entity}
format.json { render :json => #track.errors, :status => :unprocessable_entity}
end
end
end
def destroy
#track = Track.find(params[:id])
#track.destroy
respond_to do |format|
format.any(:xml, :json) { head :ok }
end
end
end
I have set up the routes as follows:
map.resources :tracks, :only => [:create, :destroy]
To send the information from the Ruby script, I have tried (1) using ActiveResource and (2) using net/http with the track information in xml format. For the latter, I'm not sure how to make the post request with net/http and also I'm unclear on how to properly format the xml. For example, can I just use to_xml on a track object?
Thank you in advance for your help.
I don't see any particular problems with your API, or how you are going about scripting it with an HTTP client. However, to get it to fit to the RESTful standard, your create call should return the object as XML or JSON. You can, indeed, simply call to_xml or to_json on the #track object. These functions accept options to further control the output. For instance, if you wish to exclude some piece of data from your API, you can pass the :except option. See the docs linked for more info.
As for your script, I personally prefer HTTParty over ActiveResource - very simple, easy to understand, and doesn't require that you fit your API exactly to the ActiveResource way of doing things. The examples are a good place to start, or have a look at the Chargify gem to see a longer example. HTTParty simply takes a Hash and converts it to XML or JSON. You don't need to have a Track object in your script (unless you really want to). Your script would be something like this:
require 'httparty'
class TrackPoster
include HTTParty
base_uri 'http://hostname.com'
def self.create_track(artist, song)
post('/tracks', :body => {
:track => { :artist => artist, :song => song }})
end
end
TrackPoster.create_track('The Beatles', 'Let It Be')
This call will return the parsed XML/JSON as a hash.

Resources