circuit 0.2.0
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/Gemfile +34 -0
- data/LICENSE +20 -0
- data/README.md +161 -0
- data/Rakefile +27 -0
- data/config.ru +7 -0
- data/description.md +5 -0
- data/docs/COMPATIBILITY.md +14 -0
- data/docs/ROADMAP.md +29 -0
- data/lib/circuit.rb +125 -0
- data/lib/circuit/behavior.rb +99 -0
- data/lib/circuit/compatibility.rb +73 -0
- data/lib/circuit/middleware.rb +6 -0
- data/lib/circuit/middleware/rewriter.rb +43 -0
- data/lib/circuit/rack.rb +14 -0
- data/lib/circuit/rack/behavioral.rb +45 -0
- data/lib/circuit/rack/builder.rb +50 -0
- data/lib/circuit/rack/multi_site.rb +22 -0
- data/lib/circuit/rack/request.rb +81 -0
- data/lib/circuit/railtie.rb +24 -0
- data/lib/circuit/storage.rb +74 -0
- data/lib/circuit/storage/memory_model.rb +70 -0
- data/lib/circuit/storage/nodes.rb +56 -0
- data/lib/circuit/storage/nodes/memory_store.rb +63 -0
- data/lib/circuit/storage/nodes/model.rb +67 -0
- data/lib/circuit/storage/nodes/mongoid_store.rb +56 -0
- data/lib/circuit/storage/sites.rb +38 -0
- data/lib/circuit/storage/sites/memory_store.rb +53 -0
- data/lib/circuit/storage/sites/model.rb +29 -0
- data/lib/circuit/storage/sites/mongoid_store.rb +43 -0
- data/lib/circuit/validators.rb +74 -0
- data/lib/circuit/version.rb +3 -0
- data/spec/internal/app/behaviors/change_path.rb +7 -0
- data/spec/internal/app/controllers/application_controller.rb +5 -0
- data/spec/internal/app/helpers/application_helper.rb +2 -0
- data/spec/internal/config/initializers/circuit.rb +7 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/db/schema.rb +1 -0
- data/spec/lib/circuit/behavior_spec.rb +113 -0
- data/spec/lib/circuit/middleware/rewriter_spec.rb +79 -0
- data/spec/lib/circuit/rack/behavioral_spec.rb +60 -0
- data/spec/lib/circuit/rack/builder_spec.rb +125 -0
- data/spec/lib/circuit/rack/multi_site_spec.rb +34 -0
- data/spec/lib/circuit/rack/request_spec.rb +80 -0
- data/spec/lib/circuit/railtie_spec.rb +34 -0
- data/spec/lib/circuit/storage/nodes_spec.rb +62 -0
- data/spec/lib/circuit/storage/sites_spec.rb +60 -0
- data/spec/lib/circuit/storage_spec.rb +20 -0
- data/spec/lib/circuit/validators_spec.rb +69 -0
- data/spec/lib/circuit_spec.rb +139 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/support/blueprints.rb +24 -0
- data/spec/support/matchers/be_current_time_matcher.rb +14 -0
- data/spec/support/matchers/extended_have_key.rb +31 -0
- data/spec/support/matchers/have_accessor_matcher.rb +13 -0
- data/spec/support/matchers/have_attribute_matcher.rb +22 -0
- data/spec/support/matchers/have_block_matcher.rb +14 -0
- data/spec/support/matchers/have_errors_on_matcher.rb +30 -0
- data/spec/support/matchers/have_module_matcher.rb +13 -0
- data/spec/support/matchers/have_reader_matcher.rb +18 -0
- data/spec/support/matchers/have_writer_matcher.rb +18 -0
- data/spec/support/matchers/set_instance_variable.rb +32 -0
- data/spec/support/spec_helpers/base_behaviors.rb +20 -0
- data/spec/support/spec_helpers/base_models.rb +64 -0
- data/spec/support/spec_helpers/logger_helpers.rb +58 -0
- data/spec/support/spec_helpers/multi_site_helper.rb +48 -0
- data/spec/support/spec_helpers/rack_helpers.rb +8 -0
- data/spec/support/spec_helpers/shared_examples/node_store.rb +87 -0
- data/spec/support/spec_helpers/shared_examples/site_store.rb +76 -0
- data/spec/support/spec_helpers/simple_machinable.rb +29 -0
- data/spec/support/spec_helpers/stores_cleaner.rb +46 -0
- data/vendor/active_support-3.2/core_ext/string/inflections.rb +53 -0
- data/vendor/active_support-3.2/inflector/methods.rb +65 -0
- data/vendor/rack-1.4/builder.rb +167 -0
- data/vendor/rack-1.4/urlmap.rb +96 -0
- metadata +238 -0
@@ -0,0 +1 @@
|
|
1
|
+
# make it empty?
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Circuit::Behavior do
|
4
|
+
class Middleware1
|
5
|
+
def initialize(app); end
|
6
|
+
end
|
7
|
+
class Middleware2
|
8
|
+
def initialize(app); end
|
9
|
+
end
|
10
|
+
class Middleware3
|
11
|
+
def initialize(app); end
|
12
|
+
end
|
13
|
+
|
14
|
+
class BaseClass
|
15
|
+
include Circuit::Behavior
|
16
|
+
use Middleware1
|
17
|
+
use Middleware2
|
18
|
+
end
|
19
|
+
|
20
|
+
class InheritedClass < BaseClass
|
21
|
+
use Middleware3
|
22
|
+
end
|
23
|
+
|
24
|
+
module NoBuilderBehavior
|
25
|
+
include Circuit::Behavior
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:base_class) { BaseClass }
|
29
|
+
let(:inherited_class) { InheritedClass }
|
30
|
+
subject { BaseClass }
|
31
|
+
|
32
|
+
it { lambda{Circuit::Behavior}.should_not raise_error }
|
33
|
+
it { lambda{ subject }.should_not raise_error }
|
34
|
+
it { subject.builder.should be_true }
|
35
|
+
|
36
|
+
context 'is listed in ancestors' do
|
37
|
+
subject { base_class.ancestors }
|
38
|
+
it { should include Circuit::Behavior }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'is listed in inherited ancestors' do
|
42
|
+
subject { inherited_class.ancestors }
|
43
|
+
it { should include Circuit::Behavior }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when mixed class is configured' do
|
47
|
+
subject { base_class.builder.instance_variable_get(:@use) }
|
48
|
+
it { subject.each { |u| u.should be_instance_of(Proc) } }
|
49
|
+
it { should have(2).procs}
|
50
|
+
it { subject[0].call(Object.new).class.should == Middleware1 }
|
51
|
+
it { subject[1].call(Object.new).class.should == Middleware2 }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'inherited stack is configured properly' do
|
55
|
+
subject { inherited_class.builder.instance_variable_get(:@use) }
|
56
|
+
it { subject.each { |u| u.should be_instance_of(Proc) } }
|
57
|
+
it { should have(3).procs}
|
58
|
+
it { subject[0].call(Object.new).class.should == Middleware1 }
|
59
|
+
it { subject[1].call(Object.new).class.should == Middleware2 }
|
60
|
+
it { subject[2].call(Object.new).class.should == Middleware3 }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "without a builder" do
|
64
|
+
subject { NoBuilderBehavior }
|
65
|
+
it { subject.builder?.should be_false }
|
66
|
+
end
|
67
|
+
|
68
|
+
context "without a cru path" do
|
69
|
+
before do
|
70
|
+
@prev_cru_path = Circuit.cru_path
|
71
|
+
Circuit.cru_path = nil
|
72
|
+
NoBuilderBehavior.instance_variable_set(:@cru_path, nil)
|
73
|
+
end
|
74
|
+
subject { NoBuilderBehavior }
|
75
|
+
it { subject.builder?.should be_false }
|
76
|
+
it do
|
77
|
+
expect { subject.cru_path }.
|
78
|
+
to raise_error(Circuit::Behavior::RackupPathError,
|
79
|
+
"Rackup path cannot be determined for NoBuilderBehavior")
|
80
|
+
end
|
81
|
+
after { Circuit.cru_path = @prev_cru_path }
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "get constants" do
|
85
|
+
context "already loaded" do
|
86
|
+
before { ChangePath }
|
87
|
+
subject { Circuit::Behavior.get("ChangePath") }
|
88
|
+
it { should == lambda{ChangePath}.call }
|
89
|
+
it { should have_module(Circuit::Behavior) }
|
90
|
+
it { subject.to_s.should == "ChangePath" }
|
91
|
+
it { subject.builder?.should be_true }
|
92
|
+
it "should not dynamically set a constant" do
|
93
|
+
Object.expects(:const_set).never
|
94
|
+
subject
|
95
|
+
end
|
96
|
+
after { Object.unstub(:const_set) }
|
97
|
+
end
|
98
|
+
|
99
|
+
context "load from .cru" do
|
100
|
+
before { Object.send(:remove_const, :RenderOk) if Object.const_defined?(:RenderOk) }
|
101
|
+
subject { Circuit::Behavior.get("RenderOk") }
|
102
|
+
it { should == lambda{RenderOk}.call }
|
103
|
+
it { should have_module(Circuit::Behavior) }
|
104
|
+
it { subject.to_s.should == "RenderOk" }
|
105
|
+
it { subject.builder?.should be_true }
|
106
|
+
it "should dynamically set a constant" do
|
107
|
+
Object.expects(:const_set).once
|
108
|
+
subject
|
109
|
+
end
|
110
|
+
after { Object.unstub(:const_set) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Circuit::Middleware::Rewriter do
|
4
|
+
include SpecHelpers::LoggerHelpers
|
5
|
+
let(:request) { mock_request() } # http://www.example.com/foo/bar/baz
|
6
|
+
|
7
|
+
def self.run_app(&block)
|
8
|
+
before do
|
9
|
+
@response = ::Rack::Builder.new do
|
10
|
+
use Circuit::Middleware::Rewriter, &block
|
11
|
+
run lambda { |env| [ 200, {}, %w[SCRIPT_NAME PATH_INFO].collect {|k| env[k]} ] }
|
12
|
+
end.to_app.call(mock_request.env)
|
13
|
+
end
|
14
|
+
subject { @response }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "should be able to not rewrite" do
|
18
|
+
run_app { |*args| args }
|
19
|
+
subject { @response }
|
20
|
+
it { should == [ 200, {}, ["", "/foo/bar/baz"] ] }
|
21
|
+
|
22
|
+
context "logger" do
|
23
|
+
subject { logger_output }
|
24
|
+
it { should be_blank }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "rewrite script_name and path_info" do
|
29
|
+
run_app do |script_name, path_info|
|
30
|
+
["/site/5#{script_name}", path_info+"/1"]
|
31
|
+
end
|
32
|
+
it { should == [ 200, {}, %w[/site/5 /foo/bar/baz/1] ] }
|
33
|
+
|
34
|
+
context "logger" do
|
35
|
+
subject { logger_output }
|
36
|
+
it { should == "[CIRCUIT] Rewriting: '/foo/bar/baz'->'/site/5/foo/bar/baz/1'\n" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "rewrite script_name and not path_info" do
|
41
|
+
run_app do |script_name, path_info|
|
42
|
+
["/site/5#{script_name}", path_info]
|
43
|
+
end
|
44
|
+
it { should == [ 200, {}, %w[/site/5 /foo/bar/baz] ] }
|
45
|
+
|
46
|
+
context "logger" do
|
47
|
+
subject { logger_output }
|
48
|
+
it { should == "[CIRCUIT] Rewriting: '/foo/bar/baz'->'/site/5/foo/bar/baz'\n" }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "rewrite path_info and not script_name" do
|
53
|
+
run_app do |script_name, path_info|
|
54
|
+
[script_name, path_info+"/1"]
|
55
|
+
end
|
56
|
+
it { should == [ 200, {}, ["", "/foo/bar/baz/1"] ] }
|
57
|
+
|
58
|
+
context "logger" do
|
59
|
+
subject { logger_output }
|
60
|
+
it { should == "[CIRCUIT] Rewriting: '/foo/bar/baz'->'/foo/bar/baz/1'\n" }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "should catch and log rewriter errors" do
|
65
|
+
run_app do |*args|
|
66
|
+
raise Circuit::Middleware::RewriteError, "an error occurred"
|
67
|
+
end
|
68
|
+
it { should == [ 200, {}, ["", "/foo/bar/baz"] ] }
|
69
|
+
|
70
|
+
context "logger" do
|
71
|
+
subject { logger_output.split(/\n/) }
|
72
|
+
it { should have(2).lines }
|
73
|
+
it { subject.first.should == "[CIRCUIT] Rewrite Error: an error occurred" }
|
74
|
+
it "second line should have the backtrace" do
|
75
|
+
subject.last.should match(/\A\s+(.+)#{Regexp.quote("rewriter_spec.rb")}\:(\d+)(\:in|$)/)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Circuit::Rack::Behavioral do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
include SpecHelpers::MultiSiteHelper
|
6
|
+
|
7
|
+
class RenderMyMiddlewareBehavior
|
8
|
+
include ::Circuit::Behavior
|
9
|
+
|
10
|
+
use(Class.new do
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
[200, {"Content-Type" => "test/html"}, ["RenderMyMiddlewareBehavior"]]
|
17
|
+
end
|
18
|
+
end)
|
19
|
+
end
|
20
|
+
|
21
|
+
def app
|
22
|
+
stub_app_with_circuit_site setup_site!(root.site, RenderMyMiddlewareBehavior)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'GET /' do
|
26
|
+
get "/"
|
27
|
+
|
28
|
+
context "status" do
|
29
|
+
subject { last_response.body }
|
30
|
+
it { should include("RenderMyMiddlewareBehavior") }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'GET / for site with no root' do
|
35
|
+
def app
|
36
|
+
stub_app_with_circuit_site dup_site_1
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise a path not found error" do
|
40
|
+
expect { get '/' }.to raise_error(Circuit::Storage::Nodes::NotFoundError, "Path not found")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "GET / without site" do
|
45
|
+
def no_site_middleware
|
46
|
+
(Class.new do
|
47
|
+
def initialize(app, site=nil) @app = app; end
|
48
|
+
def call(env) @app.call(env); end
|
49
|
+
end)
|
50
|
+
end
|
51
|
+
|
52
|
+
def app
|
53
|
+
stub_app_with_circuit_site nil, no_site_middleware
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should raise a missing site error" do
|
57
|
+
expect { get "/" }.to raise_error(Circuit::Rack::MissingSiteError, "Rack variable rack.circuit.site is missing")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Circuit::Rack::Builder do
|
4
|
+
context "without an app" do
|
5
|
+
subject { Circuit::Rack::Builder.new }
|
6
|
+
it { should respond_to(:app?) }
|
7
|
+
it { subject.app?.should be_false }
|
8
|
+
end
|
9
|
+
|
10
|
+
context "with an app" do
|
11
|
+
subject { Circuit::Rack::Builder.new(lambda { |env| [200, {}, %w[OK]]}) }
|
12
|
+
it { should respond_to(:app?) }
|
13
|
+
it { subject.app?.should be_true }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "defers to ::Rack::Builder for .ru files" do
|
17
|
+
subject { Circuit::Rack::Builder.parse_file(Circuit.cru_path.join("render_not_found.ru")).first }
|
18
|
+
it { should be_instance_of(Circuit::Rack::Builder) }
|
19
|
+
it do
|
20
|
+
::Rack::Builder.expects(:parse_file).once
|
21
|
+
subject
|
22
|
+
end
|
23
|
+
after { ::Rack::Builder.unstub(:parse_file) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "parses .cru files" do
|
27
|
+
subject { Circuit::Rack::Builder.parse_file(Circuit.cru_path.join("render_ok.cru")).first }
|
28
|
+
it { should be_instance_of(Circuit::Rack::Builder) }
|
29
|
+
it do
|
30
|
+
::Rack::Builder.expects(:parse_file).never
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
after { ::Rack::Builder.unstub(:parse_file) }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "dup without map" do
|
37
|
+
let(:use) { [mock()] }
|
38
|
+
let(:run) { mock() }
|
39
|
+
|
40
|
+
subject do
|
41
|
+
::Rack::Builder.new.tap do |b|
|
42
|
+
b.instance_variable_set(:@use, use)
|
43
|
+
b.instance_variable_set(:@run, run)
|
44
|
+
end.dup
|
45
|
+
end
|
46
|
+
|
47
|
+
it { subject.instance_variable_get(:@run).should equal(run) }
|
48
|
+
it { subject.instance_variable_get(:@use).should eql(use) }
|
49
|
+
it { subject.instance_variable_get(:@use).should_not equal(use) }
|
50
|
+
it { subject.instance_variable_get(:@map).should be_nil }
|
51
|
+
it { subject.dup.instance_variable_get(:@map).should be_nil }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "dup with map" do
|
55
|
+
let(:use) { [mock()] }
|
56
|
+
let(:map) { {"/" => mock()} }
|
57
|
+
let(:run) { mock() }
|
58
|
+
|
59
|
+
subject do
|
60
|
+
::Rack::Builder.new.tap do |b|
|
61
|
+
b.instance_variable_set(:@use, use)
|
62
|
+
b.instance_variable_set(:@map, map)
|
63
|
+
b.instance_variable_set(:@run, run)
|
64
|
+
end.dup
|
65
|
+
end
|
66
|
+
|
67
|
+
it { subject.instance_variable_get(:@run).should equal(run) }
|
68
|
+
it { subject.instance_variable_get(:@use).should eql(use) }
|
69
|
+
it { subject.instance_variable_get(:@use).should_not equal(use) }
|
70
|
+
it { subject.instance_variable_get(:@map).should eql(map) }
|
71
|
+
it { subject.instance_variable_get(:@map).should_not equal(map) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ::Rack::Builder do
|
76
|
+
context "without an app" do
|
77
|
+
subject { ::Rack::Builder.new }
|
78
|
+
it { should respond_to(:app?) }
|
79
|
+
it { subject.app?.should be_false }
|
80
|
+
end
|
81
|
+
|
82
|
+
context "with an app" do
|
83
|
+
subject { ::Rack::Builder.new(lambda { |env| [200, {}, %w[OK]]}) }
|
84
|
+
it { should respond_to(:app?) }
|
85
|
+
it { subject.app?.should be_true }
|
86
|
+
end
|
87
|
+
|
88
|
+
context "dup without map" do
|
89
|
+
let(:use) { [mock()] }
|
90
|
+
let(:run) { mock() }
|
91
|
+
|
92
|
+
subject do
|
93
|
+
::Rack::Builder.new.tap do |b|
|
94
|
+
b.instance_variable_set(:@use, use)
|
95
|
+
b.instance_variable_set(:@run, run)
|
96
|
+
end.dup
|
97
|
+
end
|
98
|
+
|
99
|
+
it { subject.instance_variable_get(:@run).should equal(run) }
|
100
|
+
it { subject.instance_variable_get(:@use).should eql(use) }
|
101
|
+
it { subject.instance_variable_get(:@use).should_not equal(use) }
|
102
|
+
it { subject.instance_variable_get(:@map).should be_nil }
|
103
|
+
it { subject.dup.instance_variable_get(:@map).should be_nil }
|
104
|
+
end
|
105
|
+
|
106
|
+
context "dup with map" do
|
107
|
+
let(:use) { [mock()] }
|
108
|
+
let(:map) { {"/" => mock()} }
|
109
|
+
let(:run) { mock() }
|
110
|
+
|
111
|
+
subject do
|
112
|
+
::Rack::Builder.new.tap do |b|
|
113
|
+
b.instance_variable_set(:@use, use)
|
114
|
+
b.instance_variable_set(:@map, map)
|
115
|
+
b.instance_variable_set(:@run, run)
|
116
|
+
end.dup
|
117
|
+
end
|
118
|
+
|
119
|
+
it { subject.instance_variable_get(:@run).should equal(run) }
|
120
|
+
it { subject.instance_variable_get(:@use).should eql(use) }
|
121
|
+
it { subject.instance_variable_get(:@use).should_not equal(use) }
|
122
|
+
it { subject.instance_variable_get(:@map).should eql(map) }
|
123
|
+
it { subject.instance_variable_get(:@map).should_not equal(map) }
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Circuit::Rack::MultiSite do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
include SpecHelpers::MultiSiteHelper
|
6
|
+
|
7
|
+
def app
|
8
|
+
Rack::Builder.app do
|
9
|
+
use Circuit::Rack::MultiSite
|
10
|
+
run Proc.new {|env| [200, {}, ["ok"]] }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'GET example.com' do
|
15
|
+
before do
|
16
|
+
get "http://#{site.host}/"
|
17
|
+
end
|
18
|
+
|
19
|
+
context "status" do
|
20
|
+
subject { last_response.status }
|
21
|
+
it { should == 200 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "GET baddomain.com" do
|
26
|
+
before do
|
27
|
+
get "http://baddomain.com/"
|
28
|
+
end
|
29
|
+
|
30
|
+
subject { last_response }
|
31
|
+
it { subject.status.should == 404 }
|
32
|
+
it { subject.body.should == "Not Found"}
|
33
|
+
end
|
34
|
+
end
|