useless-doc 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,6 +33,7 @@ module Useless
33
33
  use ::Rack::CommonLogger
34
34
  use Useless::Doc::Rack::UI
35
35
  use Useless::Doc::Rack::Stylesheet
36
+ use Useless::Doc::Rack::Retriever
36
37
  use Useless::Doc::Rack::Transform
37
38
 
38
39
  run Useless::Doc::Rack::Proxy.new(subdomains)
@@ -33,12 +33,16 @@ module Useless
33
33
  end
34
34
 
35
35
  def call(env)
36
+ unless env['useless.doc.retriever']
37
+ raise 'No retriever specified.'
38
+ end
39
+
36
40
  request = ::Rack::Request.new(env)
37
41
 
38
42
  if valid_subdomain?(request.url)
39
43
  url = Proxy.transform_url(request.url)
40
44
 
41
- if json = retrieve_resource(url)
45
+ if json = env['useless.doc.retriever'].retrieve(url)
42
46
  [200, {'Content-Type' => 'application/json'}, [json]]
43
47
  else
44
48
  [404, {'Content-Type' => 'text/plain'}, ['Documentation JSON is missing.']]
@@ -50,25 +54,9 @@ module Useless
50
54
 
51
55
  private
52
56
 
53
- def retrieve_resource(url)
54
- response = Typhoeus.options url, headers: { 'Accept' => 'application/json' }
55
- response.response_body
56
- end
57
-
58
57
  def valid_subdomain?(url)
59
58
  @subdomains.include?(url[/((?:\w|\-)+)\.doc\./, 1])
60
59
  end
61
-
62
- # +Proxy::Stub+ retrieves JSON from the spec/documents directory for
63
- # easy UI testing.
64
- #
65
- class Stub < Proxy
66
- def retrieve_resource(url)
67
- uri = URI(url)
68
- path = File.dirname(__FILE__) + "/../../../../spec/documents#{uri.path}.json"
69
- File.read(path)
70
- end
71
- end
72
60
  end
73
61
  end
74
62
  end
@@ -0,0 +1,42 @@
1
+ module Useless
2
+ module Doc
3
+ module Rack
4
+
5
+ # +Doc::Rack::Retriever+ sets an appropriate retriever for the current
6
+ # RACK_ENV. In production it uses the +Standard+ retriever which makes
7
+ # real HTTP calls. Elsewhere, it uses +Stub+ which servers corresponding
8
+ # files from the spec/documents directory.
9
+ #
10
+ class Retriever
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ if ENV['RACK_ENV'] == 'production'
17
+ env['useless.doc.retriever'] = Standard
18
+ else
19
+ env['useless.doc.retriever'] = Stub
20
+ end
21
+
22
+ @app.call(env)
23
+ end
24
+
25
+ module Standard
26
+ def self.retrieve(url)
27
+ response = Typhoeus.options url, headers: { 'Accept' => 'application/json' }
28
+ response.response_body
29
+ end
30
+ end
31
+
32
+ class Stub
33
+ def self.retrieve(url)
34
+ uri = URI(url)
35
+ path = File.dirname(__FILE__) + "/../../../../spec/documents#{uri.path}.json"
36
+ File.read(path)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,6 +12,10 @@ module Useless
12
12
  end
13
13
 
14
14
  def call(env)
15
+ unless env['useless.doc.ui']
16
+ raise 'No UI specified.'
17
+ end
18
+
15
19
  if env["PATH_INFO"].to_s == '/doc.css'
16
20
  [200, {'Content-Type' => 'text/css'}, [env['useless.doc.ui'].css]]
17
21
  else
@@ -16,12 +16,17 @@ module Useless
16
16
  end
17
17
 
18
18
  def call(env)
19
+ unless env['useless.doc.ui']
20
+ raise 'No UI specified.'
21
+ end
22
+
19
23
  response = @app.call(env)
20
24
 
21
- if response[0] == 200 and response[1]['Content-Type'] == 'application/json'
25
+ if response[0] == 200
22
26
  begin
23
27
  resource = Serialization::Load.resource(response[2].first)
24
- [200, {'Content-Type' => 'text/html'}, [env['useless.doc.ui'].html(resource)]]
28
+ html = env['useless.doc.ui'].html(resource)
29
+ [200, {'Content-Type' => 'text/html'}, [html]]
25
30
  rescue Oj::ParseError
26
31
  [502, {'Content-Type' => 'text/plain'}, ['Documentation JSON is malformed.']]
27
32
  end
@@ -1,5 +1,5 @@
1
1
  module Useless
2
2
  module Doc
3
- VERSION = '0.1.1'
3
+ VERSION = '0.1.2'
4
4
  end
5
5
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../lib/useless/doc'
1
+ ENV['RACK_ENV'] == 'test'
2
2
 
