circuit 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|