sinatra-advanced-routes 0.4.0.a
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/README.md +49 -0
- data/lib/sinatra/advanced_routes.rb +164 -0
- data/spec/sinatra/advanced_routes_spec.rb +92 -0
- data/spec/spec_helper.rb +4 -0
- metadata +104 -0
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
Sinatra::AdvancedRoutes
|
2
|
+
=======================
|
3
|
+
|
4
|
+
Makes routes first class objects in [Sinatra](http://sinatrarb.com).
|
5
|
+
|
6
|
+
BigBand
|
7
|
+
-------
|
8
|
+
|
9
|
+
Sinatra::AdvancedRoutes is part of the [BigBand](http://github.com/rkh/big_band) stack.
|
10
|
+
Check it out if you are looking for other fancy Sinatra extensions.
|
11
|
+
|
12
|
+
Example
|
13
|
+
-------
|
14
|
+
|
15
|
+
require "sinatra"
|
16
|
+
require "sinatra/advanced_routes"
|
17
|
+
|
18
|
+
admin_route = get "/admin" do
|
19
|
+
administrate_stuff
|
20
|
+
end
|
21
|
+
|
22
|
+
before do
|
23
|
+
# Let's deactivate the route if we have no password file.
|
24
|
+
if File.exists? "admin_password" then admin_route.activate
|
25
|
+
else admin_route.deactivate
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
first_route = get "/:name" do
|
30
|
+
# stuff
|
31
|
+
end
|
32
|
+
|
33
|
+
other_route = get "/foo_:name" do
|
34
|
+
# other stuff
|
35
|
+
end
|
36
|
+
|
37
|
+
# Unfortunatly first_route will catch all the requests other_route would
|
38
|
+
# have gotten, since it has been defined first. But wait, we can fix this!
|
39
|
+
other_route.promote
|
40
|
+
|
41
|
+
Usage with Sinatra::Base
|
42
|
+
------------------------
|
43
|
+
|
44
|
+
require "sinatra/base"
|
45
|
+
require "sinatra/advanced_routes"
|
46
|
+
|
47
|
+
class Foo < Sinatra::Base
|
48
|
+
register Sinatra::AdvancedRoutes
|
49
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
require "sinatra/sugar"
|
3
|
+
require "monkey"
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
Base.ignore_caller
|
7
|
+
|
8
|
+
module AdvancedRoutes
|
9
|
+
|
10
|
+
module ArrayMethods
|
11
|
+
::Array.send :include, self
|
12
|
+
|
13
|
+
def to_route(verb, args = {})
|
14
|
+
dup.to_route! verb, args
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_route!(verb, args = {})
|
18
|
+
extend Sinatra::AdvancedRoutes::Route
|
19
|
+
self.verb = verb
|
20
|
+
args.each do |key, value|
|
21
|
+
send "#{key}=", value
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def signature
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module Route
|
33
|
+
|
34
|
+
def self.new(verb, args = {})
|
35
|
+
[].to_route! verb, args
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :app, :verb, :file, :line, :path, :docstring
|
39
|
+
|
40
|
+
def pattern; self[0]; end
|
41
|
+
def keys; self[1]; end
|
42
|
+
def conditions; self[2]; end
|
43
|
+
def block; self[3]; end
|
44
|
+
alias to_proc block
|
45
|
+
|
46
|
+
def pattern=(value); self[0] = value; end
|
47
|
+
def keys=(value); self[1] = value; end
|
48
|
+
def conditions=(value); self[2] = value; end
|
49
|
+
def block=(value); self[3] = value; end
|
50
|
+
|
51
|
+
def signature
|
52
|
+
[pattern, keys, conditions, block]
|
53
|
+
end
|
54
|
+
|
55
|
+
def active?
|
56
|
+
app.routes[verb].include? self
|
57
|
+
end
|
58
|
+
|
59
|
+
def activate(at_top = false)
|
60
|
+
also_change.each { |r| r.activate }
|
61
|
+
return if active?
|
62
|
+
meth = at_top ? :unshift : :push
|
63
|
+
(app.routes[verb] ||= []).send(meth, self)
|
64
|
+
invoke_hook :route_added, verb, path, block
|
65
|
+
invoke_hook :advanced_route_activated, self
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def deactivate
|
70
|
+
also_change.each { |r| r.deactivate }
|
71
|
+
return unless active?
|
72
|
+
(app.routes[verb] ||= []).delete(signature)
|
73
|
+
invoke_hook :route_removed, verb, path, block
|
74
|
+
invoke_hook :advanced_route_deactivated, self
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def promote(upwards = true)
|
79
|
+
also_change.each { |r| r.promote(upwards) }
|
80
|
+
deactivate
|
81
|
+
activate(upwards)
|
82
|
+
end
|
83
|
+
|
84
|
+
def file?
|
85
|
+
!!@file
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
"#<Sinatra::AdvancedRoutes::Route #{ivar_inspect.join ", "}>"
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_route(verb, args = {})
|
93
|
+
args = args.dup
|
94
|
+
[:app, :verb, :file, :line, :path].each { |key| args[key] ||= send(key) }
|
95
|
+
super(verb, args)
|
96
|
+
end
|
97
|
+
|
98
|
+
def also_change(*other_routes)
|
99
|
+
(@also_change ||= []).push(*other_routes)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def ivar_inspect
|
105
|
+
[:signature, :verb, :app, :file, :line].map do |var|
|
106
|
+
value = send(var)
|
107
|
+
"@#{var}=#{value.inspect}" unless value.nil?
|
108
|
+
end.compact
|
109
|
+
end
|
110
|
+
|
111
|
+
def invoke_hook(*args)
|
112
|
+
app.send(:invoke_hook, *args)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
module ClassMethods
|
118
|
+
|
119
|
+
def get(path, opts={}, &block)
|
120
|
+
first_route, *other_routes = capture_routes { super }
|
121
|
+
first_route.also_change(*other_routes)
|
122
|
+
first_route
|
123
|
+
end
|
124
|
+
|
125
|
+
def route(verb, path, options={}, &block)
|
126
|
+
file, line = block.source_location if block.respond_to? :source_location
|
127
|
+
file ||= caller_files.first
|
128
|
+
route = super(verb, path, options, &block)
|
129
|
+
route.to_route! verb, :app => self, :file => file.expand_path, :line => line, :path => path
|
130
|
+
invoke_hook :advanced_route_added, route
|
131
|
+
@capture_routes << route if @capture_routes
|
132
|
+
route
|
133
|
+
end
|
134
|
+
|
135
|
+
def each_route(&block)
|
136
|
+
return enum_for(:each_route) if respond_to? :enum_for and not block
|
137
|
+
routes.inject([]) { |result, (verb, list)| result.push *list.each(&block) }
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def capture_routes
|
143
|
+
capture_routes_was, @capture_routes = @capture_routes, []
|
144
|
+
yield
|
145
|
+
captured, @capture_routes = @capture_routes, capture_routes_was
|
146
|
+
captured
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.registered(klass)
|
152
|
+
klass.extend ClassMethods
|
153
|
+
klass.routes.each do |verb, routes|
|
154
|
+
routes.each do |route|
|
155
|
+
route.to_route! verb, :app => klass
|
156
|
+
klass.send :invoke_hook, :advanced_root_added, route
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
register AdvancedRoutes
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Sinatra::AdvancedRoutes do
|
4
|
+
before { app :AdvancedRoutes }
|
5
|
+
[:get, :head, :post, :put, :delete].each do |verb|
|
6
|
+
describe "HTTP #{verb.to_s.upcase}" do
|
7
|
+
|
8
|
+
describe "activation" do
|
9
|
+
|
10
|
+
it "is able to deactivate routes from the outside" do
|
11
|
+
route = define_route(verb, "/foo") { "bar" }
|
12
|
+
route.should be_active
|
13
|
+
browse_route(verb, "/foo").should be_ok
|
14
|
+
route.deactivate
|
15
|
+
route.should_not be_active
|
16
|
+
browse_route(verb, "/foo").should_not be_ok
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is able to deacitvate routes from a before filter" do
|
20
|
+
route = define_route(verb, "/foo") { "bar" }
|
21
|
+
app.before { route.deactivate }
|
22
|
+
route.should be_active
|
23
|
+
browse_route(verb, "/foo").should_not be_ok
|
24
|
+
route.should_not be_active
|
25
|
+
end
|
26
|
+
|
27
|
+
it "is able to reactivate deactivated routes" do
|
28
|
+
route = define_route(verb, "/foo") { "bar" }
|
29
|
+
route.deactivate
|
30
|
+
route.activate
|
31
|
+
route.should be_active
|
32
|
+
browse_route(verb, "/foo").should be_ok
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "inspection" do
|
38
|
+
before { @route = define_route(verb, "/foo") { } }
|
39
|
+
it("exposes app") { @route.app.should == app }
|
40
|
+
it("exposes path") { @route.path.should == "/foo" }
|
41
|
+
it("exposes file") { @route.file.should == __FILE__.expand_path }
|
42
|
+
it("exposes verb") { @route.verb.should == verb.to_s.upcase }
|
43
|
+
it("exposes pattern") { @route.pattern.should == @route[0] }
|
44
|
+
it("exposes keys") { @route.keys.should == @route[1] }
|
45
|
+
it("exposes conditions") { @route.conditions.should == @route[2] }
|
46
|
+
it("exposes block") { @route.block.should == @route[3] }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "promotion" do
|
50
|
+
it "preffers promoted routes over earlier defined routes" do
|
51
|
+
next if verb == :head # cannot check body for head
|
52
|
+
bar = define_route(verb, "/foo") { "bar" }
|
53
|
+
baz = define_route(verb, "/foo") { "baz" }
|
54
|
+
browse_route(verb, "/foo").body.should == "bar"
|
55
|
+
baz.promote
|
56
|
+
browse_route(verb, "/foo").body.should == "baz"
|
57
|
+
bar.promote
|
58
|
+
browse_route(verb, "/foo").body.should == "bar"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "hooks" do
|
63
|
+
before do
|
64
|
+
@extension = Module.new
|
65
|
+
app.register @extension
|
66
|
+
end
|
67
|
+
|
68
|
+
it "triggers advanced_root_added hook" do
|
69
|
+
meth = @extension.should_receive(:advanced_route_added)
|
70
|
+
verb == :get ? meth.twice : meth.once
|
71
|
+
define_route(verb, "/foo") { }
|
72
|
+
end
|
73
|
+
|
74
|
+
it "triggers advanced_root_activated hook" do
|
75
|
+
route = define_route(verb, "/foo") { }
|
76
|
+
route.deactivate
|
77
|
+
meth = @extension.should_receive(:advanced_route_activated)
|
78
|
+
verb == :get ? meth.twice : meth.once
|
79
|
+
route.activate
|
80
|
+
end
|
81
|
+
|
82
|
+
it "triggers advanced_root_deactivated hook" do
|
83
|
+
route = define_route(verb, "/foo") { }
|
84
|
+
meth = @extension.should_receive(:advanced_route_deactivated)
|
85
|
+
verb == :get ? meth.twice : meth.once
|
86
|
+
route.deactivate
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-advanced-routes
|
3
|
+
version: &id001 !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0.a
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Konstantin Haase
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-17 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: monkey-lib
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- *id001
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: sinatra-sugar
|
26
|
+
type: :runtime
|
27
|
+
version_requirement:
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "="
|
31
|
+
- *id001
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sinatra-test-helper
|
35
|
+
type: :development
|
36
|
+
version_requirement:
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "="
|
40
|
+
- *id001
|
41
|
+
version:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: sinatra
|
44
|
+
type: :runtime
|
45
|
+
version_requirement:
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 0.9.4
|
51
|
+
version:
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
type: :development
|
55
|
+
version_requirement:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.3.0
|
61
|
+
version:
|
62
|
+
description: Make Sinatra routes first class objects (part of BigBand).
|
63
|
+
email: konstantin.mailinglists@googlemail.com
|
64
|
+
executables: []
|
65
|
+
|
66
|
+
extensions: []
|
67
|
+
|
68
|
+
extra_rdoc_files: []
|
69
|
+
|
70
|
+
files:
|
71
|
+
- lib/sinatra/advanced_routes.rb
|
72
|
+
- spec/sinatra/advanced_routes_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- README.md
|
75
|
+
has_rdoc: yard
|
76
|
+
homepage: http://github.com/rkh/sinatra-advanced-routes
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 1.3.1
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.5
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Make Sinatra routes first class objects (part of BigBand).
|
103
|
+
test_files: []
|
104
|
+
|