3
3
  RSpec.configure do |config|
4
4
  config.order = :rand
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'useless/doc/rack/application'
5
+ require 'useless/doc/rack/retriever'
6
+
7
+ describe Useless::Doc::Rack::Application do
8
+ include Rack::Test::Methods
9
+
10
+ def app
11
+ @app ||= Useless::Doc::Rack::Application.new('some-api')
12
+ end
13
+
14
+ it 'should return a 404 if the JSON cannot be retrieved' do
15
+ Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
16
+ with('http://some-api.useless.io/some/non-existant/resource').
17
+ and_return(nil)
18
+
19
+ get 'http://some-api.doc.useless.io/some/non-existant/resource'
20
+ last_response.should be_not_found
21
+ end
22
+
23
+ it 'should return a 404 if the subdomain is unknown' do
24
+ get 'http://some-other-api.doc.useless.io/some/non-existant/resource'
25
+ last_response.should be_not_found
26
+ end
27
+
28
+ it 'should return HTML if proper JSON is retrieved' do
29
+ Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
30
+ with('http://some-api.useless.io/some/resource').
31
+ and_return(load_document('twonk.json'))
32
+
33
+ get 'http://some-api.doc.useless.io/some/resource'
34
+ last_response.should be_ok
35
+ last_response.body.should =~ /<h1>\/twonks\/:id<\/h1>/
36
+ end
37
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'useless/doc/rack/proxy'
5
+
6
+ describe Useless::Doc::Rack::Proxy do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ @app ||= lambda do |env|
11
+ env['useless.doc.retriever'] = Useless::Doc::Rack::Retriever::Stub
12
+ Useless::Doc::Rack::Proxy.new('some-api').call(env)
13
+ end
14
+ end
15
+
16
+ it 'should proxy GET requests as an OPTIONS request to the corresponding API resource.' do
17
+ Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
18
+ with('http://some-api.granmal.com/some/resource').
19
+ and_return('{ "some": "response" }')
20
+
21
+ get 'http://some-api.doc.granmal.com/some/resource'
22
+ last_response.should be_ok
23
+ end
24
+
25
+ it 'should return a 404 if the proxy call returns nothing' do
26
+ Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
27
+ with('http://some-api.granmal.com/some/non-existant/resource').
28
+ and_return(nil)
29
+
30
+ get 'http://some-api.doc.granmal.com/some/non-existant/resource'
31
+ last_response.should be_not_found
32
+ end
33
+
34
+ it 'should return a 404 if the subdomain is unknown' do
35
+ get 'http://some-other-api.doc.granmal.com/some/non-existant/resource'
36
+ last_response.should be_not_found
37
+ end
38
+
39
+ it 'should raise an error if a retriever is not specified' do
40
+ app = Useless::Doc::Rack::Proxy.new('some-api')
41
+ request = Rack::MockRequest.new(app)
42
+ lambda { request.get('http://some-api.granmal.com/some/non-existant/resource') }.should raise_error(RuntimeError, 'No retriever specified.')
43
+ end
44
+
45
+ describe '.transform_url' do
46
+ it 'should drop the \'doc\' subdomain from the url for the \'io\' TLD' do
47
+ url = Useless::Doc::Rack::Proxy.transform_url 'http://some-api.doc.useless.io/some/resource'
48
+ url.should == 'http://some-api.useless.io/some/resource'
49
+ end
50
+
51
+ it 'should drop the \'doc\' subdomain from the url for the \'dev\' TLD' do
52
+ url = Useless::Doc::Rack::Proxy.transform_url 'http://some-api.doc.useless.dev/some/resource'
53
+ url.should == 'http://some-api.useless.dev/some/resource'
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'useless/doc/rack/retriever'
5
+
6
+ describe Useless::Doc::Rack::Retriever do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ @app ||= begin
11
+ base_app = lambda do |env|
12
+ [200, {'Content-Type' => 'text/plain'}, [env['useless.doc.retriever'].name]]
13
+ end
14
+
15
+ Useless::Doc::Rack::Retriever.new(base_app)
16
+ end
17
+ end
18
+
19
+ it 'should set \'useless.doc.retriever\' to an instance of `Rack::Retriever::Standard` if the RACK_ENV is production' do
20
+ begin
21
+ ENV['RACK_ENV'] = 'production'
22
+ get 'http://some-api.doc.granmal.com/some/resource'
23
+ last_response.body.should == 'Useless::Doc::Rack::Retriever::Standard'
24
+ ensure
25
+ ENV['RACK_ENV'] = 'test'
26
+ end
27
+ end
28
+
29
+ it 'should set \'useless.doc.retriever\' to an instance of `Rack::Retriever::Stub` if the RACK_ENV is development' do
30
+ begin
31
+ ENV['RACK_ENV'] = 'development'
32
+ get 'http://some-api.doc.granmal.com/some/resource'
33
+ last_response.body.should == 'Useless::Doc::Rack::Retriever::Stub'
34
+ ensure
35
+ ENV['RACK_ENV'] = 'test'
36
+ end
37
+ end
38
+
39
+ it 'should set \'useless.doc.retriever\' to an instance of `Rack::Retriever::Stub` if the RACK_ENV is test' do
40
+ get 'http://some-api.doc.granmal.com/some/resource'
41
+ last_response.body.should == 'Useless::Doc::Rack::Retriever::Stub'
42
+ end
43
+
44
+ describe Useless::Doc::Rack::Retriever::Standard do
45
+ it 'should respond to `retrieve`' do
46
+ Useless::Doc::Rack::Retriever::Standard.should respond_to(:retrieve)
47
+ end
48
+ end
49
+
50
+ describe Useless::Doc::Rack::Retriever::Stub do
51
+ it 'should respond to `retrieve`' do
52
+ Useless::Doc::Rack::Retriever::Stub.should respond_to(:retrieve)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'useless/doc/rack/stylesheet'
5
+ require 'useless/doc/ui/godel'
6
+
7
+ describe Useless::Doc::Rack::Stylesheet do
8
+ include Rack::Test::Methods
9
+
10
+ def base_app
11
+ @base_app ||= lambda do |env|
12
+ [200, {'Content-Type' => 'text/plain'}, ['Not a stylesheet request.']]
13
+ end
14
+ end
15
+
16
+ def app
17
+ @app ||= lambda do |env|
18
+ env['useless.doc.ui'] = Useless::Doc::UI::Godel
19
+ Useless::Doc::Rack::Stylesheet.new(base_app).call(env)
20
+ end
21
+ end
22
+
23
+ it 'should raise an error if a UI is not specified' do
24
+ app = Useless::Doc::Rack::Stylesheet.new(base_app)
25
+ request = Rack::MockRequest.new(app)
26
+ lambda { request.get('http://some-api.granmal.com/doc.css') }.should raise_error(RuntimeError, 'No UI specified.')
27
+ end
28
+
29
+ it 'should proxy requests if the path is not doc.css' do
30
+ get 'http://some-api.granmal.com/not/doc.css'
31
+ last_response.body.should == 'Not a stylesheet request.'
32
+ end
33
+
34
+ it 'should return the UI\'s CSS if the path is doc.css' do
35
+ Useless::Doc::UI::Godel.should_receive(:css).and_return('some css')
36
+ get 'http://some-api.granmal.com/doc.css'
37
+ last_response.body.should == 'some css'
38
+ last_response['Content-Type'].should == 'text/css'
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'useless/doc/rack/transform'
4
+ require 'useless/doc/ui/godel'
5
+
6
+ describe Useless::Doc::Rack::Transform do
7
+ def good_base
8
+ @good_base ||= lambda do |env|
9
+ [200, {'Content-Type' => 'application/json'}, [load_document('twonk.json')]]
10
+ end
11
+ end
12
+
13
+ def app(base = nil)
14
+ lambda do |env|
15
+ env['useless.doc.ui'] = Useless::Doc::UI::Godel
16
+ Useless::Doc::Rack::Transform.new(base).call(env)
17
+ end
18
+ end
19
+
20
+ it 'should raise an error if a UI is not specified' do
21
+ app = Useless::Doc::Rack::Transform.new(good_base)
22
+ request = Rack::MockRequest.new(app)
23
+ lambda { request.get('http://some-api.doc.granmal.com/some/resource') }.should raise_error(RuntimeError, 'No UI specified.')
24
+ end
25
+
26
+ it 'should return a 502 if the returned JSON cannot be parsed' do
27
+ bad_base = lambda { |env| [200, {'Content-Type' => 'application/json'}, ['invalid-json']] }
28
+ response = Rack::MockRequest.new(app(bad_base)).get('http://some-api.doc.granmal.com/some/resource')
29
+ response.status.should == 502
30
+ end
31
+
32
+ it 'should proxy through if the downstream app returns a non-200 response' do
33
+ bad_base = lambda { |env| [404, {'Content-Type' => 'text/plain'}, ['Not Found.']] }
34
+ response = Rack::MockRequest.new(app(bad_base)).get('http://some-api.doc.granmal.com/some/resource')
35
+ response.status.should == 404
36
+ response.body.should == 'Not Found.'
37
+ end
38
+
39
+ it 'should transform the JSON returned by the downstream app to HTML' do
40
+ response = Rack::MockRequest.new(app(good_base)).get('http://some-api.doc.granmal.com/some/resource')
41
+ response['Content-Type'].should == 'text/html'
42
+ response.body.should =~ /<h1>\/twonks\/:id<\/h1>/
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'useless/doc/rack/ui'
5
+
6
+ describe Useless::Doc::Rack::UI do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ @app ||= begin
11
+ base_app = lambda do |env|
12
+ [200, {'Content-Type' => 'text/plain'}, [env['useless.doc.ui'].name]]
13
+ end
14
+
15
+ Useless::Doc::Rack::UI.new(base_app)
16
+ end
17
+ end
18
+
19
+ it 'should always return `UI::Godel`' do
20
+ get 'http://some-api.doc.granmal.com/some/resource'
21
+ last_response.body.should == 'Useless::Doc::UI::Godel'
22
+ end
23
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: useless-doc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-03 00:00:00.000000000 Z
12
+ date: 2013-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: oj
@@ -158,6 +158,7 @@ files:
158
158
  - lib/useless/doc/header.rb
