pliny 0.0.1.pre

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.
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: []