pliny 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/bin/pliny-generate +6 -0
  3. data/lib/pliny.rb +21 -0
  4. data/lib/pliny/commands/generator.rb +197 -0
  5. data/lib/pliny/config_helpers.rb +24 -0
  6. data/lib/pliny/errors.rb +109 -0
  7. data/lib/pliny/extensions/instruments.rb +38 -0
  8. data/lib/pliny/log.rb +84 -0
  9. data/lib/pliny/middleware/cors.rb +46 -0
  10. data/lib/pliny/middleware/request_id.rb +42 -0
  11. data/lib/pliny/middleware/request_store.rb +14 -0
  12. data/lib/pliny/middleware/rescue_errors.rb +24 -0
  13. data/lib/pliny/middleware/timeout.rb +25 -0
  14. data/lib/pliny/middleware/versioning.rb +64 -0
  15. data/lib/pliny/request_store.rb +22 -0
  16. data/lib/pliny/router.rb +15 -0
  17. data/lib/pliny/tasks.rb +3 -0
  18. data/lib/pliny/tasks/db.rake +116 -0
  19. data/lib/pliny/tasks/test.rake +8 -0
  20. data/lib/pliny/templates/endpoint.erb +30 -0
  21. data/lib/pliny/templates/endpoint_acceptance_test.erb +40 -0
  22. data/lib/pliny/templates/endpoint_scaffold.erb +49 -0
  23. data/lib/pliny/templates/endpoint_scaffold_acceptance_test.erb +55 -0
  24. data/lib/pliny/templates/endpoint_test.erb +16 -0
  25. data/lib/pliny/templates/mediator.erb +22 -0
  26. data/lib/pliny/templates/mediator_test.erb +5 -0
  27. data/lib/pliny/templates/migration.erb +9 -0
  28. data/lib/pliny/templates/model.erb +5 -0
  29. data/lib/pliny/templates/model_migration.erb +10 -0
  30. data/lib/pliny/templates/model_test.erb +5 -0
  31. data/lib/pliny/utils.rb +31 -0
  32. data/lib/pliny/version.rb +3 -0
  33. data/test/commands/generator_test.rb +147 -0
  34. data/test/errors_test.rb +24 -0
  35. data/test/extensions/instruments_test.rb +34 -0
  36. data/test/log_test.rb +27 -0
  37. data/test/middleware/cors_test.rb +42 -0
  38. data/test/middleware/request_id_test.rb +28 -0
  39. data/test/middleware/request_store_test.rb +25 -0
  40. data/test/middleware/rescue_errors_test.rb +41 -0
  41. data/test/middleware/timeout_test.rb +32 -0
  42. data/test/middleware/versioning_test.rb +63 -0
  43. data/test/request_store_test.rb +25 -0
  44. data/test/router_test.rb +39 -0
  45. data/test/test_helper.rb +18 -0
  46. metadata +252 -0
