I have an application in Ruby on Rails with mvc framework. As of now, I have API calls in the controller but don\'t think this is the right place for them. What kind of fil
By my coding style (and understanding of MVC), external calls would be placed in a "tableless" model. RailsCasts 193 talks a bit about this concept, and a less clunky syntax is supported in Rails 4. If you need to have any manipulation of the code, the model seems like the logical place to place these. Moving those methods into the controller would work, but could create problems as your app grows.
Another consideration with external API calls is actually storing those in a database, which would should definitely be in a model at that point, so (to me) it becomes clearer that these really should be in the model.
Very late to this one, but thought I'd add my 2p/2c.
I like to try to keep my controllers clean of anything apart from controller code, which I loosely define as program flow code based on request type and parameters. For example, choosing the correct template for the request type or choosing the correct method to call based on whether a user is logged in or not.
When it comes to calculating the responses, I don't like to litter the controller with lots of code that manipulates models and sets instance parameters. This is hard to test and even harder to re-use. I prefer to defer to another object and return a single value object to the template.
Sometimes I can defer to a model: maybe it's a simple look-up and I'm just sending a single model to the template, or an array of models.
Maybe I've implemented a useful method in a model to return an appropriate value or value object.
However sometimes I'm doing something that doesn't use a model, or that uses a several models, or that doesn't feel like it should actually be cluttering up the model. In this case, neither the controller nor a model is an appropriate place for the code.
The lib directory doesn't feel right either. I tend to treat the lib directory as somewhere that contains code that I haven't been bothered to turn into gems yet. If the code I'm writing only makes sense in the context of the application, it doesn't sit well.
So I turn to service objects. Under the 'app' folder I have a 'services' folder, which contains small, functional classes that encapsulate single chunks of site behaviour. (Or sometimes, coordinate several other services to provide a simple interface for the controller.)
This allows me to slim down my controllers AND my models, and makes a perfect place to put code that needs to contact an API.
If you wanted to go one step further you could wrap the API itself in a wrapper class (or set of classes) and keep those in the lib directory (for conversion to a gem at a later date perhaps). Then the service object would perform the task of calling the API wrapper with the appropriate values (passed from the controller) and responding with something that a template can interrogate cleanly.
Of course, you can go further than this and add more layers. A presentation layer, for example, could sit between the service object (providing generic values) and format data for a specific view. (Maybe you want to provide both a web page and an RSS feed and they need different date formats for example.)
But you get the idea.
API calls to external services (3rd party) are not specific to your app, as their service is available to everyone (in theory). It is my understanding that these sorts of features go in the lib/
directory because they are not app specific. Ideally you could then pull out the code from your lib
in your project, and drop it into someone else's lib/
in another project and it would still work just fine.
Put the call in the lib/
. If you want, you can create the a model from the returned data in your controller.
It would look something like this:
app/controller/
class YourController < ApplicationController
def getDetails
# keep in mind, api call may fail so you may want surround this with a begin/rescue
api_response = YourApiCall.new.get_details(params[:id])
# perhaps create a model
@model = SomeModel.new(fname: api_response[:first_name], lname: api_response[:last_name])
# etc...
end
end
lib/
require 'HTTParty'
Class YourApiCall
def get_details(id)
HTTParty.get(base_uri, :query => {:DID => id, :DeveloperKey => devKey})
@json_hash = api_response.parsed_response
return @json_hash
end
end