syro 0.0.1
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.
- checksums.yaml +7 -0
- data/.gems +4 -0
- data/CHANGELOG +0 -0
- data/CONTRIBUTING +19 -0
- data/LICENSE +19 -0
- data/README.md +211 -0
- data/lib/syro.rb +257 -0
- data/makefile +4 -0
- data/syro.gemspec +17 -0
- data/test/all.rb +159 -0
- data/test/helper.rb +15 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c674acd1e5f50201c1a6d8ba682f73376922bf06
|
4
|
+
data.tar.gz: cf8130872298e53a52b0da3358c8c7929b81ce4b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8fb83225dc60423a43c56147159e679be6349a5da972ba9183b7bcbfe12091c630046c8e0f28d9570609f1bf64e082802c7aac7c42ac56c4025dd796cd0b08b9
|
7
|
+
data.tar.gz: a9ed177ed81d563450db71b2b30053ee2bf8c11f40dc1df3778de92de71ec7b3606895cc90413aba84af78bdb435c00fbee85483aecabefb2674bb94aec3dc01
|
data/.gems
ADDED
data/CHANGELOG
ADDED
File without changes
|
data/CONTRIBUTING
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
This code tries to solve a particular problem with a very simple
|
2
|
+
implementation. We try to keep the code to a minimum while making
|
3
|
+
it as clear as possible. The design is very likely finished, and
|
4
|
+
if some feature is missing it is possible that it was left out on
|
5
|
+
purpose. That said, new usage patterns may arise, and when that
|
6
|
+
happens we are ready to adapt if necessary.
|
7
|
+
|
8
|
+
A good first step for contributing is to meet us on IRC and discuss
|
9
|
+
ideas. We spend a lot of time on #lesscode at freenode, always ready
|
10
|
+
to talk about code and simplicity. If connecting to IRC is not an
|
11
|
+
option, you can create an issue explaining the proposed change and
|
12
|
+
a use case. We pay a lot of attention to use cases, because our
|
13
|
+
goal is to keep the code base simple. Usually the result of a
|
14
|
+
conversation is the creation of a different tool.
|
15
|
+
|
16
|
+
Please don't start the conversation with a pull request. The code
|
17
|
+
should come at last, and even though it may help to convey an idea,
|
18
|
+
more often than not it draws the attention to a particular
|
19
|
+
implementation.
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Michel Martens
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
Syro
|
2
|
+
====
|
3
|
+
|
4
|
+
Simple router for web applications.
|
5
|
+
|
6
|
+
Description
|
7
|
+
-----------
|
8
|
+
|
9
|
+
Syro is a very simple router for web applications. It was created
|
10
|
+
in the tradition of libraries like [Rum][rum] and [Cuba][cuba], but
|
11
|
+
it promotes a less flexible usage pattern. The design is inspired
|
12
|
+
by the way some Cuba applications are architected: modularity is
|
13
|
+
encouraged and sub-applications can be dispatched without any
|
14
|
+
significant performance overhead.
|
15
|
+
|
16
|
+
[rum]: http://github.com/chneukirchen/rum
|
17
|
+
[cuba]: http://cuba.is
|
18
|
+
|
19
|
+
Usage
|
20
|
+
-----
|
21
|
+
|
22
|
+
An example of a modular application would look like this:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
admin = Syro.new {
|
26
|
+
get {
|
27
|
+
res.write "Hello from admin!"
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
app = Syro.new {
|
32
|
+
on("admin") {
|
33
|
+
run(admin)
|
34
|
+
}
|
35
|
+
}
|
36
|
+
```
|
37
|
+
|
38
|
+
The block is evaluated in a sandbox where the following methods are
|
39
|
+
available: `env`, `req`, `res`, `inbox`, `call`, `run`, `halt`,
|
40
|
+
`match`, `on`, `root?`, `root`, `get?`, `post?`, `patch?`, `delete?`,
|
41
|
+
`get`, `post`, `patch` and `delete`.
|
42
|
+
|
43
|
+
As a recommendation, user created variables should be instance
|
44
|
+
variables. That way they won't mix with the API methods defined in
|
45
|
+
the sandbox. All the internal instance variables defined by Syro
|
46
|
+
are prefixed by `syro_`, like in `@syro_inbox`.
|
47
|
+
|
48
|
+
API
|
49
|
+
---
|
50
|
+
|
51
|
+
`env`: Environment variables for the request.
|
52
|
+
|
53
|
+
`req`: Helper object for accessing the request variables. It's an
|
54
|
+
instance of `Rack::Request`.
|
55
|
+
|
56
|
+
`res`: Helper object for creating the response. It's an instance
|
57
|
+
of `Syro::Response`.
|
58
|
+
|
59
|
+
`inbox`: Hash with captures and potentially other variables local
|
60
|
+
to the request.
|
61
|
+
|
62
|
+
`call`: Entry point for the application. It receives the environment
|
63
|
+
and optionally an inbox.
|
64
|
+
|
65
|
+
`run`: Runs a sub app, and accepts an inbox as an optional second
|
66
|
+
argument.
|
67
|
+
|
68
|
+
`halt`: Terminates the request. It receives an array with the
|
69
|
+
response as per Rack's specification.
|
70
|
+
|
71
|
+
`match`: Receives a String, a Symbol or a boolean, and returns true
|
72
|
+
if it matches the request.
|
73
|
+
|
74
|
+
`on`: Receives a value to be matched, and a block that will be
|
75
|
+
executed only if the request is matched.
|
76
|
+
|
77
|
+
`root?`: Returns true if the path yet to be consumed is empty.
|
78
|
+
|
79
|
+
`root`: Receives a block and calls it only if `root?` is true.
|
80
|
+
|
81
|
+
`get?`: Returns true if the `REQUEST_METHOD` is `GET`.
|
82
|
+
|
83
|
+
`get`: Receives a block and calls it only if `root?` and `get?` are
|
84
|
+
true.
|
85
|
+
|
86
|
+
`post?`: Returns true if the `REQUEST_METHOD` is `POST`.
|
87
|
+
|
88
|
+
`post`: Receives a block and calls it only if `root?` and `post?`
|
89
|
+
are true.
|
90
|
+
|
91
|
+
`patch?`: Returns true if the `REQUEST_METHOD` is `PATCH`.
|
92
|
+
|
93
|
+
`patch`: Receives a block and calls it only if `root?` and `patch?`
|
94
|
+
are true.
|
95
|
+
|
96
|
+
`delete?`: Returns true if the `REQUEST_METHOD` is `DELETE`.
|
97
|
+
|
98
|
+
`delete`: Receives a block and calls it only if `root?` and `delete?`
|
99
|
+
are true.
|
100
|
+
|
101
|
+
Examples
|
102
|
+
--------
|
103
|
+
|
104
|
+
In the following examples, the response string represents
|
105
|
+
the request path that was sent.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
app = Syro.new {
|
109
|
+
get {
|
110
|
+
res.write "GET /"
|
111
|
+
}
|
112
|
+
|
113
|
+
post {
|
114
|
+
res.write "POST /"
|
115
|
+
}
|
116
|
+
|
117
|
+
on("users") {
|
118
|
+
on(:id) {
|
119
|
+
|
120
|
+
# Captured values go to the inbox
|
121
|
+
@user = User[inbox[:id]]
|
122
|
+
|
123
|
+
get {
|
124
|
+
res.write "GET /users/42"
|
125
|
+
}
|
126
|
+
|
127
|
+
patch {
|
128
|
+
res.write "PATCH /users/42"
|
129
|
+
}
|
130
|
+
|
131
|
+
delete {
|
132
|
+
res.write "DELETE /users/42"
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
get {
|
137
|
+
res.write "GET /users"
|
138
|
+
}
|
139
|
+
|
140
|
+
post {
|
141
|
+
res.write "POST /users"
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
Matches
|
148
|
+
-------
|
149
|
+
|
150
|
+
The `on` method can receive a `String` to perform path matches; a
|
151
|
+
`Symbol` to perform path captures; and a boolean to match any true
|
152
|
+
values.
|
153
|
+
|
154
|
+
Each time `on` matches or captures a segment of the PATH, that part
|
155
|
+
of the path is consumed. The current and previous paths can be
|
156
|
+
queried by calling `prev` and `curr` on the `path` object: `path.prev`
|
157
|
+
returns the part of the path already consumed, and `path.curr`
|
158
|
+
provides the current version of the path.
|
159
|
+
|
160
|
+
Any expression that evaluates to a boolean can also be used as a
|
161
|
+
matcher. For example, a common pattern is to follow some route
|
162
|
+
only if a user is authenticated. That can be accomplished with
|
163
|
+
`on(authenticated(User))`. That example assumes there's a method
|
164
|
+
called `authenticated` that returns true or false depending on
|
165
|
+
whether or not an instance of `User` is authenticated. As a side
|
166
|
+
note, [Shield][shield] is a library that provides just that.
|
167
|
+
|
168
|
+
[shield]: https://github.com/cyx/shield
|
169
|
+
|
170
|
+
Captures
|
171
|
+
--------
|
172
|
+
|
173
|
+
When a symbol is provided, `on` will try to consume a segment of
|
174
|
+
the path. A segment is defined as any sequence of characters after
|
175
|
+
a slash and until either another slash or the end of the string.
|
176
|
+
The captured value is stored in the `inbox` hash under the key that
|
177
|
+
was provided as the argument to `on`. For example, after a call to
|
178
|
+
`on(:user_id)`, the value for the segment will be stored at
|
179
|
+
`inbox[:user_id]`. When mounting an application called `users` with
|
180
|
+
the command `run(users)`, an inbox can be provided as the second
|
181
|
+
argument: `run(users, inbox)`. That allows apps to share previous
|
182
|
+
captures.
|
183
|
+
|
184
|
+
Security
|
185
|
+
--------
|
186
|
+
|
187
|
+
There are no security features built into this routing library. A
|
188
|
+
framework using this library should implement the security layer.
|
189
|
+
|
190
|
+
Rendering
|
191
|
+
---------
|
192
|
+
|
193
|
+
There are no rendering features built into this routing library. A
|
194
|
+
framework that uses this routing library can easily implement helpers
|
195
|
+
for rendering.
|
196
|
+
|
197
|
+
Trivia
|
198
|
+
------
|
199
|
+
|
200
|
+
The name comes from SImple ROuter, so it more or less rhymes with
|
201
|
+
"zero". An initial idea was to release a new version of Cuba that
|
202
|
+
broke backward compatibility, but in the end my friends suggested
|
203
|
+
to release this as a separate library. In the future, some ideas
|
204
|
+
of this library could be included in Cuba as well.
|
205
|
+
|
206
|
+
Installation
|
207
|
+
------------
|
208
|
+
|
209
|
+
```
|
210
|
+
$ gem install syro
|
211
|
+
```
|
data/lib/syro.rb
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2015 Michel Martens
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require "rack"
|
24
|
+
require "seg"
|
25
|
+
|
26
|
+
class Syro
|
27
|
+
|
28
|
+
# Method override parameter
|
29
|
+
OVERRIDE = "_method".freeze
|
30
|
+
|
31
|
+
# HTTP environment variables
|
32
|
+
PATH_INFO = "PATH_INFO".freeze
|
33
|
+
SCRIPT_NAME = "SCRIPT_NAME".freeze
|
34
|
+
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
35
|
+
|
36
|
+
# Response headers
|
37
|
+
LOCATION = "Location".freeze
|
38
|
+
CONTENT_TYPE = "Content-Type".freeze
|
39
|
+
CONTENT_LENGTH = "Content-Length".freeze
|
40
|
+
CONTENT_TYPE_DEFAULT = "text/html".freeze
|
41
|
+
|
42
|
+
# Request methods
|
43
|
+
GET = 'GET'.freeze
|
44
|
+
POST = 'POST'.freeze
|
45
|
+
PATCH = 'PATCH'.freeze
|
46
|
+
DELETE = 'DELETE'.freeze
|
47
|
+
|
48
|
+
# Available methods
|
49
|
+
METHODS = [GET, POST, PATCH, DELETE].freeze
|
50
|
+
|
51
|
+
class Response
|
52
|
+
attr_accessor :status
|
53
|
+
|
54
|
+
attr :body
|
55
|
+
attr :headers
|
56
|
+
|
57
|
+
def initialize(headers = {})
|
58
|
+
@status = nil
|
59
|
+
@headers = headers
|
60
|
+
@body = []
|
61
|
+
@length = 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def [](key)
|
65
|
+
@headers[key]
|
66
|
+
end
|
67
|
+
|
68
|
+
def []=(key, value)
|
69
|
+
@headers[key] = value
|
70
|
+
end
|
71
|
+
|
72
|
+
def write(str)
|
73
|
+
s = str.to_s
|
74
|
+
|
75
|
+
@length += s.bytesize
|
76
|
+
@headers[Syro::CONTENT_LENGTH] = @length.to_s
|
77
|
+
@body << s
|
78
|
+
end
|
79
|
+
|
80
|
+
def redirect(path, status = 302)
|
81
|
+
@headers[Syro::LOCATION] = path
|
82
|
+
@status = status
|
83
|
+
end
|
84
|
+
|
85
|
+
def finish
|
86
|
+
[@status, @headers, @body]
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_cookie(key, value)
|
90
|
+
Rack::Utils.set_cookie_header!(@headers, key, value)
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete_cookie(key, value = {})
|
94
|
+
Rack::Utils.delete_cookie_header!(@headers, key, value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Sandbox
|
99
|
+
def initialize(code)
|
100
|
+
@syro_code = code
|
101
|
+
end
|
102
|
+
|
103
|
+
def env
|
104
|
+
@syro_env
|
105
|
+
end
|
106
|
+
|
107
|
+
def req
|
108
|
+
@syro_req
|
109
|
+
end
|
110
|
+
|
111
|
+
def res
|
112
|
+
@syro_res
|
113
|
+
end
|
114
|
+
|
115
|
+
def path
|
116
|
+
@syro_path
|
117
|
+
end
|
118
|
+
|
119
|
+
def inbox
|
120
|
+
@syro_inbox
|
121
|
+
end
|
122
|
+
|
123
|
+
def call(env, inbox)
|
124
|
+
@syro_env = env
|
125
|
+
@syro_req = Rack::Request.new(env)
|
126
|
+
@syro_res = Syro::Response.new({})
|
127
|
+
@syro_path = Seg.new(env.fetch(Syro::PATH_INFO))
|
128
|
+
@syro_inbox = inbox
|
129
|
+
|
130
|
+
|
131
|
+
if env[Syro::REQUEST_METHOD] == Syro::POST
|
132
|
+
value = @syro_req.POST[Syro::OVERRIDE]
|
133
|
+
|
134
|
+
if value != nil
|
135
|
+
env[Syro::REQUEST_METHOD] = value.upcase
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
@syro_method = Syro::METHODS.index(env[Syro::REQUEST_METHOD])
|
140
|
+
|
141
|
+
result = catch(:halt) do
|
142
|
+
instance_eval(&@syro_code)
|
143
|
+
|
144
|
+
@syro_res.status = 404
|
145
|
+
@syro_res.finish
|
146
|
+
end
|
147
|
+
|
148
|
+
if result[0].nil?
|
149
|
+
if result[2].empty?
|
150
|
+
result[0] = 404
|
151
|
+
else
|
152
|
+
result[1][Syro::CONTENT_TYPE] ||=
|
153
|
+
Syro::CONTENT_TYPE_DEFAULT
|
154
|
+
result[0] = 200
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
def run(app, inbox = {})
|
162
|
+
env[Syro::PATH_INFO] = @syro_path.curr
|
163
|
+
env[Syro::SCRIPT_NAME] = @syro_path.prev
|
164
|
+
|
165
|
+
halt(app.call(env, inbox))
|
166
|
+
end
|
167
|
+
|
168
|
+
def halt(response)
|
169
|
+
throw(:halt, response)
|
170
|
+
end
|
171
|
+
|
172
|
+
def match(arg)
|
173
|
+
case arg
|
174
|
+
when String then @syro_path.consume(arg)
|
175
|
+
when Symbol then @syro_path.capture(arg, inbox)
|
176
|
+
when true then true
|
177
|
+
else false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def on(arg)
|
182
|
+
if match(arg)
|
183
|
+
yield
|
184
|
+
|
185
|
+
halt(res.finish)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def root?
|
190
|
+
@syro_path.root?
|
191
|
+
end
|
192
|
+
|
193
|
+
def root
|
194
|
+
if root?
|
195
|
+
yield
|
196
|
+
|
197
|
+
halt(res.finish)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def get?
|
202
|
+
@syro_method == 0
|
203
|
+
end
|
204
|
+
|
205
|
+
def post?
|
206
|
+
@syro_method == 1
|
207
|
+
end
|
208
|
+
|
209
|
+
def patch?
|
210
|
+
@syro_method == 2
|
211
|
+
end
|
212
|
+
|
213
|
+
def delete?
|
214
|
+
@syro_method == 3
|
215
|
+
end
|
216
|
+
|
217
|
+
def get
|
218
|
+
if root? && get?
|
219
|
+
yield
|
220
|
+
|
221
|
+
halt(res.finish)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def post
|
226
|
+
if root? && post?
|
227
|
+
yield
|
228
|
+
|
229
|
+
halt(res.finish)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def patch
|
234
|
+
if root? && patch?
|
235
|
+
yield
|
236
|
+
|
237
|
+
halt(res.finish)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def delete
|
242
|
+
if root? && delete?
|
243
|
+
yield
|
244
|
+
|
245
|
+
halt(res.finish)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def initialize(&block)
|
251
|
+
@sandbox = Sandbox.new(block)
|
252
|
+
end
|
253
|
+
|
254
|
+
def call(env, inbox = {})
|
255
|
+
@sandbox.call(env, inbox)
|
256
|
+
end
|
257
|
+
end
|
data/makefile
ADDED
data/syro.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "syro"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "Simple router"
|
5
|
+
s.description = "Simple router for web applications"
|
6
|
+
s.authors = ["Michel Martens"]
|
7
|
+
s.email = ["michel@soveran.com"]
|
8
|
+
s.homepage = "https://github.com/soveran/syro"
|
9
|
+
s.license = "MIT"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
|
13
|
+
s.add_dependency "seg"
|
14
|
+
s.add_dependency "rack"
|
15
|
+
s.add_development_dependency "cutest"
|
16
|
+
s.add_development_dependency "rack-test"
|
17
|
+
end
|
data/test/all.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
admin = Syro.new {
|
2
|
+
get {
|
3
|
+
res.write "GET /admin"
|
4
|
+
}
|
5
|
+
}
|
6
|
+
|
7
|
+
platforms = Syro.new {
|
8
|
+
@id = inbox.fetch(:id)
|
9
|
+
|
10
|
+
get {
|
11
|
+
res.write "GET /platforms/#{@id}"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
comments = Syro.new {
|
16
|
+
get {
|
17
|
+
res.write sprintf("GET %s/%s/comments",
|
18
|
+
inbox[:path],
|
19
|
+
inbox[:post_id])
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
app = Syro.new {
|
24
|
+
get {
|
25
|
+
res.write "GET /"
|
26
|
+
}
|
27
|
+
|
28
|
+
post {
|
29
|
+
on(req.POST["user"] != nil) {
|
30
|
+
res.write "POST / (user)"
|
31
|
+
}
|
32
|
+
|
33
|
+
on(true) {
|
34
|
+
res.write "POST / (none)"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
on("foo") {
|
39
|
+
on("bar") {
|
40
|
+
on("baz") {
|
41
|
+
res.write("error")
|
42
|
+
}
|
43
|
+
|
44
|
+
get {
|
45
|
+
res.write("GET /foo/bar")
|
46
|
+
}
|
47
|
+
|
48
|
+
post {
|
49
|
+
res.write("POST /foo/bar")
|
50
|
+
}
|
51
|
+
|
52
|
+
patch {
|
53
|
+
res.write("PATCH /foo/bar")
|
54
|
+
}
|
55
|
+
|
56
|
+
delete {
|
57
|
+
res.write("DELETE /foo/bar")
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
on("bar/baz") {
|
63
|
+
get {
|
64
|
+
res.write("GET /bar/baz")
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
on("admin") {
|
69
|
+
run(admin)
|
70
|
+
}
|
71
|
+
|
72
|
+
on("platforms") {
|
73
|
+
run(platforms, id: 42)
|
74
|
+
}
|
75
|
+
|
76
|
+
on("users") {
|
77
|
+
on(:id) {
|
78
|
+
res.write(sprintf("GET /users/%s", inbox[:id]))
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
on("posts") {
|
83
|
+
@path = path.prev
|
84
|
+
|
85
|
+
on(:post_id) {
|
86
|
+
on("comments") {
|
87
|
+
run(comments, inbox.merge(path: @path))
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
setup do
|
94
|
+
Driver.new(app)
|
95
|
+
end
|
96
|
+
|
97
|
+
test "path + verb" do |f|
|
98
|
+
f.get("/foo/bar")
|
99
|
+
assert_equal 200, f.last_response.status
|
100
|
+
assert_equal "GET /foo/bar", f.last_response.body
|
101
|
+
|
102
|
+
f.patch("/foo/bar")
|
103
|
+
assert_equal 200, f.last_response.status
|
104
|
+
assert_equal "PATCH /foo/bar", f.last_response.body
|
105
|
+
|
106
|
+
f.post("/foo/bar")
|
107
|
+
assert_equal 200, f.last_response.status
|
108
|
+
assert_equal "POST /foo/bar", f.last_response.body
|
109
|
+
|
110
|
+
f.delete("/foo/bar")
|
111
|
+
assert_equal 200, f.last_response.status
|
112
|
+
assert_equal "DELETE /foo/bar", f.last_response.body
|
113
|
+
end
|
114
|
+
|
115
|
+
test "verbs match only on root" do |f|
|
116
|
+
f.get("/bar/baz/foo")
|
117
|
+
assert_equal "", f.last_response.body
|
118
|
+
assert_equal 404, f.last_response.status
|
119
|
+
end
|
120
|
+
|
121
|
+
test "mounted app" do |f|
|
122
|
+
f.get("/admin")
|
123
|
+
assert_equal "GET /admin", f.last_response.body
|
124
|
+
assert_equal 200, f.last_response.status
|
125
|
+
end
|
126
|
+
|
127
|
+
test "mounted app + inbox" do |f|
|
128
|
+
f.get("/platforms")
|
129
|
+
assert_equal "GET /platforms/42", f.last_response.body
|
130
|
+
assert_equal 200, f.last_response.status
|
131
|
+
end
|
132
|
+
|
133
|
+
test "root" do |f|
|
134
|
+
f.get("/")
|
135
|
+
assert_equal "GET /", f.last_response.body
|
136
|
+
assert_equal 200, f.last_response.status
|
137
|
+
end
|
138
|
+
|
139
|
+
test "captures" do |f|
|
140
|
+
f.get("/users/42")
|
141
|
+
assert_equal "GET /users/42", f.last_response.body
|
142
|
+
assert_equal 200, f.last_response.status
|
143
|
+
end
|
144
|
+
|
145
|
+
test "post values" do |f|
|
146
|
+
f.post("/", "user" => { "username" => "foo" })
|
147
|
+
assert_equal "POST / (user)", f.last_response.body
|
148
|
+
assert_equal 200, f.last_response.status
|
149
|
+
|
150
|
+
f.post("/")
|
151
|
+
assert_equal "POST / (none)", f.last_response.body
|
152
|
+
assert_equal 200, f.last_response.status
|
153
|
+
end
|
154
|
+
|
155
|
+
test "inherited inbox" do |f|
|
156
|
+
f.get("/posts/42/comments")
|
157
|
+
assert_equal "GET /posts/42/comments", f.last_response.body
|
158
|
+
assert_equal 200, f.last_response.status
|
159
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michel Martens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: seg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cutest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Simple router for web applications
|
70
|
+
email:
|
71
|
+
- michel@soveran.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gems
|
77
|
+
- CHANGELOG
|
78
|
+
- CONTRIBUTING
|
79
|
+
- LICENSE
|
80
|
+
- README.md
|
81
|
+
- lib/syro.rb
|
82
|
+
- makefile
|
83
|
+
- syro.gemspec
|
84
|
+
- test/all.rb
|
85
|
+
- test/helper.rb
|
86
|
+
homepage: https://github.com/soveran/syro
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata: {}
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.0.14
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: Simple router
|
110
|
+
test_files: []
|