keight 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +237 -0
- data/README.md +285 -81
- data/Rakefile +27 -1
- data/bench/benchmarker.rb +2 -2
- data/bin/k8rb +112 -305
- data/keight.gemspec +4 -6
- data/lib/keight.rb +860 -822
- data/test/keight_test.rb +1007 -933
- data/test/oktest.rb +2 -2
- metadata +4 -68
- data/CHANGES.txt +0 -64
- data/lib/keight/skeleton/.gitignore +0 -10
- data/lib/keight/skeleton/README.txt +0 -13
- data/lib/keight/skeleton/app/action.rb +0 -106
- data/lib/keight/skeleton/app/api/hello.rb +0 -39
- data/lib/keight/skeleton/app/form/.keep +0 -0
- data/lib/keight/skeleton/app/helper/.keep +0 -0
- data/lib/keight/skeleton/app/model.rb +0 -144
- data/lib/keight/skeleton/app/model/.keep +0 -0
- data/lib/keight/skeleton/app/page/welcome.rb +0 -17
- data/lib/keight/skeleton/app/template/_layout.html.eruby +0 -56
- data/lib/keight/skeleton/app/template/welcome.html.eruby +0 -6
- data/lib/keight/skeleton/app/usecase/.keep +0 -0
- data/lib/keight/skeleton/config.rb +0 -46
- data/lib/keight/skeleton/config.ru +0 -6
- data/lib/keight/skeleton/config/app.rb +0 -29
- data/lib/keight/skeleton/config/app_dev.rb +0 -8
- data/lib/keight/skeleton/config/app_prod.rb +0 -7
- data/lib/keight/skeleton/config/app_stg.rb +0 -5
- data/lib/keight/skeleton/config/app_test.rb +0 -8
- data/lib/keight/skeleton/config/server_puma.rb +0 -22
- data/lib/keight/skeleton/config/server_unicorn.rb +0 -21
- data/lib/keight/skeleton/config/urlpath_mapping.rb +0 -16
- data/lib/keight/skeleton/index.txt +0 -39
- data/lib/keight/skeleton/main.rb +0 -22
- data/lib/keight/skeleton/static/lib/.keep +0 -0
- data/lib/keight/skeleton/test/api/hello_test.rb +0 -27
- data/lib/keight/skeleton/test/test_helper.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 923c4e6baf4553cb3047243d07112aa60766c9b1
|
4
|
+
data.tar.gz: 2027f244a4b0f0b1d77a641a97ee3764c553f442
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f26f07c359d237c4e39ddde6e7003ad3de3f2e60b3e2b1847649708fdc22c75b4d49e88bc5dce10097058f4d435921611a99f22d1957a2850cad76a18e58d739
|
7
|
+
data.tar.gz: 8cc8943acb7246deb713802de0dc4873de70c62e7bed6431cdbe34503fe29d269b749090172ff4e407ab01c00f1c4250324064df239fd7484d96fa574738e0db
|
data/CHANGES.md
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
Changes
|
2
|
+
=======
|
3
|
+
|
4
|
+
Release 0.3.0 (2016-10-13)
|
5
|
+
--------------------------
|
6
|
+
|
7
|
+
### Enhancements
|
8
|
+
|
9
|
+
* URL path parameter format can accept parameter type (`int`, `str`, `date`).
|
10
|
+
ex: `/api/books/{id:int}`, `/blog/{entry_date:date}`
|
11
|
+
|
12
|
+
* `K8::Util::TemporaryFile` class added. It allows you to create temporary
|
13
|
+
file which will be removed automatically *after* sending response body.
|
14
|
+
See document of `K8::Util::TemporaryFile` for details.
|
15
|
+
|
16
|
+
* `K8::Util::ShellCommand` class added. It allows you to send command output
|
17
|
+
as response body. For example, execute SQL, convert into CSV, and send it
|
18
|
+
as response.
|
19
|
+
See document of `K8::Util::ShellCommand` for details.
|
20
|
+
|
21
|
+
* `@action_args` is available in `K8::Action` class.
|
22
|
+
`@action_args` represents URL path parameter arguments.
|
23
|
+
For example, `@action_args == [123]` when urlpath pattern is `/books/{id}`
|
24
|
+
and request path is `/books/123`.
|
25
|
+
|
26
|
+
* `@req.params_form()`, `@req.params_multipart()` and `@req.params_json()`
|
27
|
+
now take max content length.
|
28
|
+
ex:
|
29
|
+
|
30
|
+
strs = @req.params_form(10*1024*104) # max: 10MB
|
31
|
+
strs = @req.params_form # same as above
|
32
|
+
|
33
|
+
strs, files = @req.params_multipart(100*1024*1024) # max: 100MB
|
34
|
+
strs, files = @req.params_multipart # same as above
|
35
|
+
|
36
|
+
hash = @req.params_json(10*1024*104) # max: 10MB
|
37
|
+
hash = @req.params_json # same as above
|
38
|
+
|
39
|
+
* Add `K8::ActionMapping#urlpath_rexp`.
|
40
|
+
You can confirm regexp of action mapping by that attribute.
|
41
|
+
ex:
|
42
|
+
|
43
|
+
## what regexp is used for request routing?
|
44
|
+
mappings = [
|
45
|
+
['/api', [
|
46
|
+
['/books', BooksAction],
|
47
|
+
['/authors', AuthorsAction],
|
48
|
+
]
|
49
|
+
]
|
50
|
+
p K8::ActionMapping.new(mapping).urlpath_rexp
|
51
|
+
|
52
|
+
* Regard '.*' at end of urlpath pattern as extension pattern.
|
53
|
+
ex:
|
54
|
+
|
55
|
+
class BooksAPI < K8::Action
|
56
|
+
|
57
|
+
## ex: '.*' is same as '{_:<(?:\.\w+)?>}'
|
58
|
+
mapping '.*' , :GET=>:do_index
|
59
|
+
|
60
|
+
## ex: '/{id}.*' is same as '/{id}{_:<(?:\.\w+)?>}'
|
61
|
+
mapping '/{id}.*', :GET=>:do_show
|
62
|
+
|
63
|
+
def do_index
|
64
|
+
p @req.path_ext #=> ex: '.json', '.html', etc
|
65
|
+
end
|
66
|
+
def do_show(id)
|
67
|
+
p @req.path_ext #=> ex: '.json', '.html', etc
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
* Define `@req.path_ext` which returns such as `.json` or `.html`.
|
72
|
+
|
73
|
+
* Define `@resp.status_line` which returns such as `"200 OK"` or `"302 Found"`.
|
74
|
+
|
75
|
+
* `@resp.set_cookie()` now accepts Time object as `expires` keyword arg.
|
76
|
+
|
77
|
+
* New HTTP response status codes are added.
|
78
|
+
#
|
79
|
+
|
80
|
+
103 Checkpoint # Unofficial
|
81
|
+
306 Switch Proxy # -
|
82
|
+
308 Permanent Redirect # RFC 7538
|
83
|
+
421 Misdirected Request # RFC 7540
|
84
|
+
428 Precondition Required # RFC 6585
|
85
|
+
429 Too Many Requests # RFC 6585
|
86
|
+
431 Request Header Fields Too Large # RFC 6585
|
87
|
+
444 No Response # nginx
|
88
|
+
451 Unavailable For Legal Reasons # -
|
89
|
+
495 SSL Certificate Error # nginx
|
90
|
+
496 SSL Certificate Required # nginx
|
91
|
+
497 HTTP Request Sent to HTTPS Port # nginx
|
92
|
+
499 Client Closed Request # nginx
|
93
|
+
509 Bandwidth Limit Exceeded # Apache Web Server/cPanel
|
94
|
+
511 Network Authentication Required # RFC 6585
|
95
|
+
520 Unknown Error # CloudFlare
|
96
|
+
521 Web Server Is Down # CloudFlare
|
97
|
+
522 Connection Timed Out # CloudFlare
|
98
|
+
523 Origin Is Unreachable # CloudFlare
|
99
|
+
524 A Timeout Occurred" # CloudFlare
|
100
|
+
525 SSL Handshake Failed # CloudFlare
|
101
|
+
526 Invalid SSL Certificate # CloudFlare
|
102
|
+
|
103
|
+
* `k8rb cdnjs` now calls CDNJS.com api instead of scraping HTML page.
|
104
|
+
|
105
|
+
|
106
|
+
### Incomppatible changes
|
107
|
+
|
108
|
+
* URL path parameter format is changed.
|
109
|
+
(old) `/books/{id:\d+}`
|
110
|
+
(new) `/books/{id:int}` or `/books/{id:int<\d+>}`
|
111
|
+
See document for details.
|
112
|
+
|
113
|
+
* Rename `@req.method` to `@req.meth`.
|
114
|
+
`@req.method` is also available, but not recommended. Use `@req.meth`.
|
115
|
+
|
116
|
+
* Rename `K8::BaseAction[]#method` to `K8::BaseAction[]#meth`.
|
117
|
+
ex:
|
118
|
+
|
119
|
+
p BooksAction[:do_show].meth #=> :GET
|
120
|
+
p BooksAction[:do_update].meth #=> :PUT
|
121
|
+
p BooksAction[:do_delete].meth #=> :DELETE
|
122
|
+
|
123
|
+
* Rename `K8::BaseAction[]#urlpath()` to `BooksAction[]#path()`.
|
124
|
+
ex:
|
125
|
+
|
126
|
+
p BooksAction[:do_show].path(123) #=> "/api/books/123"
|
127
|
+
p BooksAction[:do_update].path(123) #=> "/api/books/123"
|
128
|
+
p BooksAction[:do_delete].path(123) #=> "/api/books/123"
|
129
|
+
|
130
|
+
* Rename `@current_action` to `@action_name` in `K8::Action` class.
|
131
|
+
It represents current action name, such as `:do_index` or `:do_show`.
|
132
|
+
|
133
|
+
* `@req.params` now raises `K8::PayloadParseError` for JSON data,
|
134
|
+
because `@req.params` should return hash object of string, but
|
135
|
+
JSON data contains non-string values.
|
136
|
+
Use `@req.json` instead fo `@req.params`.
|
137
|
+
|
138
|
+
* `@req.params` now raises `K8::PayloadParseError` for multipart data,
|
139
|
+
because `@req.params` should return hash object of string, but
|
140
|
+
multipart form contains file data which is not a string value.
|
141
|
+
Use `@req.multipart` instead fo `@req.params`.
|
142
|
+
|
143
|
+
* Rename `K8::Request` class to `K8::RackRequest`.
|
144
|
+
|
145
|
+
* Rename `K8::Response` class to `K8::RackResponse`.
|
146
|
+
|
147
|
+
* Rename `K8::REQUEST_CLASS` to `K8::RackApplication::REQUEST_CLASS`.
|
148
|
+
* Rename `K8::RESPONSE_CLASS` to `K8::RackApplication::RESPONSE_CLASS`.
|
149
|
+
* Move `K8::ActionMapping#lookup()` to `K8::RackApplication` class.
|
150
|
+
* Remove 'K8::DefaultPatterns' class.
|
151
|
+
* Remove 'K8::DEFAULT_PATTERNS' object.
|
152
|
+
|
153
|
+
* `k8rb mapping` command is removed. Use `rake mapping` instead.
|
154
|
+
* `k8rb configs` command is removed. Use `rake configs` instead.
|
155
|
+
|
156
|
+
* Remove `K8::BaseConfig` and `K8::SecretValue` classes.
|
157
|
+
Use 'benry-config' gem instead.
|
158
|
+
|
159
|
+
* Remove 'keight/skeleton/*' files. File are moved to
|
160
|
+
https://github.com/kwatch/keight-ruby-boilerpl8/releases .
|
161
|
+
|
162
|
+
* Remove `K8::RackApplicaiton#lookup_autoredirect_location()`.
|
163
|
+
|
164
|
+
|
165
|
+
### Changes
|
166
|
+
|
167
|
+
* Rewrite `K8::ActionMapping` class entirely.
|
168
|
+
* Remove `K8::ActionMethodMapping` class.
|
169
|
+
* Rewrite `K8::RackRequest#params_form`, `#params_multipart` and `#params_json`.
|
170
|
+
* Move session initialization code from `K8::BaseAction` to `K8::Action`.
|
171
|
+
* Remove dependency to `baby_erubis` gem.
|
172
|
+
|
173
|
+
|
174
|
+
Release 0.2.0 (2016-01-06)
|
175
|
+
--------------------------
|
176
|
+
|
177
|
+
* [change] `K8::RackApplication#mount()` is removed.
|
178
|
+
Use `K8::RackApplication.new([...])` instead.
|
179
|
+
|
180
|
+
* [change] `k8rb init` is renamed to `k8rb project`.
|
181
|
+
ex:
|
182
|
+
|
183
|
+
$ k8rb project myapp1
|
184
|
+
$ cd myapp1
|
185
|
+
|
186
|
+
* [enhance] Auto redirection support.
|
187
|
+
For example, `GET /books` is defined and `GET /books/` is requested,
|
188
|
+
then it will be redirected to `GET /books`.
|
189
|
+
|
190
|
+
* [enhance] Performance improved for variable urlpath.
|
191
|
+
|
192
|
+
* [enhance] Implement urlpath helpers.
|
193
|
+
ex:
|
194
|
+
|
195
|
+
p BookAPI[:do_update].method #=> :PUT
|
196
|
+
p BookAPI[:do_update].urlpath(123) #=> '/api/books/123'
|
197
|
+
p BookAPI[:do_update].form_action_attr(123) #=> '/api/books/123?_method=PUT'
|
198
|
+
|
199
|
+
* [change] `k8rb mapping` now prints output in text format, not YAML format.
|
200
|
+
|
201
|
+
* [enhance] `k8rb mapping` supports `--format=FORMAT` option.
|
202
|
+
ex:
|
203
|
+
|
204
|
+
$ k8rb mapping --format=text # or yaml/json/javascript/jquery/angular
|
205
|
+
|
206
|
+
* [enhance] `k8rb` command supports `cdnjs` action which download JavaScript
|
207
|
+
libraries from cdnjs.com.
|
208
|
+
ex:
|
209
|
+
|
210
|
+
$ k8rb cdnjs # list library
|
211
|
+
$ k8rb cdnjs 'jquery*' # search library
|
212
|
+
$ k8rb cdnjs jquery # list versions
|
213
|
+
$ k8rb cdnjs jquery 2.1.4 # download library
|
214
|
+
|
215
|
+
* [change] `k8rb init` command downloads jquery and modernizr from cdnjs.com.
|
216
|
+
|
217
|
+
* [change] `K8::Mock` and `K8::TestApp` are removed.
|
218
|
+
Use rack-test_app gem instead.
|
219
|
+
|
220
|
+
* [bugfix] Document fixed.
|
221
|
+
|
222
|
+
* [internal] Remove `K8::ActionClassMapping`, `K8::ActionRouter` and
|
223
|
+
`K8::ActionFinder` classes.
|
224
|
+
|
225
|
+
* [internal] Define new class `K8::ActionMapping` instead of remove classes.
|
226
|
+
|
227
|
+
|
228
|
+
Release 0.1.0 (2015-10-27)
|
229
|
+
--------------------------
|
230
|
+
|
231
|
+
* Public release
|
232
|
+
|
233
|
+
|
234
|
+
Release 0.0.1 (2015-10-26)
|
235
|
+
--------------------------
|
236
|
+
|
237
|
+
* Test release
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
Keight.rb README
|
2
2
|
================
|
3
3
|
|
4
|
-
($Release: 0.
|
4
|
+
($Release: 0.3.0 $)
|
5
5
|
|
6
6
|
|
7
7
|
Overview
|
8
8
|
--------
|
9
9
|
|
10
10
|
Keight.rb is the very fast web application framework for Ruby.
|
11
|
-
It
|
11
|
+
It runs about 100 times faster than Rails, and 20 times faster than Sinatra.
|
12
12
|
|
13
13
|
*Keight.rb is under development and is subject to change without notice.*
|
14
14
|
|
@@ -50,7 +50,7 @@ $ export PATH=$GEM_HOME/bin:$PATH
|
|
50
50
|
$ gem install keight
|
51
51
|
$ vi hello.rb # see below
|
52
52
|
$ vi config.ru # != 'config.rb'
|
53
|
-
$ rackup -p
|
53
|
+
$ rackup -p 4423 config.ru
|
54
54
|
```
|
55
55
|
|
56
56
|
hello.rb:
|
@@ -65,13 +65,14 @@ class HelloAction < K8::Action
|
|
65
65
|
mapping '/{id}' , :GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete
|
66
66
|
|
67
67
|
def do_index
|
68
|
-
|
69
|
-
"<h1>Hello</h1>"
|
68
|
+
return {"message"=>"Hello"} # JSON
|
69
|
+
#return "<h1>Hello</h1>" # HTML
|
70
70
|
end
|
71
71
|
|
72
72
|
def do_show(id)
|
73
73
|
## 'id' or 'xxx_id' will be converted into integer.
|
74
|
-
|
74
|
+
return {"id"=>id}
|
75
|
+
#return "<h1>id=#{id.inspect}</h1>"
|
75
76
|
end
|
76
77
|
|
77
78
|
def do_create ; "<p>create</p>"; end
|
@@ -86,13 +87,12 @@ config.ru:
|
|
86
87
|
```ruby
|
87
88
|
# -*- coding: utf-8 -*-
|
88
89
|
require 'keight'
|
89
|
-
require './hello'
|
90
90
|
|
91
91
|
mapping = [
|
92
92
|
['/api', [
|
93
|
-
['/hello'
|
94
|
-
|
95
|
-
#['/hello'
|
93
|
+
['/hello' , "./hello:HelloAction"],
|
94
|
+
### or
|
95
|
+
#['/hello' , HelloAction],
|
96
96
|
]],
|
97
97
|
]
|
98
98
|
app = K8::RackApplication.new(mapping)
|
@@ -100,31 +100,54 @@ app = K8::RackApplication.new(mapping)
|
|
100
100
|
run app
|
101
101
|
```
|
102
102
|
|
103
|
-
Open http://localhost:
|
103
|
+
Open http://localhost:4423/api/hello or http://localhost:4423/api/hello/123
|
104
104
|
with your browser.
|
105
105
|
|
106
|
-
|
106
|
+
How do you like it? Try the following steps to generate your project.
|
107
107
|
|
108
108
|
```console
|
109
109
|
$ mkdir gems # if necessary
|
110
110
|
$ export GEM_HOME=$PWD/gems # if necessary
|
111
111
|
$ export PATH=$GEM_HOME/bin:$PATH # if necessary
|
112
|
-
|
113
|
-
$
|
114
|
-
$ k8rb project myapp1
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
$
|
122
|
-
$
|
123
|
-
$
|
124
|
-
$
|
112
|
+
|
113
|
+
$ gem install keight # or: gem install boilerpl8
|
114
|
+
$ k8rb project myapp1 # or: boilerpl8 github:kwatch/keight-ruby myapp1
|
115
|
+
## select CSS framework
|
116
|
+
** 1. None
|
117
|
+
** 2. Bootstrap
|
118
|
+
** 3. Pure (recommended)
|
119
|
+
** Which CSS framework do you like? [1-3]: 1
|
120
|
+
|
121
|
+
$ cd myapp1
|
122
|
+
$ export APP_MODE=dev # 'dev', 'prod', or 'stg'
|
123
|
+
$ rake -T
|
124
|
+
$ ls public
|
125
|
+
$ rake server port=4423
|
126
|
+
$ open http://localhost:4423/
|
127
|
+
$ ab -n 10000 -c 100 http://localhost:4423/api/hello.json
|
125
128
|
```
|
126
129
|
|
127
130
|
|
131
|
+
Command `k8rb`
|
132
|
+
--------------
|
133
|
+
|
134
|
+
Keight.rb provices `k8rb`.
|
135
|
+
|
136
|
+
* `k8rb project myapp1` creates new project.
|
137
|
+
(Note: This is equvarent to `boilerpl8 github:kwatch/keight-ruby myapp1`.)
|
138
|
+
|
139
|
+
* `k8rb cdnjs -d static/lib jquery 3.1.0` downloads jQuery files
|
140
|
+
from cdnjs.com and stores into `static/lib` directory.
|
141
|
+
(Note: This is equivarent to `cdnget cdnjs jquery 3.1.0 static/lib`.)
|
142
|
+
|
143
|
+
`k8rb` command is provided mainly for backward compatibility.
|
144
|
+
You can use [boilerpl8](https://github.com/kwatch/boilerpl8/tree/ruby)
|
145
|
+
and [cdnget](https://github.com/kwatch/cdnget/tree/ruby-release)
|
146
|
+
inead of `k8rb project` and `k8rb cdnjs`.
|
147
|
+
`k8rb` is specific to Keight.rb, but both boilerpl8 and cdnget are
|
148
|
+
available in any project.
|
149
|
+
|
150
|
+
|
128
151
|
CheatSheet
|
129
152
|
----------
|
130
153
|
|
@@ -134,8 +157,8 @@ require 'keight'
|
|
134
157
|
class HelloAction < K8::Action
|
135
158
|
|
136
159
|
## mapping
|
137
|
-
mapping '',
|
138
|
-
mapping '/{name
|
160
|
+
mapping '' , :GET=>:do_hello_world
|
161
|
+
mapping '/{name:str}' , :GET=>:do_hello
|
139
162
|
|
140
163
|
## request, response, and helpers
|
141
164
|
|
@@ -148,14 +171,16 @@ class HelloAction < K8::Action
|
|
148
171
|
## request
|
149
172
|
@req # K8::Request object (!= Rack::Request)
|
150
173
|
@req.env # Rack environment
|
151
|
-
@req.
|
174
|
+
@req.meth # ex: :GET, :POST, :PUT, ...
|
152
175
|
@req.request_method # ex: "GET", "POST", "PUT", ...
|
153
|
-
@req.path # ex: '/api/hello'
|
176
|
+
@req.path # ex: '/api/hello.json'
|
177
|
+
@req.path_ext # ex: '.json'
|
178
|
+
@req.query_string # query string (String)
|
154
179
|
@req.query # query string (Hash)
|
155
180
|
@req.form # form data (Hash)
|
156
181
|
@req.multipart # multipart form data ([Hash, Hash])
|
157
182
|
@req.json # JSON data (Hash)
|
158
|
-
@req.params # query
|
183
|
+
@req.params # query or form (not multipart nor json!)
|
159
184
|
@req.cookies # cookies (Hash)
|
160
185
|
@req.xhr? # true when requested by jQuery etc
|
161
186
|
@req.client_ip_addr # ex: '127.0.0.1'
|
@@ -163,11 +188,16 @@ class HelloAction < K8::Action
|
|
163
188
|
## response
|
164
189
|
@resp # K8::Response object (!= Rack::Response)
|
165
190
|
@resp.status_code # ex: 200
|
191
|
+
@resp.status_code=(i) # ex: i=200
|
192
|
+
@resp.status_line # ex: "200 OK"
|
166
193
|
@resp.status # alias of @resp.status_code
|
194
|
+
@resp.status=(i) # alias of @resp.status_code=(i)
|
167
195
|
@resp.headers # Hash object
|
168
196
|
@resp.set_cookie(k, v) # cookie
|
169
|
-
@resp.content_type
|
170
|
-
@resp.
|
197
|
+
@resp.content_type # same as @resp.headers['Content-Type']
|
198
|
+
@resp.content_type=(s) # same as @resp.headers['Content-Type'] = s
|
199
|
+
@resp.content_length # same as @resp.headers['Content-Length']
|
200
|
+
@resp.content_length=(i) # same as @resp.headers['Content-Length'] = i.to_s
|
171
201
|
|
172
202
|
## session (requires Rack::Session)
|
173
203
|
@sess[key] = val # set session data
|
@@ -175,7 +205,7 @@ class HelloAction < K8::Action
|
|
175
205
|
|
176
206
|
## helpers
|
177
207
|
token = csrf_token() # get csrf token
|
178
|
-
validation_failed() # same as @resp.
|
208
|
+
validation_failed() # same as @resp.status = 422
|
179
209
|
return redirect_to(location, 302, flash: "message")
|
180
210
|
return send_file(filepath)
|
181
211
|
|
@@ -195,20 +225,24 @@ class HelloAction < K8::Action
|
|
195
225
|
def handle_content(content) # convert content
|
196
226
|
if content.is_a?(Hash)
|
197
227
|
@resp.content_type = 'application/json'
|
198
|
-
return
|
228
|
+
return JSON.dump(content)
|
199
229
|
end
|
200
230
|
super
|
201
231
|
end
|
202
232
|
|
203
233
|
def handle_exception(ex) # exception handler
|
204
|
-
|
205
|
-
return
|
234
|
+
proc_ = WHEN_RAISED[ex.class]
|
235
|
+
return self.instance_exec(ex, &proc_) if proc_
|
206
236
|
super
|
207
237
|
end
|
208
238
|
|
239
|
+
WHEN_RAISED = {}
|
240
|
+
WHEN_RAISED[NotFound] = proc {|ex| ... }
|
241
|
+
WHEN_RAISED[NotPermitted] = proc {|ex| ... }
|
242
|
+
|
209
243
|
def csrf_protection_required?
|
210
244
|
x = @req.method
|
211
|
-
return x == :POST || x == :PUT || x == :DELETE
|
245
|
+
return x == :POST || x == :PUT || x == :DELETE || x == :PATCH
|
212
246
|
end
|
213
247
|
|
214
248
|
end
|
@@ -219,7 +253,7 @@ urlpath_mapping = [
|
|
219
253
|
['/api', [
|
220
254
|
['/books' , './app/api/books:BooksAPI'],
|
221
255
|
['/books/{book_id}/comments'
|
222
|
-
, './app/api/
|
256
|
+
, './app/api/books:BookCommentsAPI'],
|
223
257
|
['/orders' , './app/api/orders:OrdersAPI'],
|
224
258
|
]],
|
225
259
|
]
|
@@ -231,13 +265,136 @@ opts = {
|
|
231
265
|
app = K8::RackApplication.new(urlpath_mapping, opts)
|
232
266
|
|
233
267
|
## misc
|
234
|
-
p HelloAction[:do_update].
|
235
|
-
p HelloAction[:do_update].
|
268
|
+
p HelloAction[:do_update].meth #=> :PUT
|
269
|
+
p HelloAction[:do_update].path(123) #=> "/api/books/123"
|
236
270
|
p HelloAction[:do_update].form_action_attr(123)
|
237
271
|
#=> "/api/books/123?_method=PUT"
|
238
272
|
```
|
239
273
|
|
240
274
|
|
275
|
+
URL Mapping
|
276
|
+
-----------
|
277
|
+
|
278
|
+
Rails-like mapping:
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class BookAPI < K8::Action
|
282
|
+
|
283
|
+
mapping '' , :GET=>:do_index, :POST=>:do_create
|
284
|
+
mapping '/new' , :GET=>:do_new
|
285
|
+
mapping '/{id}' , :GET=>:do_show, :PUT=>:do_update, :DELETE=>:do_delete
|
286
|
+
mapping '/{id}/edit', :GET=>:do_edit
|
287
|
+
|
288
|
+
def do_index() ; {"list": []}; end
|
289
|
+
def do_create() ; {"list": []}; end
|
290
|
+
def do_new() ; {"item": id}; end
|
291
|
+
def do_show(id) ; {"item": id}; end
|
292
|
+
def do_edit(id) ; {"item": id}; end
|
293
|
+
def do_update(id); {"item": id}; end
|
294
|
+
def do_delete(id); {"item": id}; end
|
295
|
+
# (Note: 'do_' prefix is NOT mandatory.)
|
296
|
+
|
297
|
+
end
|
298
|
+
```
|
299
|
+
|
300
|
+
Data type and pattern:
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
class BookAPI < K8::Action
|
304
|
+
|
305
|
+
##
|
306
|
+
## data type and pattern
|
307
|
+
##
|
308
|
+
mapping '/{name:str<[^/]+>}' , :GET=>:do_show1
|
309
|
+
mapping '/{id:int<\d+>}' , :GET=>:do_show2
|
310
|
+
mapping '/{birthday:date<\d\d\d\d-\d\d-\d\d>}', :GET=>:do_show3
|
311
|
+
|
312
|
+
##
|
313
|
+
## default pattern
|
314
|
+
## - '/{name:str}' is same as '/{name:str<[^/]+>}'
|
315
|
+
## - '/{id:int}' is same as '/{id:int<\d+>}'
|
316
|
+
## - '/{birthday:date}' is same as '/{birthday:date<\d\d\d\d-\d\d-\d\d>}'
|
317
|
+
##
|
318
|
+
mapping '/{name:str}' , :GET=>:do_show1
|
319
|
+
mapping '/{id:int}' , :GET=>:do_show2
|
320
|
+
mapping '/{birthday:date}' , :GET=>:do_show3
|
321
|
+
|
322
|
+
##
|
323
|
+
## default data type
|
324
|
+
## - 'id' and '*_id' are int type
|
325
|
+
## - 'date' and '*_date' are date type
|
326
|
+
## - others are str type
|
327
|
+
##
|
328
|
+
mapping '/{name}' , :GET=>:do_show1 # same as '{name:str}'
|
329
|
+
mapping '/{id}' , :GET=>:do_show2 # same as '{id:int}'
|
330
|
+
mapping '/{birth_date}', :GET=>:do_show3 # same as '{birth_date:date}'
|
331
|
+
|
332
|
+
##
|
333
|
+
## pattern with default data type
|
334
|
+
##
|
335
|
+
mapping '/{code:<[A-Z]{3}>}', ... # same as '/{code:str<[A-Z]{3}>'
|
336
|
+
mapping '/{id:<[1-9]\d*>}' , ... # same as '/{id:int<\d{4}>}'
|
337
|
+
|
338
|
+
end
|
339
|
+
```
|
340
|
+
|
341
|
+
Path extension (such as '.json' or '.html'):
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
class FooAPI < K8::Action
|
345
|
+
|
346
|
+
## ex: '.*' is same as '{_:<(?:\w+)?>}' which matches to any extension.
|
347
|
+
mapping '.*' , :GET=>:do_index
|
348
|
+
|
349
|
+
## ex: '/{id}.*' is same as '/{id}{_:<(?:\w+)?>}' which matches to any extension.
|
350
|
+
mapping '/{id}.*' , :GET=>:do_show
|
351
|
+
|
352
|
+
def do_index
|
353
|
+
p @req.path_ext #=> ex: '.json', '.html', and so on
|
354
|
+
end
|
355
|
+
|
356
|
+
def do_show(id)
|
357
|
+
p @req.path_ext #=> ex: '.json', '.html', and so on
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
```
|
362
|
+
|
363
|
+
URL mapping helper:
|
364
|
+
|
365
|
+
```ruby
|
366
|
+
## for example
|
367
|
+
p BookAPI[:do_index].meth #=> :GET
|
368
|
+
p BookAPI[:do_index].path #=> "/api/books"
|
369
|
+
p BookAPI[:do_create].meth #=> :POST
|
370
|
+
p BookAPI[:do_create].path #=> "/api/books"
|
371
|
+
p BookAPI[:do_show].meth #=> :GET
|
372
|
+
p BookAPI[:do_show].path(123) #=> "/api/books/123"
|
373
|
+
p BookAPI[:do_update].meth #=> :PUT
|
374
|
+
p BookAPI[:do_update].path(123) #=> "/api/books/123"
|
375
|
+
p BookAPI[:do_delete].meth #=> :DELETE
|
376
|
+
p BookAPI[:do_delete].path(123) #=> "/api/books/123"
|
377
|
+
|
378
|
+
p BookAPI[:do_index ].form_action_attr() #=> "/api/books"
|
379
|
+
p BookAPI[:do_create].form_action_attr() #=> "/api/books"
|
380
|
+
p BookAPI[:do_show ].form_action_attr(9) #=> "/api/books/9"
|
381
|
+
p BookAPI[:do_update].form_action_attr(9) #=> "/api/books/9?_method=PUT"
|
382
|
+
p BookAPI[:do_delete].form_action_attr(9) #=> "/api/books/9?_method=DELETE"
|
383
|
+
```
|
384
|
+
|
385
|
+
Show URL mappings:
|
386
|
+
|
387
|
+
```console
|
388
|
+
$ k8rb project myapp1 # or: boilerpl8 github:kwatch/keight-ruby myapp1
|
389
|
+
$ cd myapp1
|
390
|
+
$ rake mapping:text # list url mapping
|
391
|
+
$ rake mapping:yaml # list in YAML format
|
392
|
+
$ rake mapping:json # list in JSON format
|
393
|
+
$ rake mapping:jquery # list for jQuery
|
394
|
+
$ rake mapping:angularjs # list for AngularJS
|
395
|
+
```
|
396
|
+
|
397
|
+
|
241
398
|
Topics
|
242
399
|
------
|
243
400
|
|
@@ -272,8 +429,8 @@ This will make routing for variable one much faster.
|
|
272
429
|
|
273
430
|
### Default Pattern of URL Path Parameter
|
274
431
|
|
275
|
-
URL path parameter `{id}` and `{xxx_id}` are regarded as `{id
|
276
|
-
`{xxx_id
|
432
|
+
URL path parameter `{id}` and `{xxx_id}` are regarded as `{id:int<\d+>}` and
|
433
|
+
`{xxx_id:int<\d+>}` respectively and converted into positive interger automatically.
|
277
434
|
For example:
|
278
435
|
|
279
436
|
```ruby
|
@@ -287,8 +444,8 @@ end
|
|
287
444
|
```
|
288
445
|
|
289
446
|
URL path parameter `{date}` and `{xxx_date}` are regarded as
|
290
|
-
`{date
|
291
|
-
and converted into Date object automatically.
|
447
|
+
`{date:date<\d\d\d\d-\d\d-\d\d>}` and `{xxx_date:date<\d\d\d\d-\d\d-\d\d>}`
|
448
|
+
respectively and converted into Date object automatically.
|
292
449
|
For example:
|
293
450
|
|
294
451
|
```ruby
|
@@ -301,10 +458,8 @@ class BlogAPI < K8::Action
|
|
301
458
|
end
|
302
459
|
```
|
303
460
|
|
304
|
-
|
305
|
-
|
306
|
-
In other words, you can cancel automatic conversion by specifing regular
|
307
|
-
expression of URL path parameters.
|
461
|
+
If you don't like auto-convert, specify data type and pettern explicitly.
|
462
|
+
For example, `{id:str<\d+>}` or `{date:str<\d\d\d\d-\d\d-\d\d>}`.
|
308
463
|
|
309
464
|
|
310
465
|
### Nested Routing
|
@@ -312,8 +467,8 @@ expression of URL path parameters.
|
|
312
467
|
```ruby
|
313
468
|
urlpath_mapping = [
|
314
469
|
['/api', [
|
315
|
-
['/books'
|
316
|
-
['/books/{book_id}/comments'
|
470
|
+
['/books' , "./app/api/books:BookAPI"],
|
471
|
+
['/books/{book_id}/comments' , "./app/api/books:BookCommentsAPI"],
|
317
472
|
]],
|
318
473
|
]
|
319
474
|
```
|
@@ -322,15 +477,16 @@ urlpath_mapping = [
|
|
322
477
|
### URL Path Helpers
|
323
478
|
|
324
479
|
```ruby
|
325
|
-
p BooksAPI[:do_index].
|
326
|
-
p BooksAPI[:do_index].
|
480
|
+
p BooksAPI[:do_index].meth #=> :GET
|
481
|
+
p BooksAPI[:do_index].path() #=> "/api/books"
|
482
|
+
p BooksAPI[:do_update].form_action_attr(123) #=> "/api/books/123"
|
327
483
|
|
328
|
-
p BooksAPI[:do_update].
|
329
|
-
p BooksAPI[:do_update].
|
484
|
+
p BooksAPI[:do_update].meth #=> :PUT
|
485
|
+
p BooksAPI[:do_update].path(123) #=> "/api/books/123"
|
330
486
|
p BooksAPI[:do_update].form_action_attr(123) #=> "/api/books/123?_method=PUT"
|
331
487
|
```
|
332
488
|
|
333
|
-
(Notice that these are availabe after `K8::RackApplication`
|
489
|
+
(Notice that these are availabe after `K8::RackApplication.new()` is called.)
|
334
490
|
|
335
491
|
|
336
492
|
### Routing for JavaScript
|
@@ -338,35 +494,34 @@ p BooksAPI[:do_update].form_action_attr(123) #=> "/api/books/123?_method=PUT"
|
|
338
494
|
Keight.rb can generate JavaScript routing file.
|
339
495
|
|
340
496
|
```console
|
341
|
-
$ k8rb project myapp1
|
342
|
-
$ cd myapp1
|
343
|
-
$
|
344
|
-
$
|
345
|
-
$
|
346
|
-
$
|
347
|
-
$
|
348
|
-
$ k8rb mapping --format=javascript >> $jsfile
|
349
|
-
$ echo '};' >> $jsfile
|
497
|
+
$ k8rb project myapp1 # or: boilerpl8 github:kwatch/keight-ruby myapp1
|
498
|
+
$ cd myapp1
|
499
|
+
$ rake mapping:text # list URL path mapping
|
500
|
+
$ rake mapping:yaml # list in YAML format
|
501
|
+
$ rake mapping:json # list in JSON format
|
502
|
+
$ rake mapping:jquery # list for jQuery
|
503
|
+
$ rake mapping:angularjs # list for AngularJS
|
350
504
|
```
|
351
505
|
|
352
506
|
|
353
507
|
### Download JavaScript Libraries
|
354
508
|
|
355
|
-
|
356
|
-
It is good idea to make layout of JavaScript libraries to be same as CDN.
|
357
|
-
|
358
|
-
[cdnjs.com]: https://cdnjs.com/
|
509
|
+
Install `cdnget` gem in order to download such as jQuery or Bootstrap.
|
359
510
|
|
360
511
|
```console
|
361
|
-
$
|
362
|
-
$
|
363
|
-
$
|
364
|
-
$
|
365
|
-
$
|
366
|
-
$
|
367
|
-
$
|
368
|
-
|
369
|
-
|
512
|
+
$ gem install cdnget
|
513
|
+
$ cdnget # list CDN
|
514
|
+
$ cdnget cdnjs # list libraries
|
515
|
+
$ cdnget cdnjs 'jquery*' # search libraries
|
516
|
+
$ cdnget cdnjs jquery # list versions
|
517
|
+
$ cdnget cdnjs jquery 2.2.4 # list files
|
518
|
+
$ cdnget cdnjs jquery 2.2.4 static/lib # download files
|
519
|
+
static/lib/jquery/2.2.4/jquery.js ... Done (257,551 byte)
|
520
|
+
static/lib/jquery/2.2.4/jquery.min.js ... Done (85,578 byte)
|
521
|
+
static/lib/jquery/2.2.4/jquery.min.map ... Done (129,572 byte)
|
522
|
+
|
523
|
+
$ ls static/lib/jquery/2.2.4
|
524
|
+
jquery.js jquery.min.js jquery.min.map
|
370
525
|
```
|
371
526
|
|
372
527
|
|
@@ -382,22 +537,71 @@ I don't think Keight.rb is so fast. Other frameworks are just too slow.
|
|
382
537
|
|
383
538
|
#### How to setup template engine?
|
384
539
|
|
385
|
-
Try `k8rb project myapp1;
|
540
|
+
Try `k8rb project myapp1; less myapp1/app/action.rb`.
|
541
|
+
(or `boilerpl8 github:kwatch/keight-ruby myapp1; less myapp1/app/action.rb`).
|
386
542
|
|
387
543
|
|
388
544
|
#### How to support static files?
|
389
545
|
|
390
|
-
Try `k8rb project myapp1;
|
546
|
+
Try `k8rb project myapp1; less myapp1/app/action.rb`.
|
547
|
+
(or `boilerpl8 github:kwatch/keight-ruby myapp1; less myapp1/app/action.rb`).
|
391
548
|
|
392
549
|
|
393
550
|
#### How to setup session?
|
394
551
|
|
395
|
-
Try `k8rb project myapp1;
|
552
|
+
Try `k8rb project myapp1; less myapp1/app/config.ru`.
|
553
|
+
(or `boilerpl8 github:kwatch/keight-ruby myapp1; less myapp1/app/config.ru`).
|
554
|
+
|
555
|
+
|
556
|
+
#### Is it necessary to add 'do_' prefix to action methods?
|
557
|
+
|
558
|
+
No. You can define `index()` or `show(id)` instead of `do_index()` or
|
559
|
+
`do_show(id)`, like Ruby on Rails.
|
396
560
|
|
397
561
|
|
398
562
|
#### Can I use Rack::Request and Rack::Response instead of Keight's?
|
399
563
|
|
400
|
-
Try `K8::REQUEST_CLASS = Rack::Request
|
564
|
+
Try `K8::RackApplication::REQUEST_CLASS = Rack::Request` and
|
565
|
+
`K8::RackApplication::RESPONSE_CLASS = Rack::Response`.
|
566
|
+
|
567
|
+
|
568
|
+
#### Why does `@req.multipart` returns two Hash objects?
|
569
|
+
|
570
|
+
Because in order NOT to mix string objects and file objects in a hash.
|
571
|
+
|
572
|
+
```ruby
|
573
|
+
## str_params contains only string or array of string.
|
574
|
+
## file_params contains only file object or array of file.
|
575
|
+
str_params, file_params = @req.multipart
|
576
|
+
p str_params['name'].strip
|
577
|
+
#=> no error because str_params['name'] is a string
|
578
|
+
|
579
|
+
## It is easy to merge two hash objects (but not recommended).
|
580
|
+
p str_params.merge(file_params)
|
581
|
+
```
|
582
|
+
|
583
|
+
In contrast, `Rack::Request#POST()` may contains both string and file.
|
584
|
+
|
585
|
+
```ruby
|
586
|
+
req = Rack::Request.new(env)
|
587
|
+
p req.POST['name'].strip
|
588
|
+
#=> will raise error when req.POST['name'] is file object uploaded
|
589
|
+
```
|
590
|
+
|
591
|
+
|
592
|
+
#### Why does `@req.params` raise error when multipart form data?
|
593
|
+
|
594
|
+
Because `@req.multipart` returns two Hash objects. See above section.
|
595
|
+
|
596
|
+
|
597
|
+
#### Why does `@req.params` raise error for JSON data?
|
598
|
+
|
599
|
+
Because both `@req.query` and `@req.form` returns a Hash object containing
|
600
|
+
only string values, but `@req.json` returns a Hash object containing
|
601
|
+
non-string values sucha as integer or boolean.
|
602
|
+
|
603
|
+
Use `@req.json` instead of `@req.params` for JSON data.
|
604
|
+
|
401
605
|
|
402
606
|
|
403
607
|
License and Copyright
|