syro 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +6 -0
  3. data/lib/syro.rb +207 -90
  4. data/syro.gemspec +1 -1
  5. data/test/all.rb +44 -0
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 72ccaea865ce6b7f9dd3c646ff3fffbe74ef3a26
4
- data.tar.gz: 0254961f899a3579960d806aaeee163517064c09
3
+ metadata.gz: 42c57c65ec35da48059c8844925b6baa1a3a3897
4
+ data.tar.gz: cee94b2f71a5eeea0a07fee70d9d923d5720df7c
5
5
  SHA512:
6
- metadata.gz: 1450ad36901c308e28a71ea2a085df6d9803ccfa095dec59bd55cc27f65339d0c876e957faebb9e0579de7f21fda9181c516e979d040a4ec7e7b635e88d8c057
7
- data.tar.gz: 936d26d701776b2064a5913b686f5660efb389399500048262ebd78357cd377b6960ccb9792b57524cbd10f6b4fe85cf4fedce7b20ad0bb09183180c4bdb3e9c
6
+ metadata.gz: 79191f17a6cb4b4bdf8c2b4a8456e59ec15f5cc1ac33911502117103ad122f3a492fb80b359e6990754b8a1cd049b50da0a2b813355fd869d8e88052922e7a4f
7
+ data.tar.gz: 97ab83421c8cb0b3772404d91494f6e08389cc96d85542e4c1f355ac0bd2a9222654e530a7180902f184f4241b7085eaeee8ba6e05420d9420fe18b6b7103c54
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 1.1.0
2
+
3
+ * Move Deck methods to the Deck::API module
4
+
5
+ * Add support for custom response and request classes
6
+
1
7
  1.0.0
2
8
 
3
9
  * Yield captures as an alternative API
@@ -27,12 +27,34 @@ class Syro
27
27
  INBOX = "syro.inbox".freeze
28
28
 
29
29
  class Response
30
- LOCATION = "Location".freeze
31
- DEFAULT = "text/html".freeze
32
-
30
+ LOCATION = "Location".freeze # :nodoc:
31
+ DEFAULT = "text/html".freeze # :nodoc:
32
+
33
+ # The status of the response.
34
+ #
35
+ # res.status = 200
36
+ # res.status # => 200
37
+ #
33
38
  attr_accessor :status
34
39
 
40
+ # Returns the body of the response.
41
+ #
42
+ # res.body
43
+ # # => []
44
+ #
45
+ # res.write("there is")
46
+ # res.write("no try")
47
+ #
48
+ # res.body
49
+ # # => ["there is", "no try"]
50
+ #
35
51
  attr :body
52
+
53
+ # Returns a hash with the response headers.
54
+ #
55
+ # res.headers
56
+ # # => { "Content-Type" => "text/html", "Content-Length" => "42" }
57
+ #
36
58
  attr :headers
37
59
 
38
60
  def initialize(headers = {})
@@ -42,14 +64,37 @@ class Syro
42
64
  @length = 0
43
65
  end
44
66
 
67
+ # Returns the response header corresponding to `key`.
68
+ #
69
+ # res["Content-Type"] # => "text/html"
70
+ # res["Content-Length"] # => "42"
71
+ #
45
72
  def [](key)
46
73
  @headers[key]
47
74
  end
48
75
 
76
+ # Sets the given `value` with the header corresponding to `key`.
77
+ #
78
+ # res["Content-Type"] = "application/json"
79
+ # res["Content-Type"] # => "application/json"
80
+ #
49
81
  def []=(key, value)
50
82
  @headers[key] = value
51
83
  end
52
84
 
85
+ # Appends `str` to `body` and updates the `Content-Length` header.
86
+ #
87
+ # res.body # => []
88
+ #
89
+ # res.write("foo")
90
+ # res.write("bar")
91
+ #
92
+ # res.body
93
+ # # => ["foo", "bar"]
94
+ #
95
+ # res["Content-Length"]
96
+ # # => 6
97
+ #
53
98
  def write(str)
54
99
  s = str.to_s
55
100
 
@@ -58,11 +103,42 @@ class Syro
58
103
  @body << s
59
104
  end
60
105
 
106
+ # Sets the `Location` header to `path` and updates the status to
107
+ # `status`. By default, `status` is `302`.
108
+ #
109
+ # res.redirect("/path")
110
+ #
111
+ # res["Location"] # => "/path"
112
+ # res.status # => 302
113
+ #
114
+ # res.redirect("http://syro.ru", 303)
115
+ #
116
+ # res["Location"] # => "http://syro.ru"
117
+ # res.status # => 303
118
+ #
61
119
  def redirect(path, status = 302)
62
120
  @headers[LOCATION] = path
63
121
  @status = status
64
122
  end
65
123
 
124
+ # Returns an array with three elements: the status, headers and body.
125
+ # If the status is not set, the status is set to 404 if empty body,
126
+ # otherwise the status is set to 200 and updates the `Content-Type`
127
+ # header to `text/html`.
128
+ #
129
+ # res.status = 200
130
+ # res.finish
131
+ # # => [200, {}, []]
132
+ #
133
+ # res.status = nil
134
+ # res.finish
135
+ # # => [404, {}, []]
136
+ #
137
+ # res.status = nil
138
+ # res.write("syro")
139
+ # res.finish
140
+ # # => [200, { "Content-Type" => "text/html" }, ["syro"]]
141
+ #
66
142
  def finish
