API docs for a generic controller that handles all requests - ruby-on-rails

I need to generate api endpoints for certain database views we have in our postgres database dynamically, as we may add / delete views we don't want to update the code every time we do this.
for this i have a generic controller which handles all requests and I create model class on the fly based on which view needs to be accessed.
when a request is sent to the generic_api#index based on the end_point passed i create certain model classes on the fly and query them.
routes.rb
namespace 'api' do
namespace 'v2' do
get '*end_point', to: 'generic_api#index'
end
end
const = ClassFactory.class_object(end_point.classify, result)
Octopus.using(result['shrad'].to_sym) do
result = const.ransack(params[:q]).result.page(params[:page]).per(10000)
render json: result.to_json
end
The api itself is working as expected, however i am not sure what's a good way to generate documentation for the API since i only have one controller. we were earlier using the apipie gem but the documentation seems to be tightly coupled with controllers for each end point.
Any help on how to generate api documentation when I have a single controller handling multiple end point requests would be great, Thanks.

Related

Where would be a good place to store logic for a call to a third party api, based on Ruby on Rails best practices?

I am practicing making third-party api calls using the rest-client gem. Right now, my code for making a call to a third-party api is stored in a show method in my search_controller.rb. I'm assuming it would be better to store that logic somewhere other than a controller, in order to follow the "skinny controller" practice of ruby on rails. Where would be a better place to put this type of logic, or is this an ok place to have it?
I would say two different kinds of objects in conjunction.
Clients
These are classes that simply do HTTP calls and touch the application boundary. The reason you want to isolate this in a discrete object is that it allows you mock it in tests. It also keeps the responsibilities clear.
# adapted from the HTTParty readme
class StackExchangeClient
include HTTParty
base_uri 'api.stackexchange.com'
def initialize(service, page)
#options = { query: { site: service, page: page } }
end
def questions
self.class.get("/2.2/questions")
end
def users
self.class.get("/2.2/users")
end
end
/lib is a pretty good place to store clients since they rarely should contain any application specific logic.
Service objects
Service objects are Plain Old Ruby Objects (PORO) that are designed to
execute one single action in your domain logic and do it well.
-- Rails Service Objects: A Comprehensive Guide
class QuestionImporterService
def initialize(service, page, client: nil)
#client = client || StackExchangeClient.new(service, page)
end
def call
questions = #client.get_questions
questions.map do |attributes|
Question.new(attributes)
end
end
end
This example uses constructor injection to allow you to mock out the HTTP calls for testing.
You can place services in /app/services. Sometimes Spring gets "stuck" and does not pickup new folders in /app for autoloading in which case you restart it by calling $ spring stop.

Rails 5.2 Service objects with non current user

I am trying to forward an action to another controller through the service object methodology in rails 5.2.
The create action should pass the user id for the create action but I am failing at passing that param appropriately.
Business logic is the following: a user rents out an asset, the potential renter makes a request, when the asset owner agrees to a visit, the rent user is created as a client in another controller to organise a visit.
I am trying to address the create action in the client controller as follows:
In the rent controller :
private
def visit(room, rent)
#newclient = NewclientService.create(params)
if #newclient.save
rent.Approved!
...
else
rent.Declined!
...
end
and then in the app/service/newclient_service.rb
module NewclientService
class << self
def create(params)
#rent = Rent.find_by(id: params[:id])
user = #rent.user_id
name = #rent.user.fullname
email = #rent.user.email
Client.create(user_id: user, name: name, email: email)
end
end
end
This code does the job. The db is filled up, validations and strong params seem to work and it seems to me robust/secure enough.
Question: is the service object (my way ?) route the most preferred way for forwarding that action ?
Thanks for your help,
I like the pattern in principle and it has really cleaned up the apps which I produce. There are a couple of nice gems that I typically use to get the job done and keep the controllers clean.
I use the mutations gem and simple_command. These two together give you a nice (almost completely) consistent API. The mutations gem in particular is what I use for digesting and resolving JSON input data from params which can then handle processes for me.
This is a good pattern in the sense that it encapsulates the logic of discrete functionality very well. For example, if you have a RegisterUser mutation, you can use that in a controller or you can use it to digest a whole list of objects etc. You can even use the builder option for attributes to process deeply nested json.
I would recommend checking it out.
https://github.com/cypriss/mutations
For those times where I am not processing JSON from an API and want to create discrete encapsulated functionality I generally use simple_command https://github.com/nebulab/simple_command. This approach is also great because it allows you to use the same components from any context. For example, a command called GetLatestTweets.call() could be used equally well from a controller as it could from the REPL.
Both of these libraries provide you with a result object which you can then process as appropriate
def create
outcome = NewClientMutation.run(params.require(:resource).permit!)
if outcome.success?
render json: outcome.result, status: :created
else
render json: {resource: outcome.result, errors: outcome.errors}, status: :unprocessable_entity
end
end
In my particular case I use 'permit!' since the mutations library ignores any parameters that aren't explicitly specified which means that strong parameters aren't necessary if you use this library as it filters parameters as well.

