Created
April 29, 2011 11:54
-
-
Save technoweenie/948206 to your computer and use it in GitHub Desktop.
discarded sinatra documentation extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Sinatra module for documenting an API. This info can be exported as JSON | |
# and used to generate other forms of documentation. | |
# | |
# Example documentation for a GET request: | |
# | |
# desc "List issues for this Repository" | |
# param :milestone, Fixnum | |
# param :state, String, :default => 'open', :choices => %w(open closed) | |
# get "/repos/:user/:repo/issues" do | |
# ... | |
# end | |
# | |
# Example documentation for a POST: | |
# | |
# desc "Create an Issue" | |
# input_type Hash | |
# input :title, String | |
# input :body, String | |
# input :assignee, String | |
# input :milestone, Fixnum | |
# | |
# post "/repos/:user/:repo/issues" do | |
# ... | |
# end | |
# | |
module Api::Documentation | |
# Dumps the given Rack app's documentation as JSON to the specified | |
# directory. | |
# | |
# app - A Rack app that responds to #documented_requests. | |
# dir - A String directory name for the file. If the app has a module | |
# namespace, they are added to the dir. | |
# | |
# # saves /docs/foo/bar/app.json | |
# dump(Foo::Bar::App, "/docs") | |
# | |
# Returns nothing. | |
def self.dump(app, dir) | |
underscored = app.name.underscore | |
full_dir = File.join(dir, File.dirname(underscored)) | |
FileUtils.mkdir_p(full_dir) | |
File.open(File.join(dir, underscored+".json"), 'w') do |io| | |
io << GitHub::JSON.encode(app.documented_requests.map { |r| r.to_hash }) | |
end | |
end | |
# This is the Sinatra extension that lets you annotate requests. To use it, just | |
# register `Api::Documentation::Methods` in your Sinatra apps. | |
# | |
# See also: http://www.sinatrarb.com/extensions-wild.html | |
module Methods | |
# Public: This description is used as the title for a Request section. | |
# | |
# msg - The String description. | |
# | |
# Returns nothing. | |
def desc(msg) | |
current_docs.desc = msg | |
end | |
# Public: This describes an available GET param for the current request. | |
# | |
# name - The String name of the GET param. | |
# type - The String type: String, Integer, etc. | |
# options - Optional Hash: | |
# :default - String default value. | |
# :choices - Array of possible values. | |
# :desc - String of more information about the param. | |
# | |
# Returns nothing. | |
def param(*args) | |
current_docs.params << Api::Documentation::Param.new(*args) | |
end | |
# Public: This describes the type of object expected in the body of a POST | |
# or PUT request. | |
# | |
# type - The String type. | |
# | |
# Returns nothing. | |
def input_type(type) | |
current_docs.input.type = type.to_s | |
end | |
# Public: This describes an attribute of an expected object in the body of | |
# a POST or PUT request. | |
# | |
# key - The String field name. | |
# type - The String type: String, Integer, Time, Boolean, etc. | |
# options - Optional Hash: | |
# :desc - String of more information about the param. | |
# | |
# Returns nothing. | |
def input(key, type, options = {}) | |
current_docs.input.field(key, type.to_s, options) | |
end | |
def current_docs | |
@current_docs ||= Api::Documentation::Request.new | |
end | |
# Public: Gets all of the current documented requests. | |
# | |
# Returns an Array of Request instances. | |
def documented_requests | |
@documented_requests ||= [] | |
end | |
# This applies the previous set of docs against the given request. | |
# | |
# verb - The String verb of the request: GET/PUT/POST/DELETE | |
# path - The String route of the request. | |
# | |
# Returns nothing. | |
def reset_docs(verb, path) | |
if doc = @current_docs | |
doc.verb = verb | |
doc.path = path | |
documented_requests << doc | |
end | |
@current_docs = nil | |
end | |
# This is the best way I could see to hook into Sinatra. | |
def route(verb, path, options={}, &block) | |
reset_docs(verb, path) | |
super(verb, path, options, &block) | |
end | |
end | |
# This represents the docs of a single request. | |
class Request | |
attr_accessor :verb | |
attr_accessor :original_path | |
attr_accessor :desc | |
attr_reader :input | |
attr_reader :params | |
attr_reader :path | |
def initialize | |
@params = [] | |
@input = Input.new | |
end | |
def path=(s) | |
@original_path = s | |
@path = s.sub(/\*$/, ':id.json') | |
end | |
# Exports a Hash for serialization to JSON. | |
# | |
# Returns a Hash. | |
def to_hash | |
hash = {:params => @params.map { |p| p.to_hash }} | |
hash[:input] = @input.to_hash if @input.required? | |
[:verb, :path, :desc].inject(hash) do |memo, key| | |
memo.update key => instance_variable_get("@#{key}") | |
end | |
end | |
end | |
# This represents the accepted input of a single request. | |
class Input | |
attr_accessor :type | |
attr_reader :fields | |
# Determines if this request is expecting any input. | |
# | |
# Return true if the request needs input, or false. | |
def required? | |
@fields.present? | |
end | |
# Define a field of the expected object. | |
# | |
# key - The String field name. | |
# type - The String type: String, Integer, Time, Boolean, etc. | |
# options - Optional Hash: | |
# :desc - String of more information about the param. | |
# | |
# Returns nothing. | |
def field(key, type, options) | |
@fields << [key, type, options] | |
end | |
def initialize | |
@fields = [] | |
end | |
# Exports a Hash for serialization to JSON. | |
# | |
# Returns a Hash. | |
def to_hash | |
{:type => @type, | |
:fields => @fields.inject([]) { |memo, (key, type)| | |
memo << {:key => key, :type => type} | |
} | |
} | |
end | |
end | |
# This represents a single accepted GET parameter for a request. | |
class Param | |
attr_reader :name | |
attr_reader :type | |
attr_reader :default | |
attr_reader :choices | |
# Initializes the instance. | |
# | |
# name - The String name of the GET param. | |
# type - The String type: String, Integer, etc. | |
# options - Optional Hash: | |
# :default - String default value. | |
# :choices - Array of possible values. | |
# | |
# Returns nothing. | |
def initialize(name, type, options = {}) | |
@name = name | |
@type = type.to_s | |
@default = options[:default] | |
@choices = options[:choices] | |
end | |
# Exports a Hash for serialization to JSON. | |
# | |
# Returns a Hash. | |
def to_hash | |
[:name, :type, :default, :choices].inject({}) do |memo, key| | |
memo.update key => instance_variable_get("@#{key}") | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment