pakada-dispatch 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ - LICENSE
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
- source "http://rubygems.org"
1
+ source :rubygems
2
2
 
3
3
  gemspec
4
4
 
5
5
  gem "rake"
6
6
  gem "awesome_print"
7
+
8
+ gem "thin"
9
+ gem "shotgun"
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
1
  Routing And Action Controllers For Pakada
2
2
  ===================================================
3
3
 
4
- TODO: Write documentation
4
+ TODO
5
+ ----
6
+
7
+ - Write documentation
@@ -1,8 +1,6 @@
1
+ require "http_router"
1
2
  require "pakada"
2
- require "rack/mount"
3
- require "yajl"
4
3
 
5
- require "pakada/dispatch/util"
6
4
  require "pakada/dispatch/controller"
7
5
  require "pakada/dispatch/module"
8
6
  require "pakada/dispatch/version"
@@ -13,35 +11,18 @@ class Pakada::Dispatch
13
11
  attr_reader :router
14
12
 
15
13
  def initialize
16
- @router = Rack::Mount::RouteSet.new
14
+ @router = HttpRouter.new
17
15
  end
18
16
 
19
- def boot
20
- apply_patches
21
- load_module_controllers
22
- set_app
23
- freeze_router
24
- end
25
-
26
- def apply_patches
27
- Pakada.modules.each_value do |m|
28
- m.singleton_class.send :include, Module
29
- end
30
- end
31
-
32
- def load_module_controllers
33
- Pakada.modules.each_value {|m| m.load_controllers }
17
+ def hooks
18
+ Pakada.instance.instead_of :request, @router.method(:call)
34
19
  end
35
20
 
36
- def set_app
37
- Pakada.app = proc {|env| request env }
38
- end
39
-
40
- def freeze_router
41
- router.rehash
42
- end
43
-
44
- def request(env)
45
- router.call env
21
+ def boot
22
+ Pakada.modules.each_value {|mod|
23
+ mod.extend Pakada.safety(::Pakada::Dispatch::Module)
24
+ mod.load_controllers
25
+ mod.routes if mod.respond_to? :routes
26
+ }
46
27
  end
47
28
  end
@@ -1,169 +1,59 @@
1
+ require "rack/request"
2
+ require "rack/response"
3
+
1
4
  class Pakada
2
5
  class Dispatch
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
16
-
17
6
  module Controller
18
- include Util
19
-
20
- def self.build(&block)
21
- Class.new do
22
- include Controller
23
- class_eval &block if block
24
- end
7
+ def self.included(klass)
8
+ klass.extend Pakada.safety(self::ClassMethods)
25
9
  end
26
10
 
27
- attr_reader :request, :response, :params, :options
28
-
29
- def initialize(env)
30
- @request, @response = Rack::Request.new(env), Rack::Response.new
31
- @params = request.env["pakada.dispatch.params"] || {}
11
+ def self.create(&block)
12
+ Class.new.tap {|cls|
13
+ cls.send :include, self
14
+ cls.class_eval &block
15
+ }
32
16
  end
33
17
 
34
- def call_action(name, options = {})
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
18
+ attr_reader :options, :params, :request, :response
19
+
20
+ def initialize(env, options = {})
21
+ @request = Rack::Request.new(env)
22
+ @response = options[:response] || Rack::Response.new
41
23
 
42
- @options = action.options.merge options
43
- catch(:finish) { instance_eval &action.block }
24
+ @options, @params = options, env["router.params"]
44
25
  end
45
26
 
46
27
  def finish!
47
28
  throw :finish
48
29
  end
49
30
 
