meddler 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.2
1
+ 0.0.3
@@ -5,9 +5,12 @@ require 'meddler/builder'
5
5
 
6
6
  class Meddler
7
7
 
8
- def initialize(app, on_request, on_response, before, after, wrapped_app)
9
- wrapped_app.run(PostInterceptor.new(app, on_response, after, signal))
10
- @app = PreInterceptor.new(wrapped_app.to_app, app, on_request, before)
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
- @app.call(env)
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.to_a
52
+ [response.status, response.headers, response.body]
40
53
  else
41
- throw signal, response.to_a
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
@@ -1,27 +1,54 @@
1
1
  class Meddler
2
2
  class Builder
3
3
 
4
- def initialize(app, rules = nil, &block)
5
- @app = app
4
+ attr_reader :app, :target
5
+
6
+ def initialize(endpoint, &block)
6
7
  @target = Rack::Builder.new{}
7
8
  instance_eval(&block)
8
- @meddler = Meddler.new(@app, @on_request, @on_response, @before, @after, @target)
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
- @target.send(method, *args, &block)
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
- @on_request ||= []
17
- @on_request << block
33
+ add_to_on_request { block }
18
34
  end
19
35
 
20
36
  def on_response(&block)
21
- @on_response ||= []
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
- @meddler.call(env)
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.2
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-11-30 00:00:00 -05:00
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