sinatra-croon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +43 -0
- data/Rakefile +58 -0
- data/lib/sinatra/croon/views/docs.haml +41 -0
- data/lib/sinatra/croon/views/docs.sass +106 -0
- data/lib/sinatra/croon.rb +108 -0
- data/spec/sinatra/croon_spec.rb +84 -0
- data/spec/spec_helper.rb +11 -0
- metadata +170 -0
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Sinatra::Croon
|
2
|
+
|
3
|
+
Allows you to add inline documentation to a Sinatra app and have a web-based
|
4
|
+
documentation browser be available to you.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
require "sinatra/croon"
|
9
|
+
|
10
|
+
class MyApp < Sinatra::Base
|
11
|
+
register Sinatra::Croon
|
12
|
+
end
|
13
|
+
|
14
|
+
## Documentation Format
|
15
|
+
|
16
|
+
# Create an application.
|
17
|
+
#
|
18
|
+
# @param <name> the name of the application to create
|
19
|
+
# @param [stack] the stack on which to create the application
|
20
|
+
#
|
21
|
+
# @request
|
22
|
+
# POST /apps.json
|
23
|
+
# name=example&stack=bamboo-ree-1.8.7
|
24
|
+
# @response
|
25
|
+
# {
|
26
|
+
# "id": 1,
|
27
|
+
# "name": "example",
|
28
|
+
# "owner": "user@example.org",
|
29
|
+
# "created_at": "Sat Jan 01 00:00:00 UTC 2000",
|
30
|
+
# "stack": "bamboo-ree-1.8.7",
|
31
|
+
# "slug_size": 1000000,
|
32
|
+
# "repo_size": 500000,
|
33
|
+
# "dynos": 1,
|
34
|
+
# "workers": 0
|
35
|
+
# }
|
36
|
+
|
37
|
+
post "/apps.json" do
|
38
|
+
# do something here
|
39
|
+
end
|
40
|
+
|
41
|
+
## Web-based browser
|
42
|
+
|
43
|
+
Sinatra::Croon will create a route at `/docs` to display your documentation.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake"
|
3
|
+
require "rspec"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
7
|
+
require "sinatra/croon"
|
8
|
+
|
9
|
+
task :default => :spec
|
10
|
+
|
11
|
+
desc "Run all specs"
|
12
|
+
Rspec::Core::RakeTask.new(:spec) do |t|
|
13
|
+
t.pattern = 'spec/**/*_spec.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Generate RCov code coverage report"
|
17
|
+
task :rcov => "rcov:build" do
|
18
|
+
%x{ open coverage/index.html }
|
19
|
+
end
|
20
|
+
|
21
|
+
Rspec::Core::RakeTask.new("rcov:build") do |t|
|
22
|
+
t.pattern = 'spec/**/*_spec.rb'
|
23
|
+
t.rcov = true
|
24
|
+
t.rcov_opts = [ "--exclude", Gem.default_dir, "--exclude", "spec" ]
|
25
|
+
end
|
26
|
+
|
27
|
+
######################################################
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'jeweler'
|
31
|
+
Jeweler::Tasks.new do |s|
|
32
|
+
s.name = "sinatra-croon"
|
33
|
+
s.version = Sinatra::Croon::VERSION
|
34
|
+
|
35
|
+
s.summary = "Create documentation for an API built in Sinatra."
|
36
|
+
s.description = s.summary
|
37
|
+
s.author = "David Dollar"
|
38
|
+
s.email = "ddollar@gmail.com"
|
39
|
+
s.homepage = "http://daviddollar.org/"
|
40
|
+
|
41
|
+
s.platform = Gem::Platform::RUBY
|
42
|
+
s.has_rdoc = false
|
43
|
+
|
44
|
+
s.files = %w(Rakefile README.markdown) + Dir["{lib,spec}/**/*"]
|
45
|
+
s.require_path = "lib"
|
46
|
+
|
47
|
+
s.add_development_dependency 'rack-test', '~> 0.5.4'
|
48
|
+
s.add_development_dependency 'rake', '~> 0.8.7'
|
49
|
+
s.add_development_dependency 'rspec', '~> 2.0.0.beta.5'
|
50
|
+
s.add_development_dependency 'webrat', '~> 0.7.1'
|
51
|
+
|
52
|
+
s.add_dependency 'haml', '~> 3.0.12'
|
53
|
+
s.add_dependency 'sinatra', '~> 1.0'
|
54
|
+
end
|
55
|
+
Jeweler::GemcutterTasks.new
|
56
|
+
rescue LoadError
|
57
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
58
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
!!! 5
|
2
|
+
|
3
|
+
%html
|
4
|
+
|
5
|
+
%head
|
6
|
+
%title API Documentation
|
7
|
+
%link{ :rel => "stylesheet", :href => "/docs.css" }
|
8
|
+
|
9
|
+
%body
|
10
|
+
|
11
|
+
#content
|
12
|
+
|
13
|
+
%h1
|
14
|
+
%a{ :href => '/' } API Documentation
|
15
|
+
|
16
|
+
- docs.each do |doc|
|
17
|
+
|
18
|
+
.api
|
19
|
+
|
20
|
+
.url
|
21
|
+
%a
|
22
|
+
= doc[:verb].upcase
|
23
|
+
= doc[:uri]
|
24
|
+
|
25
|
+
.description
|
26
|
+
= doc[:description]
|
27
|
+
|
28
|
+
- if (doc[:params] || []).length > 0
|
29
|
+
.params
|
30
|
+
- doc[:params].each do |param|
|
31
|
+
.param
|
32
|
+
.type{ :class => (param[:required] && "required") }
|
33
|
+
= (param[:required]) ? "REQUIRED" : "OPTIONAL"
|
34
|
+
.name= escape_html(param[:name])
|
35
|
+
.description= param[:description]
|
36
|
+
|
37
|
+
%code.request
|
38
|
+
= display_code doc[:request]
|
39
|
+
|
40
|
+
%code.response
|
41
|
+
= display_code doc[:response]
|
@@ -0,0 +1,106 @@
|
|
1
|
+
*
|
2
|
+
:font-family Helvetica, Arial, sans-serif
|
3
|
+
|
4
|
+
=code_title
|
5
|
+
:display block
|
6
|
+
:padding 6px 10px
|
7
|
+
:font-weight bold
|
8
|
+
:background #ccc
|
9
|
+
:color #333
|
10
|
+
:margin-left -12px
|
11
|
+
:margin-right -12px
|
12
|
+
:margin-top -12px
|
13
|
+
:margin-bottom 12px
|
14
|
+
|
15
|
+
h2
|
16
|
+
:background #555
|
17
|
+
:color #fff
|
18
|
+
:border 1px #666 solid
|
19
|
+
:padding 10px
|
20
|
+
a
|
21
|
+
:color #fff
|
22
|
+
:text-decoration none
|
23
|
+
|
24
|
+
.api
|
25
|
+
:margin-bottom 24px
|
26
|
+
:background #eee
|
27
|
+
:border 1px #ccc solid
|
28
|
+
|
29
|
+
.url
|
30
|
+
:padding 6px 10px
|
31
|
+
:background #aaa
|
32
|
+
:border-bottom 1px #ccc solid
|
33
|
+
a
|
34
|
+
:color #333
|
35
|
+
:font-size 1.6em
|
36
|
+
:font-weight bold
|
37
|
+
:font-family monospace
|
38
|
+
|
39
|
+
.description
|
40
|
+
:padding 12px
|
41
|
+
|
42
|
+
code
|
43
|
+
:font-family Monaco, monospace
|
44
|
+
:white-space pre
|
45
|
+
:padding 12px
|
46
|
+
:display block
|
47
|
+
|
48
|
+
.params
|
49
|
+
:margin-bottom 10px
|
50
|
+
:padding 12px
|
51
|
+
:display block
|
52
|
+
&:before
|
53
|
+
+code_title
|
54
|
+
:content "Parameters"
|
55
|
+
.param
|
56
|
+
:line-height 30px
|
57
|
+
.name
|
58
|
+
:display inline
|
59
|
+
:font-weight bold
|
60
|
+
:font-family monospace
|
61
|
+
:font-size 1.2em
|
62
|
+
.type
|
63
|
+
:font-weight bold
|
64
|
+
:font-size 0.6em
|
65
|
+
:color #999
|
66
|
+
:display inline-block
|
67
|
+
:width 65px
|
68
|
+
.required
|
69
|
+
:color #333
|
70
|
+
.description
|
71
|
+
:display inline
|
72
|
+
|
73
|
+
.request
|
74
|
+
&:before
|
75
|
+
+code_title
|
76
|
+
:content "Request"
|
77
|
+
|
78
|
+
.response
|
79
|
+
&:before
|
80
|
+
+code_title
|
81
|
+
:content "Response"
|
82
|
+
|
83
|
+
body
|
84
|
+
:margin 0
|
85
|
+
|
86
|
+
#content
|
87
|
+
:width 800px
|
88
|
+
:margin-left auto
|
89
|
+
:margin-right auto
|
90
|
+
|
91
|
+
#index
|
92
|
+
.section
|
93
|
+
:margin-bottom 20px
|
94
|
+
.summarydoc
|
95
|
+
:border 1px #ccc solid
|
96
|
+
:padding 12px
|
97
|
+
:margin-top 10px
|
98
|
+
:background #eee
|
99
|
+
.path
|
100
|
+
:margin-bottom 10px
|
101
|
+
a
|
102
|
+
:font-family monospace
|
103
|
+
:font-weight bold
|
104
|
+
:font-size 1.4em
|
105
|
+
.description
|
106
|
+
:color #666
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "haml"
|
2
|
+
require "sinatra/base"
|
3
|
+
|
4
|
+
module Sinatra
|
5
|
+
module Croon
|
6
|
+
VERSION = "0.0.1"
|
7
|
+
|
8
|
+
def self.registered(app)
|
9
|
+
app.helpers Croon::Helpers
|
10
|
+
@app = app
|
11
|
+
|
12
|
+
app.get '/docs.css' do
|
13
|
+
pass do
|
14
|
+
content_type "text/css"
|
15
|
+
template = File.read(File.expand_path("../croon/views/docs.sass", __FILE__))
|
16
|
+
sass template
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
app.get '/docs' do
|
21
|
+
pass do
|
22
|
+
template = File.read(File.expand_path("../croon/views/docs.haml", __FILE__))
|
23
|
+
haml template, :locals => { :docs => documentation }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.route_added(verb, path, block)
|
29
|
+
return if verb.to_s == "HEAD"
|
30
|
+
|
31
|
+
route_location = caller(1).reject { |line| line =~ /sinatra\/base/ }.first
|
32
|
+
file, line = route_location.split(':')
|
33
|
+
doc = Croon::Parser.parse_route_documentation(file, line.to_i)
|
34
|
+
|
35
|
+
if doc[:description]
|
36
|
+
doc[:verb] = verb
|
37
|
+
doc[:uri] = path
|
38
|
+
@app.documentation << doc
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def documentation
|
43
|
+
@documentation ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
module Helpers
|
47
|
+
def display_code(code)
|
48
|
+
Haml::Filters::Preserve.render(Haml::Filters::Escaped.render(code))
|
49
|
+
end
|
50
|
+
|
51
|
+
def documentation
|
52
|
+
self.class.documentation
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Parser
|
57
|
+
def self.parse_route_documentation(filename, line)
|
58
|
+
all_lines = File.read(filename).split("\n").reverse
|
59
|
+
index_start = all_lines.length - line + 1
|
60
|
+
num_lines = all_lines[index_start..-1].index { |l| !['', '#'].include?(l.strip[0..0]) }
|
61
|
+
doc_lines = all_lines[index_start, num_lines].reverse
|
62
|
+
|
63
|
+
parse_comments(doc_lines)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.parse_comments(comments)
|
67
|
+
key = :description
|
68
|
+
|
69
|
+
comments.inject({}) do |parsed, comment|
|
70
|
+
case comment.strip
|
71
|
+
when /\A#\s*@param\s+(.+?)\s+(.+)/ then
|
72
|
+
parsed[:params] ||= []
|
73
|
+
required = $1[0..0] == '<'
|
74
|
+
name = $1[1..-2]
|
75
|
+
parsed[:params] << { :name => name, :description => $2, :required => required }
|
76
|
+
when /\A#\s*@(\w+)\s*(.*)\Z/ then
|
77
|
+
key = $1.to_sym
|
78
|
+
parsed[key] ||= []
|
79
|
+
parsed[key] << $2 if $2
|
80
|
+
when /\A#(.+)\Z/ then
|
81
|
+
parsed[key] ||= []
|
82
|
+
parsed[key] << $1
|
83
|
+
end
|
84
|
+
parsed
|
85
|
+
end.inject({}) do |flattened, (k, v)|
|
86
|
+
case v.first
|
87
|
+
when String then
|
88
|
+
flattened[k] = strip_left(v.reject { |l| l.strip == "" }.join("\n"))
|
89
|
+
else
|
90
|
+
flattened[k] = v
|
91
|
+
end
|
92
|
+
flattened
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.strip_left(code)
|
97
|
+
first_line = code.split("\n").first
|
98
|
+
return code unless first_line
|
99
|
+
num_spaces = first_line.match(/\A */)[0].length
|
100
|
+
code.split("\n").map do |line|
|
101
|
+
line[num_spaces..-1]
|
102
|
+
end.join("\n")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
register Croon
|
108
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "sinatra/base"
|
3
|
+
require "sinatra/croon"
|
4
|
+
|
5
|
+
class MockSinatraApp < Sinatra::Base
|
6
|
+
register Sinatra::Croon
|
7
|
+
|
8
|
+
# Show an application.
|
9
|
+
#
|
10
|
+
# @request GET /apps.json
|
11
|
+
# @response { "status" : "ok" }
|
12
|
+
|
13
|
+
get "/apps.json" do
|
14
|
+
# do something here
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create an application.
|
18
|
+
#
|
19
|
+
# @param <name> the name of the application to create
|
20
|
+
# @param [stack] the stack on which to create the application
|
21
|
+
#
|
22
|
+
# @request
|
23
|
+
# POST /apps.json
|
24
|
+
# name=example&stack=bamboo-ree-1.8.7
|
25
|
+
#
|
26
|
+
# @response
|
27
|
+
# {
|
28
|
+
# "id": 1,
|
29
|
+
# "name": "example",
|
30
|
+
# "owner": "user@example.org",
|
31
|
+
# "created_at": "Sat Jan 01 00:00:00 UTC 2000",
|
32
|
+
# "stack": "bamboo-ree-1.8.7",
|
33
|
+
# "slug_size": 1000000,
|
34
|
+
# "repo_size": 500000,
|
35
|
+
# "dynos": 1,
|
36
|
+
# "workers": 0
|
37
|
+
# }
|
38
|
+
|
39
|
+
post "/apps.json" do
|
40
|
+
# do something here
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Sinatra::Croon do
|
45
|
+
include Rack::Test::Methods
|
46
|
+
include Webrat::Matchers
|
47
|
+
|
48
|
+
let(:app) { MockSinatraApp.new }
|
49
|
+
|
50
|
+
it "can set a description" do
|
51
|
+
get "/docs"
|
52
|
+
last_response.should have_selector(".description", :content => "Show an application.")
|
53
|
+
last_response.should have_selector(".description", :content => "Create an application.")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can set params" do
|
57
|
+
get "/docs"
|
58
|
+
last_response.should have_selector(".param") do |param|
|
59
|
+
param.should have_selector(".type.required")
|
60
|
+
param.should have_selector(".name", :content => "name")
|
61
|
+
param.should have_selector(".description", :content => "the name of the application to create")
|
62
|
+
end
|
63
|
+
last_response.should have_selector(".param") do |param|
|
64
|
+
param.should have_selector(".name", :content => "stack")
|
65
|
+
param.should have_selector(".description", :content => "the stack on which to create the application")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can set a request" do
|
70
|
+
get "/docs"
|
71
|
+
last_response.should have_selector(".request", :content => "GET /apps.json")
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can set a response" do
|
75
|
+
get "/docs"
|
76
|
+
last_response.should have_selector(".response", :content => '{ "status" : "ok" }')
|
77
|
+
end
|
78
|
+
|
79
|
+
it "has a default style" do
|
80
|
+
get "/docs.css"
|
81
|
+
last_response.should =~ /\.response/
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-croon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- David Dollar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-21 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack-test
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 5
|
33
|
+
- 4
|
34
|
+
version: 0.5.4
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rake
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 49
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 8
|
49
|
+
- 7
|
50
|
+
version: 0.8.7
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 62196457
|
62
|
+
segments:
|
63
|
+
- 2
|
64
|
+
- 0
|
65
|
+
- 0
|
66
|
+
- beta
|
67
|
+
- 5
|
68
|
+
version: 2.0.0.beta.5
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id003
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: webrat
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 1
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
- 7
|
83
|
+
- 1
|
84
|
+
version: 0.7.1
|
85
|
+
type: :development
|
86
|
+
version_requirements: *id004
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: haml
|
89
|
+
prerelease: false
|
90
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ~>
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 31
|
96
|
+
segments:
|
97
|
+
- 3
|
98
|
+
- 0
|
99
|
+
- 12
|
100
|
+
version: 3.0.12
|
101
|
+
type: :runtime
|
102
|
+
version_requirements: *id005
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: sinatra
|
105
|
+
prerelease: false
|
106
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ~>
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 15
|
112
|
+
segments:
|
113
|
+
- 1
|
114
|
+
- 0
|
115
|
+
version: "1.0"
|
116
|
+
type: :runtime
|
117
|
+
version_requirements: *id006
|
118
|
+
description: Create documentation for an API built in Sinatra.
|
119
|
+
email: ddollar@gmail.com
|
120
|
+
executables: []
|
121
|
+
|
122
|
+
extensions: []
|
123
|
+
|
124
|
+
extra_rdoc_files:
|
125
|
+
- README.md
|
126
|
+
files:
|
127
|
+
- Rakefile
|
128
|
+
- lib/sinatra/croon.rb
|
129
|
+
- lib/sinatra/croon/views/docs.haml
|
130
|
+
- lib/sinatra/croon/views/docs.sass
|
131
|
+
- spec/sinatra/croon_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- README.md
|
134
|
+
has_rdoc: true
|
135
|
+
homepage: http://daviddollar.org/
|
136
|
+
licenses: []
|
137
|
+
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options:
|
140
|
+
- --charset=UTF-8
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
hash: 3
|
149
|
+
segments:
|
150
|
+
- 0
|
151
|
+
version: "0"
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
hash: 3
|
158
|
+
segments:
|
159
|
+
- 0
|
160
|
+
version: "0"
|
161
|
+
requirements: []
|
162
|
+
|
163
|
+
rubyforge_project:
|
164
|
+
rubygems_version: 1.3.7
|
165
|
+
signing_key:
|
166
|
+
specification_version: 3
|
167
|
+
summary: Create documentation for an API built in Sinatra.
|
168
|
+
test_files:
|
169
|
+
- spec/sinatra/croon_spec.rb
|
170
|
+
- spec/spec_helper.rb
|