50
- def json(obj)
51
- response.headers["Content-Type"] = "application/json"
52
- response.write obj.respond_to?(:to_json) ? obj.to_json : Yajl::Encoder.encode(obj)
53
- finish!
54
- end
55
-
56
- def redirect(*args)
57
- response.status = (Fixnum === args[0]) ? args.shift : 303
58
-
59
- if Symbol === args[0]
60
- url = url(*args)
61
- elsif args[0].respond_to? :to_url
62
- url = args[0].to_url
63
- elsif args[0]
64
- url = args[0]
65
- else
66
- raise ArgumentError, "Controller#redirect needs a URL"
67
- end
68
-
69
- response.headers["Location"] = url
70
- finish!
71
- end
72
-
73
- def forbidden(format = nil)
74
- response.status = 403
75
- if format == :json
76
- response.headers["Content-Type"] = "application/json"
77
- response.write Yajl::Encoder.encode({
78
- :status => {
79
- :code => 403,
80
- :message => "Forbidden"
81
- }
82
- })
83
- else
84
- response.write "403 Forbidden"
85
- end
86
- finish!
87
- end
88
-
89
- def not_found(format = nil)
90
- response.status = 404
91
- if format == :json
92
- response.headers["Content-Type"] = "application/json"
93
- response.write Yajl::Encoder.encode({
94
- :status => {
95
- :code => 404,
96
- :message => "Not Found"
97
- }
98
- })
99
- else
100
- response.write "404 Not Found"
101
- end
102
- finish!
103
- end
104
-
105
31
  module ClassMethods
106
32
  def actions
107
- @actions ||= []
33
+ @actions ||= {}
108
34
  end
109
35
 
110
36
  def action(name, options = {}, &block)
111
37
  if block
112
- options[:methods] ||= :all
113
- unless options[:methods].respond_to?(:each) || options[:methods] == :all
114
- options[:methods] = [options[:methods]]
115
- end
116
- actions << Action.new(name, block, options)
117
- return
118
- end
119
-
120
- matches = actions.select {|a| a.name == name }
121
- return NOT_FOUND_ENDPOINT if matches.empty?
122
-
123
- proc do |env|
124
- method = (env["REQUEST_METHOD"] || "GET").downcase.to_sym
125
- allowed = matches.select do |a|
126
- a.options[:methods] == :all || a.options[:methods].include?(method)
127
- end
128
-
129
- if allowed.empty?
130
- allow = matches.inject([]) {|m, a| m.push(*a.options[:methods]); m }
131
- METHOD_NOT_ALLOWED_ENDPOINT.call(allow).call(env)
132
- else
133
- controller = new env
134
- controller.call_action allowed.first.name, options
135
- controller.response.finish
136
- end
38
+ actions[name] = proc {|env, opts = {}|
39
+ c = self.new(env, options.merge(opts))
40
+ catch(:finish) { c.instance_eval &block }
41
+ c.response.finish
42
+ }
137
43
  end
138
-
139
- # elsif actions[name]
140
- # proc do |env|
141
- # method = env["REQUEST_METHOD"]
142
- # allowed = actions[name].options[:methods]
143
- # if method && allowed != :all && !allowed.include?(method.downcase.to_sym)
144
- # [405, {"Allow" => allowed.map {|m| m.to_s.upcase }.join(", ")}, ["Method Not Allowed"]]
145
- # else
146
- # controller = new env
147
- # controller.call_action name, options
148
- # controller.response.finish
149
- # end
150
- # end
151
- # else
152
- # proc {|env| [404, {"X-Cascade" => "pass"}, []] }
153
- # end
44
+ actions[name]
154
45
  end
155
46
 
