Ruby API - Accept parameters and execute script - ruby-on-rails

I have created a rails project that has some code that I would like to execute as an API. I am using the rails-api gem.
The file is located in app/controllers/api/stats.rb.
I would like to be able to execute that script and return json output by visiting a link such as this - http://sampleapi.com/stats/?location=USA?state=Florida.
How should I configure my project so that when I visit that link it runs my code?

the file should be called stats_controller.rb app/controllers/api/stats_controller.rb
you can create an index method where you can add your code
class API::StatsController < ApplicationController
def index
#your code here
render json: your_result
end
end
in the file config/routes.rb you should add
get 'stats' => 'api/stats#index', as: 'stats'
To access the params in the url you can do it in your index method with params[:location] ,params[:state]

Here's how I would think of this:
in app/controllers/api/stats_controller.rb
module Api
class StatsController
def index
# your code implementation
# you can also fetch/filter your query strings here params[:location] or params[:state]
render json: result # dependent on if you have a view
end
end
end
in config/routes.rb
# the path option changes the path from `/api` to `/` so in this case instead of /api/stats you get /stats
namespace :api, path: '/', defaults: { format: :json } do
resources :stats, only: [:index] # or other actions that should be allowed here
end
Let me know if this works

Related

Rails testing render action in controller

I have a dynamically driven Rails application where views and determined by the path requested.
routes.rb:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get ':tenant', to: 'tenants#home'
get '/:path', to: 'tenants#page', :constraints => { :path => /.*/ }
end
TenantsController:
class TenantsController < ApplicationController
def home
render :template => params[:tenant] + "/home"
end
def page
render :template => params[:path]
end
end
As you can see what simply happens is we get the path constraint from the endpoint and render a view template from it.
I would like to write a test that ensures rails requests a view template that matches the URL requested. (Basically test the page method within TenantsController).
Given that I do not want to tie my test into tenants that may be in the system, how can I write a test for this generically without the test knowing about some Tennant?
You can write tests for your controller using the assert_template method:
test "should render the correct template" do
['path1', 'path2'].each do |path|
get '/', params: { path: path }
assert_template "#{path}"
end
end
This guide provides more details on how to test your controllers.

Unable to access model data from action

I have a controller:
class Api::V1::Account::SharingController < ApplicationController
def show
sharings = Account::Sharing.all
render json: sharings, status: :ok
end
end
The model:
path: models/account/sharing.rb
class Account::Sharing < ActiveRecord::Base
end
Route:
scope '/account' do
resource :sharing, controller: 'account.sharing', path: 'sharings'
end
I get the this error: uninitialized constant Api::V1::Account::SharingController::Account, this is because of sharings = Account::Sharing.all
In rails console, I am able to use and get data with: Account::Sharing.all
Why is this happening?
UPDATE
Changes the folder to accounts and now my SharingController sits in accounts/sharing_controller.rb and is working.
The problem is that in the controller it searches for Account::Sharing in the controller's namespace. Try prefixing it with :: to tell it to search from the 'root' namespace like so:
::Account::Sharing.all
yuo need to define routes like that.
scope '/api' do
scope '/v1' do
scope '/accounts' do
resource :sharing, controller: 'account.sharing', path: 'sharings'
end
end
end

Circular dependency detected while autoloading constant Api::V1::VersionsController

I am building a rails API, and I want to see the data in my browser when I use the appropriate url followed by .json or .xml. I am getting the following error:
Circular dependency detected while autoloading constant Api::V1::VersionsController
I set up a versions_controller.rb in the path music-app/app/controllers/api/v1/versions_controller.rb:
class API::V1::VersionsController < ApplicationController
respond_to :json, :xml
def index
#versions = Version.all
respond_with(#versions)
end
def show
#versions = Song.find_by(:id => params[:song_id]).versions
respond_with(#versions)
end
end
and my routes:
namespace :api do
namespace :v1 do
get '/versions' => 'versions#index'
get '/versions/:id' => 'versions#show'
end
end
Am I missing something, and how can this be resolved?
It seems to be the uppercase of API in the controller that is causing your issues.
Try capitalized
class Api::V1::VersionsController < ApplicationController

Using versioning in a Rails API app- Cannot render JSON for a particular controller action

I created a practice rails app where I have created a namespace and versioned like is demonstrated in this railscast. Everything is working fine and I can see the json output in the browser
Then I added the Rabl gem and was trying to render the rabl views but I get an empty JSON array rendered in the browser
Here is what I did systematically to get the versioning working
1) changed the routes file to look like this
App0828::Application.routes.draw do
namespace :api, defaults: { format: 'json'} do
namespace :v1 do
resources :vendors
end
end
#resources :vendors
#root to: 'vendors#index'
end
2) The created thise files
app/copntrollers/api/v1/vendors_controller.rb
Inside the vendors_controller.rb I added the following code
module Api
module V1
class VendorsController < ApplicationController
class Vendor < ::Vendor
#subclass vendor class so thatyou can extend its behaviour just for this version
#add any functions specific to this version here
end
respond_to :json
def index
respond_with Vendor.all
end
def show
respond_with Vendor.find(params[:id])
end
..........
3) Then I pointed my browser to this url "http://localhost:3000/api/v1/vendors"
And I can see the json output in my browser
4) Then I added the rabl gem
5) restarted the server
6) Changed the above file at app/copntrollers/api/v1/vendors_controller.rb
from the version above to the version below
module Api
module V1
class VendorsController < ApplicationController
class Vendor < ::Vendor
#subclass vendor class so thatyou can extend its behaviour just for this version
#add any functions specific to this version here
end
respond_to :json
def index
render 'api/v1/index.json.rabl'
end
def show
render 'api/v1/show.json.rabl'
end
.......
7) I created the following files with this code:
file: app/views/api/v1/show.json.rabl
code: object #vendor
attributes :id, :name
file: app/views/api/v1/index.json.rabl
code: object #vendors
attributes :id, :name
8) Routes file looks like this
api_v1_vendors GET /api/v1/vendors(.:format) api/v1/vendors#index {:format=>"json"}
POST /api/v1/vendors(.:format) api/v1/vendors#create {:format=>"json"}
new_api_v1_vendor GET /api/v1/vendors/new(.:format) api/v1/vendors#new {:format=>"json"}
edit_api_v1_vendor GET /api/v1/vendors/:id/edit(.:format) api/v1/vendors#edit {:format=>"json"}
api_v1_vendor GET /api/v1/vendors/:id(.:format) api/v1/vendors#show {:format=>"json"}
PUT /api/v1/vendors/:id(.:format) api/v1/vendors#update {:format=>"json"}
DELETE /api/v1/vendors/:id(.:format) api/v1/vendors#destroy {:format=>"json"}
9) Finally I went to url: "http://localhost:3000/api/v1/vendors.json"
And all I see in the browser is an empty JSON hash: "{}"
So clearly it cannot access the instance variables. Seems to be some issue with being out of scope. Im not sure how to proceed next. I couldn't find any examples online of versioning with rabl. Any suggestions? Id really appreciate it
Thanks
Where are you setting the instance variables?
First, you probably should have this line: respond_to :json in your Api::ApplicationController (or something like that, basically the controller from which every other controller in the Api module inherit).
You dont need these: render 'api/v1/index.json.rabl'.
Just set your instance variables:
def index
#vendors = Vendor.all
respond_with #vendors
end
def show
#vendor = Vendor.find(params[:id])
respond_with #vendor
end

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.

Resources