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