156
- METHODS.each do |m|
157
- eval <<-R, nil, __FILE__, __LINE__
158
- def #{m}(name, options = {}, &block)
159
- action(name, options.merge(:methods => :#{m}), &block)
160
- end
161
- R
47
+ def to_proc
48
+ method(:call).to_proc
49
+ end
50
+
51
+ def call(env)
52
+ env["router.params"] ||= {}
53
+ name = env["router.params"][:action] ||
54
+ env["REQUEST_METHOD"].downcase
55
+ action(name.to_sym).call env
162
56
  end
163
- end
164
-
165
- def self.included(klass)
166
- klass.extend ClassMethods
167
57
  end
168
58
  end
169
59
  end
@@ -1,67 +1,38 @@
1
+ require "pathname"
2
+
1
3
  class Pakada
2
4
  class Dispatch
3
5
  module Module
4
- include Util
6
+ def controller_path
7
+ # TODO change this when Pakada uses Pathname for module paths
8
+ #@controller_path ||= path.join("controllers")
9
+ @controller_path ||= Pathname.new(path).join("controllers")
10
+ end
5
11
 
6
- attr_reader :controllers
12
+ def controllers
13
+ @controllers ||= {}
14
+ end
15
+
16
+ def controller(name, &block)
17
+ if block
18
+ controllers[name] = Pakada.safety(Controller).create(&block)
19
+ else
20
+ controllers[name]
21
+ end
22
+ end
7
23
 
8
24
  def load_controllers
9
- @controllers = {}
10
- Dir["#{path}/controllers/*.rb"].each do |file|
11
- next unless file.match %r{.*/[a-z0-9_]+\.rb$}
12
- name = file.split("/").last.split(".").first
13
- @controllers[name.to_sym] = Controller.build do
14
- class_eval File.read(file), file, 1
15
- end
16
- end if path
25
+ controller_path.each_entry {|path|
26
+ path = controller_path.join(path)
27
+ next unless path.file?
28
+ controller(path.basename(".rb").to_s.to_sym) {
29
+ class_eval File.read(path), path.to_s, 1
30
+ }
31
+ } if controller_path.exist?
17
32
  end
18
33
 
19
- def route(name = nil, opts)
20
- route = opts.keys.find {|k| !k.kind_of? Symbol }
21
- target = opts.delete route
22
- splats = []
23
- unless route.is_a? Regexp
24
- route = route.gsub(/\((.*)\)/, '(\1)?')
25
- .gsub(/:([a-z0-9\-_]+)/, '(?<\1>[a-z0-9\-_]+)')
26
- .gsub(/\*([a-z0-9\-_]+)/) do |match|
27
- splats << $1.to_sym
28
- '(?<' + $1 + '>([a-z0-9\-_]/?)*)'
29
- end
30
- route = Regexp.compile "^#{route}$"
31
- end
32
-
33
- defaults = opts.dup
34
- target = "::" + target unless target.include? "::"
35
- target = target + "#" unless target.include? "#"
36
- parts = target.split /::|#/
37
- defaults[:module] = parts[0].to_sym unless !parts[0] || parts[0].empty?
38
- defaults[:controller] = parts[1].to_sym unless !parts[1] || parts[1].empty?
39
- defaults[:action] = parts[2].to_sym unless !parts[2] || parts[2].empty?
40
-
41
- app = proc do |env|
42
- env["pakada.dispatch.route"] = name
43
- params = env["pakada.dispatch.params"] = env["rack.routing_args"].dup
44
-
45
- params.each_pair do |key, value|
46
- if splats.include? key
47
- params[key] = value.split("/").select {|v| !v.empty? }
48
- end
49
- if [:module, :controller, :action].include? key
50
- params[key] = value.to_sym
51
- end
52
- end
53
-
54
- mod = Pakada[params[:module]]
55
- controller = mod.controllers[params[:controller].to_sym] if mod
56
- if controller
57
- controller.action(params[:action]).call(env)
58
- else
59
- [404, {"X-Cascade" => "pass"}, []]
60
- end
61
- end
62
-
63
- defaults[:module] = module_name unless defaults[:module]
64
- Pakada[:dispatch].router.add_route app, {:path_info => route}, defaults, name
34
+ def route(name, pattern, &block)
35
+ Pakada[:dispatch].router.add(pattern).name(name).to &block
65
36
  end
66
37
  end
67
38
  end
@@ -1,5 +1,5 @@
1
1
  class Pakada
2
2
  class Dispatch
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -12,14 +12,14 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Routing And Action Controllers For Pakada}
13
13
  #s.description = %q{TODO: Write a gem description}
14
14
 
15
- s.add_dependency "pakada", "~> 0.2"
16
- s.add_dependency "rack-mount"
17
- s.add_dependency "yajl-ruby"
15
+ s.add_dependency "pakada"
16
+ s.add_dependency "http_router"
18
17
 
19
18
  s.add_development_dependency "rspec"
20
- s.add_development_dependency "fakefs"
19
+ s.add_development_dependency "yard"
20
+ s.add_development_dependency "rdiscount"
21
21
 
22
- s.files = `git ls-files`.split("\n") - [".gitignore", ".rvmrc"]
22
+ s.files = `git ls-files`.split("\n") - [".gitignore", "config.ru"]
23
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
24
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
25
  s.require_paths = ["lib"]
@@ -1,366 +1,151 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Pakada::Dispatch::Controller do
4
- describe ".build" do
5
- it "builds an empty controller class" do
6
- subject.build.should respond_to(:action)
7
- subject.build.should have(0).actions
8
- end
9
-
10
- describe "when called with a block" do
11
- it "executes the block in the class' context" do
12
- subject.build { action(:foo) {} }.should have(1).actions
13
- end
14
- end
15
- end
16
- end
17
-
18
- describe "SomeController" do
19
- subject do
20
- Pakada::Dispatch::Controller.build
21
- end
22
-
23
- describe ".action" do
24
- describe "when called with a block" do
25
- it "adds an action to the controller" do
26
- block = proc {}
27
- subject.action :foo, &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
36
- end
37
- end
38
-
39
- describe "when called with a block and a hash" do
40
- it "adds an action with options" do
41
- block = proc {}
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]
53
- end
54
- end
55
-
56
- describe "when called without a block" do
57
- it "builds a Rack end-point" do
58
- subject.action(:foo) {}
59
-
60
- env = {"foo" => "123"}
61
- options = {:key => :value}
62
- response = stub "response", :finish => [200, {}, []]
63
- controller = stub "controller", :response => response
64
-
65
- subject.should_receive(:new).twice.with(env) { controller }
66
-
67
- controller.should_receive(:call_action).once.with(:foo, {})
68
- subject.action(:foo).call(env).should == response.finish
69
-
70
- controller.should_receive(:call_action).once.with(:foo, options)
71
- subject.action(:foo, options).call(env)
72
- end
73
-
74
- describe "and with the name of a non-existing action" do
75
- it "returns a 404 end-point" do
76
- subject.action(:foo).call({}).should == [
77
- 404, {"X-Cascade" => "pass"}, ["Not Found"]
78
- ]
79
- end
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
4
+ let(:block) { proc {} }
5
+ let(:ctrlr_mod) { Pakada.safety Pakada::Dispatch::Controller }
6
+ let(:ctrlr) { ctrlr_mod.create &block }
101
7
 
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
8
+ context ".included hook" do
9
+ it "extends the includer class with ClassMethods" do
10
+ mod = Pakada.safety(Pakada::Dispatch::Controller::ClassMethods)
11
+ ctrlr.singleton_class.included_modules.should include(mod)
108
12
  end