67
143
  if @status.nil?
68
144
  if @body.empty?
@@ -76,142 +152,183 @@ class Syro
76
152
  [@status, @headers, @body]
77
153
  end
78
154
 
155
+ # Sets a cookie into the response.
156
+ #
157
+ # res.set_cookie("foo", "bar")
158
+ # res["Set-Cookie"] # => "foo=bar"
159
+ #
160
+ # res.set_cookie("foo2", "bar2")
161
+ # res["Set-Cookie"] # => "foo=bar\nfoo2=bar2"
162
+ #
163
+ # res.set_cookie("bar", {
164
+ # domain: ".example.com",
165
+ # path: "/",
166
+ # # max_age: 0,
167
+ # # expires: Time.now + 10_000,
168
+ # secure: true,
169
+ # httponly: true,
170
+ # value: "bar"
171
+ # })
172
+ #
173
+ # res["Set-Cookie"].split("\n").last
174
+ # # => "bar=bar; domain=.example.com; path=/; secure; HttpOnly
175
+ #
176
+ # **NOTE:** This method doesn't sign and/or encrypt the value of the cookie.
177
+ #
79
178
  def set_cookie(key, value)
80
179
  Rack::Utils.set_cookie_header!(@headers, key, value)
81
180
  end
82
181
 
182
+ # Deletes cookie.
183
+ #
184
+ # res.set_cookie("foo", "bar")
185
+ # res["Set-Cookie"]
186
+ # # => "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
187
+ #
83
188
  def delete_cookie(key, value = {})
84
189
  Rack::Utils.delete_cookie_header!(@headers, key, value)
85
190
  end
86
191
  end
87
192
 
88
193
  class Deck
89
- def initialize(code)
90
- @syro_code = code
91
- end
194
+ module API
195
+ def initialize(code)
196
+ @syro_code = code
197
+ end
92
198
 
93
- def env
94
- @syro_env
95
- end
199
+ def env
200
+ @syro_env
201
+ end
96
202
 
97
- def req
98
- @syro_req
99
- end
203
+ def req
204
+ @syro_req
205
+ end
100
206
 
101
- def res
102
- @syro_res
103
- end
207
+ def res
208
+ @syro_res
209
+ end
104
210
 
105
- def path
106
- @syro_path
107
- end
211
+ def path
212
+ @syro_path
213
+ end
108
214
 
109
- def inbox
110
- @syro_inbox
111
- end
215
+ def inbox
216
+ @syro_inbox
217
+ end
112
218
 
113
- def default_headers
114
- return {}
115
- end
219
+ def default_headers
220
+ return {}
221
+ end
116
222
 
117
- def call(env, inbox)
118
- @syro_env = env
119
- @syro_req = Rack::Request.new(env)
120
- @syro_res = Syro::Response.new(default_headers)
121
- @syro_path = Seg.new(env.fetch(Rack::PATH_INFO))
122
- @syro_inbox = inbox
223
+ def request_class
224
+ Rack::Request
225
+ end
123
226
 
124
- catch(:halt) do
125
- instance_eval(&@syro_code)
227
+ def response_class
228
+ Syro::Response
229
+ end
230
+
231
+ def call(env, inbox)
232
+ @syro_env = env
233
+ @syro_req = request_class.new(env)
234
+ @syro_res = response_class.new(default_headers)
235
+ @syro_path = Seg.new(env.fetch(Rack::PATH_INFO))
236
+ @syro_inbox = inbox
237
+
238
+ catch(:halt) do
239
+ instance_eval(&@syro_code)
126
240
 
127
- @syro_res.finish
241
+ @syro_res.finish
242
+ end
128
243
  end
129
- end
130
244
 
131
- def run(app, inbox = {})
132
- path, script = env[Rack::PATH_INFO], env[Rack::SCRIPT_NAME]
245
+ def run(app, inbox = {})
246
+ path, script = env[Rack::PATH_INFO], env[Rack::SCRIPT_NAME]
133
247
 
134
- env[Rack::PATH_INFO] = @syro_path.curr
135
- env[Rack::SCRIPT_NAME] = @syro_path.prev
136
- env[Syro::INBOX] = inbox
248
+ env[Rack::PATH_INFO] = @syro_path.curr
249
+ env[Rack::SCRIPT_NAME] = @syro_path.prev
250
+ env[Syro::INBOX] = inbox
137
251
 
138
- halt(app.call(env))
139
- ensure
140
- env[Rack::PATH_INFO], env[Rack::SCRIPT_NAME] = path, script
141
- end
252
+ halt(app.call(env))
253
+ ensure
254
+ env[Rack::PATH_INFO], env[Rack::SCRIPT_NAME] = path, script
255
+ end
142
256
 
