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.
Files changed (75) hide show
  1. data/Gemfile +34 -0
  2. data/LICENSE +20 -0
  3. data/README.md +161 -0
  4. data/Rakefile +27 -0
  5. data/config.ru +7 -0
  6. data/description.md +5 -0
  7. data/docs/COMPATIBILITY.md +14 -0
  8. data/docs/ROADMAP.md +29 -0
  9. data/lib/circuit.rb +125 -0
  10. data/lib/circuit/behavior.rb +99 -0
  11. data/lib/circuit/compatibility.rb +73 -0
  12. data/lib/circuit/middleware.rb +6 -0
  13. data/lib/circuit/middleware/rewriter.rb +43 -0
  14. data/lib/circuit/rack.rb +14 -0
  15. data/lib/circuit/rack/behavioral.rb +45 -0
  16. data/lib/circuit/rack/builder.rb +50 -0
  17. data/lib/circuit/rack/multi_site.rb +22 -0
  18. data/lib/circuit/rack/request.rb +81 -0
  19. data/lib/circuit/railtie.rb +24 -0
  20. data/lib/circuit/storage.rb +74 -0
  21. data/lib/circuit/storage/memory_model.rb +70 -0
  22. data/lib/circuit/storage/nodes.rb +56 -0
  23. data/lib/circuit/storage/nodes/memory_store.rb +63 -0
  24. data/lib/circuit/storage/nodes/model.rb +67 -0
  25. data/lib/circuit/storage/nodes/mongoid_store.rb +56 -0
  26. data/lib/circuit/storage/sites.rb +38 -0
  27. data/lib/circuit/storage/sites/memory_store.rb +53 -0
  28. data/lib/circuit/storage/sites/model.rb +29 -0
  29. data/lib/circuit/storage/sites/mongoid_store.rb +43 -0
  30. data/lib/circuit/validators.rb +74 -0
  31. data/lib/circuit/version.rb +3 -0
  32. data/spec/internal/app/behaviors/change_path.rb +7 -0
  33. data/spec/internal/app/controllers/application_controller.rb +5 -0
  34. data/spec/internal/app/helpers/application_helper.rb +2 -0
  35. data/spec/internal/config/initializers/circuit.rb +7 -0
  36. data/spec/internal/config/routes.rb +3 -0
  37. data/spec/internal/db/schema.rb +1 -0
  38. data/spec/lib/circuit/behavior_spec.rb +113 -0
  39. data/spec/lib/circuit/middleware/rewriter_spec.rb +79 -0
  40. data/spec/lib/circuit/rack/behavioral_spec.rb +60 -0
  41. data/spec/lib/circuit/rack/builder_spec.rb +125 -0
  42. data/spec/lib/circuit/rack/multi_site_spec.rb +34 -0
  43. data/spec/lib/circuit/rack/request_spec.rb +80 -0
  44. data/spec/lib/circuit/railtie_spec.rb +34 -0
  45. data/spec/lib/circuit/storage/nodes_spec.rb +62 -0
  46. data/spec/lib/circuit/storage/sites_spec.rb +60 -0
  47. data/spec/lib/circuit/storage_spec.rb +20 -0
  48. data/spec/lib/circuit/validators_spec.rb +69 -0
  49. data/spec/lib/circuit_spec.rb +139 -0
  50. data/spec/spec_helper.rb +79 -0
  51. data/spec/support/blueprints.rb +24 -0
  52. data/spec/support/matchers/be_current_time_matcher.rb +14 -0
  53. data/spec/support/matchers/extended_have_key.rb +31 -0
  54. data/spec/support/matchers/have_accessor_matcher.rb +13 -0
  55. data/spec/support/matchers/have_attribute_matcher.rb +22 -0
  56. data/spec/support/matchers/have_block_matcher.rb +14 -0
  57. data/spec/support/matchers/have_errors_on_matcher.rb +30 -0
  58. data/spec/support/matchers/have_module_matcher.rb +13 -0
  59. data/spec/support/matchers/have_reader_matcher.rb +18 -0
  60. data/spec/support/matchers/have_writer_matcher.rb +18 -0
  61. data/spec/support/matchers/set_instance_variable.rb +32 -0
  62. data/spec/support/spec_helpers/base_behaviors.rb +20 -0
  63. data/spec/support/spec_helpers/base_models.rb +64 -0
  64. data/spec/support/spec_helpers/logger_helpers.rb +58 -0
  65. data/spec/support/spec_helpers/multi_site_helper.rb +48 -0
  66. data/spec/support/spec_helpers/rack_helpers.rb +8 -0
  67. data/spec/support/spec_helpers/shared_examples/node_store.rb +87 -0
  68. data/spec/support/spec_helpers/shared_examples/site_store.rb +76 -0
  69. data/spec/support/spec_helpers/simple_machinable.rb +29 -0
  70. data/spec/support/spec_helpers/stores_cleaner.rb +46 -0
  71. data/vendor/active_support-3.2/core_ext/string/inflections.rb +53 -0
  72. data/vendor/active_support-3.2/inflector/methods.rb +65 -0
  73. data/vendor/rack-1.4/builder.rb +167 -0
  74. data/vendor/rack-1.4/urlmap.rb +96 -0
  75. metadata +238 -0
@@ -0,0 +1,3 @@
1
+ module Circuit
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ module ChangePath
2
+ include Circuit::Behavior
3
+
4
+ use Circuit::Middleware::Rewriter do |script_name, path_info|
5
+ ["/site/5#{script_name}", path_info]
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ respond_to :html
3
+
4
+ protect_from_forgery
5
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,7 @@
1
+ if defined?(::Mongoid)
2
+ Circuit.set_site_store :mongoid_store
3
+ Circuit.set_node_store :mongoid_store
4
+ else
5
+ Circuit.set_site_store :memory_store
6
+ Circuit.set_node_store :memory_store
7
+ end
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -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