109
13
  end
110
14
 
111
- describe "#initialize" do
112
- it "sets up Rack::Request and Response objects" do
113
- env = {"foo" => "123"}
114
- controller = subject.new env
115
-
116
- controller.request.env.should == env
117
- controller.response.should respond_to(:finish)
118
- end
15
+ context ".create(name) { ... }" do
16
+ let(:klass) { stub "class" }
17
+
18
+ before { Class.stub :new => klass }
119
19
 
120
- it "populates #params with the routing parameters" do
121
- params = {:foo => :bar}
122
- controller = subject.new "pakada.dispatch.params" => params
123
- controller.params.should == params
20
+ it "includes itself into a new class and calls the block in its context" do
21
+ klass.should_receive(:include).with ctrlr_mod
22
+ klass.should_receive(:class_eval) {|&blk| block.should equal(blk) }
124
23
 
125
- controller = subject.new({})
126
- controller.params.should == {}
24
+ ctrlr
127
25
  end
128
26
  end
129
27
 
130
- describe "#call_action" do
131
- it "invokes the action's block in the controller's scope" do
132
- subject.action(:foo) { do_something }
133
- controller = subject.new({})
134
- controller.should_receive :do_something
135
-
136
- controller.call_action :foo
137
- end
28
+ context "#initialize(env, options)" do
29
+ let(:env) { {"router.params" => stub("router params")} }
30
+ let(:options) { {:response => stub("response")} }
31
+ let(:ctrlr_obj) { ctrlr.new env, options }
138
32
 
