Module: Maturate
- Defined in:
- lib/maturate.rb
Overview
Add version behaviors to a controller that extends this module.
If you have an API-only rails 4 application, you’ll want to extend ApplicationController
with this module.
Example:
class ApplicationController < ActionController::Base
extend Maturate
self.api_versions = ['alpha', 'beta', 'gamma']
# by default the current_api_version would be the last in the list
# but maybe that is in pre-release status
self.current_api_version = 'beta'
end
Routing
Maturate requires the api_version parameter to be set. Putting the api_version in the route is a good interface for clients and makes it easy to manage your versions through the routes file.
Example:
scope '/api' do
scope '/:api_version' do
# ...
end
end
This method means keeping all endpoints from all your api versions defined in the scope. You can return 404 from your controllers based on api_version
to manage endpoints that are not in particular versions of the API.
class HumansController
def index
four_oh_four if api_version != 'alpha'
@resources = Human.all
end
private
def four_oh_four
render text: 'Page Not Found', status: 404
end
end
You could also be more explicit in your route file:
namespace '/api' do
namespace '/alpha', defaults: {api_version: 'alpha'} do
# ....
end
namespace '/gamma', defaults {api_version: 'gamma'} do
# ...
end
# The for the current api should still be parametrized,
# so +current+ and invalid versions will match.
namespace '/:api_version' do
# ...
end
end
You can also use lambdas to share routes, if that makes sense for you:
shared_routes = lambda do
resources :humans, only: [:show]
end
namespace '/alpha', defaults: {api_version: 'alpha'} do
shared_routes.call
# more routes just in alpha...
end
# if the namespace *only* has the common routes
namespace '/gamma', defaults: {api_version: 'gamma'}, &shared_routes
Views and Variants
It adds a before_action
that will set request.variant
to the current api version. This allows you to create different view files for different versions of your api, but allows you to use the same controller action to serve all versions.
For example, given API versions “1.0”, “1.1”, and “2.0” that all serve the same Human resource, you can have files “humans/index.json+1.0.jbuilder”, “humans/index.html+1.1.jbuilder”, and “humans/index.json.jbuilder”, where the first two revisions are explictily served their correct variant (as denoted by “+1.x” in the file name) and the 2.0 API is served by the less explicit view file with no variant.
Rails introduced variants to deal with different clients, eg render a tablet or phone optimized view for content. In the case of an API you don’t want to discriminate against types of user-agents, so the only variant you would reasonably introduce is based on versioning.
Controllers
If you have one controller that serves many versions of the API, you may want to add some includes
or other methods to your query scopes.
def show
@resoruces = Humans.all
if api_version == 'gamma'
@resources.includes(:compensation_packages)
end
end
If the methods diverge by a large amount:
def show
if api_version == 'gamma'
show_gamma
else
show_legacy
end
end
private
def show_legacy
end
def show_gamma
end
Or, if the available actions in a controller greatly diverge between versions you can stick a version’s controllers in their own namespace, a la Versionist.
# config/routes.rb
namespace '/alpha', defaults: {api_version: 'alpha'} do
resources :humans, controller: AlphaVersion::Humans
end
Defined Under Namespace
Modules: InstanceMethods Classes: InvalidVersion
Class Method Summary collapse
Instance Method Summary collapse
Class Method Details
.extended(kls) ⇒ Object
145 146 147 148 149 |
# File 'lib/maturate.rb', line 145 def self.extended kls kls.send :include, InstanceMethods setup_api_versioning kls setup_request_handling kls end |
Instance Method Details
#current_api_version=(str) ⇒ Object
151 152 153 154 155 156 157 158 |
# File 'lib/maturate.rb', line 151 def current_api_version= str unless api_versions.include?(str) msg = "#{str} is not a known version. Known: #{api_versions}. " + "Use `self.api_versions = [...]` to set known versions." raise InvalidVersion, msg end self._current_api_version = str end |
#skip_versioned_url_generation(opts = {}) ⇒ Object
160 161 162 |
# File 'lib/maturate.rb', line 160 def skip_versioned_url_generation opts={} prepend_before_action :skip_versioned_url_generation, opts end |