= API Documentation Tool {}[http://travis-ci.org/Pajk/apipie-rails]
== What?
Every API needs a documentation otherwise no one know how to use it.
It is not easy to maintain such documentation always up to date if it
develops over time. Additionaly to this most programmers don't like writing
documentation and so it could happen that your API is useless because
of outdated documentation. If the documentation is just text completely
separated from code then there is no simple way to verify its correctness.
This gem adds some new methods to Rails controllers that can be used
to describe resources which are exposed by our API. It is meant to
be used for {RESTful}[http://cs.wikipedia.org/wiki/Representational_State_Transfer]
web services and because we are talking about Rails you probably know
what it is all about.
Informations you entered with provided DSL are used to generate
online documentation and validate values of incoming requests parameters.
== Why?
Documentation will be closer to your code and thus less prone to become
obsolete. You get documentation frontend without effort. And if you want your
own design you can do it anytime because json API for your API documentation
is provided. There are no limits on how to use those informations, the
builtin web interface is only one of possible options.
Request parameters will be validated before they will get in touch with yours
controller action or model code. And you inform API users about expected
parameters values. This information will be always actual because otherwise
your tests fail (of course you have tests!).
This is how the web interface look like. Try it live on
http://restapi-likes.rhcloud.com. Example users resource
description are taken from Twitter(c) API.
https://img.skitch.com/20120428-nruk3e87xs2cu4ydsjujdh11fq.png
https://img.skitch.com/20120428-bni2cmq5cyhjuw1jkd78e3qjxn.png
== How?
=== Gemfile
Add +apipie+ gem to your gemfile and run bundle.
gem 'apipie-rails'
For bleeding edge version use GitHub version
gem 'apipie-rails', :git => 'git://github.com/Pajk/apipie-rails.git'
=== Config file
Create configuration file in e.g. */config/initializers/apipie.rb*.
You can set application name, footer text, API and documentation base URL
and turn off validations. You can also choose your favorite markup language
of full descriptions.
[app_name] Name of your application used in breadcrumbs navigation.
[copyright] Copyright information (shown in page footer).
[doc_base_url] Documentation frontend base url.
[api_base_url] Base url of your API, most probably /api.
[validate] Parameters validation is turned off when set to false.
[app_info] Application long description.
[reload_controllers] Set to enable/disable reloading controllers (and the documentation with it), by default enabled in development.
[api_controllers_matcher] For reloading to work properly you need to specify where your API controllers are.
[markup] You can choose markup language for descriptions of your application,
resources and methods. RDoc is the default but you can choose from
Apipie::Markup::Markdown.new or Apipie::Markup::Textile.new.
In order to use Markdown you need Redcarpet gem and for Textile you
need RedCloth. Add those to your gemfile and run bundle if you
want to use them. You can also add any other markup language
processor.
Example:
Apipie.configure do |config|
config.app_name = "Test app"
config.copyright = "© 2012 Pavel Pokorny"
config.doc_base_url = "/apidoc"
config.api_base_url = "/api"
config.validate = false
config.markup = Apipie::Markup::Markdown.new
config.reload_controllers = true
config.api_controllers_matcher = File.join(Rails.root, "app", "controllers", "**","*.rb")
config.app_info = <<-DOC
This is where you can inform user about your application and API
in general.
DOC
end
=== Routes
Add +apipie+ to your *routes.rb*, that's all.
=== Resource Description
Resource can be described in corresponding controller by calling +resource_description+ method with block. Parameters described here are used
for every method unless they are overridden with NilValidator.
Parameters are inherited in controller hierarchy. So for example you can define
in base ApplicationController parameters for authentication and they will
be checked in every request.
[name] You can force resource to display under some alias.
[short] One line short description.
[path] Relative URL path of this resource.
[version] Version of this resource API, use arbitrary string.
[formats] Resource level request / response formats.
[param] The very same parameter description as you will use method
description. Generally use this for parameters that apply
to every method in the controller (such as :user hash).
It is possible to hide it for individual methods by setting
it's validator to nil.
[description] The full multiline description of the resource and
yours API options
Example:
class UsersController < ApplicationController
resource_description do
name 'Members'
short 'Site members'
path '/users'
version '1.0 - 3.4.2012'
formats ['json', 'xml']
param :id, Fixnum, :desc => "User ID", :required => false
param :user, Hash, :desc => 'Param description for all methods' do
param :username, String, :required => true,
:desc => "Username for login"
param :password, String, :required => true,
:desc => "Password for login"
end
description <<-DOC
Full description of this resource.
DOC
end
#...
end
=== Method Description
Then describe methods available to your API.
[api] Say how is this method exposed and provide short description.
The first parameter is HTTP method (one of :GET/:POST/:PUT/:DELETE).
The second parameter is relative URL path which is mapped to this
method. The last parameter is methods short description.
You can use this +api+ method more than once for one method. It could
be useful when there are more routes mapped to it.
[param] Look at Parameter description section for details.
[formats] Method level request / response formats.
[error] Describe each possible error that can happend what calling this
method. HTTP response code and description can be provided.
[description] Full method description which will be converted to HTML by
choosen markup language processor.
[example] Provide example of server response, whole communication or response type.
It will be formatted as code.
[see] Provide reference to another method, this has to be string with
controller_name#method_name.
Example:
api :GET, "/users/:id", "Show user profile"
error :code => 401, :desc => "Unauthorized"
error :code => 404, :desc => "Not Found"
param :session, String, :desc => "user is logged in", :required => true
param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
param :array_param, [100, "one", "two", 1, 2], :desc => "array validator"
param :boolean_param, [true, false], :desc => "array validator with boolean"
param :proc_param, lambda { |val|
val == "param value" ? true : "The only good value is 'param value'."
}, :desc => "proc validator"
description "method description"
formats ['json', 'jsonp', 'xml']
example " 'user': {...} "
see "users#showme"
def show
#...
end
== Parameter Description
Use +param+ to describe every possible parameter. You can use Hash validator
in cooperation with block given to param method to describe nested parameters.
[name] The first argument is parameter name as a symbol.
[validator] Second parameter is parameter validator, choose one from section
Validators.
[desc] Parameter description.
[required] Set this true/false to make it required/optional.
Default is optional
Example:
param :user, Hash, :desc => "User info" do
param :username, String, :desc => "Username for login", :required => true
param :password, String, :desc => "Password for login", :required => true
param :membership, ["standard","premium"], :desc => "User membership"
end
def create
#...
end
== Validators
Every parameter needs to have associated validator. For now there are some
basic validators. You can always provide your own to reach complex
results.
If validations are enabled (default state) the parameters of every
request are validated. If the value is wrong a +ArgumentError+ exception
is raised and can be rescued and processed. It contains some description
of parameter value expectations. Validations can be turned off
in configuration file.
=== TypeValidator
Check the parameter type. Only String, Hash and Array are supported
for the sake of simplicity. Read more to to find out how to add
your own validator.
param :session, String, :desc => "user is logged in", :required => true
param :facts, Hash, :desc => "Additional optional facts about the user"
=== RegexpValidator
Check parameter value against given regular expression.
param :regexp_param, /^[0-9]* years/, :desc => "regexp param"
=== ArrayValidator
Check if parameter value is included given array.
param :array_param, [100, "one", "two", 1, 2], :desc => "array validator"
=== ProcValidator
If you need more complex validation and you know you won't reuse it you
can use Proc/lambda validator. Provide your own Proc taking value
of parameter as the only argument. Return true if value pass validation
or return some text about what is wrong. _Don't use the keyword *return*
if you provide instance of Proc (with lambda it is ok), just use the last
statement return property of ruby_.
param :proc_param, lambda { |val|
val == "param value" ? true : "The only good value is 'param value'."
}, :desc => "proc validator"
=== HashValidator
You can describe hash parameters in depth if you provide a block with
description of nested values.
param :user, Hash, :desc => "User info" do
param :username, String, :desc => "Username for login", :required => true
param :password, String, :desc => "Password for login", :required => true
param :membership, ["standard","premium"], :desc => "User membership"
end
=== NilValidator
In fact there is any NilValidator but setting it to nil can be used to
override parameters described on resource level.
Example:
param :user, nil
def destroy
#...
end
=== Adding custom validator
Only basic validators are included but it is really easy to add your own.
Create new initializer with subclass of Apipie::Validator::BaseValidator.
Two methods are required to implement - instance method
validate(value) and class method
build(param_description, argument, options, block).
When searching for validator +build+ method of every subclass of
Apipie::Validator::BaseValidator is called. The first one whitch return
constructed validator object is used.
Example: Adding IntegerValidator
We want to check if parameter value is an integer like this:
param :id, Integer, :desc => "Company ID"
So we create apipie_validators.rb initializer with this content:
class IntegerValidator < Apipie::Validator::BaseValidator
def initialize(param_description, argument)
super(param_description)
@type = argument
end
def validate(value)
return false if value.nil?
!!(value.to_s =~ /^[-+]?[0-9]+$/)
end
def self.build(param_description, argument, options, block)
if argument == Integer || argument == Fixnum
self.new(param_description, argument)
end
end
def error
"Parameter \#{@param_name} expecting to be \#{@type.name},
got: \#{@error_value.class.name}"
end
def description
"Parameter has to be #{@type}."
end
end
Parameters of the build method:
[param_description] Instance of Apipie::ParamDescription contains all
given informations about validated parameter.
[argument] Specified validator, in our example it is +Integer+
[options] Hash with specified options, for us just
{:desc => "Company ID"}
[block] Block converted into Proc, use it as you desire. In this example nil.
== Markup
The default markup language is {RDoc}[http://rdoc.rubyforge.org/RDoc/Markup.html]. It can be changed in
config file (config.markup=) to one of these:
[Markdown] Use Apipie::Markup::Markdown.new. You need Redcarpet gem.
[Textile] Use Apipie::Markup::Textile.new. You need RedCloth gem.
Or provide you own object with to_html(text) method.
For inspiration this is how Textile look like:
class Textile
def initialize
require 'RedCloth'
end
def to_html(text)
RedCloth.new(text).to_html
end
end
== Static page
To generate static version of documentation run rake apipie:static task.
It will create folder with one-page documentation in your public directory. Rename
or delete it if you want to use 'normal' dynamic version again.
== {CLI client}[https://github.com/Pajk/apipie-rails/wiki/CLI-client]
== {Extractor}[https://github.com/Pajk/apipie-rails/wiki/Extractor]
== Future features
* Dynamic CLI client (read info from documentation API)
* Add documentation json API description to readme
* Possibility to create requests from web documentation and check responses