kenji 0.7 → 1.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.
- data/README.md +33 -11
- data/bin/{kenji-init → kenji} +9 -1
- data/inited/lib/README.md +2 -0
- data/lib/kenji/version.rb +1 -1
- data/lib/kenji.rb +39 -18
- data/spec/5/controllers/main.rb +10 -0
- data/spec/kenji_spec.rb +29 -2
- metadata +7 -9
- data/inited/configuration/README +0 -1
- data/inited/lib/README +0 -0
- data/inited/models/README +0 -1
- data/inited/scripts/README +0 -1
- data/inited/tmp/README +0 -1
data/README.md
CHANGED
@@ -5,23 +5,37 @@ Kenji is a lightweight backend framework for Ruby.
|
|
5
5
|
|
6
6
|
## Rationale
|
7
7
|
|
8
|
-
Kenji believes that a traditional web application should be divided into two
|
8
|
+
Kenji believes that a traditional web application should be divided into two
|
9
|
+
parts: an client application running in the browser (HTML/JS/CSS), and
|
10
|
+
a backend API with which it communicates. Kenji is the backend side of the
|
11
|
+
equation, while the front-end architecture is left up to the user. (Popular
|
12
|
+
options are [backbone][] and [spine][].)
|
9
13
|
|
10
14
|
[backbone]: http://documentcloud.github.com/backbone/
|
11
15
|
[spine]: http://spinejs.com/
|
12
16
|
|
13
|
-
Kenji believes that in order to keep clean and organized code, routes should be
|
17
|
+
Kenji believes that in order to keep clean and organized code, routes should be
|
18
|
+
defined inline with their code.
|
14
19
|
|
15
|
-
Kenji believes that an app should be usable as a library from scripts or from
|
20
|
+
Kenji believes that an app should be usable as a library from scripts or from
|
21
|
+
the command line. An app should be automatable and testable.
|
16
22
|
|
17
|
-
Lastly, Kenji is opinionated, but only about things that directly pertain to
|
23
|
+
Lastly, Kenji is opinionated, but only about things that directly pertain to
|
24
|
+
routing and code architecture. Kenji believes in being a ligthweight module
|
25
|
+
that only solves the problem it focuses on. Everything else is left up to the
|
26
|
+
user. (ORM, data store, web server, message queue, front-end framework,
|
27
|
+
deployment process, etc.)
|
18
28
|
|
19
29
|
|
20
30
|
### Routing
|
21
31
|
|
22
|
-
Kenji wants you to organize your code into logical units of code, aka.
|
32
|
+
Kenji wants you to organize your code into logical units of code, aka.
|
33
|
+
controllers. The controllers will automatically be selected based on the url
|
34
|
+
requested, and the rest of the route is defined inline in the controller, with
|
35
|
+
a domain-specific-language.
|
23
36
|
|
24
|
-
The canonical Hello World example for the URL `/hello/world` in Kenji would
|
37
|
+
The canonical Hello World example for the URL `/hello/world` in Kenji would
|
38
|
+
look like this, in `controller/hello.rb`:
|
25
39
|
|
26
40
|
```ruby
|
27
41
|
class HelloController < Kenji::Controller
|
@@ -55,7 +69,8 @@ end
|
|
55
69
|
|
56
70
|
### Data Transport
|
57
71
|
|
58
|
-
JSON is used as the singular data transport for Kenji. Requests are assumed to
|
72
|
+
JSON is used as the singular data transport for Kenji. Requests are assumed to
|
73
|
+
have:
|
59
74
|
|
60
75
|
Content-Type: application/json; charset=utf-8
|
61
76
|
Accept: application/json; charset=utf-8
|
@@ -63,10 +78,11 @@ JSON is used as the singular data transport for Kenji. Requests are assumed to h
|
|
63
78
|
|
64
79
|
## Usage
|
65
80
|
|
66
|
-
Getting started with Kenji could not be any easier. All it takes is a few lines
|
81
|
+
Getting started with Kenji could not be any easier. All it takes is a few lines
|
82
|
+
and a terminal:
|
67
83
|
|
68
84
|
$ gem install kenji # (once kenji is on the rubygems main source)
|
69
|
-
$ kenji
|
85
|
+
$ kenji init app_name; cd app_name
|
70
86
|
$ rackup # launch the webserver
|
71
87
|
|
72
88
|
And already, your app is ready to go:
|
@@ -78,12 +94,18 @@ And already, your app is ready to go:
|
|
78
94
|
## Requirements & Assumptions
|
79
95
|
|
80
96
|
- Requires RubyGems and Bundler.
|
81
|
-
- Requires Rack
|
82
|
-
- Requires Ruby 1.9.
|
97
|
+
- Requires Rack ~> 1.5.3.
|
98
|
+
- Requires Ruby >= 1.9.3.
|
83
99
|
|
84
100
|
|
85
101
|
## Changelog
|
86
102
|
|
103
|
+
#### 0.7
|
104
|
+
|
105
|
+
- Pass can now contain variables, that get set as @ivars on the controller.
|
106
|
+
Thanks @nicotaing.
|
107
|
+
- Accessors for `response_headers`
|
108
|
+
|
87
109
|
#### 0.6.5
|
88
110
|
|
89
111
|
- Automatically handle CORS / Access-Control.
|
data/bin/{kenji-init → kenji}
RENAMED
@@ -1,6 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
|
4
|
+
unless ARGV.first == 'init'
|
5
|
+
puts 'Usage: kenji init [project-name]'
|
6
|
+
Process.exit
|
7
|
+
end
|
8
|
+
|
9
|
+
puts 'Initializing a new Kenji project... :)'
|
10
|
+
|
11
|
+
path = File.expand_path('.' << '/' << (ARGV[1] || '.'))
|
4
12
|
app_name = File.basename(path)
|
5
13
|
|
6
14
|
Dir.mkdir(path) unless File.directory?(path)
|
data/lib/kenji/version.rb
CHANGED
data/lib/kenji.rb
CHANGED
@@ -27,6 +27,11 @@ module Kenji
|
|
27
27
|
#
|
28
28
|
# - :auto_cors => true | false # automatically deal with
|
29
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
35
|
#
|
31
36
|
def initialize(env, root, options = {})
|
32
37
|
@headers = {
|
@@ -38,7 +43,8 @@ module Kenji
|
|
38
43
|
@env = env
|
39
44
|
|
40
45
|
@options = {
|
41
|
-
auto_cors: true
|
46
|
+
auto_cors: true,
|
47
|
+
root_controller: nil
|
42
48
|
}.merge(options)
|
43
49
|
end
|
44
50
|
|
@@ -57,21 +63,28 @@ module Kenji
|
|
57
63
|
|
58
64
|
|
59
65
|
# new routing code
|
66
|
+
method = @env['REQUEST_METHOD'].downcase.to_sym
|
67
|
+
|
60
68
|
segments = path.split('/')
|
61
|
-
segments = segments.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
segments = segments.unshift('') unless segments.first == '' # ensure existence of leading /'s empty segment
|
70
|
+
|
71
|
+
if @options[:root_controller]
|
72
|
+
controller = controller_instance(@options[:root_controller])
|
73
|
+
subpath = segments.join('/')
|
74
|
+
out = controller.call(method, subpath).to_json
|
75
|
+
success = true
|
76
|
+
else
|
77
|
+
acc = ''; out = '', success = false
|
78
|
+
while head = segments.shift
|
79
|
+
acc = "#{acc}/#{head}"
|
80
|
+
if controller = controller_for(acc) # if we have a valid controller
|
81
|
+
begin
|
82
|
+
subpath = '/'+segments.join('/')
|
83
|
+
out = controller.call(method, subpath).to_json
|
84
|
+
end
|
85
|
+
success = true
|
86
|
+
break
|
72
87
|
end
|
73
|
-
success = true
|
74
|
-
break
|
75
88
|
end
|
76
89
|
end
|
77
90
|
|
@@ -137,8 +150,7 @@ module Kenji
|
|
137
150
|
response = { # default structure. TODO: figure out if i really want to keep this
|
138
151
|
:status => code,
|
139
152
|
:message => message
|
140
|
-
}
|
141
|
-
hash.each { |k,v| response[k]=v }
|
153
|
+
}.merge(hash)
|
142
154
|
throw(:KenjiRespondControlFlowInterrupt, [@status, @headers, [response.to_json]])
|
143
155
|
end
|
144
156
|
|
@@ -164,7 +176,8 @@ module Kenji
|
|
164
176
|
end
|
165
177
|
end
|
166
178
|
|
167
|
-
# Will attempt to fetch the controller, and verify that it is a implements
|
179
|
+
# Will attempt to fetch the controller, and verify that it is a implements
|
180
|
+
# call.
|
168
181
|
#
|
169
182
|
def controller_for(subpath)
|
170
183
|
subpath = '/_' if subpath == '/'
|
@@ -173,7 +186,15 @@ module Kenji
|
|
173
186
|
require path
|
174
187
|
controller_name = subpath.split('/').last.sub(/^_/, 'Root')
|
175
188
|
controller_class = Object.const_get(controller_name.to_s.to_camelcase+'Controller')
|
176
|
-
|
189
|
+
controller_instance(controller_class)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Attempts to instantiate the controller class, set up as a Kenji
|
193
|
+
# controller.
|
194
|
+
#
|
195
|
+
def controller_instance(controller_class)
|
196
|
+
# ensure protocol compliance
|
197
|
+
return unless controller_class.method_defined?(:call) && controller_class.instance_method(:call).arity == 2
|
177
198
|
controller = controller_class.new
|
178
199
|
controller.kenji = self if controller.respond_to?(:kenji=)
|
179
200
|
return controller if controller
|
data/spec/kenji_spec.rb
CHANGED
@@ -9,9 +9,9 @@ require 'kenji'
|
|
9
9
|
|
10
10
|
# NOTE: these tests make use of the controllers defined in test/controllers.
|
11
11
|
|
12
|
-
def app_for(path)
|
12
|
+
def app_for(path, opts={})
|
13
13
|
lambda do |env|
|
14
|
-
kenji = Kenji::Kenji.new(env, File.dirname(__FILE__)+'/'+path)
|
14
|
+
kenji = Kenji::Kenji.new(env, File.dirname(__FILE__)+'/'+path, opts)
|
15
15
|
kenji.stderr = double(puts: nil)
|
16
16
|
kenji.call
|
17
17
|
end
|
@@ -158,4 +158,31 @@ describe Kenji::Kenji, 'expected reponses' do
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
+
context '5' do
|
162
|
+
before do
|
163
|
+
require File.expand_path('./5/controllers/main.rb', File.dirname(__FILE__))
|
164
|
+
end
|
165
|
+
|
166
|
+
def app; app_for('5', root_controller: Spec5::MainController); end
|
167
|
+
|
168
|
+
it "should use main controller for /" do
|
169
|
+
get '/'
|
170
|
+
expected_response = { :foo => 'bar' }.to_json
|
171
|
+
last_response.body.should == expected_response
|
172
|
+
last_response.status.should == 200
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should use main controller for /path" do
|
176
|
+
get '/path'
|
177
|
+
expected_response = { :baz => 'bar' }.to_json
|
178
|
+
last_response.body.should == expected_response
|
179
|
+
last_response.status.should == 200
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should 404 on main controller" do
|
183
|
+
put '/main/foo/foo_id/pass/pass_id'
|
184
|
+
last_response.status.should == 404
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
161
188
|
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: '0
|
4
|
+
version: '1.0'
|
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-05-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -95,7 +95,7 @@ description: A lightweight Ruby web framework.
|
|
95
95
|
email:
|
96
96
|
- kenneth@ballenegger.com
|
97
97
|
executables:
|
98
|
-
- kenji
|
98
|
+
- kenji
|
99
99
|
extensions: []
|
100
100
|
extra_rdoc_files: []
|
101
101
|
files:
|
@@ -104,20 +104,16 @@ files:
|
|
104
104
|
- LICENSE
|
105
105
|
- README.md
|
106
106
|
- Rakefile
|
107
|
-
- bin/kenji
|
107
|
+
- bin/kenji
|
108
108
|
- inited/.gitignore
|
109
109
|
- inited/Gemfile
|
110
110
|
- inited/README.md
|
111
111
|
- inited/__APP_NAME__
|
112
112
|
- inited/config.ru
|
113
|
-
- inited/configuration/README
|
114
113
|
- inited/controllers/main.rb
|
115
|
-
- inited/lib/README
|
116
|
-
- inited/models/README
|
114
|
+
- inited/lib/README.md
|
117
115
|
- inited/public/humans.txt
|
118
116
|
- inited/public/index.html
|
119
|
-
- inited/scripts/README
|
120
|
-
- inited/tmp/README
|
121
117
|
- inited/tmp/always_restart.txt
|
122
118
|
- kenji.gemspec
|
123
119
|
- lib/kenji.rb
|
@@ -131,6 +127,7 @@ files:
|
|
131
127
|
- spec/4/controllers/bar.rb
|
132
128
|
- spec/4/controllers/main.rb
|
133
129
|
- spec/4/controllers/pass.rb
|
130
|
+
- spec/5/controllers/main.rb
|
134
131
|
- spec/kenji_spec.rb
|
135
132
|
homepage: https://github.com/kballenegger/kenji
|
136
133
|
licenses: []
|
@@ -164,5 +161,6 @@ test_files:
|
|
164
161
|
- spec/4/controllers/bar.rb
|
165
162
|
- spec/4/controllers/main.rb
|
166
163
|
- spec/4/controllers/pass.rb
|
164
|
+
- spec/5/controllers/main.rb
|
167
165
|
- spec/kenji_spec.rb
|
168
166
|
has_rdoc:
|
data/inited/configuration/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
This is where configuration goes.
|
data/inited/lib/README
DELETED
File without changes
|
data/inited/models/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
This is where models go.
|
data/inited/scripts/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
This is where scripts go.
|
data/inited/tmp/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
This folder is for Rack & Passenger to use.
|