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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 62e18bf6b83082bb11e2c86ee25a98175a3ba17f
4
- data.tar.gz: 8fcf8e7c3821264435e5ca43e72956ca33e83235
3
+ metadata.gz: 01a74a1104ce1713e00244f93d3ad2e67817992f
4
+ data.tar.gz: 4e39d179e03349cf5c5773a10afb345c18b81b22
5
5
  SHA512:
6
- metadata.gz: 4fd5f69b2bd489c67e9c2b424b9cc4cf892af72362931f04b63dc117566f2732630b09179c1cafbae73a49db88bc95d9c6ec75922e66fdf9055a0b3eccf00fa2
7
- data.tar.gz: d6def9714ba4c73d2a59716ef0be3251cf28d139f678ea9fd7bbd83e843f7dd6e76a11f366625449e58f55744ea2ce649ec0444ee71d4ad9b2c1d4e17eb35cf6
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
- Early days of development, so things may change dramatically. Or not. Who knows.
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.2'
23
+ gem 'sliver', '~> 0.2.3'
17
24
  ```
18
25
 
19
26
  ## Usage
20
27
 
21
- Create a new API (a Rack app, of course), and specify paths with corresponding
22
- responses. Responses can be anything that responds to `call` with a single
23
- argument (the request environment) and returns a standard Rack response (an
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
- So, a response can be as simple as a lambda/proc, or it can be a complex class.
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
- # PUT /v1/change
41
- api.connect :put, '/change', ChangeAction
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
- class ChangeAction
45
- include Sliver::Action
48
+ ### Sliver::Action Endpoints
46
49
 
47
- # You don't *need* to implement this method - the underlying implementation
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
- # In this case, the call method is never invoked.
53
- response.status = 401
54
- response.body = ['Unauthorised']
52
+ ```ruby
53
+ app = Sliver::API.new do |api|
54
+ api.connect :get, '/changes', ChangesAction
55
+ end
55
56
 
56
- true
57
- end
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
- If you want all responses to API requests to share some behaviour - say, for
84
- example, you are always returning JSON - then you can create your own base class
85
- for this purpose:
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
- class JSONAction
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
- response.headers['Content-Type'] = 'application/json'
93
- response.body = [JSON.generate(response.body)]
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 = {'status' => 'OK'}
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
- super
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
- NOT_FOUND = lambda { |environment| [404, {}, ['Not Found']] }
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
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  Gem::Specification.new do |spec|
3
3
  spec.name = 'sliver'
4
- spec.version = '0.2.2'
4
+ spec.version = '0.2.3'
5
5
  spec.authors = ['Pat Allan']
6
6
  spec.email = ['pat@freelancing-gods.com']
7
7
  spec.summary = %q{Lightweight, simple Rack APIs}
@@ -52,6 +52,7 @@ describe 'Basic Sliver API' do
52
52
 
53
53
  expect(last_response.status).to eq(404)
54
54
  expect(last_response.body).to eq('Not Found')
55
+ expect(last_response.headers['X-Cascade']).to eq('pass')
55
56
  end
56
57
 
57
58
  it 'matches against regular expressions' do
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.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: 2015-09-02 00:00:00.000000000 Z
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.3.0
100
+ rubygems_version: 2.6.11
101
101
  signing_key:
102
102
  specification_version: 4
103
103
  summary: Lightweight, simple Rack APIs