lego-core 0.0.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/.gitignore +3 -0
- data/README.rdoc +95 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/examples/config.ru +137 -0
- data/lego-core.gemspec +70 -0
- data/lib/lego.rb +38 -0
- data/lib/lego/controller.rb +162 -0
- data/lib/lego/controller/action_context.rb +56 -0
- data/lib/lego/controller/config.rb +25 -0
- data/lib/lego/controller/route_handler.rb +86 -0
- data/lib/lego/plugin.rb +5 -0
- data/lib/lego/plugin/controller/not_found.rb +11 -0
- data/spec/integration/test_full_stack_spec.rb +82 -0
- data/spec/lego/controller/action_context_spec.rb +73 -0
- data/spec/lego/controller/config_spec.rb +102 -0
- data/spec/lego/controller/route_handler_spec.rb +188 -0
- data/spec/lego/controller_spec.rb +297 -0
- data/spec/lego/plugin/controller/not_found_spec.rb +22 -0
- data/spec/lego/plugin_spec.rb +0 -0
- data/spec/lego_spec.rb +90 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +42 -0
- metadata +86 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
require File.join('spec', '/spec_helper')
|
2
|
+
|
3
|
+
describe Lego::Controller::RouteHandler do
|
4
|
+
|
5
|
+
context '.match_all_routes with matching route' do
|
6
|
+
before do
|
7
|
+
@route_handler = empty_route_handler
|
8
|
+
@env = {}
|
9
|
+
@env["REQUEST_METHOD"] = 'GET'
|
10
|
+
@valid_route = {:path => '/myadmin'}
|
11
|
+
@other_route = {:path => '/foo'}
|
12
|
+
@fake_routes = {:get => [@other_route, @other_route, @valid_route], :post => [{}]}
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should return matching route if found' do
|
16
|
+
@route_handler.should_receive(:routes).and_return(@fake_routes)
|
17
|
+
@route_handler.should_receive(:run_matchers).with(@other_route, @env).exactly(2).times.and_return(false)
|
18
|
+
@route_handler.should_receive(:run_matchers).with(@valid_route, @env).and_return(true)
|
19
|
+
@route_handler.match_all_routes(@env).should eql(@valid_route)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return false when no matching route is found' do
|
23
|
+
@route_handler.should_receive(:routes).and_return({:get=> [{}]})
|
24
|
+
@route_handler.should_receive(:run_matchers).and_return(false)
|
25
|
+
@route_handler.match_all_routes(@env).should eql(nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '.run_matchers' do
|
30
|
+
before do
|
31
|
+
@route_handler = empty_route_handler
|
32
|
+
@env = {}
|
33
|
+
@route = { :path => '/mypath' }
|
34
|
+
module Match1; end
|
35
|
+
module Match2; end
|
36
|
+
@matchers = [Match1, Match2]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should return true when matching route found' do
|
40
|
+
Match1.should_receive(:match_route).with(@route, @env).and_return(false)
|
41
|
+
Match2.should_receive(:match_route).with(@route, @env).and_return(true)
|
42
|
+
@route_handler.should_receive(:matchers).and_return(@matchers)
|
43
|
+
@route_handler.run_matchers(@route, @env).should eql(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return false when no matching route found' do
|
47
|
+
Match1.should_receive(:match_route).with(@route, @env).and_return(false)
|
48
|
+
Match2.should_receive(:match_route).with(@route, @env).and_return(false)
|
49
|
+
@route_handler.should_receive(:matchers).and_return(@matchers)
|
50
|
+
@route_handler.run_matchers(@route, @env).should eql(false)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context '.routes' do
|
55
|
+
before do
|
56
|
+
@route_handler = empty_route_handler
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should return empty routes when new' do
|
60
|
+
@route_handler.routes.should eql({
|
61
|
+
:get => [],
|
62
|
+
:post => [],
|
63
|
+
:put => [],
|
64
|
+
:head => [],
|
65
|
+
:delete => [],
|
66
|
+
:not_found => nil
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context '.add_route' do
|
72
|
+
before do
|
73
|
+
@route_handler = empty_route_handler
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should appen routes with their options' do
|
77
|
+
route_options1 = { :path => '/a_path1' }
|
78
|
+
route_options2 = { :path => '/a_path2' }
|
79
|
+
@route_handler.add_route(:get, route_options1)
|
80
|
+
@route_handler.add_route(:get, route_options2)
|
81
|
+
@route_handler.routes[:get].should eql([route_options1, route_options2])
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should set not_found route if method is :not_found' do
|
85
|
+
route_options = { :action_block => '{block}' }
|
86
|
+
@route_handler.add_route(:not_found, route_options)
|
87
|
+
@route_handler.routes[:not_found].should eql(route_options)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '.matchers' do
|
92
|
+
before do
|
93
|
+
@route_handler = empty_route_handler
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should return an empty Array when new' do
|
97
|
+
@route_handler.matchers.should eql([])
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
context '.add_matcher' do
|
103
|
+
before do
|
104
|
+
@route_handler = empty_route_handler
|
105
|
+
module ValidMatcher; def self.match_route(route, env); end; end
|
106
|
+
module BrokenMatcher; end
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should store route matchers if valid' do
|
110
|
+
@route_handler.add_matcher(ValidMatcher)
|
111
|
+
@route_handler.matchers.should eql([ValidMatcher])
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should raise InvalidMatcher error if invalid' do
|
115
|
+
@route_handler.should_receive(:validate_matcher).and_return(false)
|
116
|
+
lambda { @route_handler.add_matcher(BrokenMatcher) }.should raise_error(Lego::Controller::RouteHandler::InvalidMatcher)
|
117
|
+
@route_handler.matchers.should == []
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context '.validate_matcher' do
|
122
|
+
before do
|
123
|
+
@route_handler = empty_route_handler
|
124
|
+
module MyBrokenMatcher; end
|
125
|
+
module MyValidMatcher; def self.match_route(); end; end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should return true if a valid Matcher module' do
|
129
|
+
@route_handler.validate_matcher(MyValidMatcher).should be_true
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should return false if not a valid Matcher module' do
|
133
|
+
@route_handler.validate_matcher(MyBrokenMatcher).should be_false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'when extended' do
|
138
|
+
before do
|
139
|
+
reset_lego_base
|
140
|
+
end
|
141
|
+
it 'plugins should be copied from base class' do
|
142
|
+
|
143
|
+
module GlobalPlug
|
144
|
+
def self.register(lego)
|
145
|
+
lego.add_plugin :router, self
|
146
|
+
end
|
147
|
+
|
148
|
+
def another_routehandler
|
149
|
+
add_route(:get, {})
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.match_route(route, env); end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Create two fake plugins
|
156
|
+
Object.const_set(:App1Plug, GlobalPlug.clone)
|
157
|
+
Object.const_set(:App2Plug, GlobalPlug.clone)
|
158
|
+
|
159
|
+
Lego.plugin GlobalPlug
|
160
|
+
|
161
|
+
create_new_app("MyApp1", Lego::Controller)
|
162
|
+
create_new_app("MyApp2", Lego::Controller)
|
163
|
+
|
164
|
+
MyApp1.plugin App1Plug
|
165
|
+
MyApp2.plugin App2Plug
|
166
|
+
|
167
|
+
Lego::Controller::RouteHandler.matchers.should eql([GlobalPlug])
|
168
|
+
MyApp1::RouteHandler.matchers.should eql([GlobalPlug, App1Plug])
|
169
|
+
MyApp2::RouteHandler.matchers.should eql([GlobalPlug, App2Plug])
|
170
|
+
end
|
171
|
+
|
172
|
+
after do
|
173
|
+
rm_const "GlobalPlug", "App1Plug", "App2Plug", "MyApp1", "MyApp2"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
def empty_route_handler
|
180
|
+
route_handler = Lego::Controller::RouteHandler.clone
|
181
|
+
route_handler.class_eval "remove_instance_variable :@route_cache if @route_cache"
|
182
|
+
route_handler.class_eval "remove_instance_variable :@matcher_cache if @matcher_cache"
|
183
|
+
route_handler
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
|
188
|
+
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require File.join('spec', '/spec_helper')
|
2
|
+
|
3
|
+
describe Lego::Controller do
|
4
|
+
|
5
|
+
it 'should contain an ActionContext Class' do
|
6
|
+
Lego::Controller::ActionContext.should be_kind_of(Class)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should contain an RouteHandler Module' do
|
10
|
+
Lego::Controller::RouteHandler.should be_kind_of(Module)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should load Lego::Plugin::Controller::NotFound by default' do
|
14
|
+
Lego::Controller.methods.should include('not_found')
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when inherited' do
|
18
|
+
before do
|
19
|
+
create_new_app("MyController", Lego::Controller)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should create a new ActionContext for the inheriting class' do
|
23
|
+
MyController::ActionContext.object_id.should_not == Lego::Controller::ActionContext.object_id
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should setup a constant on ActionContext holding a reference to the application class' do
|
27
|
+
MyController::ActionContext::ApplicationClass.should eql(MyController)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should create a new RouteHandler for the inheriting class' do
|
31
|
+
MyController::RouteHandler.object_id.should_not == Lego::Controller::RouteHandler.object_id
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should create a new Config for the inheriting class' do
|
35
|
+
MyController::Config.object_id.should_not == Lego::Controller::Config.object_id
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should create a new Config for every inheriting class' do
|
39
|
+
create_new_app "MyOtherController", Lego::Controller
|
40
|
+
MyController::Config.object_id.should_not == MyOtherController::Config.object_id
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '.plugin <plugin_module>' do
|
45
|
+
before do
|
46
|
+
create_new_app("MyApp", Lego::Controller)
|
47
|
+
module MyPlugin
|
48
|
+
def self.register(lego)
|
49
|
+
lego.register self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
after do
|
55
|
+
rm_const "MyPlugin", "MyApp"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have a class method named plugin' do
|
59
|
+
MyApp.should respond_to(:plugin)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should call self.register on <plugin_module>' do
|
63
|
+
MyPlugin.should_receive(:register).with(MyApp)
|
64
|
+
MyApp.plugin MyPlugin
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context '.add_plugin :view, <plugin_module>' do
|
69
|
+
before do
|
70
|
+
create_new_app("MyApp", Lego::Controller)
|
71
|
+
module MyViewPlugin
|
72
|
+
def self.register(lego)
|
73
|
+
lego.add_plugin :view, self
|
74
|
+
end
|
75
|
+
def makebold(content)
|
76
|
+
"<b>#{content}</b>"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should make plugin methods availibe to ActionContext' do
|
82
|
+
MyApp::ActionContext.instance_methods.should_not include('makebold')
|
83
|
+
MyApp.plugin MyViewPlugin
|
84
|
+
MyApp::ActionContext.instance_methods.should include('makebold')
|
85
|
+
end
|
86
|
+
|
87
|
+
after do
|
88
|
+
rm_const "MyViewPlugin", "MyApp"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context '.add_plugin :controller, <plugin_module>' do
|
93
|
+
before do
|
94
|
+
create_new_app("MyApp", Lego::Controller)
|
95
|
+
module MyPlugin2
|
96
|
+
def self.register(lego)
|
97
|
+
lego.add_plugin :controller, PluginMethods
|
98
|
+
end
|
99
|
+
module PluginMethods
|
100
|
+
def get_this;end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
after do
|
106
|
+
rm_const "MyPlugin2", "MyApp"
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should extend controller with <plugin_module>' do
|
110
|
+
MyApp.should_not respond_to(:get_this)
|
111
|
+
MyApp.plugin MyPlugin2
|
112
|
+
MyApp.should respond_to(:get_this)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context '.add_route <method> <route>' do
|
117
|
+
before do
|
118
|
+
create_new_app("MyApp", Lego::Controller)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should be defined' do
|
122
|
+
MyApp.should respond_to(:add_route)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should add a route to RouteHandler' do
|
126
|
+
route = {:path => '/somewhere'}
|
127
|
+
method = :get
|
128
|
+
MyApp::RouteHandler.should_receive(:add_route).with(method, route)
|
129
|
+
MyApp.add_route(method, route)
|
130
|
+
end
|
131
|
+
after { rm_const 'MyApp' }
|
132
|
+
end
|
133
|
+
|
134
|
+
context '.add_plugin :router, <plugin_module>' do
|
135
|
+
before do
|
136
|
+
create_new_app("MyApp", Lego::Controller)
|
137
|
+
module MyRouter
|
138
|
+
def self.register(lego)
|
139
|
+
lego.add_plugin :router, RouterMethods
|
140
|
+
end
|
141
|
+
module RouterMethods
|
142
|
+
extend self
|
143
|
+
def match_route
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
after do
|
150
|
+
rm_const "MyRouter", "MyApp"
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should not extend controller with <plugin_module>' do
|
154
|
+
MyApp.plugin MyRouter
|
155
|
+
MyApp.should_not respond_to(:match_route)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should call add_matcher on RouteHandler with <plugin_module>' do
|
159
|
+
MyApp::RouteHandler.should_receive(:add_matcher).with(MyRouter::RouterMethods)
|
160
|
+
MyApp.plugin MyRouter
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context '.call <env>' do
|
165
|
+
context 'with a route that matches' do
|
166
|
+
before do
|
167
|
+
@env = ["Environment"]
|
168
|
+
create_new_app("MyApp", Lego::Controller)
|
169
|
+
MyApp::RouteHandler.should_receive(:match_all_routes).with(@env).and_return("route")
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should create a new instance of ActionContext' do
|
173
|
+
mock = mock("ActionContext instance")
|
174
|
+
mock.should_receive(:run).with("route", @env)
|
175
|
+
MyApp::ActionContext.should_receive(:new).and_return(mock)
|
176
|
+
end
|
177
|
+
|
178
|
+
after do
|
179
|
+
MyApp.call(@env)
|
180
|
+
rm_const "MyApp"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'with a route that don\'t matches' do
|
185
|
+
before do
|
186
|
+
@env = ["Environment"]
|
187
|
+
create_new_app("MyApp", Lego::Controller)
|
188
|
+
MyApp::RouteHandler.should_receive(:match_all_routes).with(@env).and_return(nil)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should create a new instance of ActionContext' do
|
192
|
+
MyApp::ActionContext.should_not_receive(:new)
|
193
|
+
end
|
194
|
+
|
195
|
+
after do
|
196
|
+
MyApp.call(@env).should eql(
|
197
|
+
[404, {'Content-Type' => 'text/html'}, '404 - Not found']
|
198
|
+
)
|
199
|
+
rm_const "MyApp"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context 'without matching route and a :not_found route defined' do
|
204
|
+
before do
|
205
|
+
@env = ["Rack Env"]
|
206
|
+
@routes = { :not_found => {}}
|
207
|
+
create_new_app("MyApp", Lego::Controller)
|
208
|
+
MyApp::RouteHandler.should_receive(:match_all_routes).with(@env).and_return(nil)
|
209
|
+
MyApp::RouteHandler.should_receive(:routes).and_return( @routes )
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should create a new ActionContext with the :not_found route' do
|
213
|
+
action_context = mock("ActionContext instance")
|
214
|
+
MyApp::ActionContext.should_receive(:new).and_return(action_context)
|
215
|
+
action_context.should_receive(:run).with(@routes[:not_found], @env)
|
216
|
+
end
|
217
|
+
|
218
|
+
after do
|
219
|
+
MyApp.call(@env)
|
220
|
+
rm_const "MyApp"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context ".set" do
|
226
|
+
context 'on Lego::Controller' do
|
227
|
+
|
228
|
+
it "should proxy the method call to its own Config" do
|
229
|
+
Lego::Controller::Config.should_receive(:set).and_return(nil)
|
230
|
+
Lego::Controller.set :foo => "bar"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "on subclasses" do
|
235
|
+
|
236
|
+
before do
|
237
|
+
create_new_app "MyApp", Lego::Controller
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should proxy the method call to its own Confg" do
|
241
|
+
MyApp::Config.should_receive(:set).and_return(nil)
|
242
|
+
MyApp.set :foo => "bar"
|
243
|
+
end
|
244
|
+
|
245
|
+
after do
|
246
|
+
rm_const "MyApp"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context ".environment <env>" do
|
252
|
+
|
253
|
+
context 'on Lego::Controller' do
|
254
|
+
|
255
|
+
before do
|
256
|
+
@config_block = lambda {}
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should proxy the method call to its own config' do
|
260
|
+
Lego::Controller::Config.should_receive(:environment).with(nil, &@config_block).and_return(nil)
|
261
|
+
Lego::Controller.environment(&@config_block)
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'take an optional <env> argument' do
|
265
|
+
Lego::Controller::Config.should_receive(:environment).
|
266
|
+
with('development', &@config_block).
|
267
|
+
and_return(nil)
|
268
|
+
|
269
|
+
Lego::Controller.environment('development', &@config_block)
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should raise an ArgumentError if no block is provided' do
|
273
|
+
lambda {
|
274
|
+
Lego::Controller.environment(:development)
|
275
|
+
}.should raise_error(ArgumentError, "No block provided")
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context 'on subclasses' do
|
280
|
+
|
281
|
+
before do
|
282
|
+
create_new_app "MyApp", Lego::Controller
|
283
|
+
@config_block = lambda {}
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'should proxy the method call to its own config' do
|
287
|
+
MyApp::Config.should_receive(:environment).with(nil, &@config_block).and_return(nil)
|
288
|
+
MyApp.environment(&@config_block)
|
289
|
+
end
|
290
|
+
|
291
|
+
after do
|
292
|
+
rm_const "MyApp"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|