139
- it "populates #options" do
140
- subject.action(:foo, :key => :value) {}
141
- controller = subject.new({})
142
-
143
- controller.call_action :foo
144
- controller.options[:key].should == :value
33
+ it "sets the options and routing params objects" do
34
+ ctrlr_obj.options.should equal(options)
35
+ ctrlr_obj.params.should equal(env["router.params"])
145
36
  end
146
37
 
147
- describe "when called with the name of a non-existing action" do
148
- it "raises an error" do
149
- controller = subject.new({})
150
- proc { controller.call_action :foo }.should raise_error(ArgumentError)
151
- end
38
+ it "sets the request and response objects" do
39
+ ctrlr_obj.request.env.should equal(env)
40
+ ctrlr_obj.response.should equal(options[:response])
152
41
  end
153
42
 
154
- describe "when called with a hash" do
155
- it "overrides the action's options" do
156
- subject.action(:foo, :key => :value) {}
157
- controller = subject.new({})
158
-
159
- controller.call_action :foo, :key => :value2
160
- controller.options[:key].should == :value2
161
- end
43
+ it "defaults to an empty response object" do
44
+ old_response = options.delete :response
45
+ ctrlr_obj.response.status.should equal(200)
46
+ ctrlr_obj.response.should_not equal(old_response)
162
47
  end
163
48
  end
164
49
 
165
- describe "#finish!" do
166
- it "immediately finishes the action's execution" do
167
- finishing = nil
168
- subject.action :foo do
169
- finishing = :now
170
- finish!
171
- finishing = :nooot
172
- end
173
-
174
- subject.new({}).call_action :foo
175
- finishing.should == :now
50
+ context "#finish!" do
51
+ it "throws :finish" do
52
+ proc { ctrlr.new({}).finish! }.should throw_symbol(:finish)
176
53
  end
177
54
  end
178
55
 
179
- describe "#redirect" do
180
- describe "when called with a URL string" do
181
- it "redirects to the URL and finishes" do
182
- controller = subject.new({})
183
- controller.should_receive :finish!
184
- controller.redirect "/something"
185
-
186
- controller.response.headers["Location"].should == "/something"
187
- end
56
+ context ".actions" do
57
+ it "is an empty Hash" do
58
+ ctrlr.actions.should be_an(Hash)
59
+ ctrlr.actions.should be_empty
188
60
  end
61
+ end
62
+
63
+ context ".action(name, options) { ... }" do
64
+ let(:block) { proc {} }
65
+ let(:options) { {:foo => stub("foo option")} }
66
+ let(:override_options) { {:bar => stub("bar option")} }
67
+ let(:merged_options) { options.merge override_options }
68
+ let(:env) { stub "env" }
69
+ let(:ctrlr_obj) { stub "controller object", :response => response }
70
+ let(:response) { stub "response", :finish => stub("finished response") }
189
71
 
190
- describe "when called with an object that responds to #to_url" do
191
- it "redirects to the URL returned by #to_url and finishes" do
192
- controller = subject.new({})
193
- controller.should_receive :finish!
194
- controller.redirect stub("object", :to_url => "/something")
195
-
196
- controller.response.headers["Location"].should == "/something"
197
- end
198
- end
72
+ before {
73
+ ctrlr.action :foo, options, &block
74
+ ctrlr.stub :new => ctrlr_obj
75
+ }
199
76
 