159
159
  - lib/useless/doc/rack/application.rb
160
160
  - lib/useless/doc/rack/proxy.rb
161
+ - lib/useless/doc/rack/retriever.rb
161
162
  - lib/useless/doc/rack/stylesheet.rb
162
163
  - lib/useless/doc/rack/transform.rb
163
164
  - lib/useless/doc/rack/ui.rb
@@ -177,7 +178,12 @@ files:
177
178
  - spec/documents/twonk.json
178
179
  - spec/spec_helper.rb
179
180
  - spec/useless/doc/dsl_spec.rb
180
- - spec/useless/doc/proxy_spec.rb
181
+ - spec/useless/doc/rack/application_spec.rb
182
+ - spec/useless/doc/rack/proxy_spec.rb
183
+ - spec/useless/doc/rack/retriever_spec.rb
184
+ - spec/useless/doc/rack/stylesheet_spec.rb
185
+ - spec/useless/doc/rack/transform_spec.rb
186
+ - spec/useless/doc/rack/ui_spec.rb
181
187
  - spec/useless/doc/serialization/dump_spec.rb
182
188
  - spec/useless/doc/serialization/load_spec.rb
183
189
  - spec/useless/doc/sinatra_spec.rb
@@ -210,7 +216,12 @@ test_files:
210
216
  - spec/documents/twonk.json
