syro 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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