sliver 0.0.5 → 0.1.0

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: df142423f5a7c0f22884aea4f0399060f48c996d
4
- data.tar.gz: d3a322be8b8ad57a96e47799b4f5fcb9f3c7b01c
3
+ metadata.gz: 0bdc908dcc8c917965d1acfd2b79b8f290917e7a
4
+ data.tar.gz: cb6fcbcbe3f42041052410562e6d9d7cf68edbd9
5
5
  SHA512:
6
- metadata.gz: 3b81d54267bd062858aa75e4a8973af6e85d453f72a867a465689c4287c1311d50013676bb7f0910e2ba77c54429673eb4c7101661439969edae09b0f8e5cc9b
7
- data.tar.gz: 07f226603923b2580c1f29ffe864d0d320207eb670f7bbad77b2f689832532fa9df115b1e00313ff36d0287c3cba8c4c50f8fc480d7780e3278ae83c3a9d277d
6
+ metadata.gz: bd4f2fb4cf61ee61a845ddeaa5c4e37ab737c152727aafc016a6379601e619a9cf802ba5fcc019987e3989d95f4e03d65ddacdf55a9a3d592ff9ea52dbaf2f80
7
+ data.tar.gz: 4b93baa6b44dd3eadb0914bae0e10fba15687933642cbca7b36c5f25ae352c6835bda079667362b0adc409259e2546ca6a057a8bb24eb438e4f3296c014ad11f
data/README.md CHANGED
@@ -13,7 +13,7 @@ Early days of development, so things may change dramatically. Or not. Who knows.
13
13
  Add it to your Gemfile like any other gem, or install it manually.
14
14
 
15
15
  ```ruby
16
- gem 'sliver', '~> 0.0.5'
16
+ gem 'sliver', '~> 0.1.0'
17
17
  ```
18
18
 
19
19
  ## Usage
@@ -26,8 +26,8 @@ array with three items: status code, headers, and body).
26
26
  So, a response can be as simple as a lambda/proc, or it can be a complex class.
27
27
  If you want to deal with classes, you can mix in `Sliver::Action` to take
28
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 to
30
- the standard Rack response. Each instance of a class that mixes in
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
31
  `Sliver::Action` is handling a specific API request.
32
32
 
