tynn 2.0.0.beta3 → 2.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +83 -660
- data/lib/tynn.rb +186 -50
- data/lib/tynn/environment.rb +1 -84
- data/lib/tynn/json.rb +24 -25
- data/lib/tynn/render.rb +72 -33
- data/lib/tynn/request.rb +2 -82
- data/lib/tynn/response.rb +4 -118
- data/lib/tynn/secure_headers.rb +2 -24
- data/lib/tynn/session.rb +4 -87
- data/lib/tynn/ssl.rb +12 -71
- data/lib/tynn/static.rb +1 -19
- data/lib/tynn/test.rb +2 -125
- data/lib/tynn/utils.rb +51 -8
- data/lib/tynn/version.rb +1 -1
- data/lib/tynn/x/versioning.rb +29 -0
- data/test/json_test.rb +197 -7
- data/test/render_test.rb +56 -8
- data/test/request_test.rb +2 -0
- data/test/response_test.rb +7 -3
- data/test/routing_test.rb +56 -4
- data/test/settings_test.rb +31 -0
- data/test/versioning_test.rb +201 -0
- metadata +10 -35
- data/lib/tynn/base.rb +0 -434
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4098115e1a01a01f10ae4209a2ffa63d9296cee4
|
4
|
+
data.tar.gz: f7f7fa51ec9c4ac2202f68f2f6425b33454ff46a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28661462ba5afd767a7da093852c67093e18449b63bee21bca0d302c3c632f0c1fe3f709847a12bcb46bcb4a1652babe5f64080e32131f3d0fcd03a09232d4ce
|
7
|
+
data.tar.gz: 3c01a710785c25eceba0af49791c3b265daff653d4107b8bd6029bafcdc45f16a9945eb0ae5430cb0ccce4669e257b92f0f9ddebfaa8c897a04acc98368ae349
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2015-2016 Francesco
|
3
|
+
Copyright (c) 2015-2016 Francesco Rodriguez
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,96 +1,29 @@
|
|
1
1
|
# Tynn
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/frodsan/tynn.svg?branch=master)](https://travis-ci.org/frodsan/tynn)
|
4
|
+
[![Documentation Status](https://readthedocs.org/projects/tynn/badge/?version=latest)](http://tynn.xyz/en/latest/?badge=latest)
|
4
5
|
[![Dependency Status](https://gemnasium.com/badges/github.com/frodsan/tynn.svg)](https://gemnasium.com/github.com/frodsan/tynn)
|
5
|
-
[![Code Climate](https://codeclimate.com/github/frodsan/tynn/badges/gpa.svg)](https://codeclimate.com/github/frodsan/tynn)
|
6
|
-
|
7
|
-
A thin library for web development in Ruby.
|
8
|
-
|
9
|
-
* [Getting Started](#getting-started)
|
10
|
-
* [Assumptions](#assumptions)
|
11
|
-
* [Installation](#installation)
|
12
|
-
* [Hello World!](#hello-world)
|
13
|
-
* [Under the Hood](#under-the-hood)
|
14
|
-
* [Tynn on Rack](#tynn-on-rack)
|
15
|
-
* [Routing Basics](#routing-basics)
|
16
|
-
* [HTTP Verbs](#http-verbs)
|
17
|
-
* [Matching path segments](#matching-path-segments)
|
18
|
-
* [Capturing path segments](#capturing-path-segments)
|
19
|
-
* [Matching more than paths](#matching-more-than-paths)
|
20
|
-
* [Routing rules](#routing-rules)
|
21
|
-
* [Composing Applications](#composing-applications)
|
22
|
-
* [Managing Request and Response](#managing-request-and-response)
|
23
|
-
* [Accessing Request Parameters](#accessing-request-parameters)
|
24
|
-
* [Setting a Status Code](#setting-a-status-code)
|
25
|
-
* [Writting to the Response Body](#writting-to-the-response-body)
|
26
|
-
* [Setting HTTP Headers](#setting-http-headers)
|
27
|
-
* [Redirecting a Request](#redirecting-a-request)
|
28
|
-
* [Halting a Request](#halting-a-request)
|
29
|
-
* [Extending Tynn](#extending-tynn)
|
30
|
-
* [Middleware](#middleware)
|
31
|
-
* [Plugins](#plugins)
|
32
|
-
* [Settings](#settings)
|
33
|
-
* [Default Plugins](#default-plugins)
|
34
|
-
* [Environments](#environments)
|
35
|
-
* [Method Override](#method-override)
|
36
|
-
* [JSON](#json)
|
37
|
-
* [Rendering Templates](#rendering-templates)
|
38
|
-
* [Static Files](#static-files)
|
39
|
-
* [Sessions](#sessions)
|
40
|
-
* [Security](#security)
|
41
|
-
* [Secure Headers](#secure-headers)
|
42
|
-
* [HTTPS](#https)
|
43
|
-
* [Testing](#testing)
|
44
|
-
* [API Reference](http://api.tynn.xyz/2.0.0)
|
45
|
-
* [Troubleshooting](#troubleshooting)
|
46
|
-
* [Application handler is missing](#missing_handler)
|
47
|
-
* [Application middleware is frozen](#frozen_middleware)
|
48
|
-
* [Secret key is required](#no_secret_key)
|
49
|
-
* [Secret key is shorter than 30 characters](#short_secret_key)
|
50
|
-
* [Changelog](#changelog)
|
51
|
-
* [Development](#development)
|
52
|
-
* [Contributing](#contributing)
|
53
|
-
* [Build History](#build-history)
|
54
|
-
* [License](#license)
|
55
|
-
|
56
|
-
**NOTE. There is an online version of this README at http://tynn.xyz/.**
|
57
6
|
|
58
|
-
|
59
|
-
|
60
|
-
Tynn is a minimal and flexible library for building JSON web services and web applications in Ruby. It's designed with two main goals: simplicity and extensibility.
|
61
|
-
|
62
|
-
Tynn offers the essentials to handle HTTP requests and also includes mechanisms to easily extend its functionality through [plugins](#plugins) and [middleware][middleware]. Tynn ships with a set of [default plugins](#default-plugins) that you can combine to meet your needs.
|
63
|
-
|
64
|
-
Tynn is not a framework. There is no built-in support for connecting to databases or a common application architecture. This gives you the freedom to choose the appropiate tools for the job at hand.
|
65
|
-
|
66
|
-
Like most web libraries in Ruby, Tynn is built on top of [Rack], a Ruby webserver interface. Because of this, it has the benefits of using existing libraries and a variety of web servers for free.
|
7
|
+
Tynn is a simple and flexible library for building JSON web services and web applications in Ruby.
|
67
8
|
|
68
|
-
|
9
|
+
## Installation
|
69
10
|
|
70
|
-
|
71
|
-
|
72
|
-
This section serves as a high-level introduction to the core features of Tynn. It also covers installation and walks through the creation of a simple application. To get the most out of it, it's recommended to have a basic knowledge about Ruby and HTTP.
|
73
|
-
|
74
|
-
### Installation
|
75
|
-
|
76
|
-
Installing Tynn is pretty straightforward. From the command line, use the `gem` command:
|
11
|
+
Installing Tynn is pretty straightforward:
|
77
12
|
|
78
13
|
```
|
79
14
|
$ gem install tynn
|
80
15
|
```
|
81
16
|
|
82
|
-
If you prefer to use [Bundler] instead, set up the environment with:
|
17
|
+
If you prefer to use [Bundler](http://bundler.io/) instead, set up the environment with:
|
83
18
|
|
84
|
-
```
|
19
|
+
```ruby
|
85
20
|
$ bundle init
|
86
21
|
```
|
87
22
|
|
88
|
-
Then,
|
23
|
+
Then, add this line to the generated Gemfile:
|
89
24
|
|
90
25
|
```ruby
|
91
|
-
|
92
|
-
|
93
|
-
gem "tynn", "~> 2.0"
|
26
|
+
gem "tynn"
|
94
27
|
```
|
95
28
|
|
96
29
|
Finally, install the dependencies with:
|
@@ -99,673 +32,163 @@ Finally, install the dependencies with:
|
|
99
32
|
$ bundle install
|
100
33
|
```
|
101
34
|
|
102
|
-
|
103
|
-
|
104
|
-
### Hello World!
|
35
|
+
## Hello World!
|
105
36
|
|
106
|
-
|
37
|
+
Here's a minimal application:
|
107
38
|
|
108
39
|
```ruby
|
40
|
+
# config.ru
|
109
41
|
require "tynn"
|
110
42
|
|
111
|
-
Tynn
|
112
|
-
|
43
|
+
class MyApp < Tynn
|
44
|
+
define do
|
113
45
|
on get do
|
114
46
|
res.write("Hello World!")
|
115
47
|
end
|
116
48
|
end
|
117
49
|
end
|
118
|
-
```
|
119
50
|
|
120
|
-
|
121
|
-
|
122
|
-
```ruby
|
123
|
-
require File.expand_path("app", __dir__)
|
124
|
-
|
125
|
-
run(Tynn)
|
51
|
+
run(MyApp)
|
126
52
|
```
|
127
53
|
|
128
|
-
|
129
|
-
|
130
|
-
![Starting a Web Server](https://raw.githubusercontent.com/frodsan/tynn/master/docs/images/rackup.png)
|
131
|
-
|
132
|
-
> **NOTE:** To stop the web server, hit `Ctrl+C` in the terminal window where it's running. To verify that the server has stopped you should see your command prompt cursor again.
|
133
|
-
|
134
|
-
Now open a browser window and navigate to http://localhost:9292 to see the greeting message.
|
54
|
+
Start the application with:
|
135
55
|
|
136
|
-
![Hello World!](https://raw.githubusercontent.com/frodsan/tynn/master/docs/images/hello-world.png)
|
137
|
-
|
138
|
-
### Under the hood
|
139
|
-
|
140
|
-
Tynn is all about routing. Routing determines how an application should respond to a client request to a URI (or path) and a specific HTTP request method. In our previous example, that would be the root path (`/`) and the HTTP GET method.
|
141
|
-
|
142
|
-
To get a look on what's happening under the hood, let's see line 4:
|
143
|
-
|
144
|
-
```ruby
|
145
|
-
on root? do
|
146
|
-
# ...
|
147
|
-
end
|
148
56
|
```
|
149
|
-
|
150
|
-
That's a little taste of what we call routing matchers in Tynn. The purpose of the `on` matcher is to execute the given block only if the given argument returns `true`. In our case, `root?` returns `true` because we are accessing the root path (`/`) from our browser.
|
151
|
-
|
152
|
-
One line below there is another type of matcher:
|
153
|
-
|
154
|
-
```ruby
|
155
|
-
on get do
|
156
|
-
res.write("Hello World!")
|
157
|
-
end
|
57
|
+
$ rackup config.ru
|
158
58
|
```
|
159
59
|
|
160
|
-
|
161
|
-
|
162
|
-
```ruby
|
163
|
-
Tynn.define do
|
164
|
-
on get do
|
165
|
-
res.write("Hello World!")
|
166
|
-
end
|
167
|
-
end
|
168
|
-
```
|
169
|
-
|
170
|
-
> **Note.** If you're not familiar with the HTTP methods, check out this [Wikipedia page](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods).
|
171
|
-
|
172
|
-
### Tynn on Rack
|
173
|
-
|
174
|
-
The last part of the puzzle is the `config.ru` file. As we mentioned earlier, Tynn is built on top of Rack ... but what is Rack?
|
175
|
-
|
176
|
-
Rack deals with HTTP requests. It provides a minimal interface between web servers supporting Ruby and Ruby web frameworks. Without Rack, Tynn would have to implement its own handler for each Ruby web server.
|
177
|
-
|
178
|
-
To run the hello world application you used `rackup`, one of the tools that comes with Rack. To use `rackup`, you need to supply a `config.ru` file. This file connects the Rack interface with our Tynn application through the `run` method.
|
179
|
-
|
180
|
-
```ruby
|
181
|
-
run(Tynn)
|
182
|
-
```
|
183
|
-
|
184
|
-
Rack also figures out which server you have available in your system. When we used `rackup`, it fired up WEBrick, a web server built into Ruby by default.
|
185
|
-
|
186
|
-
```
|
187
|
-
$ rackup
|
188
|
-
[2016-05-14 00:25:11] INFO WEBrick 1.3.1
|
189
|
-
```
|
190
|
-
|
191
|
-
Most popular web servers, like [Puma], [Unicorn] or [Thin], have built-in support for Rack, and thus support Tynn too. You can read more about Rack visiting their home page: http://rack.github.io/.
|
192
|
-
|
193
|
-
## Routing basics
|
194
|
-
|
195
|
-
This section covers the routing features of Tynn, such as route definitions, composing applications, and so on.
|
196
|
-
|
197
|
-
### HTTP Verbs
|
198
|
-
|
199
|
-
TODO.
|
200
|
-
|
201
|
-
### Matching path segments
|
202
|
-
|
203
|
-
TODO.
|
204
|
-
|
205
|
-
### Capturing path segments
|
206
|
-
|
207
|
-
TODO.
|
208
|
-
|
209
|
-
### Matching more than paths
|
210
|
-
|
211
|
-
TODO.
|
212
|
-
|
213
|
-
### Routing rules
|
214
|
-
|
215
|
-
TODO.
|
216
|
-
|
217
|
-
### Composing Applications
|
218
|
-
|
219
|
-
TODO.
|
220
|
-
|
221
|
-
## Managing Request and Response
|
222
|
-
|
223
|
-
This section covers the many features Tynn provides to handle requests and responses.
|
224
|
-
|
225
|
-
### Accessing Request Parameters
|
226
|
-
|
227
|
-
TODO.
|
228
|
-
|
229
|
-
### Setting a Status Code
|
230
|
-
|
231
|
-
TODO.
|
232
|
-
|
233
|
-
### Writting to the Response Body
|
234
|
-
|
235
|
-
TODO.
|
60
|
+
Open a browser window and navigate to <http://localhost:9292> to see the greeting message:
|
236
61
|
|
237
|
-
|
62
|
+
![Hello World!](https://raw.githubusercontent.com/frodsan/tynn/master/docs/images/hello-world.png)
|
238
63
|
|
239
|
-
|
64
|
+
## Features
|
240
65
|
|
241
|
-
|
242
|
-
Tynn.define do
|
243
|
-
on get do
|
244
|
-
res.headers["Content-Type"] = "application/json"
|
245
|
-
|
246
|
-
res.write(JSON.generate(status: "ok"))
|
247
|
-
end
|
248
|
-
end
|
249
|
-
```
|
66
|
+
### Routing
|
250
67
|
|
251
|
-
|
68
|
+
Tynn is all about routing. Routing determines how an application should respond to a client request to a URI (or path) and a specific HTTP request method.
|
252
69
|
|
253
70
|
```ruby
|
254
|
-
|
255
|
-
|
256
|
-
Tynn.define do
|
71
|
+
MyApp.define do
|
257
72
|
on get do
|
258
|
-
res.write(
|
73
|
+
res.write("GET /")
|
259
74
|
end
|
260
|
-
end
|
261
|
-
```
|
262
|
-
|
263
|
-
### Redirecting a Request
|
264
|
-
|
265
|
-
To redirect a request to a different location, use `res.redirect`.
|
266
|
-
|
267
|
-
```ruby
|
268
|
-
res.redirect("/pricing")
|
269
|
-
```
|
270
|
-
|
271
|
-
To redirect to a different site, use a fully-qualified URL.
|
272
|
-
|
273
|
-
```ruby
|
274
|
-
res.redirect("https://google.com")
|
275
|
-
```
|
276
|
-
|
277
|
-
By default, the status code is set to `302` (Found). To set a different status code, pass an integer as a second parameter.
|
278
|
-
|
279
|
-
```ruby
|
280
|
-
res.redirect("/projects/1", 301)
|
281
|
-
```
|
282
|
-
|
283
|
-
### Halting a Request
|
284
|
-
|
285
|
-
To immediately stop a request within a route, you can use the `halt` method.
|
286
|
-
|
287
|
-
```ruby
|
288
|
-
halt([status, headers, body])
|
289
|
-
```
|
290
|
-
|
291
|
-
Use `res.finish` to return a response as per Rack's specification.
|
292
|
-
|
293
|
-
```ruby
|
294
|
-
if current_user.nil?
|
295
|
-
res.redirect("/login")
|
296
|
-
|
297
|
-
halt(res.finish)
|
298
|
-
end
|
299
|
-
|
300
|
-
# This won't be reached if current_user is nil
|
301
|
-
```
|
302
|
-
|
303
|
-
## Extending Tynn
|
304
|
-
|
305
|
-
TODO.
|
306
75
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
to use Rack middleware in Tynn. This is how you add a middleware (for example
|
311
|
-
`YourMiddleware`) to your app:
|
312
|
-
|
313
|
-
```ruby
|
314
|
-
Tynn.use(YourMiddleware)
|
315
|
-
```
|
316
|
-
|
317
|
-
You can use any Rack middleware to your app, it is not specific to Tynn. You
|
318
|
-
can find a list of Rack middleware [here][middleware].
|
319
|
-
|
320
|
-
[middleware]: https://github.com/rack/rack/wiki/list-of-middleware
|
321
|
-
|
322
|
-
### Plugins
|
323
|
-
|
324
|
-
A way to extend Tynn is to use the plugin API. A plugin is just a module which can contain any of the following rules:
|
325
|
-
|
326
|
-
- If a `ClassMethods` module is defined, it extends the application class.
|
327
|
-
|
328
|
-
- If a `InstanceMethods` module is defined, it's included in the application.
|
329
|
-
|
330
|
-
- If a `setup` method is defined, it will be called last. This method can be used to configure the plugin.
|
331
|
-
|
332
|
-
The following is a complete example of the plugin API.
|
333
|
-
|
334
|
-
```ruby
|
335
|
-
require "valuta"
|
336
|
-
|
337
|
-
module CurrencyHelper
|
338
|
-
def self.setup(app, currency: "$")
|
339
|
-
self.currency = currency
|
340
|
-
end
|
341
|
-
|
342
|
-
module ClassMethods
|
343
|
-
def currency
|
344
|
-
@currency
|
345
|
-
end
|
346
|
-
|
347
|
-
def currency=(currency)
|
348
|
-
@currency = currency
|
76
|
+
on "posts" do
|
77
|
+
on get do
|
78
|
+
res.write("GET /posts")
|
349
79
|
end
|
350
|
-
end
|
351
80
|
|
352
|
-
|
353
|
-
|
354
|
-
Valuta.convert(value, prefix: self.class.currency)
|
81
|
+
on post do
|
82
|
+
res.write("POST /posts")
|
355
83
|
end
|
356
|
-
end
|
357
|
-
end
|
358
|
-
```
|
359
|
-
|
360
|
-
To load the plugin into the application, use the `plugin` method.
|
361
|
-
|
362
|
-
```ruby
|
363
|
-
App.plugin(CurrencyHelper, currency: "$")
|
364
|
-
```
|
365
|
-
|
366
|
-
Here is the plugin in action:
|
367
|
-
|
368
|
-
```ruby
|
369
|
-
App.currency # => "$"
|
370
|
-
|
371
|
-
App.define do
|
372
|
-
on get do
|
373
|
-
res.write(to_currency(4567))
|
374
|
-
end
|
375
|
-
end
|
376
|
-
# GET / => 200 $4,567
|
377
|
-
```
|
378
84
|
|
379
|
-
|
85
|
+
on :id do |id|
|
86
|
+
@post = Post[id]
|
380
87
|
|
381
|
-
|
88
|
+
on post.nil? do
|
89
|
+
res.status = 404
|
90
|
+
res.write("Not found")
|
91
|
+
end
|
382
92
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
class Guests < Tynn; end
|
387
|
-
class Users < Tynn; end
|
388
|
-
class Adminds < Tynn; end
|
389
|
-
|
390
|
-
Users.set(:layout, "users/layout")
|
391
|
-
Admins.set(:layout, "admins/layout")
|
392
|
-
|
393
|
-
Guests.settings[:layout] # => "layout"
|
394
|
-
Users.settings[:layout] # => "users/layout"
|
395
|
-
Admins.settings[:layout] # => "admins/layout"
|
396
|
-
```
|
93
|
+
on get do
|
94
|
+
res.write("GET /posts/#{ @post.id }")
|
95
|
+
end
|
397
96
|
|
398
|
-
|
97
|
+
on put do
|
98
|
+
res.write("PUT /posts/#{ @post.id }")
|
99
|
+
end
|
399
100
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
def currency=(currency)
|
404
|
-
set(:currency, currency)
|
405
|
-
end
|
101
|
+
on patch do
|
102
|
+
res.write("PATCH /posts/#{ @post.id }")
|
103
|
+
end
|
406
104
|
|
407
|
-
|
408
|
-
|
105
|
+
on delete do
|
106
|
+
res.write("DELETE /posts/#{ @post.id }")
|
107
|
+
end
|
409
108
|
end
|
410
109
|
end
|
411
110
|
end
|
412
111
|
```
|
413
112
|
|
414
|
-
|
415
|
-
|
416
|
-
Tynn ships with a set of default plugins. The following sections cover the default plugins and extensions shipped with Tynn.
|
113
|
+
### Composition
|
417
114
|
|
418
|
-
|
419
|
-
|
420
|
-
Tynn ships with [Tynn::Environment] to set and check the current environment for the application.
|
115
|
+
You can mount an application inside another. This adds the possibility to run modular applications.
|
421
116
|
|
422
117
|
```ruby
|
423
|
-
|
424
|
-
require "tynn/environment"
|
425
|
-
|
426
|
-
Tynn.plugin(Tynn::Environment)
|
427
|
-
```
|
428
|
-
|
429
|
-
The default environment is based on the `RACK_ENV` environment variable.
|
430
|
-
|
431
|
-
```ruby
|
432
|
-
ENV["RACK_ENV"]
|
433
|
-
# => "test"
|
434
|
-
|
435
|
-
Tynn.environment
|
436
|
-
# => :test
|
437
|
-
```
|
438
|
-
|
439
|
-
If `ENV["RACK_ENV"]` is `nil`, the default value is `:development`.
|
440
|
-
|
441
|
-
```ruby
|
442
|
-
Tynn.environment
|
443
|
-
# => :development
|
444
|
-
```
|
445
|
-
|
446
|
-
To change the current environment, use the `environment=` method.
|
447
|
-
|
448
|
-
```ruby
|
449
|
-
Tynn.environment = :development
|
450
|
-
|
451
|
-
Tynn.environment
|
452
|
-
# => :development
|
453
|
-
```
|
454
|
-
|
455
|
-
To check the current environment, use: `development?`, `test?`,
|
456
|
-
`production?` or `staging?`.
|
457
|
-
|
458
|
-
```ruby
|
459
|
-
Tynn.plugin(Tynn::SSL) if Tynn.production?
|
460
|
-
```
|
461
|
-
|
462
|
-
Perform operations on specific environments with the `configure` method.
|
463
|
-
|
464
|
-
```ruby
|
465
|
-
Tynn.configure(:development) do |app|
|
466
|
-
app.use(Tynn::Static, %w(/js /css /images))
|
118
|
+
class Posts < MyApp
|
467
119
|
end
|
468
120
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
```
|
473
|
-
|
474
|
-
### JSON
|
475
|
-
|
476
|
-
Tynn ships with [Tynn::JSON] to serve JSON resources. It adds a helper method, called `json`, that generates a JSON document and writes it to the response body. This also automatically sets the content type header to `application/json`.
|
477
|
-
|
478
|
-
```ruby
|
479
|
-
require "tynn"
|
480
|
-
require "tynn/json"
|
481
|
-
|
482
|
-
Tynn.define do
|
483
|
-
on "array" do
|
484
|
-
on get do
|
485
|
-
json(["foo", "bar", "baz"])
|
486
|
-
end
|
121
|
+
Posts.define do
|
122
|
+
on get do
|
123
|
+
res.write("GET /posts")
|
487
124
|
end
|
488
125
|
|
489
|
-
|
490
|
-
on get do
|
491
|
-
json(foo: "bar")
|
492
|
-
end
|
493
|
-
end
|
126
|
+
# ...
|
494
127
|
end
|
495
|
-
```
|
496
|
-
|
497
|
-
### Rendering Templates
|
498
|
-
|
499
|
-
TODO.
|
500
|
-
|
501
|
-
### Method Override
|
502
|
-
|
503
|
-
HTML Forms only support GET and POST requests. To perform other actions such as PUT, PATCH or DELETE, use the [Rack::MethodOverride] middleware. Note that there is no need to add any new dependencies to the application as it's included in Rack already.
|
504
128
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
This uses a POST form to simulate a request with a non-supported method. In order to succeed, a hidden input field, with the name `_method` and the method name as the value, needs to be included. The following example simulates a PUT
|
510
|
-
request.
|
511
|
-
|
512
|
-
```html
|
513
|
-
<form method="POST" action="/posts/1">
|
514
|
-
<input type="hidden" name="_method" value="PUT">
|
515
|
-
<!-- ... -->
|
516
|
-
</form>
|
517
|
-
```
|
518
|
-
|
519
|
-
Now, this will trigger the `put` matcher in the application.
|
520
|
-
|
521
|
-
```ruby
|
522
|
-
Posts.define do
|
523
|
-
on put do
|
524
|
-
post.update(req.params["post"])
|
129
|
+
MyApp.define do
|
130
|
+
on "posts" do
|
131
|
+
run(Posts)
|
525
132
|
end
|
526
133
|
end
|
527
134
|
```
|
528
135
|
|
529
|
-
|
530
|
-
|
531
|
-
Tynn ships with [Tynn::Static] to serve static files such as images, CSS, JavaScript and others.
|
532
|
-
|
533
|
-
```ruby
|
534
|
-
require "tynn"
|
535
|
-
require "tynn/static"
|
536
|
-
|
537
|
-
Tynn.plugin(Tynn::Static, %w(/js /css /images))
|
538
|
-
```
|
539
|
-
|
540
|
-
By default, static files are served from the folder `public` in the current directory. You can specify a different location by passing the `:root` option:
|
541
|
-
|
542
|
-
```ruby
|
543
|
-
Tynn.plugin(Tynn::Static, %w(/js /css /images), root: "assets")
|
544
|
-
```
|
545
|
-
|
546
|
-
As you can see in the table below, the name of static directory is not included in the URL because the files are looked up relative to that directory.
|
547
|
-
|
136
|
+
Settings and extensions are inherited by the subclasses.
|
548
137
|
|
549
|
-
|
550
|
-
| ---------------------------- | -------------------------------------- |
|
551
|
-
| ./public/js/application.js | http://example.org/js/application.js |
|
552
|
-
| ./public/css/application.css | http://example.org/css/application.css |
|
553
|
-
| ./public/images/logo.png | http://example.org/images/logo.png |
|
138
|
+
### Plugins & Middleware
|
554
139
|
|
555
|
-
|
556
|
-
|
557
|
-
```ruby
|
558
|
-
Tynn.plugin(
|
559
|
-
Tynn::Static,
|
560
|
-
%w(/js /css /images),
|
561
|
-
root: File.expand_path("public", __dir__)
|
562
|
-
)
|
563
|
-
```
|
564
|
-
|
565
|
-
### Sessions
|
566
|
-
|
567
|
-
TODO.
|
568
|
-
|
569
|
-
## Security
|
570
|
-
|
571
|
-
Many of the default plugins include secure default options. Here are some plugins that can improve the security of your web application.
|
572
|
-
|
573
|
-
### Secure Headers
|
574
|
-
|
575
|
-
Tynn ships with the [Tynn::SecureHeaders] plugin.
|
140
|
+
Tynn includes mechanisms to easily extend its functionality through plugins and middleware. It also ships with a set of [default plugins][documentation] that you can combine to meet your needs:
|
576
141
|
|
577
142
|
```ruby
|
578
143
|
require "tynn"
|
144
|
+
require "tynn/environments"
|
145
|
+
require "tynn/json"
|
579
146
|
require "tynn/secure_headers"
|
147
|
+
require "tynn/ssl"
|
580
148
|
|
581
|
-
Tynn
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
- **X-Content-Type-Options:** Prevents IE and Chrome from [Content Type Sniffing][sniffing]. Defaults to `"nosniff"`.
|
587
|
-
|
588
|
-
- **X-Frame-Options:** Provides [Clickjacking] protection. Defaults to `"deny"`.
|
589
|
-
|
590
|
-
- **X-XSS-Protection:** Enables the XSS protection filter built into IE, Chrome and Safari. This filter is usually enabled by default, the use of this header is to re-enable it if it was turned off by the user. Defaults to `"1; mode=block"</tt`.
|
591
|
-
|
592
|
-
You can configure the default headers through the `:default_headers` setting.
|
593
|
-
|
594
|
-
```ruby
|
595
|
-
Tynn.set(:default_headers, {
|
596
|
-
"X-Frame-Options" => "sameorigin"
|
597
|
-
})
|
598
|
-
```
|
599
|
-
|
600
|
-
### HTTTPS
|
601
|
-
|
602
|
-
TODO.
|
603
|
-
|
604
|
-
## Testing
|
605
|
-
|
606
|
-
Tynn ships with [Tynn::Test], a simple helper class to simulate requests to your application.
|
607
|
-
|
608
|
-
```ruby
|
609
|
-
require "tynn"
|
610
|
-
require "tynn/test"
|
611
|
-
|
612
|
-
Tynn.define do
|
613
|
-
on get do
|
614
|
-
res.write("hei")
|
615
|
-
end
|
149
|
+
class MyApp < Tynn
|
150
|
+
plugin(Tynn::Environments)
|
151
|
+
plugin(Tynn::JSON)
|
152
|
+
plugin(Tynn::SecureHeaders)
|
153
|
+
plugin(Tynn::SSL, Tynn.production?)
|
616
154
|
end
|
617
155
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
200 == ts.res.status # => true
|
622
|
-
"hei" == ts.res.body.join # => true
|
623
|
-
```
|
624
|
-
|
625
|
-
[Tynn::Test] is test-framework agnostic. The following example uses [Minitest]:
|
626
|
-
|
627
|
-
```ruby
|
628
|
-
require "minitest/autorun"
|
629
|
-
require "tynn/test"
|
630
|
-
|
631
|
-
class GuestsRouteTest < Minitest::Test
|
632
|
-
def setup
|
633
|
-
@ts = Tynn::Test.new
|
634
|
-
end
|
635
|
-
|
636
|
-
def test_home
|
637
|
-
@ts.get("/")
|
638
|
-
|
639
|
-
assert_equal 200, @ts.res.status
|
640
|
-
assert_equal "Hello World!", @ts.res.body.join
|
641
|
-
assert_equal "text/html", @ts.res.headers["Content-Type"]
|
156
|
+
MyApp.define do
|
157
|
+
on get do
|
158
|
+
json(message: "Hello World!")
|
642
159
|
end
|
643
160
|
end
|
644
161
|
```
|
645
162
|
|
646
|
-
|
647
|
-
|
648
|
-
## Troubleshooting
|
649
|
-
|
650
|
-
If you have problems with running Tynn, see below or [ask in GitHub Issues](https://github.com/frodsan/tynn/issues/new).
|
651
|
-
|
652
|
-
* <a name="missing_handler"></a>
|
653
|
-
**Application handler is missing**
|
654
|
-
<sup>[[link](#missing_handler)]</sup>
|
655
|
-
|
656
|
-
Application handler is probably not set. Try `#define`:
|
657
|
-
|
658
|
-
```ruby
|
659
|
-
MyApp.define do
|
660
|
-
# ...
|
661
|
-
end
|
662
|
-
|
663
|
-
run(MyApp)
|
664
|
-
```
|
665
|
-
|
666
|
-
* <a name="frozen_middleware"></a>
|
667
|
-
**Application middleware is frozen**
|
668
|
-
<sup>[[link](#frozen_middleware)]</sup>
|
669
|
-
|
670
|
-
Application middleware is frozen when application handler is set. Please, set the middleware before setting the application handler.
|
671
|
-
|
672
|
-
```ruby
|
673
|
-
# bad
|
674
|
-
MyApp.define do
|
675
|
-
# ...
|
676
|
-
end
|
677
|
-
|
678
|
-
MyApp.use(Middleware)
|
679
|
-
|
680
|
-
# good
|
681
|
-
MyApp.use(Middleware)
|
163
|
+
Tynn includes plugins to manage sessions, serve static files, render view templates, and so on.
|
682
164
|
|
683
|
-
|
684
|
-
# ...
|
685
|
-
end
|
686
|
-
```
|
687
|
-
|
688
|
-
* <a name="no_secret_key"></a>
|
689
|
-
**Secret key is required**
|
690
|
-
<sup>[[link](#no_secret_key)]</sup>
|
691
|
-
|
692
|
-
[Tynn::Session] uses a secret key to sign the cookie's data, thus unauthorized means can't alter it. Please, add the secret option to your code:
|
693
|
-
|
694
|
-
```ruby
|
695
|
-
MyApp.plugin(Tynn::Session, secret: "__change_me__", ...)
|
696
|
-
```
|
697
|
-
|
698
|
-
If you're sharing your code publicly, make sure the secret key is kept private. Knowing the secret allows an attacker to tamper the data. You can use environment variables to store the secret key:
|
699
|
-
|
700
|
-
```ruby
|
701
|
-
MyApp.plugin(Tynn::Session, secret: ENV.fetch("SESSION_SECRET"), ...)
|
702
|
-
```
|
703
|
-
|
704
|
-
* <a name="short_secret_key"></a>
|
705
|
-
**Secret key is shorter than 30 characters**
|
706
|
-
<sup>[[link](#short_secret_key)]</sup>
|
707
|
-
|
708
|
-
The secret provided is shorter than the minimum length. Make sure the secret is long and all random. You can generate a secure secret key with:
|
709
|
-
|
710
|
-
```
|
711
|
-
$ ruby -r securerandom -e "puts SecureRandom.hex(64)"
|
712
|
-
929234f24e8c7450166a88142a...
|
713
|
-
```
|
165
|
+
### Rack Ecosystem
|
714
166
|
|
715
|
-
|
167
|
+
Like most web libraries in Ruby, Tynn is built on top of [Rack](http://rack.github.io/), a Ruby webserver interface. Because of this, it has the benefits of using existing libraries and a variety of web servers for free, like [Puma](http://puma.io/) or [Unicorn](https://unicorn.bogomips.org/).
|
716
168
|
|
717
|
-
|
169
|
+
### Security by default
|
718
170
|
|
719
|
-
|
171
|
+
In addition to the security-related plugins shipped with Tynn, all the plugins provide secure options by default, like escaping HTML unsafe characters, CSRF protection, preventing session tampering, etc.
|
720
172
|
|
721
|
-
Fork the project with:
|
722
173
|
|
723
|
-
|
724
|
-
$ git clone git@github.com:frodsan/tynn.git
|
725
|
-
```
|
174
|
+
## Getting Started
|
726
175
|
|
727
|
-
|
176
|
+
You can read more about Tynn in the [official documentation][documentation].
|
728
177
|
|
729
|
-
|
730
|
-
$ bundle install
|
731
|
-
```
|
732
|
-
|
733
|
-
To run the test suite, do:
|
178
|
+
## Compatibility
|
734
179
|
|
735
|
-
|
736
|
-
$ rake test
|
737
|
-
```
|
180
|
+
Tynn supports Ruby 2.3+ and Rack 2.0+.
|
738
181
|
|
739
182
|
## Contributing
|
740
183
|
|
741
|
-
|
184
|
+
If you'd like to contribute to Tynn, check the [contribution guidelines](https://github.com/frodsan/tynn/blob/master/docs/contributing.md).
|
742
185
|
|
743
|
-
##
|
186
|
+
## Inspiration
|
744
187
|
|
745
|
-
[
|
188
|
+
Tynn takes design cues from other web libraries like [Rails](http://rubyonrails.org/), [Sinatra](http://www.sinatrarb.com/), and [Cuba](http://cuba.is/).
|
746
189
|
|
747
190
|
## License
|
748
191
|
|
749
192
|
Tynn is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
750
193
|
|
751
|
-
[
|
752
|
-
[capybara]: https://github.com/jnicklas/capybara
|
753
|
-
[clickjacking]: https://www.owasp.org/index.php/Clickjacking
|
754
|
-
[cuba]: http://cuba.is/
|
755
|
-
[minitest]: https://github.com/seattlerb/minitest
|
756
|
-
[puma]: http://puma.io/
|
757
|
-
[rack]: http://rack.github.io/
|
758
|
-
[rack::test]: https://github.com/brynary/rack-test
|
759
|
-
[rack::methodoverride]: http://www.rubydoc.info/github/rack/rack/Rack/MethodOverride
|
760
|
-
[rails]: http://rubyonrails.org/
|
761
|
-
[sinatra]: http://www.sinatrarb.com/
|
762
|
-
[sniffing]: https://msdn.microsoft.com/library/gg622941(v=vs.85).aspx
|
763
|
-
[thin]: http://code.macournoyer.com/thin/
|
764
|
-
[tynn::environment]: http://api.tynn.xyz/2.0.0/Tynn/Environment.html
|
765
|
-
[tynn::json]: http://api.tynn.xyz/2.0.0/Tynn/JSON.html
|
766
|
-
[tynn::render]: http://api.tynn.xyz/2.0.0/Tynn/Render.html
|
767
|
-
[tynn::secureheaders]: http://api.tynn.xyz/2.0.0/Tynn/SecureHeaders.html
|
768
|
-
[tynn::session]: http://api.tynn.xyz/2.0.0/Tynn/Session.html
|
769
|
-
[tynn::static]: http://api.tynn.xyz/2.0.0/Tynn/Static.html
|
770
|
-
[tynn::test]: http://api.tynn.xyz/2.0.0/Tynn/Test.html
|
771
|
-
[unicorn]: https://unicorn.bogomips.org/
|
194
|
+
[documentation]: http://tynn.readthedocs.io/en/latest/
|