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.
@@ -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
- action = self.class.actions[name]
24
- raise ArgumentError, "No such action - #{name}" unless action
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
- actions[name] = Action.new block, options
89
- elsif actions[name]
90
- proc do |env|
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
 
@@ -1,5 +1,5 @@
1
1
  class Pakada
2
2
  class Dispatch
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
@@ -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[:foo].options.should == {}
29
- subject.actions[:foo].block.should == block
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, options, &block
38
- subject.actions[:foo].options.should == options
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 "when called with the name of a non-existing action" do
61
- it "builds an X-Cascade: pass app" do
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: false
5
- segments:
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-01-31 00:00:00 +01:00
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: -572190931
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: -572190931
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.3.7
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>