143
- def halt(response)
144
- throw(:halt, response)
145
- end
257
+ def halt(response)
258
+ throw(:halt, response)
259
+ end
146
260
 
147
- def match(arg)
148
- case arg
149
- when String then @syro_path.consume(arg)
150
- when Symbol then @syro_path.capture(arg, inbox)
151
- when true then true
152
- else false
261
+ def match(arg)
262
+ case arg
263
+ when String then @syro_path.consume(arg)
264
+ when Symbol then @syro_path.capture(arg, inbox)
265
+ when true then true
266
+ else false
267
+ end
153
268
  end
154
- end
155
269
 
156
- def on(arg)
157
- if match(arg)
158
- yield(inbox[arg])
270
+ def on(arg)
271
+ if match(arg)
272
+ yield(inbox[arg])
159
273
 
160
- halt(res.finish)
274
+ halt(res.finish)
275
+ end
161
276
  end
162
- end
163
277
 
164
- def root?
165
- @syro_path.root?
166
- end
278
+ def root?
279
+ @syro_path.root?
280
+ end
167
281
 
168
- def root
169
- if root?
170
- yield
282
+ def root
283
+ if root?
284
+ yield
171
285
 
172
- halt(res.finish)
286
+ halt(res.finish)
287
+ end
173
288
  end
174
- end
175
289
 
176
- def get
177
- if root? && req.get?
178
- yield
290
+ def get
291
+ if root? && req.get?
292
+ yield
179
293
 
180
- halt(res.finish)
294
+ halt(res.finish)
295
+ end
181
296
  end
182
- end
183
297
 
184
- def put
185
- if root? && req.put?
186
- yield
298
+ def put
299
+ if root? && req.put?
300
+ yield
187
301
 
188
- halt(res.finish)
302
+ halt(res.finish)
303
+ end
189
304
  end
190
- end
191
305
 
192
- def post
193
- if root? && req.post?
194
- yield
306
+ def post
307
+ if root? && req.post?
308
+ yield
195
309
 
196
- halt(res.finish)
310
+ halt(res.finish)
311
+ end
197
312
  end
198
- end
199
313
 
200
- def patch
201
- if root? && req.patch?
202
- yield
314
+ def patch
315
+ if root? && req.patch?
316
+ yield
203
317
 
204
- halt(res.finish)
318
+ halt(res.finish)
319
+ end
205
320
  end
206
- end
207
321
 
208
- def delete
209
- if root? && req.delete?
210
- yield
322
+ def delete
323
+ if root? && req.delete?
324
+ yield
211
325
 
212
- halt(res.finish)
326
+ halt(res.finish)
327
+ end
213
328
  end
214
329
  end
330
+
331
+ include API
215
332
  end
216
333
 
217
334
  def initialize(deck = Deck, &code)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "syro"
3
- s.version = "1.0.0"
3
+ s.version = "1.1.0"
4
4
  s.summary = "Simple router"
5
5
  s.description = "Simple router for web applications"
6
6
  s.authors = ["Michel Martens"]
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  class RackApp
2
4
  def call(env)
3
5
  [200, {"Content-Type" => "text/html"}, ["GET /rack"]]
@@ -17,6 +19,28 @@ class DefaultHeaders < Syro::Deck
17
19
  end
18
20
  end
19
21
 
22
+ class CustomRequestAndResponse < Syro::Deck
23
+ class JSONRequest < Rack::Request
24
+ def params
25
+ JSON.parse(body.read)
26
+ end
27
+ end
28
+
29
+ class JSONResponse < Syro::Response
30
+ def write(s)
31
+ super(JSON.generate(s))
32
+ end
33
+ end
34
+
35
+ def request_class
36
+ JSONRequest
37
+ end
38
+
39
+ def response_class
40
+ JSONResponse
41
+ end
42
+ end
43
+
20
44
  textual = Syro.new(TextualDeck) {
21
45
  get {
22
46
  text("GET /textual")
@@ -25,6 +49,14 @@ textual = Syro.new(TextualDeck) {
25
49
 
26
50
  default_headers = Syro.new(DefaultHeaders) { }
27
51
 
52
+ json = Syro.new(CustomRequestAndResponse) {
53
+ root {
54
+ params = req.params
55
+
56
+ res.write(params)
57
+ }
58
+ }
59
+
28
60
  admin = Syro.new {
29
61
  get {
30
62
  res.write("GET /admin")
@@ -155,6 +187,10 @@ app = Syro.new {
155
187
  on("headers") {
156
188
  run(default_headers)
157
189
  }
190
+
191
+ on("json") {
192
+ run(json)
193
+ }
158
194
  }
159
195
 
160
196
  setup do
@@ -270,3 +306,11 @@ test "default headers" do |f|
270
306
 
271
307
  assert_equal "text/html", f.last_response.headers["Content-Type"]
272
308
  end
309
+
310
+ test "custom request and response class" do |f|
311
+ params = JSON.generate(foo: "foo")
312
+
313
+ f.post("/json", params)
314
+
315
+ assert_equal params, f.last_response.body
316
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syro
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michel Martens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-06 00:00:00.000000000 Z
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: seg