jersey 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c73c2c478b36fbd1a0574323dcbb7f023264517d
4
+ data.tar.gz: d7f9a1f7e5cd742a9de16d62c12b8b9cce788137
5
+ SHA512:
6
+ metadata.gz: 81638bfb73758bf758ae0245c163f88c2bf01c9214736a036c74f00e4a8f3a6309888588e07be9878783835f4ebad5bf02c348be1ec5dbb2adcdac72637d095e
7
+ data.tar.gz: 49fc8716d35f5e8b8d1cffb85d912e391c5325b22e8e72a751b9aedebaee14a94f82d9e27727b92773808d660790bcd4f0f9f95914845ac1d002adb499efced3
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jersey.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'minitest'
8
+ gem 'rack-test', require: 'rack/test'
9
+ gem 'logfmt'
10
+ gem 'puma'
11
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 csquared
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,233 @@
1
+ # Jersey
2
+
3
+ <img src="http://geology.com/state-map/maps/new-jersey-physical-map.gif" height="200px" />
4
+
5
+ Because [worse is better](http://en.wikipedia.org/wiki/Worse_is_better).
6
+
7
+ Jersey is a gem for people who want to write excellent APIs but have their
8
+ own opinions on how to structure their code and projects.
9
+
10
+ Jersey provides sensible defaults that are composed with sensible pieces, making
11
+ it easy to compose your own stack or use Jesery's compositions.
12
+
13
+ ## Features
14
+ - env-conf for easy ENV based configuration
15
+ - request context aware request logging
16
+ - structured data loggers - json and logfmt
17
+ - unified exception handling
18
+
19
+ ### `Jersey.setup`
20
+
21
+ Setup embodies a few opinions we have about apps:
22
+ - Having a notion of 'environment' where the configuration lives
23
+ - Using a dependency manager
24
+ - Always using UTC for the Time zone
25
+ - Always printing times in ISO8601 format
26
+
27
+ `Jersey.setup` allows any app that has Gemfiles and `.env` files to
28
+ "just work" in development *and* production with just:
29
+
30
+ ```ruby
31
+ require 'jersey'
32
+ Jersey.setup
33
+ ```
34
+
35
+ Which is usually included as the first line of code in a library consuming
36
+ Jersey. For tests, you will want to set `RACK_ENV` to `test` before
37
+ requiring your library that uses the above.
38
+
39
+ ### `Jersey::API::Base`
40
+
41
+ Combines all of the Jersey middleware with a few standard middleware
42
+ from Rack and sinatra-contrib.
43
+
44
+ ```ruby
45
+ class API < Jersey::API::Base
46
+ get '/hello' do
47
+ Jersey.log(at: "hello")
48
+ 'hello'
49
+ end
50
+
51
+ get '/not_found' do
52
+ raise NotFound, "y u no here?"
53
+ end
54
+ end
55
+ ```
56
+
57
+
58
+ ```
59
+ $ curl http://localhost:9292/hello
60
+ ```
61
+
62
+ Server logs:
63
+ ```
64
+ at=start method=GET path=/hello request_id=2c018557-d246-4b04-a7d7-fc74bae67ec0 now=2014-11-11T18:04:25+00:00
65
+ at=hello now=2014-11-11T18:04:25+00:00 request_id=2c018557-d246-4b04-a7d7-fc74bae67ec0
66
+ at=finish method=GET path=/hello status=200 size#bytes=5 route_signature=/hello elapsed=0.000 request_id=2c018557-d246-4b04-a7d7-fc74bae67ec0 now=2014-11-11T18:04:25+00:00
67
+ ```
68
+
69
+ Unified, structured logging with all the info you will wish you had:
70
+
71
+ ```
72
+ $ curl http://localhost:9292/not_found | jq '.'
73
+ ```
74
+
75
+ Server logs:
76
+ ```
77
+ at=start method=GET path=/not_found request_id=f3085630-05cf-4314-a7dd-5855e752594b now=2014-11-11T18:05:15+00:00
78
+ at=finish method=GET path=/not_found status=404 size#bytes=6212 route_signature=/not_found elapsed=0.001 request_id=f3085630-05cf-4314-a7dd-5855e752594b now=2014-11-11T18:05:15+00:00
79
+ ```
80
+
81
+ Response:
82
+ ```json
83
+ {
84
+ "error": {
85
+ "type": "NotFound",
86
+ "message": "y u no here?",
87
+ "backtrace": [
88
+ "/Users/csquared/projects/jersey/examples/readme.ru:11:in `block in <class:API>'",
89
+ "/Users/csquared/projects/jersey/.bundle/bundle/ruby/2.1.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1603:in `call'",
90
+ ....
91
+ ]
92
+ }
93
+ }
94
+ ```
95
+
96
+ Unified, strucutred error handling. Notice how all we needed to do was raise `NotFound`
97
+ and we get a 404 response code (in the server logs) and our error message as part of the JSON payload.
98
+
99
+ #### `Jersey::HTTP::Errors`
100
+
101
+ Includes Ruby `Error` objects named with camel case for all of the HTTP 4xx and 5xx
102
+ errors. This allows you to raise `NotFound` as an error that has the `STATUS_CODE`
103
+ defined.
104
+
105
+ Allows uniform HTTP error handling when combined with the `ErrorHandler` sinatra extension.
106
+
107
+ #### Usage
108
+ Mix-in to any class that wants to raise HTTP errors, usually an API class.
109
+
110
+ ```ruby
111
+ class API < Sinatra::Base
112
+ include Jersey::HTTP::Errors
113
+ end
114
+ ```
115
+
116
+ ### `Jersey::Extensions::ErrorHandler`
117
+
118
+ Unifies error responses. If the error object's class has a `STATUS_CODE` defined (such as the
119
+ errors in `Jersey::HTTP::Errors`), this will use that as the HTTP return status. The error
120
+ message and backtraces are included in responses assuming that this in for an internal API
121
+ over secured channels and therefore favors ease of debugging over the security risk of
122
+ including the backtrace. This is something I may want to configure.
123
+
124
+ #### Usage
125
+ Register as a Sinatra extension
126
+
127
+ ```ruby
128
+ class API < Sinatra::Base
129
+ register Jersey::Extensions::ErrorHandler
130
+ end
131
+ ```
132
+
133
+ #### `Jersey::Extensions::RouteSignature`
134
+ Adds a `ROUTE_SIGNATURE` to the `env` for each request, which is the name of an api endpoint
135
+ as it is *defined* versus the path that reaches your app.
136
+ For example, when you define a route such as ` get "/hello/:id"`, the `ROUTE_SIGNATURE` would
137
+ equal `"/hello/:id"`.
138
+ When combined with the `RequestLogger`,
139
+ it greatly simplifies creating aggregate statistics about the traffic hitting various api endpoints.
140
+
141
+ *Note:* this is considered a hack and something that sinatra should, but does not, handle.
142
+
143
+ #### Usage
144
+ Register as a Sinatra extension
145
+
146
+ ```ruby
147
+ class API < Sinatra::Base
148
+ register Jersey::Extensions::RouteSignature
149
+ end
150
+ ```
151
+
152
+ #### `Jersey::Middleware::RequestID`
153
+
154
+ Creates a random request id for every http request, stored both in thread local storage
155
+ via the `RequestStore` and in the Rack `env`.
156
+
157
+
158
+ Works with or without explicitly including `RequestStore::Middleware`.
159
+
160
+ #### Usage
161
+ Use as a Rack middleware
162
+
163
+ ```ruby
164
+ class API < Sinatra::Base
165
+ use Jersey::Middleware::RequestID
166
+ end
167
+ ```
168
+
169
+ #### `Jersey::Middleware::RequestLogger`
170
+
171
+ Logs http start and finish and errors in a structured logging format.
172
+
173
+ It defaults to using the `Jersey.logger` singleton which is `RequestStore`-aware.
174
+ Anything in `RequestStore[:log]` will get appended to the log data. (This is how request ids
175
+ are made available to logger calls outside of HTTP blocks but within HTTP request lifecycles).
176
+
177
+ Because I think request_ids are important, the logger will try to get them from either the
178
+ `RequestStore` or the `env`.
179
+
180
+ Logs at request start:
181
+
182
+ at: "start",
183
+ request_id: env['REQUEST_ID'],
184
+ method: request.request_method,
185
+ path: request.path_info,
186
+ content_type: request.content_type,
187
+ content_length: request.content_length
188
+
189
+ Logs at request finish:
190
+
191
+ at: "finish",
192
+ method: request.request_method,
193
+ path: request.path_info,
194
+ status: status,
195
+ content_length: headers['Content-Length'],
196
+ route_signature: env['ROUTE_SIGNATURE'],
197
+ elapsed: (Time.now - @request_start).to_f,
198
+ request_id: env['REQUEST_ID']
199
+
200
+
201
+ #### Usage
202
+ Use as a Rack middleware
203
+
204
+ ```ruby
205
+ class API < Sinatra::Base
206
+ use Jersey::Middleware::RequestLogger
207
+ end
208
+ ```
209
+
210
+ ## Installation
211
+
212
+ Add this line to your application's Gemfile:
213
+
214
+ ```ruby
215
+ gem 'jersey'
216
+ ```
217
+
218
+ And then execute:
219
+
220
+ $ bundle
221
+
222
+ Or install it yourself as:
223
+
224
+ $ gem install jersey
225
+
226
+
227
+ ## Contributing
228
+
229
+ 1. Fork it ( https://github.com/[my-github-username]/jersey/fork )
230
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
231
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
232
+ 4. Push to the branch (`git push origin my-new-feature`)
233
+ 5. Create a new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,15 @@
1
+ require 'jersey'
2
+ Jersey.setup
3
+
4
+ class API < Jersey::API::Base
5
+ get '/hello' do
6
+ Jersey.log(at: "hello")
7
+ 'hello'
8
+ end
9
+
10
+ get '/not_found' do
11
+ raise NotFound, "y u no here?"
12
+ end
13
+ end
14
+
15
+ run API
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jersey/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jersey"
8
+ spec.version = Jersey::VERSION
9
+ spec.authors = ["csquared"]
10
+ spec.email = ["christopher.continanza@gmail.com"]
11
+ spec.summary = %q{Write APIs in the New Jersey Style}
12
+ spec.description = %q{Set of composable middleware and helpers for production sinatra APIs}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sinatra", "~> 1.4"
22
+ spec.add_dependency 'sinatra-contrib', "~> 1.4"
23
+ spec.add_dependency 'env-conf'
24
+ spec.add_dependency 'request_store'
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
@@ -0,0 +1,10 @@
1
+ require "jersey/version"
2
+ require 'jersey/log'
3
+
4
+ module Jersey
5
+ end
6
+
7
+ require 'jersey/setup'
8
+ require 'jersey/api'
9
+ require 'jersey/base'
10
+ require 'jersey/time'
@@ -0,0 +1,12 @@
1
+ module Jersey
2
+ module API
3
+ module Middleware
4
+ end
5
+
6
+ module Extensions
7
+ end
8
+
9
+ module Helpers
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ require "sinatra/base"
2
+ require 'sinatra/json'
3
+ require 'json'
4
+ require 'request_store'
5
+
6
+ # jersey
7
+ require 'jersey/http_errors'
8
+ require 'jersey/middleware/request_id'
9
+ require 'jersey/middleware/request_logger'
10
+ require 'jersey/extensions/route_signature'
11
+ require 'jersey/extensions/error_handler'
12
+ require 'jersey/helpers/log'
13
+
14
+ module Jersey::API
15
+ class Base < Sinatra::Base
16
+ include Jersey::HTTP::Errors
17
+
18
+ register Jersey::Extensions::RouteSignature
19
+ register Jersey::Extensions::ErrorHandler
20
+
21
+ use Rack::Deflater
22
+ use Rack::ConditionalGet
23
+ use Rack::ETag
24
+
25
+ use Jersey::Middleware::RequestID
26
+ use Jersey::Middleware::RequestLogger
27
+
28
+ helpers Sinatra::JSON
29
+ helpers Jersey::Helpers::Log
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module Jersey::Extensions
2
+ module ErrorHandler
3
+ def self.registered(app)
4
+ app.set :dump_errors, false
5
+ app.set :raise_errors, false
6
+ app.set :show_exceptions, false
7
+
8
+ # APIS should always return meaningful standardized errors
9
+ # with statuses that best match HTTP conventions
10
+ #
11
+ # Places nicely with Jersey::HTTP::Errors
12
+ app.error do
13
+ content_type(:json)
14
+ e = env['sinatra.error']
15
+ # get status code from Jersey Errors
16
+ if e.class.const_defined?(:STATUS_CODE)
17
+ status(e.class::STATUS_CODE)
18
+ else
19
+ status(500)
20
+ end
21
+
22
+ json(error: {
23
+ type: e.class.name.split('::').last,
24
+ message: e.message,
25
+ backtrace: e.backtrace
26
+ })
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ module Jersey::Extensions
2
+ module RouteSignature
3
+ def self.registered(app)
4
+ app.helpers do
5
+ def route_signature
6
+ env["ROUTE_SIGNATURE"]
7
+ end
8
+ end
9
+ end
10
+
11
+ def route(verb, path, *)
12
+ condition { env["ROUTE_SIGNATURE"] = path.to_s }
13
+ super
14
+ end
15
+ end
16
+ end