meddler 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -1
- data/Rakefile +14 -0
- data/VERSION +1 -1
- data/lib/meddler.rb +22 -9
- data/lib/meddler/builder.rb +36 -9
- data/spec/meddler_spec.rb +151 -0
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -2,7 +2,30 @@
|
|
2
2
|
|
3
3
|
== Hey, someone meddled with my middleware!
|
4
4
|
|
5
|
-
This lets you have on_request and on_response wrappers around a builder, and optionally skip them in either case.
|
5
|
+
This lets you have +on_request+ and +on_response+ wrappers around a builder, and optionally skip them in either case. It also gives you before and after even hooks, to do something else awesome before you proceed.
|
6
|
+
|
7
|
+
=== Why?
|
8
|
+
|
9
|
+
There's a few reasons you might want to do this. One is that you may have an expensive sub stack that only needs to run on specifiec urls, or you may want to make something available only to part of the userbase. Here's an example:
|
10
|
+
|
11
|
+
require 'meddler'
|
12
|
+
use Meddler::Builder do
|
13
|
+
on_request {|req| req.will_be_expensive? }
|
14
|
+
use ExpensiveMiddleware
|
15
|
+
use AnotherExpensiveMiddleware
|
16
|
+
end
|
17
|
+
|
18
|
+
Or perhaps you want to give all of your developers Rack::Bug even in production.
|
19
|
+
|
20
|
+
require 'meddler'
|
21
|
+
use Meddler::Builder do
|
22
|
+
on_request do |req|
|
23
|
+
req.env['warden'].authenticate
|
24
|
+
user = req.env['warden'].user
|
25
|
+
user && user.staff_developer?
|
26
|
+
end
|
27
|
+
use Rack::Bug
|
28
|
+
end
|
6
29
|
|
7
30
|
== Usage
|
8
31
|
|
@@ -11,6 +34,7 @@ In your rackup file, do this:
|
|
11
34
|
require 'meddler'
|
12
35
|
use Meddler::Builder do
|
13
36
|
on_request {|req| req.post?}
|
37
|
+
before {|req| puts "yup, we passed, and now we're onto passing in the request"}
|
14
38
|
use MungePostMiddleware
|
15
39
|
end
|
16
40
|
|
@@ -19,6 +43,8 @@ Or, on response
|
|
19
43
|
require 'meddler'
|
20
44
|
use Meddler::Builder do
|
21
45
|
on_response {|resp| resp.status == 200}
|
46
|
+
after {|req| puts "yup, we passed, just about to pass the response back to the middleware stack in here"}
|
22
47
|
use MiddlewareThatLikesOkThings
|
23
48
|
end
|
24
49
|
|
50
|
+
You can have as many +on_request+ and +on_response+ as you'd like, and processing will stop on the first one that returns false. +before+ and +after+ hooks will get called in order and be called with an instance of <tt>Rack::Request</tt> and <tt>Rack::Response</tt> respectively.
|
data/Rakefile
CHANGED
@@ -15,3 +15,17 @@ rescue LoadError
|
|
15
15
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
16
16
|
end
|
17
17
|
|
18
|
+
require 'spec'
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
task :spec => 'spec:all'
|
21
|
+
namespace(:spec) do
|
22
|
+
Spec::Rake::SpecTask.new(:all) do |t|
|
23
|
+
t.spec_opts ||= []
|
24
|
+
t.spec_opts << "-rubygems"
|
25
|
+
t.spec_opts << "-rlib/meddler"
|
26
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
27
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/lib/meddler.rb
CHANGED
@@ -5,9 +5,12 @@ require 'meddler/builder'
|
|
5
5
|
|
6
6
|
class Meddler
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
attr_reader :original_app, :app
|
9
|
+
|
10
|
+
def initialize(original_app, on_request, on_response, before, after, wrapped_app, skip_app = original_app)
|
11
|
+
@original_app, @skip_app = original_app, skip_app
|
12
|
+
wrapped_app.run(PostInterceptor.new(original_app, on_response, after, signal))
|
13
|
+
@app = PreInterceptor.new(wrapped_app.to_app, skip_app, on_request, before)
|
11
14
|
end
|
12
15
|
|
13
16
|
def signal
|
@@ -16,9 +19,19 @@ class Meddler
|
|
16
19
|
|
17
20
|
def call(env)
|
18
21
|
response = catch(signal) do
|
19
|
-
|
22
|
+
app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
if response.length == 4 && response.last == signal
|
26
|
+
response.pop
|
27
|
+
if @skip_app != @original_app
|
28
|
+
@skip_app.call(env)
|
29
|
+
else
|
30
|
+
response
|
31
|
+
end
|
32
|
+
else
|
33
|
+
response
|
20
34
|
end
|
21
|
-
response
|
22
35
|
end
|
23
36
|
|
24
37
|
|
@@ -36,9 +49,9 @@ class Meddler
|
|
36
49
|
response = Rack::Response.new(raw_response[2], raw_response[0], raw_response[1])
|
37
50
|
if rules.nil? || rules.all?{|r| r.call(response)}
|
38
51
|
filters && filters.each{|f| f.call(response)}
|
39
|
-
response.
|
52
|
+
[response.status, response.headers, response.body]
|
40
53
|
else
|
41
|
-
throw signal, response.
|
54
|
+
throw signal, [response.status, response.headers, response.body, signal]
|
42
55
|
end
|
43
56
|
end
|
44
57
|
|
@@ -57,9 +70,9 @@ class Meddler
|
|
57
70
|
request = Rack::Request.new(env)
|
58
71
|
if rules.nil? || rules.all?{|f| f.call(request)}
|
59
72
|
filters && filters.each{|f| f.call(request)}
|
60
|
-
app.call(env)
|
73
|
+
app.call(request.env)
|
61
74
|
else
|
62
|
-
skip_app.call(env)
|
75
|
+
skip_app.call(request.env)
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|
data/lib/meddler/builder.rb
CHANGED
@@ -1,27 +1,54 @@
|
|
1
1
|
class Meddler
|
2
2
|
class Builder
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
attr_reader :app, :target
|
5
|
+
|
6
|
+
def initialize(endpoint, &block)
|
6
7
|
@target = Rack::Builder.new{}
|
7
8
|
instance_eval(&block)
|
8
|
-
@
|
9
|
+
@app = if @run_endpoint
|
10
|
+
Meddler.new(@run_endpoint, @on_request, @on_response, @before, @after, @target, endpoint)
|
11
|
+
else
|
12
|
+
Meddler.new(endpoint, @on_request, @on_response, @before, @after, @target)
|
13
|
+
end
|
9
14
|
end
|
10
15
|
|
11
16
|
def method_missing(method, *args, &block)
|
12
|
-
|
17
|
+
if method == :run
|
18
|
+
@run_endpoint = args.first
|
19
|
+
else
|
20
|
+
target.send(method, *args, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_to_on_request
|
25
|
+
(@on_request ||= []) << yield
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_to_on_response
|
29
|
+
(@on_response ||= []) << yield
|
13
30
|
end
|
14
31
|
|
15
32
|
def on_request(&block)
|
16
|
-
|
17
|
-
@on_request << block
|
33
|
+
add_to_on_request { block }
|
18
34
|
end
|
19
35
|
|
20
36
|
def on_response(&block)
|
21
|
-
|
22
|
-
@on_response << block
|
37
|
+
add_to_on_response { block }
|
23
38
|
end
|
24
39
|
|
40
|
+
def on_status(status)
|
41
|
+
add_to_on_response { proc{|response| status === response.status } }
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_path_info(path_info)
|
45
|
+
add_to_on_request { proc{|request| path_info === request.path_info } }
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_xhr?(invert = false)
|
49
|
+
add_to_on_request { proc{|request| invert ^ request.xhr? } }
|
50
|
+
end
|
51
|
+
|
25
52
|
def before(&block)
|
26
53
|
@before ||= []
|
27
54
|
@before << block
|
@@ -33,7 +60,7 @@ class Meddler
|
|
33
60
|
end
|
34
61
|
|
35
62
|
def call(env)
|
36
|
-
|
63
|
+
app.call(env)
|
37
64
|
end
|
38
65
|
|
39
66
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
class TestMiddleware
|
4
|
+
attr_reader :state
|
5
|
+
|
6
|
+
@@last_instance = nil
|
7
|
+
|
8
|
+
def self.last_instance
|
9
|
+
@@last_instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reset_last_instance!
|
13
|
+
@last_instance = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(app)
|
17
|
+
@@last_instance = self
|
18
|
+
@state = :initial
|
19
|
+
@app = app
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
@state = :pre
|
24
|
+
response = @app.call(env)
|
25
|
+
@state = :post
|
26
|
+
response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Meddler do
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
TestMiddleware.reset_last_instance!
|
34
|
+
@app = proc {|env| [200, {'Content-type' => 'text/html', 'Content-length' => '5'}, ['hello']]}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should run normally" do
|
38
|
+
@builder = Meddler::Builder.new(@app) do
|
39
|
+
use TestMiddleware
|
40
|
+
end
|
41
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
42
|
+
TestMiddleware.last_instance.state.should == :post
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should stop on_request" do
|
46
|
+
@builder = Meddler::Builder.new(@app) do
|
47
|
+
on_request{|request| request.post? }
|
48
|
+
use TestMiddleware
|
49
|
+
end
|
50
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
51
|
+
TestMiddleware.last_instance.state.should == :initial
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should stop multiple on_request" do
|
55
|
+
@builder = Meddler::Builder.new(@app) do
|
56
|
+
on_request{|request| request.get? }
|
57
|
+
on_request{|request| request.path_info == '/' }
|
58
|
+
use TestMiddleware
|
59
|
+
end
|
60
|
+
@builder.call(Rack::MockRequest.env_for('/test'))
|
61
|
+
TestMiddleware.last_instance.state.should == :initial
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should stop on_repsonse" do
|
65
|
+
@builder = Meddler::Builder.new(@app) do
|
66
|
+
on_response{|response| response.status == 404 }
|
67
|
+
use TestMiddleware
|
68
|
+
end
|
69
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
70
|
+
TestMiddleware.last_instance.state.should == :pre
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should stop on_status" do
|
74
|
+
@builder = Meddler::Builder.new(@app) do
|
75
|
+
on_status 300..500
|
76
|
+
use TestMiddleware
|
77
|
+
end
|
78
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
79
|
+
TestMiddleware.last_instance.state.should == :pre
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should stop on_path_info" do
|
83
|
+
@builder = Meddler::Builder.new(@app) do
|
84
|
+
on_path_info '/path'
|
85
|
+
use TestMiddleware
|
86
|
+
end
|
87
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
88
|
+
TestMiddleware.last_instance.state.should == :initial
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should stop on_xhr?" do
|
92
|
+
@builder = Meddler::Builder.new(@app) do
|
93
|
+
on_xhr?
|
94
|
+
use TestMiddleware
|
95
|
+
end
|
96
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
97
|
+
TestMiddleware.last_instance.state.should == :initial
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should stop multiple on_repsonse" do
|
101
|
+
@builder = Meddler::Builder.new(@app) do
|
102
|
+
on_response{|response| response.status == 200 }
|
103
|
+
on_response{|response| response.length == 10 }
|
104
|
+
use TestMiddleware
|
105
|
+
end
|
106
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
107
|
+
TestMiddleware.last_instance.state.should == :pre
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should call before" do
|
111
|
+
before_called = false
|
112
|
+
@builder = Meddler::Builder.new(@app) do
|
113
|
+
before{|response| before_called = true}
|
114
|
+
use TestMiddleware
|
115
|
+
end
|
116
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
117
|
+
before_called.should be_true
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should call after" do
|
121
|
+
after_called = false
|
122
|
+
@builder = Meddler::Builder.new(@app) do
|
123
|
+
after{|response| after_called = true}
|
124
|
+
use TestMiddleware
|
125
|
+
end
|
126
|
+
@builder.call(Rack::MockRequest.env_for('/'))
|
127
|
+
after_called.should be_true
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should be able to act as an endpoint" do
|
131
|
+
after_called = false
|
132
|
+
@builder = Meddler::Builder.new(@app) do
|
133
|
+
use TestMiddleware
|
134
|
+
run proc {|env| [200, {'Content-type' => 'text/html', 'Content-length' => '10'}, ['hellohello']]}
|
135
|
+
end
|
136
|
+
response = @builder.call(Rack::MockRequest.env_for('/'))
|
137
|
+
response.last.should == ['hellohello']
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should be able to act as an endpoint (which gets skipped)" do
|
141
|
+
after_called = false
|
142
|
+
@builder = Meddler::Builder.new(@app) do
|
143
|
+
on_status 404
|
144
|
+
use TestMiddleware
|
145
|
+
run proc {|env| [200, {'Content-type' => 'text/html', 'Content-length' => '10'}, ['hellohello']]}
|
146
|
+
end
|
147
|
+
response = @builder.call(Rack::MockRequest.env_for('/'))
|
148
|
+
response.last.should == ['hello']
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meddler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Hull
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-01 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -55,5 +55,5 @@ rubygems_version: 1.3.5
|
|
55
55
|
signing_key:
|
56
56
|
specification_version: 3
|
57
57
|
summary: Hey, someone meddled with my middleware!
|
58
|
-
test_files:
|
59
|
-
|
58
|
+
test_files:
|
59
|
+
- spec/meddler_spec.rb
|