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 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
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require "rack/test"
3
+ require "rspec"
4
+ require "webrat"
5
+ require "webrat/core/matchers"
6
+
7
+ $:.unshift "lib"
8
+
9
+ Rspec.configure do |config|
10
+ config.color_enabled = true
11
+ end
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