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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +237 -0
  3. data/README.md +285 -81
  4. data/Rakefile +27 -1
  5. data/bench/benchmarker.rb +2 -2
  6. data/bin/k8rb +112 -305
  7. data/keight.gemspec +4 -6
  8. data/lib/keight.rb +860 -822
  9. data/test/keight_test.rb +1007 -933
  10. data/test/oktest.rb +2 -2
  11. metadata +4 -68
  12. data/CHANGES.txt +0 -64
  13. data/lib/keight/skeleton/.gitignore +0 -10
  14. data/lib/keight/skeleton/README.txt +0 -13
  15. data/lib/keight/skeleton/app/action.rb +0 -106
  16. data/lib/keight/skeleton/app/api/hello.rb +0 -39
  17. data/lib/keight/skeleton/app/form/.keep +0 -0
  18. data/lib/keight/skeleton/app/helper/.keep +0 -0
  19. data/lib/keight/skeleton/app/model.rb +0 -144
  20. data/lib/keight/skeleton/app/model/.keep +0 -0
  21. data/lib/keight/skeleton/app/page/welcome.rb +0 -17
  22. data/lib/keight/skeleton/app/template/_layout.html.eruby +0 -56
  23. data/lib/keight/skeleton/app/template/welcome.html.eruby +0 -6
  24. data/lib/keight/skeleton/app/usecase/.keep +0 -0
  25. data/lib/keight/skeleton/config.rb +0 -46
  26. data/lib/keight/skeleton/config.ru +0 -6
  27. data/lib/keight/skeleton/config/app.rb +0 -29
  28. data/lib/keight/skeleton/config/app_dev.rb +0 -8
  29. data/lib/keight/skeleton/config/app_prod.rb +0 -7
  30. data/lib/keight/skeleton/config/app_stg.rb +0 -5
  31. data/lib/keight/skeleton/config/app_test.rb +0 -8
  32. data/lib/keight/skeleton/config/server_puma.rb +0 -22
  33. data/lib/keight/skeleton/config/server_unicorn.rb +0 -21
  34. data/lib/keight/skeleton/config/urlpath_mapping.rb +0 -16
  35. data/lib/keight/skeleton/index.txt +0 -39
  36. data/lib/keight/skeleton/main.rb +0 -22
  37. data/lib/keight/skeleton/static/lib/.keep +0 -0
  38. data/lib/keight/skeleton/test/api/hello_test.rb +0 -27
  39. 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: 5674423ea6090c2163b9bff12f3ec5f2a1542dce
4
- data.tar.gz: ecde3e22c5720bfd8be224b45bb3f2e28db092dc
3
+ metadata.gz: 923c4e6baf4553cb3047243d07112aa60766c9b1
4
+ data.tar.gz: 2027f244a4b0f0b1d77a641a97ee3764c553f442
5
5
  SHA512:
6
- metadata.gz: 0a976045af16e6ff169a3fe46decb0b9c0fe8d30ed4848dc8d5669298766c3651be375c2dc4690614f3b740c4b599ec209594d82203aaf3e3f05397f1eade1a3
7
- data.tar.gz: 75720c41ec1250de00b5aeb2c1ed332b674d6c90e7a238bf4263fc1226caf9eedabb9c7d00ca1278f4a9d0609e9f59110d01e0b8e60f7e525d045e01bf744467
6
+ metadata.gz: f26f07c359d237c4e39ddde6e7003ad3de3f2e60b3e2b1847649708fdc22c75b4d49e88bc5dce10097058f4d435921611a99f22d1957a2850cad76a18e58d739
7
+ data.tar.gz: 8cc8943acb7246deb713802de0dc4873de70c62e7bed6431cdbe34503fe29d269b749090172ff4e407ab01c00f1c4250324064df239fd7484d96fa574738e0db
@@ -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.2.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 is about 100 times faster than Rails and 20 times faster than Sinatra.
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 8000 config.ru
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
- #{"message"=>"Hello"} # JSON
69
- "<h1>Hello</h1>" # HTML
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
- "<h1>Hello: id=#{id.inspect}</h1>"
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' , HelloAction],
94
- ## or
95
- #['/hello' , "./hello:HelloAction"],
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:8000/api/hello or http://localhost:8000/api/hello/123
103
+ Open http://localhost:4423/api/hello or http://localhost:4423/api/hello/123
104
104
  with your browser.
105
105
 
106
- Do you like it? If so, try `k8rb project myapp1` to generate project skeleton.
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
- $ gem install -N keight
113
- $ k8rb help # show help
114
- $ k8rb project myapp1 # create new project
115
- $ cd myapp1/
116
- $ rake setup # install gems and download libs
117
- $ export APP_ENV=dev # 'dev', 'prod', or 'stg'
118
- $ k8rb help mapping
119
- $ k8rb mapping # list urlpath mappings
120
- $ k8rb mapping --format=javascript # or jquery,angular,json,yaml
121
- $ k8rb configs # list config parameters
122
- $ rake server port=8000
123
- $ open http://localhost:8000/
124
- $ ab -n 10000 -c 100 http://localhost:8000/api/hello
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 '', :GET=>:do_hello_world
138
- mapping '/{name:\w+}', :GET=>:do_hello
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.method # ex: :GET, :POST, :PUT, ...
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, form, multipart or json
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 # same as @resp.headers['Content-Type']
170
- @resp.content_length # same as @resp.headers['Content-Length'].to_i
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.status_code = 422
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 [JSON.dump(content)]
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
- meth = "on_#{ex.class.name}"
205
- return __send__(meth, ex) if respond_to?(meth)
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/book_comments:BookCommentsAPI'],
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].method #=> :GET
235
- p HelloAction[:do_update].urlpath(123) #=> "/api/books/123"
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:\d+}` and
276
- `{xxx_id:\d+}` respectively and converted into positive interger automatically.
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:\d\d\d\d-\d\d-\d\d}` and `{xxx_date:\d\d\d\d-\d\d-\d\d}` respectively
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
- **If you specify `{id:\d+}` or `{date:\d\d\d\d-\d\d-\d\d}` explicitly,
305
- URL path parameter value is not converted into integer nor Date object.**
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' , BookAPI],
316
- ['/books/{book_id}/comments' , BookCommentsAPI],
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].method #=> :GET
326
- p BooksAPI[:do_index].urlpath() #=> "/api/books"
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].method #=> :PUT
329
- p BooksAPI[:do_update].urlpath(123) #=> "/api/books/123"
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` object is created.)
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
- $ k8rb mapping --format=javascript | less # or 'jquery', 'angular'
344
- $ mkdir -p static/js
345
- $ jsfile=static/js/urlpath_mapping.js
346
- $ rm -f $jsfile
347
- $ echo 'var Mapping = {' >> $jsfile
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
- Keight.rb can download Javascript or CSS libraries from [cdnjs.com].
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
- $ k8rb project myapp1
362
- $ cd myapp1/
363
- $ k8rb help cdnjs
364
- $ k8rb cdnjs # list libraries
365
- $ k8rb cdnjs 'jquery*' # search libraries
366
- $ k8rb cdnjs jquery # list versions
367
- $ k8rb cdnjs jquery 2.1.4 # download library
368
- ## or
369
- $ k8rb cdnjs --basedir=static/lib jquery 2.1.4
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; cd myapp1; less app/action.rb`.
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; cd myapp1; less app/action.rb`.
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; cd myapp1; less config.ru`.
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; K8::RESPONSE_CLASS = Rack::Response`.
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