useless-doc 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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