200
- describe "when called with a route name and (optionally) a hash" do
201
- it "redirects to the URL generated by Util#url and finishes" do
202
- controller = subject.new({})
203
- controller.should_receive :finish!
204
- controller.should_receive(:url).with(:myroute, :key => "value") { "/something" }
205
- controller.redirect :myroute, :key => "value"
206
-
207
- controller.response.headers["Location"].should == "/something"
208
- end
77
+ it "creates an action proc" do
78
+ ctrlr.action(:foo).should respond_to(:call)
209
79
  end
210
80
 
211
- it "accepts an HTTP status code" do
212
- controller = subject.new({})
213
- controller.stub :finish!
214
- controller.redirect 302, "/"
215
-
216
- controller.response.status.should == 302
81
+ it "'s proc creates a controller object passing env and options" do
82
+ ctrlr.should_receive(:new).with(env, merged_options) { ctrlr_obj }
83
+ ctrlr.action(:foo).call env, override_options
217
84
  end
218
85
 
219
- it "uses 303 See Other as the default HTTP status code" do
220
- controller = subject.new({})
221
- controller.stub :finish!
222
- controller.redirect "/"
223
-
224
- controller.response.status.should == 303
86
+ it "'s proc instance_evals the action proc catching :finish" do
87
+ ctrlr_obj.should_receive(:instance_eval) {|&blk| block.should equal(blk) }
88
+ ctrlr.action(:foo).call env
225
89
  end
226
90
 
227
- it "raises an ArgumentError if the arguments don't include a URL" do
228
- controller = subject.new({})
229
-
230
- proc { controller.redirect }.should raise_error(ArgumentError)
231
- proc { controller.redirect 303 }.should raise_error(ArgumentError)
91
+ it "'s proc returns the finished response" do
92
+ ctrlr.action(:foo).call(env).should equal(response.finish)
232
93
  end
233
94
  end
234
95
 
235
- describe "#not_found" do
236
- it "sets a 404 status code, writes a message and finishes" do
237
- subject.action(:foo) { not_found }
238
- controller = subject.new({})
239
-
240
- controller.should_receive :finish!
241
- controller.call_action :foo
242
-
243
- controller.response.status.should == 404
244
- controller.response.body.join.should == "404 Not Found"
245
- end
96
+ context ".action(name)" do
97
+ let(:action) { stub "action" }
98
+ before { ctrlr.actions[:foo] = action }
246
99
 
247
- describe "when called with :json as argument" do
248
- it "writes the message in JSON format" do
249
- subject.action(:foo) { not_found :json }
250
- controller = subject.new({})
251
- controller.call_action :foo
252
-
253
- controller.response.headers["Content-Type"].should == "application/json"
254
- Yajl::Parser.parse(controller.response.body.join).should == {
255
- "status" => {
256
- "code" => 404,
257
- "message" => "Not Found"
258
- }
259
- }
260
- end
100
+ it "returns the specified action" do
101
+ ctrlr.action(:foo).should equal(action)
261
102
  end
262
103
  end
263
104
 
264
- describe "#forbidden" do
265
- it "sets a 403 status code, writes a message and finishes" do
266
- subject.action(:foo) { forbidden }
267
- controller = subject.new({})
268
-
269
- controller.should_receive :finish!
270
- controller.call_action :foo
271
-
272
- controller.response.status.should == 403
273
- controller.response.body.join.should == "403 Forbidden"
274
- end
105
+ context ".to_proc" do
106
+ let(:env) { stub "env" }
107
+ let(:response) { stub "response" }
275
108
 
