pakada-dispatch 0.0.3 → 0.0.4
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/lib/pakada/dispatch/controller.rb +66 -10
- data/lib/pakada/dispatch/version.rb +1 -1
- data/spec/controller_spec.rb +86 -10
- metadata +6 -21
- data/.project +0 -12
@@ -1,6 +1,18 @@
|
|
1
1
|
class Pakada
|
2
2
|
class Dispatch
|
3
|
-
Action = Struct.new :block, :options
|
3
|
+
Action = Struct.new :name, :block, :options
|
4
|
+
|
5
|
+
METHODS = [:get, :head, :post, :put, :delete]
|
6
|
+
|
7
|
+
NOT_FOUND_ENDPOINT = proc do |env|
|
8
|
+
[404, {"X-Cascade" => "pass"}, ["Not Found"]]
|
9
|
+
end
|
10
|
+
METHOD_NOT_ALLOWED_ENDPOINT = proc do |methods|
|
11
|
+
proc do |env|
|
12
|
+
allow = methods.map {|m| m.to_s.upcase }.join(", ")
|
13
|
+
[405, {"Allow" => allow}, ["Method Not Allowed"]]
|
14
|
+
end
|
15
|
+
end
|
4
16
|
|
5
17
|
module Controller
|
6
18
|
include Util
|
@@ -20,8 +32,12 @@ class Pakada
|
|
20
32
|
end
|
21
33
|
|
22
34
|
def call_action(name, options = {})
|
23
|
-
|
24
|
-
|
35
|
+
method = (request.request_method || "GET").downcase.to_sym
|
36
|
+
action = self.class.actions.find do |a|
|
37
|
+
a.name == name && (
|
38
|
+
a.options[:methods] == :all || a.options[:methods].include?(method))
|
39
|
+
end
|
40
|
+
raise ArgumentError, "No such action - #{name} (#{method})" unless action
|
25
41
|
|
26
42
|
@options = action.options.merge options
|
27
43
|
catch(:finish) { instance_eval &action.block }
|
@@ -80,21 +96,61 @@ class Pakada
|
|
80
96
|
|
81
97
|
module ClassMethods
|
82
98
|
def actions
|
83
|
-
@actions ||=
|
99
|
+
@actions ||= []
|
84
100
|
end
|
85
101
|
|
86
102
|
def action(name, options = {}, &block)
|
87
103
|
if block
|
88
|
-
|
89
|
-
|
90
|
-
|
104
|
+
options[:methods] ||= :all
|
105
|
+
unless options[:methods].respond_to?(:each) || options[:methods] == :all
|
106
|
+
options[:methods] = [options[:methods]]
|
107
|
+
end
|
108
|
+
actions << Action.new(name, block, options)
|
109
|
+
return
|
110
|
+
end
|
111
|
+
|
112
|
+
matches = actions.select {|a| a.name == name }
|
113
|
+
return NOT_FOUND_ENDPOINT if matches.empty?
|
114
|
+
|
115
|
+
proc do |env|
|
116
|
+
method = (env["REQUEST_METHOD"] || "GET").downcase.to_sym
|
117
|
+
allowed = matches.select do |a|
|
118
|
+
a.options[:methods] == :all || a.options[:methods].include?(method)
|
119
|
+
end
|
120
|
+
|
121
|
+
if allowed.empty?
|
122
|
+
allow = matches.inject([]) {|m, a| m.push(*a.options[:methods]); m }
|
123
|
+
METHOD_NOT_ALLOWED_ENDPOINT.call(allow).call(env)
|
124
|
+
else
|
91
125
|
controller = new env
|
92
|
-
controller.call_action name, options
|
126
|
+
controller.call_action allowed.first.name, options
|
93
127
|
controller.response.finish
|
94
128
|
end
|
95
|
-
else
|
96
|
-
proc {|env| [404, {"X-Cascade" => "pass"}, []] }
|
97
129
|
end
|
130
|
+
|
131
|
+
# elsif actions[name]
|
132
|
+
# proc do |env|
|
133
|
+
# method = env["REQUEST_METHOD"]
|
134
|
+
# allowed = actions[name].options[:methods]
|
135
|
+
# if method && allowed != :all && !allowed.include?(method.downcase.to_sym)
|
136
|
+
# [405, {"Allow" => allowed.map {|m| m.to_s.upcase }.join(", ")}, ["Method Not Allowed"]]
|
137
|
+
# else
|
138
|
+
# controller = new env
|
139
|
+
# controller.call_action name, options
|
140
|
+
# controller.response.finish
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
# else
|
144
|
+
# proc {|env| [404, {"X-Cascade" => "pass"}, []] }
|
145
|
+
# end
|
146
|
+
end
|
147
|
+
|
148
|
+
METHODS.each do |m|
|
149
|
+
eval <<-R, nil, __FILE__, __LINE__
|
150
|
+
def #{m}(name, options = {}, &block)
|
151
|
+
action(name, options.merge(:methods => :#{m}), &block)
|
152
|
+
end
|
153
|
+
R
|
98
154
|
end
|
99
155
|
end
|
100
156
|
|
data/spec/controller_spec.rb
CHANGED
@@ -25,17 +25,31 @@ describe "SomeController" do
|
|
25
25
|
it "adds an action to the controller" do
|
26
26
|
block = proc {}
|
27
27
|
subject.action :foo, &block
|
28
|
-
subject.actions
|
29
|
-
|
28
|
+
subject.actions.find {|a| a.name == :foo }.block.should == block
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "and twice with the same name" do
|
32
|
+
it "adds a second action with the same name" do
|
33
|
+
2.times { subject.action :foo, &proc {} }
|
34
|
+
subject.should have(2).actions
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
39
|
describe "when called with a block and a hash" do
|
34
40
|
it "adds an action with options" do
|
35
|
-
options = double "options"
|
36
41
|
block = proc {}
|
37
|
-
subject.action :foo,
|
38
|
-
subject.actions
|
42
|
+
subject.action :foo, {}, &block
|
43
|
+
subject.actions.find {|a| a.name == :foo }.options.should respond_to(:[])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "defaults the :methods option to :all" do
|
47
|
+
subject.action :foo, &proc {}
|
48
|
+
subject.actions.find {|a| a.name == :foo }.options[:methods].should == :all
|
49
|
+
|
50
|
+
subject.actions.clear
|
51
|
+
subject.action :foo, {:methods => :get}, &proc {}
|
52
|
+
subject.actions.find {|a| a.name == :foo }.options[:methods].should == [:get]
|
39
53
|
end
|
40
54
|
end
|
41
55
|
|
@@ -57,12 +71,40 @@ describe "SomeController" do
|
|
57
71
|
subject.action(:foo, options).call(env)
|
58
72
|
end
|
59
73
|
|
60
|
-
describe "
|
61
|
-
it "
|
74
|
+
describe "and with the name of a non-existing action" do
|
75
|
+
it "returns a 404 end-point" do
|
62
76
|
subject.action(:foo).call({}).should == [
|
63
|
-
404, {"X-Cascade" => "pass"}, []
|
77
|
+
404, {"X-Cascade" => "pass"}, ["Not Found"]
|
78
|
+
]
|
64
79
|
end
|
65
80
|
end
|
81
|
+
|
82
|
+
it "checks if the :methods constraint matches" do
|
83
|
+
subject.action :foo, :methods => [:get, :head], &proc {}
|
84
|
+
subject.action(:foo).call("REQUEST_METHOD" => "POST").should == [
|
85
|
+
405, {"Allow" => "GET, HEAD"}, ["Method Not Allowed"]
|
86
|
+
]
|
87
|
+
subject.action(:foo).call("REQUEST_METHOD" => "GET")[0].should == 200
|
88
|
+
subject.action(:foo).call({})[0].should == 200
|
89
|
+
|
90
|
+
subject.action :foo, :methods => :all, &proc {}
|
91
|
+
subject.action(:foo).call("REQUEST_METHOD" => "PUT")[0].should == 200
|
92
|
+
end
|
93
|
+
|
94
|
+
it "tries the next equal-named action if the constraint doesn't match" do
|
95
|
+
subject.action(:foo, :methods => :get) { not_found }
|
96
|
+
subject.action(:foo) {}
|
97
|
+
subject.action(:foo).call("REQUEST_METHOD" => "POST")[0].should == 200
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
Pakada::Dispatch::METHODS.each do |m|
|
103
|
+
describe ".#{m}" do
|
104
|
+
it "forwards to .action(:#{m}, ...)" do
|
105
|
+
subject.should_receive(:action).with(:foo, :methods => m)
|
106
|
+
subject.send(m, :foo, &proc {})
|
107
|
+
end
|
66
108
|
end
|
67
109
|
end
|
68
110
|
|
@@ -242,18 +284,52 @@ describe "SomeController" do
|
|
242
284
|
end
|
243
285
|
end
|
244
286
|
|
287
|
+
describe Pakada::Dispatch::NOT_FOUND_ENDPOINT do
|
288
|
+
subject { Pakada::Dispatch::NOT_FOUND_ENDPOINT.call({}) }
|
289
|
+
|
290
|
+
it "returns a 404 response" do
|
291
|
+
subject[0].should == 404
|
292
|
+
subject[2].join.should == "Not Found"
|
293
|
+
end
|
294
|
+
|
295
|
+
it "is stackable" do
|
296
|
+
subject[1]["X-Cascade"].should == "pass"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe Pakada::Dispatch::METHOD_NOT_ALLOWED_ENDPOINT do
|
301
|
+
subject do
|
302
|
+
Pakada::Dispatch::METHOD_NOT_ALLOWED_ENDPOINT.call([:get, :post]).call({})
|
303
|
+
end
|
304
|
+
|
305
|
+
it "returns a 405 response" do
|
306
|
+
subject[0].should == 405
|
307
|
+
subject[2].join.should == "Method Not Allowed"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "returns an appropriate Allow header" do
|
311
|
+
subject[1]["Allow"].should == "GET, POST"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
245
315
|
describe Pakada::Dispatch::Action do
|
316
|
+
describe "#name" do
|
317
|
+
it "returns the action's name" do
|
318
|
+
Pakada::Dispatch::Action.new(:asdf).name.should == :asdf
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
246
322
|
describe "#block" do
|
247
323
|
it "returns the action's code block" do
|
248
324
|
block = double "block"
|
249
|
-
Pakada::Dispatch::Action.new(block).block.should == block
|
325
|
+
Pakada::Dispatch::Action.new(nil, block).block.should == block
|
250
326
|
end
|
251
327
|
end
|
252
328
|
|
253
329
|
describe "#options" do
|
254
330
|
it "returns the actions' options hash" do
|
255
331
|
options = double "options"
|
256
|
-
Pakada::Dispatch::Action.new(nil, options).options.should == options
|
332
|
+
Pakada::Dispatch::Action.new(nil, nil, options).options.should == options
|
257
333
|
end
|
258
334
|
end
|
259
335
|
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pakada-dispatch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
version: 0.0.3
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.4
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Lars Gierth
|
@@ -14,7 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date: 2011-
|
13
|
+
date: 2011-02-09 00:00:00 +01:00
|
18
14
|
default_executable:
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
@@ -24,8 +20,6 @@ dependencies:
|
|
24
20
|
requirements:
|
25
21
|
- - ">="
|
26
22
|
- !ruby/object:Gem::Version
|
27
|
-
segments:
|
28
|
-
- 0
|
29
23
|
version: "0"
|
30
24
|
type: :runtime
|
31
25
|
prerelease: false
|
@@ -37,8 +31,6 @@ dependencies:
|
|
37
31
|
requirements:
|
38
32
|
- - ">="
|
39
33
|
- !ruby/object:Gem::Version
|
40
|
-
segments:
|
41
|
-
- 0
|
42
34
|
version: "0"
|
43
35
|
type: :runtime
|
44
36
|
prerelease: false
|
@@ -50,8 +42,6 @@ dependencies:
|
|
50
42
|
requirements:
|
51
43
|
- - ">="
|
52
44
|
- !ruby/object:Gem::Version
|
53
|
-
segments:
|
54
|
-
- 0
|
55
45
|
version: "0"
|
56
46
|
type: :runtime
|
57
47
|
prerelease: false
|
@@ -63,8 +53,6 @@ dependencies:
|
|
63
53
|
requirements:
|
64
54
|
- - ">="
|
65
55
|
- !ruby/object:Gem::Version
|
66
|
-
segments:
|
67
|
-
- 0
|
68
56
|
version: "0"
|
69
57
|
type: :development
|
70
58
|
prerelease: false
|
@@ -76,8 +64,6 @@ dependencies:
|
|
76
64
|
requirements:
|
77
65
|
- - ">="
|
78
66
|
- !ruby/object:Gem::Version
|
79
|
-
segments:
|
80
|
-
- 0
|
81
67
|
version: "0"
|
82
68
|
type: :development
|
83
69
|
prerelease: false
|
@@ -92,7 +78,6 @@ extensions: []
|
|
92
78
|
extra_rdoc_files: []
|
93
79
|
|
94
80
|
files:
|
95
|
-
- .project
|
96
81
|
- .rspec
|
97
82
|
- Gemfile
|
98
83
|
- LICENSE
|
@@ -124,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
109
|
requirements:
|
125
110
|
- - ">="
|
126
111
|
- !ruby/object:Gem::Version
|
127
|
-
hash:
|
112
|
+
hash: 972238593
|
128
113
|
segments:
|
129
114
|
- 0
|
130
115
|
version: "0"
|
@@ -133,14 +118,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
118
|
requirements:
|
134
119
|
- - ">="
|
135
120
|
- !ruby/object:Gem::Version
|
136
|
-
hash:
|
121
|
+
hash: 972238593
|
137
122
|
segments:
|
138
123
|
- 0
|
139
124
|
version: "0"
|
140
125
|
requirements: []
|
141
126
|
|
142
127
|
rubyforge_project:
|
143
|
-
rubygems_version: 1.
|
128
|
+
rubygems_version: 1.5.0
|
144
129
|
signing_key:
|
145
130
|
specification_version: 3
|
146
131
|
summary: Routing And Action Controllers For Pakada
|
data/.project
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<projectDescription>
|
3
|
-
<name>pakada-dispatch</name>
|
4
|
-
<comment></comment>
|
5
|
-
<projects>
|
6
|
-
</projects>
|
7
|
-
<buildSpec>
|
8
|
-
</buildSpec>
|
9
|
-
<natures>
|
10
|
-
<nature>com.aptana.ruby.core.rubynature</nature>
|
11
|
-
</natures>
|
12
|
-
</projectDescription>
|