Create basic RESTful API Server to a datastore

I have the most basic of questions, but the more I think about it, the more complex it gets.
I've been using rails and it follows the MVC paradigm in that db and api calls are abstracted through calls generated through the controller. This seems way too heavy for what I want.
1) I want a simple (basic) web server that sits in front of my datastore. (The contents of which happen to be stored in a directory structure that follows: /LOCATIONS/LOCATION/PRESENTERS/PRESENTER/YEAR/MN/)
2) I want to be able to host json files within that directory structure and GET them as needed.
3) I want to be able to PUT/POST append to those json files.
Seems like all I'd need is nginx with my datastore as a doc root and index.html files at critical places within the structure (e.g. site.com/Locations/index.html , site.com/locations/SF/presenters/solomon/index.html)?
How would I begin to solve this problem, (without the use of controllers of coarse)?
MVC Frameworks
without the use of controllers
You must be aware that there are many more frameworks than Rails out there, so when you ask about using a system to "sit in front of your datastore", you're really looking for different frameworks to handle requests, of which there are many.
The problem you have is how do you keep data consistency, whilst ensuring you can handle the relevant API requests (through JSON). The answer is to look at how the systems you're examining work.
I can only really vouch for Rails (it's the only framework I've got production apps for) -
--
Rails
Creating an API in Rails is so simple - I don't know why you'd think about doing anything else
Using the MVC principle might seem bloated to you, but the security, structure and extensibility it provides is unmatched.
Here is how to create an API in Rails:
#config/routes.rb
namespace :api do
resources :controller, only: [:update, :create] #-> only PUT / POST
end
#app/controllers/api/your_controller.rb
class API::YourController < ApplicationController
respond_to :json
def update
# handle PUT request
end
def create
# handle POST request
end
end
#app/models/model.rb
Class Model < ActiveRecord::Base
end
This is all you need - you'll be able to access domain.com/api/controller.json POST to create data, and domain.com/api/controller/4.json PUT to update it :)

Abstracting Database and Proper Controller Implementation

I'm using MongoDB as a backend for my project, but I don't specifically want a dependency on Mongo for the life of the project in case I later decide to change it or in case unit testing is easier without the database. As written, however, my controller is strictly dependent on MongoDB.
Mostly taken from the MongoDB tutorial, I have the following in a config/initializers/mongo.rb file.
MongoMapper.connection = Mongo::Connection.new('localhost')
MongoMapper.database = 'database'
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
MongoMapper.connection.connect if forked
end
end
In a controller for querying the US states, I have the following code:
class StateController < ApplicationController
def index
states = MongoMapper.connection.db('database').collection('state').find()
render :json => states
end
end
There are a few issues I see right off the bat (there are probably more):
As previously mentioned, the controller has a hard dependency on MongoDB.
I'm not using the database property of the MongoMapper class (it's hardcoded in the controller).
I don't necessarily want to go through an HTTP endpoint every single time I want a reference to a collection of states --though I'd like to keep this option available. For example, if a signup page has a drop down list for the user to select their home state, it seems foolish to require a client-side jQuery.get() to populate the list of states. It seems to me it would make more sense to have the states pre-fetched server-side and build the drop down list on the server.
For #3, I could certainly query the states in any action method that renders a view that requires the list of states and save them in #states (or something similar), but this would be a lot of duplicate code.
What would be the best way to architect this to allow for less coupling and more code reuse?
First, you should have a Model for states:
class State
include MongoMapper::Document
end
Then, in your controller, you should access via that:
class StatesController < ApplicationController
def index
render :json => State.all
end
end
This way, your controller has no idea what underlying datastore it's using.
Finally, to reduce the need to make a HTTP call, but assuming you're building this in javascript, you code:
<div id="#states" data-states="<%= #states.to_json %>"></div>
Then load that from $("#states").data("states")

Using different logic for the same respond_to format in Rails

My Rails application is currently using JSON as the respond_to format for AJAX forms on the site. I'm planning to create a public API for the application, and I'd like to use JSON for it also. How might I distinguish between an AJAX form and an API call in my controllers if the requested format for both is JSON?
I'd recommend you keep the public api in a separate controller, and have a different route since you say you have different logic. You can have namespaced controllers such as app/controllers/api/users_controller.rb and namespace your routes like:
namespace :admin do
resources :users
end
and leave your existing controllers with ajax actions used by your site as they are.

Resources