keight 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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