kenji 1.0 → 1.1.1
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.
- data/README.md +25 -1
- data/lib/kenji/app.rb +25 -0
- data/lib/kenji/controller.rb +14 -15
- data/lib/kenji/version.rb +1 -2
- data/lib/kenji.rb +50 -17
- data/spec/5/controllers/main.rb +13 -0
- data/spec/kenji_spec.rb +26 -1
- metadata +3 -2
data/README.md
CHANGED
@@ -81,7 +81,7 @@ have:
|
|
81
81
|
Getting started with Kenji could not be any easier. All it takes is a few lines
|
82
82
|
and a terminal:
|
83
83
|
|
84
|
-
$ gem install kenji
|
84
|
+
$ gem install kenji
|
85
85
|
$ kenji init app_name; cd app_name
|
86
86
|
$ rackup # launch the webserver
|
87
87
|
|
@@ -100,6 +100,30 @@ And already, your app is ready to go:
|
|
100
100
|
|
101
101
|
## Changelog
|
102
102
|
|
103
|
+
#### 1.1.1
|
104
|
+
|
105
|
+
- No longer catching ArgumentErrors when calling the block for a route. This
|
106
|
+
fixes a bug where Kenji incorrectly responds with a 404 when the block is
|
107
|
+
passed the wrong number of arguments.
|
108
|
+
- Fixed logic for matching a pass. the path now must match the pass exactly
|
109
|
+
whereas before the pass would match if any subset of the path matched the
|
110
|
+
pass.
|
111
|
+
|
112
|
+
#### 1.1
|
113
|
+
|
114
|
+
- Kenji::App is a simply wrapper that can and should be used in `config.ru`
|
115
|
+
files. It avoids the need to wrap the Kenji initialization in a lambda.
|
116
|
+
- Kenji's stderr is now configurable as an option.
|
117
|
+
- The new option `catch_exceptions` (default true) configures whether Kenji
|
118
|
+
will automatically rescue and log exceptions.
|
119
|
+
- The root path argument to initializing Kenji is now deprecated, and replaced
|
120
|
+
with the `directory` named option. It is only necessary to set this when not
|
121
|
+
using a `root_controller`.
|
122
|
+
|
123
|
+
#### 1.0
|
124
|
+
|
125
|
+
- ? TODO: fill me in
|
126
|
+
|
103
127
|
#### 0.7
|
104
128
|
|
105
129
|
- Pass can now contain variables, that get set as @ivars on the controller.
|
data/lib/kenji/app.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Kenji
|
3
|
+
|
4
|
+
# Kenji::App is a simple wrapper class that helps avoid the awkward wrapping
|
5
|
+
# in a lambda typically necessary for using Kenji as a Rack app. Instead,
|
6
|
+
# simply do the following:
|
7
|
+
#
|
8
|
+
# run Kenji::App.new(directory: Dir.getwd)
|
9
|
+
#
|
10
|
+
# Any options passed will also be forwarded to Kenji.
|
11
|
+
#
|
12
|
+
# Kenji::App has one instance for the app, unlike Kenji::Kenji which has one
|
13
|
+
# instance per request.
|
14
|
+
#
|
15
|
+
class App
|
16
|
+
|
17
|
+
def initialize(opts={})
|
18
|
+
@opts = opts
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
Kenji.new(env, @opts).call
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/kenji/controller.rb
CHANGED
@@ -6,11 +6,14 @@ module Kenji
|
|
6
6
|
attr_accessor :kenji
|
7
7
|
|
8
8
|
# Routes below will accept routes in the format, eg.:
|
9
|
+
#
|
9
10
|
# /hello/:id/children
|
11
|
+
#
|
10
12
|
# Can contain any number of :id, but must be in their own url segment.
|
11
|
-
# Colon-prefixed segments become variables, passed onto the given block as
|
12
|
-
# The name given to the variable is irrelevant, and is thrown
|
13
|
-
#
|
13
|
+
# Colon-prefixed segments become variables, passed onto the given block as
|
14
|
+
# arguments. The name given to the variable is irrelevant, and is thrown
|
15
|
+
# away: /hello/:/children is equivalent to the example above. Given block
|
16
|
+
# must have correct arity.
|
14
17
|
|
15
18
|
# Route GET
|
16
19
|
def self.get(path, &block)
|
@@ -46,9 +49,9 @@ module Kenji
|
|
46
49
|
|
47
50
|
# Route a given path to the correct block, for any given methods
|
48
51
|
#
|
49
|
-
# Note: this works by building a tree for the path,
|
50
|
-
#
|
51
|
-
|
52
|
+
# Note: this works by building a tree for the path, each node being a path
|
53
|
+
# segment or variable segment, and the leaf @action being the block
|
54
|
+
#
|
52
55
|
def self.route(*methods, path, &block)
|
53
56
|
# bind the block to self as an instance method, so its context is correct
|
54
57
|
define_method(:_tmp_route_action, &block)
|
@@ -139,16 +142,11 @@ module Kenji
|
|
139
142
|
node = node[:':'] # attempt to find a variable segment
|
140
143
|
variables << segment # either we've found a variable, or the `unless` below will trigger
|
141
144
|
else
|
142
|
-
|
143
|
-
searching = false
|
145
|
+
return attempt_fallback(path) # route failed to match variable or segment node so attempt fallback
|
144
146
|
end
|
145
147
|
end
|
146
148
|
if node && action = node[:@action] # the block is stored in the @action leaf
|
147
|
-
|
148
|
-
return action.bind(self).call(*variables)
|
149
|
-
rescue ArgumentError # assuming argument error means route not defined
|
150
|
-
return attempt_fallback(path) # TODO: might want to check arity instead
|
151
|
-
end
|
149
|
+
return action.bind(self).call(*variables)
|
152
150
|
else # or, fallback if necessary store the block for each method
|
153
151
|
return attempt_fallback(path)
|
154
152
|
end
|
@@ -197,8 +195,9 @@ module Kenji
|
|
197
195
|
variables[match[1].to_sym] = e
|
198
196
|
match = true
|
199
197
|
else
|
200
|
-
match
|
201
|
-
|
198
|
+
# if there is no match it should not pass
|
199
|
+
break unless match = node.has_key?(e.to_sym)
|
200
|
+
node = node[e.to_sym]
|
202
201
|
end
|
203
202
|
|
204
203
|
break if node[:@controller]
|
data/lib/kenji/version.rb
CHANGED
data/lib/kenji.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'kenji/controller'
|
3
|
+
require 'kenji/app'
|
3
4
|
require 'kenji/string_extensions'
|
4
5
|
require 'rack'
|
5
6
|
|
@@ -20,32 +21,65 @@ module Kenji
|
|
20
21
|
#
|
21
22
|
# `env` should be the environment hash provided by Rack.
|
22
23
|
#
|
23
|
-
# `root` is the root directory (as output by File.expand_path)
|
24
|
-
# directory structure.
|
24
|
+
# *deprecated* `root` is the root directory (as output by File.expand_path)
|
25
|
+
# of the Kenji directory structure. This is deprecated, please use the
|
26
|
+
# :directory option below.
|
25
27
|
#
|
26
28
|
# `options` is an options hash that accepts the following keys:
|
27
29
|
#
|
28
|
-
#
|
29
|
-
# CORS / Access-Control
|
30
|
-
# - :root_controller => Object # when set, Kenji will not attempt to
|
31
|
-
# discover controllers based on
|
32
|
-
# convention, but rather will always
|
33
|
-
# use this controller. use `pass` to
|
34
|
-
# build a controller hierarchy
|
30
|
+
# :auto_cors => true | false
|
35
31
|
#
|
36
|
-
|
32
|
+
# automatically deal with CORS / Access-Control
|
33
|
+
#
|
34
|
+
# :directory => String (path)
|
35
|
+
#
|
36
|
+
# this is the preferred way of setting the root directory, when
|
37
|
+
# necessary. you should either set a root directory (which defaults to
|
38
|
+
# the current working directory), or set a root_controller. both are
|
39
|
+
# not necessary, as the directory is only used for auto-discovering
|
40
|
+
# controllers.
|
41
|
+
#
|
42
|
+
# :root_controller => Object
|
43
|
+
#
|
44
|
+
# when set, Kenji will not attempt to discover controllers based on
|
45
|
+
# convention, but rather will always use this controller. use `pass` to
|
46
|
+
# build a controller hierarchy
|
47
|
+
#
|
48
|
+
# :catch_exceptions => true | false
|
49
|
+
#
|
50
|
+
# when true, Kenji will catch exceptions, print them in stderr, and and
|
51
|
+
# return a standard 500 error
|
52
|
+
#
|
53
|
+
# :stderr => IO
|
54
|
+
#
|
55
|
+
# an IO stread, this is where Kenji logging goes by default. defaults
|
56
|
+
# to $stderr
|
57
|
+
#
|
58
|
+
def initialize(env, *rest)
|
59
|
+
raise ArgumentError unless rest.count == 2 || rest.count == 1
|
60
|
+
root, options = *rest
|
61
|
+
options, root = root, options if root.is_a?(Hash)
|
62
|
+
options ||= {}
|
63
|
+
|
37
64
|
@headers = {
|
38
65
|
'Content-Type' => 'application/json'
|
39
66
|
}
|
40
67
|
@status = 200
|
41
|
-
@root = File.expand_path(root) + '/'
|
42
|
-
@stderr = $stderr
|
43
68
|
@env = env
|
69
|
+
|
70
|
+
# deal with legacy root argument behavior
|
71
|
+
options[:directory] = File.expand_path(root) if root
|
44
72
|
|
45
73
|
@options = {
|
46
74
|
auto_cors: true,
|
47
|
-
|
75
|
+
catch_exceptions: true,
|
76
|
+
root_controller: nil,
|
77
|
+
directory: File.expand_path(Dir.getwd),
|
78
|
+
stderr: $stderr
|
48
79
|
}.merge(options)
|
80
|
+
|
81
|
+
@stderr = @options[:stderr]
|
82
|
+
@root = @options[:directory] + '/'
|
49
83
|
end
|
50
84
|
|
51
85
|
# This method does all the work!
|
@@ -78,10 +112,8 @@ module Kenji
|
|
78
112
|
while head = segments.shift
|
79
113
|
acc = "#{acc}/#{head}"
|
80
114
|
if controller = controller_for(acc) # if we have a valid controller
|
81
|
-
|
82
|
-
|
83
|
-
out = controller.call(method, subpath).to_json
|
84
|
-
end
|
115
|
+
subpath = '/'+segments.join('/')
|
116
|
+
out = controller.call(method, subpath).to_json
|
85
117
|
success = true
|
86
118
|
break
|
87
119
|
end
|
@@ -93,6 +125,7 @@ module Kenji
|
|
93
125
|
[@status, @headers, [out]]
|
94
126
|
end
|
95
127
|
rescue Exception => e
|
128
|
+
raise e unless @options[:catch_exceptions]
|
96
129
|
@stderr.puts e.inspect # log exceptions
|
97
130
|
e.backtrace.each {|b| @stderr.puts " #{b}" }
|
98
131
|
response_500
|
data/spec/5/controllers/main.rb
CHANGED
@@ -3,8 +3,21 @@ module Spec5
|
|
3
3
|
get '/' do
|
4
4
|
{ :foo => 'bar' }
|
5
5
|
end
|
6
|
+
|
6
7
|
get '/path' do
|
7
8
|
{ :baz => 'bar' }
|
8
9
|
end
|
10
|
+
|
11
|
+
get '/foo/:foo_id/:bar_id' do |foo_id|
|
12
|
+
{ :baz => 'bar' }
|
13
|
+
end
|
14
|
+
|
15
|
+
get "/bar/:foo_id/:bar_id" do |foo_id, bar_id, baz_id|
|
16
|
+
{ :baz => 'bar' }
|
17
|
+
end
|
18
|
+
|
19
|
+
get "/foobar/:foo_id/:bar_id" do |foo_id, bar_id|
|
20
|
+
{ :foo => foo_id, :bar => bar_id }
|
21
|
+
end
|
9
22
|
end
|
10
23
|
end
|
data/spec/kenji_spec.rb
CHANGED
@@ -17,7 +17,7 @@ def app_for(path, opts={})
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
describe Kenji::Kenji, 'expected
|
20
|
+
describe Kenji::Kenji, 'expected responses' do
|
21
21
|
include Rack::Test::Methods
|
22
22
|
|
23
23
|
context '1' do
|
@@ -106,6 +106,10 @@ describe Kenji::Kenji, 'expected reponses' do
|
|
106
106
|
last_response.body.should == expected_response
|
107
107
|
end
|
108
108
|
|
109
|
+
it 'should not match subsets of the route' do
|
110
|
+
get 'something/child/foo'
|
111
|
+
last_response.status.should == 404
|
112
|
+
end
|
109
113
|
end
|
110
114
|
|
111
115
|
context '3' do
|
@@ -183,6 +187,27 @@ describe Kenji::Kenji, 'expected reponses' do
|
|
183
187
|
put '/main/foo/foo_id/pass/pass_id'
|
184
188
|
last_response.status.should == 404
|
185
189
|
end
|
190
|
+
|
191
|
+
it "should pass variables to blocks" do
|
192
|
+
get "/foobar/foo_id/bar_id"
|
193
|
+
expected_response = { :foo => "foo_id", :bar => "bar_id" }.to_json
|
194
|
+
last_response.body.should == expected_response
|
195
|
+
last_response.status.should == 200
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should throw ArgumentError when there are too many arguments" do
|
199
|
+
get "foo/foo_id/bar_id"
|
200
|
+
last_response.status.should == 500
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should throw ArgumentError when there are too few arguments" do
|
204
|
+
get "bar/foo_id/bar_id"
|
205
|
+
last_response.status.should == 500
|
206
|
+
end
|
186
207
|
end
|
208
|
+
|
209
|
+
# TODO: Write unit tests for :catch_exceptions option.
|
210
|
+
# TODO: Write unit tests for Kenji::App
|
211
|
+
# TODO: Write unit tests for new root directory behavior.
|
187
212
|
|
188
213
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kenji
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- inited/tmp/always_restart.txt
|
118
118
|
- kenji.gemspec
|
119
119
|
- lib/kenji.rb
|
120
|
+
- lib/kenji/app.rb
|
120
121
|
- lib/kenji/controller.rb
|
121
122
|
- lib/kenji/string_extensions.rb
|
122
123
|
- lib/kenji/version.rb
|