pakada-dispatch 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/.project +12 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +4 -0
- data/Rakefile +9 -0
- data/lib/pakada-dispatch.rb +1 -0
- data/lib/pakada/dispatch.rb +43 -0
- data/lib/pakada/dispatch/controller.rb +53 -0
- data/lib/pakada/dispatch/module.rb +70 -0
- data/lib/pakada/dispatch/version.rb +5 -0
- data/pakada-dispatch.gemspec +25 -0
- data/spec/controller_spec.rb +138 -0
- data/spec/dispatch_spec.rb +80 -0
- data/spec/module_spec.rb +298 -0
- data/spec/spec_helper.rb +14 -0
- metadata +133 -0
data/.project
ADDED
@@ -0,0 +1,12 @@
|
|
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>
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Lars Gierth
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "pakada/dispatch"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "pakada"
|
2
|
+
require "rack/mount"
|
3
|
+
|
4
|
+
require "pakada/dispatch/controller"
|
5
|
+
require "pakada/dispatch/module"
|
6
|
+
require "pakada/dispatch/version"
|
7
|
+
|
8
|
+
class Pakada
|
9
|
+
class Dispatch
|
10
|
+
include Pakada::Module
|
11
|
+
|
12
|
+
attr_reader :router
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@router = Rack::Mount::RouteSet.new
|
16
|
+
end
|
17
|
+
|
18
|
+
before :boot, :apply_dispatch_patches, :before => :all, do |ctx|
|
19
|
+
Pakada.modules.each_value do |m|
|
20
|
+
m.singleton_class.send :include, Pakada::Dispatch::Module
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
before :boot, :load_controllers do |ctx|
|
25
|
+
Pakada.modules.each_value do |mod|
|
26
|
+
mod.load_controllers
|
27
|
+
end
|
28
|
+
Pakada.hooking.invoke :routing
|
29
|
+
end
|
30
|
+
|
31
|
+
before :boot, :set_dispatch_app do |ctx|
|
32
|
+
Pakada.app = proc {|env| hookable :dispatch, env }
|
33
|
+
end
|
34
|
+
|
35
|
+
after :boot, :freeze_router do |ctx|
|
36
|
+
router.rehash
|
37
|
+
end
|
38
|
+
|
39
|
+
hookable :dispatch do |ctx|
|
40
|
+
ctx.result = router.call ctx.args
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Pakada
|
2
|
+
class Dispatch
|
3
|
+
Action = Struct.new :block, :options
|
4
|
+
|
5
|
+
module Controller
|
6
|
+
def self.build(&block)
|
7
|
+
Class.new do
|
8
|
+
include Controller
|
9
|
+
instance_eval &block if block
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :request, :response, :params, :options
|
14
|
+
|
15
|
+
def initialize(env)
|
16
|
+
@request, @response = Rack::Request.new(env), Rack::Response.new
|
17
|
+
@params = request.env["rack.routing_args"] || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def call_action(name, options = {})
|
21
|
+
action = self.class.actions[name]
|
22
|
+
raise ArgumentError, "No such action - #{name}" unless action
|
23
|
+
|
24
|
+
@options = action.options.merge options
|
25
|
+
instance_eval &action.block
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def actions
|
30
|
+
@actions ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def action(name, options = {}, &block)
|
34
|
+
if block
|
35
|
+
actions[name] = Action.new block, options
|
36
|
+
elsif actions[name]
|
37
|
+
proc do |env|
|
38
|
+
controller = new env
|
39
|
+
controller.call_action name, options
|
40
|
+
controller.response.finish
|
41
|
+
end
|
42
|
+
else
|
43
|
+
proc {|env| [404, {"X-Cascade" => "pass"}, []] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.included(klass)
|
49
|
+
klass.extend ClassMethods
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class Pakada
|
2
|
+
class Dispatch
|
3
|
+
module Module
|
4
|
+
attr_reader :controllers
|
5
|
+
|
6
|
+
def load_controllers
|
7
|
+
@controllers = {}
|
8
|
+
Dir["#{path}/controllers/*.rb"].each do |file|
|
9
|
+
next unless file.match %r{.*/[a-z0-9_]+\.rb$}
|
10
|
+
name = file.split("/").last.split(".").first
|
11
|
+
@controllers[name.to_sym] = Controller.build do
|
12
|
+
instance_eval File.read(file), file, 1
|
13
|
+
end
|
14
|
+
end if path
|
15
|
+
end
|
16
|
+
|
17
|
+
def route(name = nil, opts)
|
18
|
+
route = opts.keys.find {|k| !k.kind_of? Symbol }
|
19
|
+
target = opts.delete route
|
20
|
+
splats = []
|
21
|
+
unless route.is_a? Regexp
|
22
|
+
route = route.gsub(/\((.*)\)/, '(\1)?')
|
23
|
+
.gsub(/:([a-z0-9\-_]+)/, '(?<\1>[a-z0-9\-_]+)')
|
24
|
+
.gsub(/\*([a-z0-9\-_]+)/) do |match|
|
25
|
+
splats << $1.to_sym
|
26
|
+
'(?<' + $1 + '>([a-z0-9\-_]/?)*)'
|
27
|
+
end
|
28
|
+
route = Regexp.compile "^#{route}$"
|
29
|
+
end
|
30
|
+
|
31
|
+
defaults = opts.dup
|
32
|
+
target = "::" + target unless target.include? "::"
|
33
|
+
target = target + "#" unless target.include? "#"
|
34
|
+
parts = target.split /::|#/
|
35
|
+
defaults[:module] = parts[0].to_sym unless !parts[0] || parts[0].empty?
|
36
|
+
defaults[:controller] = parts[1].to_sym unless !parts[1] || parts[1].empty?
|
37
|
+
defaults[:action] = parts[2].to_sym unless !parts[2] || parts[2].empty?
|
38
|
+
|
39
|
+
app = proc do |env|
|
40
|
+
env["pakada.dispatch.route"] = name
|
41
|
+
params = env["pakada.dispatch.params"] = env["rack.routing_args"].dup
|
42
|
+
|
43
|
+
params.each_pair do |key, value|
|
44
|
+
if splats.include? key
|
45
|
+
params[key] = value.split("/").select {|v| !v.empty? }
|
46
|
+
end
|
47
|
+
if [:module, :controller, :action].include? key
|
48
|
+
params[key] = value.to_sym
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
mod = Pakada[params[:module]]
|
53
|
+
controller = mod.controllers[params[:controller].to_sym] if mod
|
54
|
+
if controller
|
55
|
+
controller.action(params[:action].to_sym).call env
|
56
|
+
else
|
57
|
+
[404, {"X-Cascade" => "pass"}, []]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
defaults[:module] = module_name unless defaults[:module]
|
62
|
+
Pakada[:dispatch].router.add_route app, {:path_info => route}, defaults, name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def url(*args)
|
67
|
+
router.url({}, *args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pakada/dispatch/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "pakada-dispatch"
|
7
|
+
s.version = Pakada::Dispatch::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Lars Gierth"]
|
10
|
+
s.email = ["lars.gierth@gmail.com"]
|
11
|
+
s.homepage = "https://rubygems.org/gems/pakada-dispatch"
|
12
|
+
s.summary = %q{Routing And Action Controllers For Pakada}
|
13
|
+
#s.description = %q{TODO: Write a gem description}
|
14
|
+
|
15
|
+
s.add_dependency "pakada"
|
16
|
+
s.add_dependency "rack-mount"
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
s.add_development_dependency "fakefs"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n") - [".gitignore", ".rvmrc"]
|
22
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
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[:foo].options.should == {}
|
29
|
+
subject.actions[:foo].block.should == block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "when called with a block and a hash" do
|
34
|
+
it "adds an action with options" do
|
35
|
+
options = double "options"
|
36
|
+
block = proc {}
|
37
|
+
subject.action :foo, options, &block
|
38
|
+
subject.actions[:foo].options.should == options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "when called without a block" do
|
43
|
+
it "builds a Rack end-point" do
|
44
|
+
subject.action(:foo) {}
|
45
|
+
|
46
|
+
env = {"foo" => "123"}
|
47
|
+
options = {:key => :value}
|
48
|
+
response = stub "response", :finish => [200, {}, []]
|
49
|
+
controller = stub "controller", :response => response
|
50
|
+
|
51
|
+
subject.should_receive(:new).twice.with(env) { controller }
|
52
|
+
|
53
|
+
controller.should_receive(:call_action).once.with(:foo, {})
|
54
|
+
subject.action(:foo).call(env).should == response.finish
|
55
|
+
|
56
|
+
controller.should_receive(:call_action).once.with(:foo, options)
|
57
|
+
subject.action(:foo, options).call(env)
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "when called with the name of a non-existing action" do
|
61
|
+
it "builds an X-Cascade: pass app" do
|
62
|
+
subject.action(:foo).call({}).should == [
|
63
|
+
404, {"X-Cascade" => "pass"}, []]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#initialize" do
|
70
|
+
it "sets up Rack::Request and Response objects" do
|
71
|
+
env = {"foo" => "123"}
|
72
|
+
controller = subject.new env
|
73
|
+
|
74
|
+
controller.request.env.should == env
|
75
|
+
controller.response.should respond_to(:finish)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "populates #params with the routing parameters" do
|
79
|
+
params = {:foo => :bar}
|
80
|
+
controller = subject.new "rack.routing_args" => params
|
81
|
+
controller.params.should == params
|
82
|
+
|
83
|
+
controller = subject.new({})
|
84
|
+
controller.params.should == {}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#call_action" do
|
89
|
+
it "invokes the action's block in the controller's scope" do
|
90
|
+
subject.action(:foo) { do_something }
|
91
|
+
controller = subject.new({})
|
92
|
+
controller.should_receive :do_something
|
93
|
+
|
94
|
+
controller.call_action :foo
|
95
|
+
end
|
96
|
+
|
97
|
+
it "populates #options" do
|
98
|
+
subject.action(:foo, :key => :value) {}
|
99
|
+
controller = subject.new({})
|
100
|
+
|
101
|
+
controller.call_action :foo
|
102
|
+
controller.options[:key].should == :value
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "when called with the name of a non-existing action" do
|
106
|
+
it "raises an error" do
|
107
|
+
controller = subject.new({})
|
108
|
+
proc { controller.call_action :foo }.should raise_error(ArgumentError)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "when called with a hash" do
|
113
|
+
it "overrides the action's options" do
|
114
|
+
subject.action(:foo, :key => :value) {}
|
115
|
+
controller = subject.new({})
|
116
|
+
|
117
|
+
controller.call_action :foo, :key => :value2
|
118
|
+
controller.options[:key].should == :value2
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe Pakada::Dispatch::Action do
|
125
|
+
describe "#block" do
|
126
|
+
it "returns the action's code block" do
|
127
|
+
block = double "block"
|
128
|
+
Pakada::Dispatch::Action.new(block).block.should == block
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#options" do
|
133
|
+
it "returns the actions' options hash" do
|
134
|
+
options = double "options"
|
135
|
+
Pakada::Dispatch::Action.new(nil, options).options.should == options
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Pakada::Dispatch do
|
4
|
+
subject { Pakada::Dispatch.new }
|
5
|
+
|
6
|
+
it "is a Pakada module" do
|
7
|
+
subject.module_name.should == :dispatch
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".initialize" do
|
11
|
+
it "initializes the router" do
|
12
|
+
subject.router.should respond_to :add_route
|
13
|
+
subject.router.should respond_to :call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ".before(:boot, :apply_dispatch_patches)" do
|
18
|
+
before do
|
19
|
+
Pakada.load_modules
|
20
|
+
Pakada.load_hooking
|
21
|
+
end
|
22
|
+
|
23
|
+
it "freedom-patches all loaded modules" do
|
24
|
+
Pakada.hooking.invoke :boot
|
25
|
+
Pakada.modules.each_value do |m|
|
26
|
+
modules = m.singleton_class.included_modules
|
27
|
+
modules.should include(Pakada::Dispatch::Module)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".before(:boot, :load_controllers)" do
|
33
|
+
before do
|
34
|
+
Pakada.load_modules
|
35
|
+
Pakada.load_hooking
|
36
|
+
end
|
37
|
+
|
38
|
+
it "calls .load_controllers on each module" do
|
39
|
+
Pakada.modules.each_value do |mod|
|
40
|
+
mod.should_receive :load_controllers
|
41
|
+
end
|
42
|
+
Pakada.hooking.invoke :boot
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".before(:boot, :set_dispatch_app)" do
|
47
|
+
it "overwrites Pakada.app with its own app" do
|
48
|
+
Pakada.load_modules
|
49
|
+
Pakada.load_hooking
|
50
|
+
Pakada.hooking.invoke :boot
|
51
|
+
|
52
|
+
req = {"key" => "value"}
|
53
|
+
resp = [200, {}, []]
|
54
|
+
Pakada.hooking.should_receive(:invoke).with(:dispatch, req) { resp }
|
55
|
+
Pakada.app.call(req).should == resp
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ".before(:boot, :freeze_router)" do
|
60
|
+
it "freezes the router" do
|
61
|
+
Pakada.load_modules
|
62
|
+
Pakada.load_hooking
|
63
|
+
|
64
|
+
Pakada[:dispatch].router.should_receive :rehash
|
65
|
+
Pakada.hooking.invoke :boot
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".hookable(:dispatch)" do
|
70
|
+
it "passes the request to the router" do
|
71
|
+
Pakada.load_modules
|
72
|
+
Pakada.load_hooking
|
73
|
+
|
74
|
+
req = {"key" => "value"}
|
75
|
+
resp = [200, {}, []]
|
76
|
+
Pakada[:dispatch].router.should_receive(:call).with(req) { resp }
|
77
|
+
Pakada.hooking.invoke(:dispatch, req).should == resp
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/module_spec.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Pakada::Dispatch::Module do
|
4
|
+
describe "#load_controllers" do
|
5
|
+
before do
|
6
|
+
FakeFS.activate!
|
7
|
+
end
|
8
|
+
|
9
|
+
subject do
|
10
|
+
FileUtils.mkdir_p "/mymodule/controllers"
|
11
|
+
File.open "/mymodule/controllers/foo.rb", "w" do |f|
|
12
|
+
f.write "action(:foo) {}"
|
13
|
+
end
|
14
|
+
|
15
|
+
Class.new do
|
16
|
+
@module_name, @path = :foo, "/mymodule"
|
17
|
+
include Pakada::Module
|
18
|
+
include Pakada::Dispatch::Module
|
19
|
+
end.new
|
20
|
+
end
|
21
|
+
|
22
|
+
it "iterates over the module's controllers/ directory and builds a controller for each file" do
|
23
|
+
subject.load_controllers
|
24
|
+
subject.controllers[:foo].should have(1).actions
|
25
|
+
end
|
26
|
+
|
27
|
+
it "does nothing if the module doesn't have a path" do
|
28
|
+
subject.class.instance_variable_set :@path, nil
|
29
|
+
subject.load_controllers
|
30
|
+
subject.should have(0).controllers
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
FakeFS.deactivate!
|
35
|
+
FakeFS::FileSystem.clear
|
36
|
+
|
37
|
+
Pakada::Module.descendants.delete :foo
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#route" do
|
42
|
+
describe "when called without a symbol" do
|
43
|
+
it "creates an anonymous route" do
|
44
|
+
Pakada.boot
|
45
|
+
|
46
|
+
Pakada[:dispatch].router.should_receive(:add_route) do |*args|
|
47
|
+
args[0].should respond_to(:call)
|
48
|
+
args[1][:path_info].should be_a(Regexp)
|
49
|
+
args[2].should == {
|
50
|
+
:module => :dispatch,
|
51
|
+
:controller => :foo,
|
52
|
+
:action => :bar
|
53
|
+
}
|
54
|
+
args[3].should be_nil
|
55
|
+
end
|
56
|
+
Pakada[:dispatch].route "/pattern" => "foo#bar"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "takes a hash that" do
|
61
|
+
before { Pakada.boot }
|
62
|
+
|
63
|
+
subject { Pakada[:dispatch] }
|
64
|
+
|
65
|
+
it "contains a route-pattern => target-action pair" do
|
66
|
+
subject.router.should_receive(:add_route) do |*args|
|
67
|
+
args[0].should respond_to(:call)
|
68
|
+
args[1][:path_info].should == %r{^/pattern$}
|
69
|
+
args[2].should == {
|
70
|
+
:module => :foo,
|
71
|
+
:controller => :bar,
|
72
|
+
:action => :baz
|
73
|
+
}
|
74
|
+
args[3].should == :myroute
|
75
|
+
end
|
76
|
+
subject.route :myroute, "/pattern" => "foo::bar#baz"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can contain default values for parameters" do
|
80
|
+
subject.router.should_receive(:add_route) do |*args|
|
81
|
+
args[0].should respond_to(:call)
|
82
|
+
args[1][:path_info].should == %r{^/pattern$}
|
83
|
+
args[2].should == {
|
84
|
+
:module => :dispatch,
|
85
|
+
:controller => :foo,
|
86
|
+
:action => :bar,
|
87
|
+
:key => :value
|
88
|
+
}
|
89
|
+
args[3].should == :myroute
|
90
|
+
end
|
91
|
+
subject.route :myroute, "/pattern" => "#bar", :key => :value, :controller => :foo
|
92
|
+
end
|
93
|
+
|
94
|
+
it "whose contained default values won't override the target-action" do
|
95
|
+
subject.router.should_receive(:add_route) do |*args|
|
96
|
+
args[0].should respond_to(:call)
|
97
|
+
args[1][:path_info].should be_a(Regexp)
|
98
|
+
args[2].should == {
|
99
|
+
:module => :dispatch,
|
100
|
+
:controller => :foo,
|
101
|
+
:action => :bar
|
102
|
+
}
|
103
|
+
args[3].should == :myroute
|
104
|
+
end
|
105
|
+
subject.route :myroute, "/pattern" => "foo#bar", :action => :baz
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#route's route-pattern" do
|
111
|
+
before { Pakada.boot }
|
112
|
+
|
113
|
+
subject { Pakada[:dispatch] }
|
114
|
+
|
115
|
+
it "can contain :normal parameters" do
|
116
|
+
subject.router.should_receive(:add_route) do |*args|
|
117
|
+
args[1][:path_info].should == %r{^/(?<key>[a-z0-9\-_]+)/(?<key2>[a-z0-9\-_]+)$}
|
118
|
+
end
|
119
|
+
subject.route :myroute, "/:key/:key2" => ""
|
120
|
+
end
|
121
|
+
|
122
|
+
it "can contain *splat parameters" do
|
123
|
+
subject.router.should_receive(:add_route) do |*args|
|
124
|
+
args[1][:path_info].should == %r{^/(?<key>([a-z0-9\-_]/?)*)$}
|
125
|
+
end
|
126
|
+
subject.route :myroute, "/*key" => ""
|
127
|
+
end
|
128
|
+
|
129
|
+
it "can contain optional parts" do
|
130
|
+
subject.router.should_receive(:add_route) do |*args|
|
131
|
+
args[1][:path_info].should == %r{^(/(?<key>[a-z0-9\-_]+))?/(?<key2>[a-z0-9\-_]+)$}
|
132
|
+
end
|
133
|
+
subject.route :myroute, "(/:key)/:key2" => ""
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#route's target-action" do
|
138
|
+
before { Pakada.boot }
|
139
|
+
|
140
|
+
subject { Pakada[:dispatch] }
|
141
|
+
|
142
|
+
it "uses the current module's name as default :module parameter" do
|
143
|
+
subject.router.should_receive :add_route do |*args|
|
144
|
+
args[2].should == {
|
145
|
+
:module => :dispatch
|
146
|
+
}
|
147
|
+
end
|
148
|
+
subject.route "/" => ""
|
149
|
+
end
|
150
|
+
|
151
|
+
it "can consist of a controller and an action" do
|
152
|
+
subject.router.should_receive :add_route do |*args|
|
153
|
+
args[2].should == {
|
154
|
+
:module => :dispatch,
|
155
|
+
:controller => :foo,
|
156
|
+
:action => :bar
|
157
|
+
}
|
158
|
+
end
|
159
|
+
subject.route "/" => "foo#bar"
|
160
|
+
end
|
161
|
+
|
162
|
+
it "can consist of an action" do
|
163
|
+
subject.router.should_receive :add_route do |*args|
|
164
|
+
args[2].should == {
|
165
|
+
:module => :dispatch,
|
166
|
+
:action => :bar
|
167
|
+
}
|
168
|
+
end
|
169
|
+
subject.route "/" => "#bar"
|
170
|
+
end
|
171
|
+
|
172
|
+
it "can consist of a controller" do
|
173
|
+
subject.router.should_receive :add_route do |*args|
|
174
|
+
args[2].should == {
|
175
|
+
:module => :dispatch,
|
176
|
+
:controller => :foo
|
177
|
+
}
|
178
|
+
end
|
179
|
+
subject.route "/" => "foo"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "can consist of a module" do
|
183
|
+
subject.router.should_receive :add_route do |*args|
|
184
|
+
args[2].should == {
|
185
|
+
:module => :mymodule,
|
186
|
+
}
|
187
|
+
end
|
188
|
+
subject.route "/" => "mymodule::"
|
189
|
+
end
|
190
|
+
|
191
|
+
it "can consist of a module and a controller" do
|
192
|
+
subject.router.should_receive :add_route do |*args|
|
193
|
+
args[2].should == {
|
194
|
+
:module => :mymodule,
|
195
|
+
:controller => :foo
|
196
|
+
}
|
197
|
+
end
|
198
|
+
subject.route "/" => "mymodule::foo"
|
199
|
+
end
|
200
|
+
|
201
|
+
it "can consist of a module and an action" do
|
202
|
+
subject.router.should_receive :add_route do |*args|
|
203
|
+
args[2].should == {
|
204
|
+
:module => :mymodule,
|
205
|
+
:action => :bar
|
206
|
+
}
|
207
|
+
end
|
208
|
+
subject.route "/" => "mymodule::#bar"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "#route's Rack end-point" do
|
213
|
+
def stub_action(route, params = {}, route_name = nil, &block)
|
214
|
+
params = {
|
215
|
+
:module => subject.module_name,
|
216
|
+
:controller => :bar,
|
217
|
+
:action => :baz
|
218
|
+
}.merge(params)
|
219
|
+
|
220
|
+
controller, action = double("controller"), double("action")
|
221
|
+
subject.controllers[:bar] = controller
|
222
|
+
controller.should_receive(:action).with(:baz).and_return(action)
|
223
|
+
action.should_receive(:call) {|env| block.call env }
|
224
|
+
|
225
|
+
subject.router.should_receive :add_route do |app, *nothing|
|
226
|
+
app.call "rack.routing_args" => params
|
227
|
+
end
|
228
|
+
subject.route route_name, route => ""
|
229
|
+
end
|
230
|
+
|
231
|
+
before { Pakada.boot }
|
232
|
+
|
233
|
+
subject { Pakada[:dispatch] }
|
234
|
+
|
235
|
+
it "splits splat parameters" do
|
236
|
+
stub_action "/*something", :something => "/some///thing/123" do |env|
|
237
|
+
env["pakada.dispatch.params"][:something].should == ["some", "thing", "123"]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "makes sure the module, controller and action params are symbols" do
|
242
|
+
stub_action "/", {
|
243
|
+
:module => subject.module_name.to_s,
|
244
|
+
:controller => "bar",
|
245
|
+
:action => "baz"
|
246
|
+
} do |env|
|
247
|
+
[:module, :controller, :action].each do |k|
|
248
|
+
env["pakada.dispatch.params"][k].should be_kind_of(Symbol)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
it "calls the respective action" do
|
254
|
+
called = false
|
255
|
+
stub_action("/") {|env| called = true }
|
256
|
+
|
257
|
+
called.should be_true
|
258
|
+
end
|
259
|
+
|
260
|
+
it "returns an X-Cascade: pass response if the module or controller doesn't exist" do
|
261
|
+
subject.router.should_receive :add_route do |app, *nothing|
|
262
|
+
env = {"rack.routing_args" => {
|
263
|
+
:module => :dispatch,
|
264
|
+
:controller => :bar,
|
265
|
+
:action => :foofoofoo
|
266
|
+
}}
|
267
|
+
app.call(env).should == [404, {"X-Cascade" => "pass"}, []]
|
268
|
+
end
|
269
|
+
subject.route "/" => ""
|
270
|
+
end
|
271
|
+
|
272
|
+
it "puts the matched route's name into the environment if it has a name" do
|
273
|
+
stub_action "/with_name", {}, :myroute do |env|
|
274
|
+
env["pakada.dispatch.route"].should == :myroute
|
275
|
+
end
|
276
|
+
|
277
|
+
stub_action "/without_name" do |env|
|
278
|
+
env["pakada.dispatch.route"].should be_nil
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "#uri" do
|
284
|
+
before { Pakada.boot }
|
285
|
+
|
286
|
+
subject { Pakada[:dispatch] }
|
287
|
+
|
288
|
+
it "passes over to #router.url" do
|
289
|
+
subject.route :myroute, "/:key" => "foo#bar", :key => "value"
|
290
|
+
|
291
|
+
subject.router.should_receive(:url).with({}, :myroute, :key => "value2").and_return("/value2")
|
292
|
+
subject.url(:myroute, :key => "value2").should == "/value2"
|
293
|
+
|
294
|
+
subject.router.should_receive(:url).with({}).and_return("/")
|
295
|
+
subject.url.should == "/"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup :default, :development
|
3
|
+
|
4
|
+
require "pakada/dispatch"
|
5
|
+
|
6
|
+
require "fakefs/safe"
|
7
|
+
|
8
|
+
begin
|
9
|
+
require "awesome_print"
|
10
|
+
rescue LoadError; end
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.before(:each) { Pakada.reset_instance }
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pakada-dispatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Lars Gierth
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-25 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: pakada
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rack-mount
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: fakefs
|
61
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: *id004
|
72
|
+
description:
|
73
|
+
email:
|
74
|
+
- lars.gierth@gmail.com
|
75
|
+
executables: []
|
76
|
+
|
77
|
+
extensions: []
|
78
|
+
|
79
|
+
extra_rdoc_files: []
|
80
|
+
|
81
|
+
files:
|
82
|
+
- .project
|
83
|
+
- .rspec
|
84
|
+
- Gemfile
|
85
|
+
- LICENSE
|
86
|
+
- README.md
|
87
|
+
- Rakefile
|
88
|
+
- lib/pakada-dispatch.rb
|
89
|
+
- lib/pakada/dispatch.rb
|
90
|
+
- lib/pakada/dispatch/controller.rb
|
91
|
+
- lib/pakada/dispatch/module.rb
|
92
|
+
- lib/pakada/dispatch/version.rb
|
93
|
+
- pakada-dispatch.gemspec
|
94
|
+
- spec/controller_spec.rb
|
95
|
+
- spec/dispatch_spec.rb
|
96
|
+
- spec/module_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: https://rubygems.org/gems/pakada-dispatch
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: -75253869
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: -75253869
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
requirements: []
|
126
|
+
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.3.7
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: Routing And Action Controllers For Pakada
|
132
|
+
test_files: []
|
133
|
+
|