33
33
  ```ruby
data/lib/sliver/action.rb CHANGED
@@ -5,12 +5,11 @@ module Sliver::Action
5
5
 
6
6
  module ClassMethods
7
7
  def call(environment)
8
- response = Sliver::Response.new
9
-
10
- action = new(environment, response)
11
- action.call unless action.skip?
8
+ Sliver::Runner.new(self, environment).call
9
+ end
12
10
 
13
- response.to_a
11
+ def guards
12
+ []
14
13
  end
15
14
  end
16
15
 
@@ -18,6 +17,10 @@ module Sliver::Action
18
17
  @environment, @response = environment, response
19
18
  end
20
19
 
20
+ def request
21
+ @request ||= Rack::Request.new environment
22
+ end
23
+
21
24
  def skip?
22
25
  false
23
26
  end
@@ -26,7 +29,7 @@ module Sliver::Action
26
29
 
27
30
  attr_reader :environment, :response
28
31
 
29
- def request
30
- @request ||= Rack::Request.new environment
32
+ def path_params
33
+ @path_params ||= environment['sliver.path'].to_params environment
31
34
  end
32
35
  end
data/lib/sliver/api.rb CHANGED
@@ -2,15 +2,13 @@ class Sliver::API
2
2
  NOT_FOUND = lambda { |environment| [404, {}, ['Not Found']] }
3
3
 
4
4
  def initialize(&block)
5
- @endpoints = Hash.new { |hash, key| hash[key] = Sliver::Endpoints.new }
5
+ @endpoints = Sliver::Endpoints.new
6
6
 
7
7
  block.call self
8
8
  end
9
9
 
10
10
  def call(environment)
11
- method = environment['REQUEST_METHOD']
12
- path = environment['PATH_INFO']
13
- endpoint = endpoints[method].find(path)
11
+ endpoint = endpoints.find environment
14
12
 
15
13
  endpoint.nil? ? NOT_FOUND.call(environment) : invoke(endpoint, environment)
16
14
  end
@@ -20,9 +18,7 @@ class Sliver::API
20
18
  end
21
19
 
22
20
  def connect(method, path, action)
23
- method = method.to_s.upcase
24
-
25
- endpoints[method].append path, action
21
+ endpoints.append Sliver::Path.new(method, path), action
26
22
  end
27
23
 
28
24
  private
@@ -4,19 +4,15 @@ class Sliver::Endpoints
4
4
  end
5
5
 
6
6
  def append(path, action)
7
- path = '/' if path == ''
8
-
9
7
  paths[path] = action
10
8
  end
11
9
 
12
- def find(path)
13
- path = '/' if path == ''
14
-
15
- key = paths.keys.detect { |key|
16
- key.is_a?(String) ? (key == path) : path[/\A#{key}\z/]
17
- }
10
+ def find(environment)
11
+ key = paths.keys.detect { |key| key.matches?(environment) }
12
+ return nil unless key
18
13
 
19
- key && paths[key]
14
+ environment['sliver.path'] = key
15
+ paths[key]
20
16
  end
21
17
 
22
18
  private
@@ -0,0 +1,43 @@
1
+ class Sliver::Path
2
+ def initialize(http_method, string)
3
+ @http_method = http_method.to_s.upcase
4
+ @string = normalised_path string
5
+ end
6
+
7
+ def matches?(environment)
8
+ method = environment['REQUEST_METHOD']
9
+ path = normalised_path environment['PATH_INFO']
10
+
11
+ http_method == method && path[string_to_regexp]
12
+ end
13
+
14
+ def to_params(environment)
15
+ return {} unless matches?(environment)
16
+
17
+ path = normalised_path environment['PATH_INFO']
18
+ values = path.scan(string_to_regexp).flatten
19
+
20
+ string_keys.each_with_index.inject({}) do |hash, (key, index)|
21
+ hash[key] = values[index]
22
+ hash
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :http_method, :string
29
+
30
+ def normalised_path(string)
31
+ string == '' ? '/' : string
32
+ end
33
+
34
+ def string_keys
35
+ @string_keys ||= string.to_s.scan(/:([\w-]+)/i).flatten
36
+ end
37
+
38
+ def string_to_regexp
39
+ @string_to_regexp ||= Regexp.new(
40
+ "\\A" + string.to_s.gsub(/:[\w-]+/, "([\\w-]+)") + "\\z"
41
+ )
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ class Sliver::Runner
2
+ def initialize(klass, environment)
3
+ @klass, @environment = klass, environment
4
+ end
5
+
6
+ def call
7
+ guard_classes.each do |guard_class|
8
+ guard = guard_class.new(action)
9
+ return guard.response unless guard.continue?
10
+ end
11
+
12
+ action.call unless action.skip?
13
+
14
+ response.to_a
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :klass, :environment
20
+
21
+ def action
22
+ @action ||= klass.new environment, response
23
+ end
24
+
25
+ def guard_classes
26
+ klass.guards
27
+ end
28
+
29
+ def response
30
+ @response ||= Sliver::Response.new
31
+ end
32
+ end
data/lib/sliver.rb CHANGED
@@ -5,4 +5,6 @@ end
5
5
  require 'sliver/action'
6
6
  require 'sliver/api'
7
7
  require 'sliver/endpoints'
8
+ require 'sliver/path'
8
9
  require 'sliver/response'
10
+ require 'sliver/runner'
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.0.5'
4
+ spec.version = '0.1.0'
5
5
  spec.authors = ['Pat Allan']
6
6
  spec.email = ['pat@freelancing-gods.com']
7
7
  spec.summary = %q{Lightweight, simple Rack APIs}
@@ -42,6 +42,61 @@ class SkippedAction
42
42
  end
43
43
  end
44
44
 
45
+ class MyParamGuard
46
+ def initialize(action)
47
+ @action = action
48
+ end
49
+
50
+ def continue?
51
+ action.request.params['hello'] == 'world'
52
+ end
53
+
54
+ def response
55
+ [404, {}, ['Not Found']]
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :action
61
+ end
62
+
63
+ class GuardedAction
64
+ include Sliver::Action
65
+
66
+ def self.guards
67
+ [MyParamGuard]
68
+ end
69
+
70
+ def call
71
+ response.status = 200
72
+ response.body = ['Welcome']
73
+ end
74
+ end
75
+
76
+ class UnguardedAction < GuardedAction
77
+ def self.guards
78
+ super - [MyParamGuard]
79
+ end
80
+ end
81
+
82
+ class IdAction
83
+ include Sliver::Action
84
+
85
+ def call
86
+ response.status = 200
87
+ response.body = [path_params['id']]
88
+ end
89
+ end
90
+
91
+ class MultiPathPartAction
92
+ include Sliver::Action
93
+
94
+ def call
95
+ response.status = 200
96
+ response.body = ["#{path_params['first']}:#{path_params['second']}"]
97
+ end
98
+ end
99
+
45
100
  describe 'Class-based Sliver API' do
46
101
  include Rack::Test::Methods
47
102
 
@@ -50,6 +105,10 @@ describe 'Class-based Sliver API' do
50
105
  api.connect :put, '/echo', EchoAction
51
106
  api.connect :get, '/addition', AdditionAction
52
107
  api.connect :get, '/skip', SkippedAction
108
+ api.connect :get, '/guard', GuardedAction
109
+ api.connect :get, '/unguard', UnguardedAction
110
+ api.connect :get, '/my/:id', IdAction
111
+ api.connect :get, '/my/:first/:second', MultiPathPartAction
53
112
  end }
54
113
 
55
114
  it 'constructs responses' do
@@ -78,4 +137,39 @@ describe 'Class-based Sliver API' do
78
137
  expect(last_response.status).to eq(400)
79
138
  expect(last_response.body).to eq('Invalid')
80
139
  end
140
+
141
+ it 'blocks guarded actions if they cannot continue' do
142
+ get '/guard'
143
+
144
+ expect(last_response.status).to eq(404)
145
+ expect(last_response.body).to eq('Not Found')
146
+ end
147
+
148
+ it 'accepts guarded actions that meet criteria' do
149
+ get '/guard', 'hello' => 'world'
150
+
151
+ expect(last_response.status).to eq(200)
152
+ expect(last_response.body).to eq('Welcome')
153
+ end
154
+
155
+ it 'respects subclass guard changes' do
156
+ get '/unguard'
157
+
158
+ expect(last_response.status).to eq(200)
159
+ expect(last_response.body).to eq('Welcome')
160
+ end
161
+
162
+ it 'handles path parameter markers' do
163
+ get '/my/10'
164
+
165
+ expect(last_response.status).to eq(200)
166
+ expect(last_response.body).to eq('10')
167
+ end
168
+
169
+ it 'handles multiple path parameter markers' do
170
+ get '/my/10/foo'
171
+
172
+ expect(last_response.status).to eq(200)
173
+ expect(last_response.body).to eq('10:foo')
174
+ end
81
175
  end
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.0.5
4
+ version: 0.1.0
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-01-01 00:00:00.000000000 Z
11
+ date: 2015-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -69,7 +69,9 @@ files:
69
69
  - lib/sliver/action.rb
70
70
  - lib/sliver/api.rb
71
71
  - lib/sliver/endpoints.rb
72
+ - lib/sliver/path.rb
72
73
  - lib/sliver/response.rb
74
+ - lib/sliver/runner.rb
73
75
  - sliver.gemspec
74
76
  - spec/acceptance/class_api_spec.rb
75
77
  - spec/acceptance/lambda_api_spec.rb