276
- describe "when called with :json as argument" do
277
- it "writes the message in JSON format" do
278
- subject.action(:foo) { forbidden :json }
279
- controller = subject.new({})
280
- controller.call_action :foo
281
-
282
- controller.response.headers["Content-Type"].should == "application/json"
283
- Yajl::Parser.parse(controller.response.body.join).should == {
284
- "status" => {
285
- "code" => 403,
286
- "message" => "Forbidden"
287
- }
288
- }
289
- end
109
+ it "forwards to #call" do
110
+ ctrlr.should_receive(:call).with(env) { response }
111
+ ctrlr.to_proc.call(env).should equal(response)
290
112
  end
291
113
  end
292
114
 
293
- describe "#json" do
294
- it "sets the JSON Content-Type header, writes JSON output and finishes" do
295
- subject.action(:foo) { json :key => "value" }
296
- controller = subject.new({})
297
-
298
- controller.should_receive :finish!
299
- controller.call_action :foo
300
-
301
- controller.response.headers["Content-Type"].should == "application/json"
302
- Yajl::Parser.parse(controller.response.body.join).should == {"key" => "value"}
303
- end
115
+ context ".call(env)" do
116
+ let(:env) {
117
+ {
118
+ "router.params" => {
119
+ :action => :foo
120
+ },
121
+ "REQUEST_METHOD" => "bar"
122
+ }
123
+ }
304
124
 
305
- it "tries to call #to_json and use its result as output" do
306
- obj = double "output", :to_json => '{"key":"value"}'
307
- obj.should_receive :to_json
308
-
309
- subject.action(:foo) { json obj }
310
- controller = subject.new({})
311
- controller.call_action :foo
125
+ before {
126
+ ctrlr.action(:foo) {}
127
+ ctrlr.action(:bar) {}
128
+ }
129
+
130
+ it "sets default routing params" do
131
+ ctrlr.action(:bar).should_receive(:call) {|e|
132
+ e["router.params"].should == {}
133
+ }
312
134
 
313
- Yajl::Parser.parse(controller.response.body.join).should == {"key" => "value"}
135
+ env.delete "router.params"
136
+ ctrlr.call env
314
137
  end
315
- end
316
- end
317
-
318
- describe Pakada::Dispatch::NOT_FOUND_ENDPOINT do
319
- subject { Pakada::Dispatch::NOT_FOUND_ENDPOINT.call({}) }
320
-
321
- it "returns a 404 response" do
322
- subject[0].should == 404
323
- subject[2].join.should == "Not Found"
324
- end
325
-
326
- it "is stackable" do
327
- subject[1]["X-Cascade"].should == "pass"
328
- end
329
- end
330
-
331
- describe Pakada::Dispatch::METHOD_NOT_ALLOWED_ENDPOINT do
332
- subject do
333
- Pakada::Dispatch::METHOD_NOT_ALLOWED_ENDPOINT.call([:get, :post]).call({})
334
- end
335
-
336
- it "returns a 405 response" do
337
- subject[0].should == 405
338
- subject[2].join.should == "Method Not Allowed"
339
- end
340
-
341
- it "returns an appropriate Allow header" do
342
- subject[1]["Allow"].should == "GET, POST"
343
- end
344
- end
345
-
346
- describe Pakada::Dispatch::Action do
347
- describe "#name" do
348
- it "returns the action's name" do
349
- Pakada::Dispatch::Action.new(:asdf).name.should == :asdf
350
- end
351
- end
352
-
353
- describe "#block" do
354
- it "returns the action's code block" do
355
- block = double "block"
356
- Pakada::Dispatch::Action.new(nil, block).block.should == block
138
+
139
+ it "calls the action from routing params by default" do
140
+ ctrlr.action(:foo).should_receive :call
141
+ ctrlr.call env
357
142
  end
358
- end
359
-
360
- describe "#options" do
361
- it "returns the actions' options hash" do
362
- options = double "options"
363
- Pakada::Dispatch::Action.new(nil, nil, options).options.should == options
143
+
144
+ it "calls the REQUEST_METHOD action as fallback" do
145
+ ctrlr.action(:bar).should_receive :call
146
+
147
+ env.delete "router.params"
148
+ ctrlr.call env
364
149
  end
365
150
  end
366
151
  end