@@ -0,0 +1,42 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::CORS do
4
+ def app
5
+ Rack::Builder.new do
6
+ use Rack::Lint
7
+ use Pliny::Middleware::CORS
8
+ run Sinatra.new {
9
+ get "/" do
10
+ "hi"
11
+ end
12
+ }
13
+ end
14
+ end
15
+
16
+ it "doesn't do anything when the Origin header is not present" do
17
+ get "/"
18
+ assert_equal 200, last_response.status
19
+ assert_equal "hi", last_response.body
20
+ assert_equal nil, last_response.headers["Access-Control-Allow-Origin"]
21
+ end
22
+
23
+ it "intercepts OPTION requests to render a stub (preflight request)" do
24
+ header "Origin", "http://localhost"
25
+ options "/"
26
+ assert_equal 200, last_response.status
27
+ assert_equal "", last_response.body
28
+ assert_equal "GET, POST, PUT, PATCH, DELETE, OPTIONS",
29
+ last_response.headers["Access-Control-Allow-Methods"]
30
+ assert_equal "http://localhost",
31
+ last_response.headers["Access-Control-Allow-Origin"]
32
+ end
33
+
34
+ it "delegates other calls, adding the CORS headers to the response" do
35
+ header "Origin", "http://localhost"
36
+ get "/"
37
+ assert_equal 200, last_response.status
38
+ assert_equal "hi", last_response.body
39
+ assert_equal "http://localhost",
40
+ last_response.headers["Access-Control-Allow-Origin"]
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::RequestID do
4
+ def app
5
+ Rack::Builder.new do
6
+ use Rack::Lint
7
+ use Pliny::Middleware::RequestID
8
+ run Sinatra.new {
9
+ get "/" do
10
+ env["REQUEST_IDS"].join(",")
11
+ end
12
+ }
13
+ end
14
+ end
15
+
16
+ it "tags responses with Request-Id" do
17
+ get "/"
18
+ assert_match Pliny::Middleware::RequestID::UUID_PATTERN,
19
+ last_response.headers["Request-Id"]
20
+ end
21
+
22
+ it "accepts incoming request IDs" do
23
+ id = SecureRandom.uuid
24
+ header "Request-Id", id
25
+ get "/"
26
+ assert_includes last_response.body, id
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::RequestStore do
4
+ def app
5
+ Rack::Builder.new do
6
+ use Rack::Lint
7
+ use Pliny::Middleware::RequestStore, store: Pliny::RequestStore
8
+ run Sinatra.new {
9
+ get "/" do
10
+ "hi"
11
+ end
12
+ }
13
+ end
14
+ end
15
+
16
+ it "clears the store" do
17
+ mock(Pliny::RequestStore).clear!
18
+ get "/"
19
+ end
20
+
21
+ it "seeds the store" do
22
+ mock(Pliny::RequestStore).seed.with_any_args
23
+ get "/"
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::RescueErrors do
4
+ include Rack::Test::Methods
5
+
6
+ class BadMiddleware
7
+ def call(env)
8
+ if env["PATH_INFO"] == "/api-error"
9
+ raise Pliny::Errors::ServiceUnavailable
10
+ else
11
+ raise "Omg!"
12
+ end
13
+ end
14
+ end
15
+
16
+ def app
17
+ Rack::Builder.new do
18
+ use Rack::Lint
19
+ use Pliny::Middleware::RescueErrors
20
+ run BadMiddleware.new
21
+ end
22
+ end
23
+
24
+ it "intercepts Pliny errors and renders" do
25
+ get "/api-error"
26
+ assert_equal 503, last_response.status
27
+ error_json = MultiJson.decode(last_response.body)
28
+ assert_equal "service_unavailable", error_json["id"]
29
+ assert_equal "Service unavailable.", error_json["message"]
30
+ assert_equal 503, error_json["status"]
31
+ end
32
+
33
+ it "intercepts exceptions and renders" do
34
+ get "/"
35
+ assert_equal 500, last_response.status
36
+ error_json = MultiJson.decode(last_response.body)
37
+ assert_equal "internal_server_error", error_json["id"]
38
+ assert_equal "Internal server error.", error_json["message"]
39
+ assert_equal 500, error_json["status"]
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::Timeout do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Rack::Builder.new do
8
+ use Rack::Lint
9
+ use Pliny::Middleware::Timeout
10
+ run Sinatra.new {
11
+ get "/" do
12
+ 200
13
+ end
14
+
15
+ get "/timeout" do
16
+ raise Pliny::Middleware::Timeout::RequestTimeout
17
+ end
18
+ }
19
+ end
20
+ end
21
+
22
+ it "passes through requests that don't timeout normally" do
23
+ get "/"
24
+ assert_equal 200, last_response.status
25
+ end
26
+
27
+ it "responds with an error on a timeout" do
28
+ assert_raises(Pliny::Errors::ServiceUnavailable) do
29
+ get "/timeout"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Middleware::Versioning do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Rack::Builder.new do
8
+ use Rack::Lint
9
+ use Pliny::Middleware::Versioning, default: '2', app_name: 'pliny'
10
+ run Sinatra.new {
11
+ get "/" do
12
+ MultiJson.encode env
13
+ end
14
+ }
15
+ end
16
+ end
17
+
18
+ it "produces default version on application/json" do
19
+ get '/', {}, {'HTTP_ACCEPT' => 'application/json'}
20
+ json = MultiJson.decode(last_response.body)
21
+ assert_equal 'application/json', json['HTTP_ACCEPT']
22
+ assert_equal '2', json['HTTP_X_API_VERSION']
23
+ end
24
+
25
+ it "errors without a version specified on application/vnd.pliny+json" do
26
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.pliny+json'}
27
+ error = { id: :bad_version, message: <<-eos }
28
+ Please specify a version along with the MIME type. For example, `Accept: application/vnd.pliny+json; version=1`.
29
+ eos
30
+
31
+ assert_equal 400, last_response.status
32
+ assert_equal MultiJson.encode(error), last_response.body
33
+ end
34
+
35
+ it "ignores a wrong app name" do
36
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.chuck_norris+json'}
37
+ json = MultiJson.decode(last_response.body)
38
+ assert_equal 'application/vnd.chuck_norris+json', json['HTTP_ACCEPT']
39
+ assert_equal '2', json['HTTP_X_API_VERSION']
40
+ end
41
+
42
+ it "produces a version on application/vnd.pliny+json; version=3" do
43
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.pliny+json; version=3'}
44
+ json = MultiJson.decode(last_response.body)
45
+ assert_equal 'application/json', json['HTTP_ACCEPT']
46
+ assert_equal '3', json['HTTP_X_API_VERSION']
47
+ end
48
+
49
+ # this behavior is pretty sketchy, but a pretty extreme edge case
50
+ it "handles multiple MIME types" do
51
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.pliny+json; version=3; q=0.5, text/xml'}
52
+ json = MultiJson.decode(last_response.body)
53
+ assert_equal 'text/xml, application/json; q=0.5', json['HTTP_ACCEPT']
54
+ assert_equal '3', json['HTTP_X_API_VERSION']
55
+ end
56
+
57
+ it "produces the priority version on multiple types" do
58
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.pliny+json; version=4; q=0.5, application/vnd.pliny+json; version=3'}
59
+ json = MultiJson.decode(last_response.body)
60
+ assert_equal 'application/json, application/json; q=0.5', json['HTTP_ACCEPT']
61
+ assert_equal '3', json['HTTP_X_API_VERSION']
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::RequestStore do
4
+ before do
5
+ @env = {
6
+ "REQUEST_IDS" => ["abc", "def"]
7
+ }
8
+ end
9
+
10
+ it "seeds :request_id" do
11
+ Pliny::RequestStore.seed(@env)
12
+ assert_equal "abc,def", Pliny::RequestStore.store[:request_id]
13
+ end
14
+
15
+ it "seeds :log_context" do
16
+ Pliny::RequestStore.seed(@env)
17
+ assert_equal "abc,def", Pliny::RequestStore.store[:log_context][:request_id]
18
+ end
19
+
20
+ it "is cleared by clear!" do
21
+ Pliny::RequestStore.seed(@env)
22
+ Pliny::RequestStore.clear!
23
+ assert_nil Pliny::RequestStore.store[:request_id]
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ describe Pliny::Router do
4
+
5
+ describe "specifying a version" do
6
+ def app
7
+ Rack::Builder.new do
8
+ use Rack::Lint
9
+ use Pliny::Middleware::Versioning, default: '2', app_name: 'pliny'
10
+
11
+ use Pliny::Router do
12
+ version '3' do
13
+ mount Sinatra.new {
14
+ get '/' do
15
+ "API V3"
16
+ end
17
+ }
18
+ end
19
+ end
20
+
21
+ run Sinatra.new {
22
+ get "/" do
23
+ "No API"
24
+ end
25
+ }
26
+ end
27
+ end
28
+
29
+ it "should not run on any api" do
30
+ get '/'
31
+ assert_equal 'No API', last_response.body
32
+ end
33
+
34
+ it "should run on API V3" do
35
+ get '/', {}, {'HTTP_ACCEPT' => 'application/vnd.pliny+json; version=3'}
36
+ assert_equal 'API V3', last_response.body
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ # make sure this is set before Sinatra is required
2
+ ENV["RACK_ENV"] = "test"
3
+
4
+ require "bundler"
5
+ Bundler.require(:default, :test)
6
+
7
+ require "minitest/autorun"
8
+ require "rr"
9
+
10
+ require_relative "../lib/pliny"
11
+
12
+ class MiniTest::Spec
13
+ include Rack::Test::Methods
14
+
15
+ before do
16
+ Pliny::RequestStore.clear!
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,252 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pliny
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Brandur Leach
8
+ - Pedro Belo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '4.1'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 4.1.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '4.1'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.1.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: multi_json
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.9.3
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '1.9'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.9.3
54
+ - !ruby/object:Gem::Dependency
55
+ name: pg
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.17'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.17.1
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '0.17'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.17.1
74
+ - !ruby/object:Gem::Dependency
75
+ name: prmd
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.1'
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 0.1.1
84
+ type: :runtime
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0.1'
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 0.1.1
94
+ - !ruby/object:Gem::Dependency
95
+ name: sequel
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '4.9'
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 4.9.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.9'
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 4.9.0
114
+ - !ruby/object:Gem::Dependency
115
+ name: sinatra
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '1.4'
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 1.4.5
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.4'
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 1.4.5
134
+ - !ruby/object:Gem::Dependency
135
+ name: http_accept
136
+ requirement: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: '0.1'
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 0.1.5
144
+ type: :runtime
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0.1'
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 0.1.5
154
+ - !ruby/object:Gem::Dependency
155
+ name: sinatra-router
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '0.2'
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: 0.2.3
164
+ type: :runtime
165
+ prerelease: false
166
+ version_requirements: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: '0.2'
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: 0.2.3
174
+ description: Pliny is a set of base classes and helpers to help developers write excellent
175
+ APIs in Sinatra
176
+ email:
177
+ - brandur@mutelight.org
178
+ - pedrobelo@gmail.com
179
+ executables:
180
+ - pliny-generate
181
+ extensions: []
182
+ extra_rdoc_files: []
183
+ files:
184
+ - bin/pliny-generate
185
+ - lib/pliny.rb
186
+ - lib/pliny/commands/generator.rb
187
+ - lib/pliny/config_helpers.rb
188
+ - lib/pliny/errors.rb
189
+ - lib/pliny/extensions/instruments.rb
190
+ - lib/pliny/log.rb
191
+ - lib/pliny/middleware/cors.rb
192
+ - lib/pliny/middleware/request_id.rb
193
+ - lib/pliny/middleware/request_store.rb
194
+ - lib/pliny/middleware/rescue_errors.rb
195
+ - lib/pliny/middleware/timeout.rb
196
+ - lib/pliny/middleware/versioning.rb
197
+ - lib/pliny/request_store.rb
198
+ - lib/pliny/router.rb
199
+ - lib/pliny/tasks.rb
200
+ - lib/pliny/tasks/db.rake
201
+ - lib/pliny/tasks/test.rake
202
+ - lib/pliny/templates/endpoint.erb
203
+ - lib/pliny/templates/endpoint_acceptance_test.erb
204
+ - lib/pliny/templates/endpoint_scaffold.erb
205
+ - lib/pliny/templates/endpoint_scaffold_acceptance_test.erb
206
+ - lib/pliny/templates/endpoint_test.erb
207
+ - lib/pliny/templates/mediator.erb
208
+ - lib/pliny/templates/mediator_test.erb
209
+ - lib/pliny/templates/migration.erb
210
+ - lib/pliny/templates/model.erb
211
+ - lib/pliny/templates/model_migration.erb
212
+ - lib/pliny/templates/model_test.erb
213
+ - lib/pliny/utils.rb
214
+ - lib/pliny/version.rb
215
+ - test/commands/generator_test.rb
216
+ - test/errors_test.rb
217
+ - test/extensions/instruments_test.rb
218
+ - test/log_test.rb
219
+ - test/middleware/cors_test.rb
220
+ - test/middleware/request_id_test.rb
221
+ - test/middleware/request_store_test.rb
222
+ - test/middleware/rescue_errors_test.rb
223
+ - test/middleware/timeout_test.rb
224
+ - test/middleware/versioning_test.rb
225
+ - test/request_store_test.rb
226
+ - test/router_test.rb
227
+ - test/test_helper.rb
228
+ homepage: https://github.com/heroku/pliny
229
+ licenses:
230
+ - MIT
231
+ metadata: {}
232
+ post_install_message:
233
+ rdoc_options: []
234
+ require_paths:
235
+ - lib
236
+ required_ruby_version: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - ">="
239
+ - !ruby/object:Gem::Version
240
+ version: '0'
241
+ required_rubygems_version: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - ">"
244
+ - !ruby/object:Gem::Version
245
+ version: 1.3.1
246
+ requirements: []
247
+ rubyforge_project:
248
+ rubygems_version: 2.2.2
249
+ signing_key:
250
+ specification_version: 4
251
+ summary: Basic tooling to support API apps in Sinatra
252
+ test_files: []