keight 0.2.0 → 0.3.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.
- 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
|