211
217
  - spec/spec_helper.rb
212
218
  - spec/useless/doc/dsl_spec.rb
213
- - spec/useless/doc/proxy_spec.rb
219
+ - spec/useless/doc/rack/application_spec.rb
220
+ - spec/useless/doc/rack/proxy_spec.rb
221
+ - spec/useless/doc/rack/retriever_spec.rb
222
+ - spec/useless/doc/rack/stylesheet_spec.rb
223
+ - spec/useless/doc/rack/transform_spec.rb
224
+ - spec/useless/doc/rack/ui_spec.rb
214
225
  - spec/useless/doc/serialization/dump_spec.rb
215
226
  - spec/useless/doc/serialization/load_spec.rb
216
227
  - spec/useless/doc/sinatra_spec.rb
@@ -1,48 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- require 'rack/test'
4
- require 'useless/doc/proxy'
5
- require 'useless/doc/ui/godel'
6
-
7
- describe Useless::Doc::Proxy do
8
- include Rack::Test::Methods
9
-
10
- def app
11
- @app ||= begin
12
- ui = Useless::Doc::UI::Godel
13
- Useless::Doc::Proxy.new(ui)
14
- end
15
- end
16
-
17
- describe 'as a Rack application' do
18
- it 'should proxy GET requests as an OPTIONS request to the corresponding API resource.' do
19
- app.should_receive(:retrieve_resource).
20
- with('http://some-api.granmal.com/some/resource').
21
- and_return('{ "some": "response" }')
22
-
23
- get 'http://some-api.doc.granmal.com/some/resource'
24
- last_response.should be_ok
25
- end
26
-
27
- it 'should return a 404 if the proxy call returns nothing' do
28
- app.should_receive(:retrieve_resource).
29
- with('http://some-api.granmal.com/some/non-existant/resource').
30
- and_return(nil)
31
-
32
- get 'http://some-api.doc.granmal.com/some/non-existant/resource'
33
- last_response.should be_not_found
34
- end
35
- end
36
-
37
- describe '.transform_url' do
38
- it 'should drop the \'doc\' subdomain from the url for the \'io\' TLD' do
39
- url = Useless::Doc::Proxy.transform_url 'http://some-api.doc.useless.io/some/resource'
40
- url.should == 'http://some-api.useless.io/some/resource'
41
- end
42
-
43
- it 'should drop the \'doc\' subdomain from the url for the \'dev\' TLD' do
44
- url = Useless::Doc::Proxy.transform_url 'http://some-api.doc.useless.dev/some/resource'
45
- url.should == 'http://some-api.useless.dev/some/resource'
46
- end
47
- end
48
- end