sliver 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +165 -36
- data/lib/sliver/api.rb +2 -1
- data/sliver.gemspec +1 -1
- data/spec/acceptance/lambda_api_spec.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01a74a1104ce1713e00244f93d3ad2e67817992f
|
4
|
+
data.tar.gz: 4e39d179e03349cf5c5773a10afb345c18b81b22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ad1ca7c87d8250f4d664e1ea5fb4f698e822780cd2715e370a05413aab4d858e68bc9d8e9d20d6f46bd61a5e114c6462e5280013f41362d9bf141fdb03548ff
|
7
|
+
data.tar.gz: 4093e264929558478d157796a2c8b8bc108e531bae2c58b2ddf40d1912af78658980c2109a06719ebd8da2977e6ba35ea68f16c84812d048f7a22606bce6c84a
|
data/README.md
CHANGED
@@ -6,57 +6,61 @@ A super simple, extendable Rack API.
|
|
6
6
|
[![Code Climate](https://codeclimate.com/github/pat/sliver.png)](https://codeclimate.com/github/pat/sliver)
|
7
7
|
[![Gem Version](https://badge.fury.io/rb/sliver.svg)](http://badge.fury.io/rb/sliver)
|
8
8
|
|
9
|
-
|
9
|
+
## Why?
|
10
|
+
|
11
|
+
Ruby doesn't lack for web frameworks, especially ones focused on APIs, but Sliver is a little different from others I've come across.
|
12
|
+
|
13
|
+
* It focuses on one class per endpoint, for increased Single Responsibility Principle friendliness.
|
14
|
+
* Separate classes allows for better code organisation, instead of everything in one file.
|
15
|
+
* It's a pretty small layer on top of Rack, which is the only dependency, which keeps things light and fast.
|
16
|
+
* Guards and processors provide some structures for managing authentication, JSON responses and other common behaviours across actions (similar to Rails before_action filters).
|
10
17
|
|
11
18
|
## Installation
|
12
19
|
|
13
20
|
Add it to your Gemfile like any other gem, or install it manually.
|
14
21
|
|
15
22
|
```ruby
|
16
|
-
gem 'sliver', '~> 0.2.
|
23
|
+
gem 'sliver', '~> 0.2.3'
|
17
24
|
```
|
18
25
|
|
19
26
|
## Usage
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
array with three items: status code, headers, and body).
|
28
|
+
At its most basic level, Sliver is a simple routing engine to other Rack endpoints. You can map out a bunch of routes (with the HTTP method and the path), and the corresponding endpoints for requests that come in on those routes.
|
29
|
+
|
30
|
+
### Lambda Endpoints
|
25
31
|
|
26
|
-
|
27
|
-
If you want to deal with classes, you can mix in `Sliver::Action` to take
|
28
|
-
advantage of some helper methods (and it already stores environment via
|
29
|
-
`attr_reader`), and it returns a `Sliver::Response` class which is translated
|
30
|
-
to the standard Rack response. Each instance of a class that mixes in
|
31
|
-
`Sliver::Action` is handling a specific API request.
|
32
|
+
Here's an example using lambdas, where the responses must match Rack's expected output (an array with three items: status code, headers, and body).
|
32
33
|
|
33
34
|
```ruby
|
34
35
|
app = Sliver::API.new do |api|
|
35
|
-
# GET /v1/
|
36
36
|
api.connect :get, '/', lambda { |environment|
|
37
37
|
[200, {}, ['How dare the Premier ignore my invitations?']]
|
38
38
|
}
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
api.connect :post '/hits', lambda { |environment|
|
41
|
+
HitMachine.create! Rack::Request.new(environment).params[:hit]
|
42
|
+
|
43
|
+
[200, {}, ["He'll have to go!"]]
|
44
|
+
}
|
42
45
|
end
|
46
|
+
```
|
43
47
|
|
44
|
-
|
45
|
-
include Sliver::Action
|
48
|
+
### Sliver::Action Endpoints
|
46
49
|
|
47
|
-
|
48
|
-
# returns false.
|
49
|
-
def skip?
|
50
|
-
return false unless environment['user'].nil?
|
50
|
+
However, it can be nice to encapsulate each endpoint in its own class - to keep responsibilities clean and concise. Sliver provides a module `Sliver::Action` which makes this approach reasonably simple, with helper methods to the Rack environment and a `response` object, which can have `status`, `headers` and `body` set (which is automatically translated into the Rack response).
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
```ruby
|
53
|
+
app = Sliver::API.new do |api|
|
54
|
+
api.connect :get, '/changes', ChangesAction
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
57
|
+
class ChangesAction
|
58
|
+
include Sliver::Action
|
58
59
|
|
59
60
|
def call
|
61
|
+
# This isn't a realistic implementation - just examples of how
|
62
|
+
# to interact with the provided variables.
|
63
|
+
|
60
64
|
# Change the status:
|
61
65
|
response.status = 404
|
62
66
|
|
@@ -80,30 +84,155 @@ class ChangeAction
|
|
80
84
|
end
|
81
85
|
```
|
82
86
|
|
83
|
-
|
84
|
-
|
85
|
-
|
87
|
+
### Path Parameters
|
88
|
+
|
89
|
+
Much like Rails, you can have named parameters in your paths, which are available via `path_params` within your endpoint behaviour:
|
86
90
|
|
87
91
|
```ruby
|
88
|
-
|
92
|
+
app = Sliver::API.new do |api|
|
93
|
+
api.connect :get, '/changes/:id', ChangeAction
|
94
|
+
end
|
95
|
+
|
96
|
+
class ChangeAction
|
89
97
|
include Sliver::Action
|
90
98
|
|
91
99
|
def call
|
92
|
-
|
93
|
-
|
100
|
+
change = Change.find path_params['id']
|
101
|
+
|
102
|
+
response.status = 200
|
103
|
+
response.body = ["Change: #{change.name}"]
|
94
104
|
end
|
95
105
|
end
|
106
|
+
```
|
107
|
+
|
108
|
+
It's worth noting that unlike Rails, these values are not mixed into the standard `params` hash.
|
109
|
+
|
110
|
+
### Guards
|
111
|
+
|
112
|
+
Sometimes you're going to have endpoints where you want to check certain things before getting into the core implementation: one example could be to check whether the request is made by an authenticated user. In Sliver, you can do this via Guards:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
app = Sliver::API.new do |api|
|
116
|
+
api.connect :get, '/changes/:id', ChangeAction
|
117
|
+
end
|
118
|
+
|
119
|
+
class ChangeAction
|
120
|
+
include Sliver::Action
|
121
|
+
|
122
|
+
def self.guards
|
123
|
+
[AuthenticatedUserGuard]
|
124
|
+
end
|
96
125
|
|
97
|
-
class ChangeAction < JSONAction
|
98
126
|
def call
|
127
|
+
change = Change.find path_params['id']
|
128
|
+
|
99
129
|
response.status = 200
|
100
|
-
response.body = {
|
130
|
+
response.body = ["Change: #{change.name}"]
|
131
|
+
end
|
132
|
+
|
133
|
+
def user
|
134
|
+
@user ||= User.find_by :key => request.env['Authentication']
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class AuthenticatedUserGuard < Sliver::Hook
|
139
|
+
def continue?
|
140
|
+
action.user.present?
|
141
|
+
end
|
101
142
|
|
102
|
-
|
143
|
+
def respond
|
144
|
+
response.status = 401
|
145
|
+
response.body = ['Unauthorised: valid session is required']
|
103
146
|
end
|
104
147
|
end
|
105
148
|
```
|
106
149
|
|
150
|
+
Guards inheriting from `Sliver::Hook` just need to respond to `call`, and have access to `action` (your endpoint instance) and `response` (which will be turned into a proper Rack response).
|
151
|
+
|
152
|
+
They are set in the action via a class method (which must return an array of classes), and a guard instance must respond to two methods: `continue?` and `respond`. If `continue?` returns true, then the main action `call` method is used. Otherwise, it's skipped, and the guard's `respond` method needs to set the alternative response.
|
153
|
+
|
154
|
+
### Processors
|
155
|
+
|
156
|
+
Processors are behaviours that happen after the endpoint logic has been performed. These are particularly useful for transforming the response, perhaps to JSON if your API is expected to always return JSON:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
app = Sliver::API.new do |api|
|
160
|
+
api.connect :get, '/changes/:id', ChangeAction
|
161
|
+
end
|
162
|
+
|
163
|
+
class ChangeAction
|
164
|
+
include Sliver::Action
|
165
|
+
|
166
|
+
def self.processors
|
167
|
+
[JSONProcessor]
|
168
|
+
end
|
169
|
+
|
170
|
+
def call
|
171
|
+
change = Change.find path_params['id']
|
172
|
+
|
173
|
+
response.status = 200
|
174
|
+
response.body = {:id => change.id, :name => change.name}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class JSONProcessor < Sliver::Hook
|
179
|
+
def call
|
180
|
+
response.body = [JSON.generate(response.body)]
|
181
|
+
response.headers['Content-Type'] = 'application/json'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
Processors inheriting from `Sliver::Hook` just need to respond to `call`, and have access to `action` (your endpoint instance) and `response` (which will be turned into a proper Rack response).
|
187
|
+
|
188
|
+
### Testing
|
189
|
+
|
190
|
+
Because your API is a Rack app, it can be tested using `rack-test`'s helper methods. Here's a quick example for RSpec:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
describe 'My API' do
|
194
|
+
include Rack::Test::Methods
|
195
|
+
|
196
|
+
let(:app) { MyApi.new }
|
197
|
+
|
198
|
+
it 'responds to GET requests' do
|
199
|
+
get '/'
|
200
|
+
|
201
|
+
expect(last_response.status).to eq(200)
|
202
|
+
expect(last_response.headers['Content-Type']).to eq('text/plain')
|
203
|
+
expect(last_response.body).to eq('foo')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
### Running via config.ru
|
209
|
+
|
210
|
+
It's pretty easy to run your Sliver API via a `config.ru` file:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
require 'rubygems'
|
214
|
+
require 'bundler'
|
215
|
+
|
216
|
+
Bundler.setup :default
|
217
|
+
$:.unshift File.dirname(__FILE__) + '/lib'
|
218
|
+
|
219
|
+
require 'my_app'
|
220
|
+
|
221
|
+
run MyApp::API.new
|
222
|
+
```
|
223
|
+
|
224
|
+
### Running via Rails
|
225
|
+
|
226
|
+
Of course, you can also run your API within the context of Rails by mounting it in your `config/routes.rb` file:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
MyRailsApp::Application.routes.draw do
|
230
|
+
mount Api::V1.new => '/api/v1'
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
There is also the [sliver-rails](https://github.com/pat/sliver-rails) gem which adds some nice extensions to Sliver with Rails in mind.
|
235
|
+
|
107
236
|
## Contributing
|
108
237
|
|
109
238
|
Please note that this project now has a [Contributor Code of Conduct](http://contributor-covenant.org/version/1/0/0/). By participating in this project you agree to abide by its terms.
|
@@ -116,5 +245,5 @@ Please note that this project now has a [Contributor Code of Conduct](http://con
|
|
116
245
|
|
117
246
|
## Licence
|
118
247
|
|
119
|
-
Copyright (c) 2014, Sliver is developed and maintained by Pat Allan, and is
|
248
|
+
Copyright (c) 2014-2015, Sliver is developed and maintained by Pat Allan, and is
|
120
249
|
released under the open MIT Licence.
|
data/lib/sliver/api.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class Sliver::API
|
2
|
-
|
2
|
+
NOT_FOUND_RESPONSE = [404, {'X-Cascade' => 'pass'}, ['Not Found']].freeze
|
3
|
+
NOT_FOUND = lambda { |environment| NOT_FOUND_RESPONSE }
|
3
4
|
|
4
5
|
def initialize(&block)
|
5
6
|
@endpoints = Sliver::Endpoints.new
|
data/sliver.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sliver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
97
|
version: '0'
|
98
98
|
requirements: []
|
99
99
|
rubyforge_project:
|
100
|
-
rubygems_version: 2.
|
100
|
+
rubygems_version: 2.6.11
|
101
101
|
signing_key:
|
102
102
|
specification_version: 4
|
103
103
|
summary: Lightweight, simple Rack APIs
|