circuit 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,79 @@
1
+ require 'rubygems'
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "/lib/circuit/version.rb"
5
+ add_filter "/spec/"
6
+ end
7
+
8
+ require 'bundler'
9
+
10
+ require 'simplecov'
11
+ SimpleCov.start
12
+
13
+ required_groups = [:default, :development]
14
+
15
+ # Require gems and attempt to load mongo
16
+ begin
17
+ Bundler.require *(required_groups+[:mongo])
18
+ rescue LoadError
19
+ Bundler.require *required_groups
20
+ end
21
+ # Determine if we want to run tests with mongo (i.e. whether mongo was loaded)
22
+ $mongo_tests = !!Bundler.definition.requested_specs.detect {|s| s.name == "mongo"}
23
+
24
+ require 'combustion'
25
+
26
+ require 'machinist'
27
+ require 'machinist/mongoid' if $mongo_tests
28
+
29
+ Mongoid.load! "spec/internal/config/mongoid.yml" if $mongo_tests
30
+
31
+ Combustion.initialize! :action_controller, :action_view, :sprockets
32
+
33
+ require 'rails/mongoid' if $mongo_tests
34
+ require 'rspec/rails'
35
+ require 'rspec/rails/mocha'
36
+ require 'support/blueprints'
37
+
38
+ # Requires supporting ruby files with custom matchers and macros, etc,
39
+ # in spec/support/ and its subdirectories.
40
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
41
+
42
+ RSpec.configure do |config|
43
+ config.mock_with :mocha
44
+ config.include SpecHelpers::RackHelpers
45
+
46
+ if $mongo_tests
47
+ config.include Mongoid::Matchers
48
+
49
+ config.after(:each) do
50
+ Mongoid.master.collections.select do |collection|
51
+ collection.name !~ /system/
52
+ end.each(&:drop)
53
+ end
54
+
55
+ # Clean up the database
56
+ require 'database_cleaner'
57
+ config.before(:suite) do
58
+ DatabaseCleaner.strategy = :truncation
59
+ DatabaseCleaner.orm = "mongoid"
60
+ end
61
+
62
+ config.before(:each) do
63
+ DatabaseCleaner.clean
64
+ end
65
+ end
66
+
67
+ config.after(:each) do
68
+ Circuit::Storage::Sites::MemoryStore::Site.all.clear
69
+ Circuit::Storage::Nodes::MemoryStore::Node.all.clear
70
+ end
71
+
72
+ config.before(:each) { stub_time! }
73
+ end
74
+
75
+ # freeze time, so time tests appear to run without time passing.
76
+ def stub_time!
77
+ @time = Time.zone.now
78
+ Time.zone.stubs(:now).returns(@time)
79
+ end
@@ -0,0 +1,24 @@
1
+ require 'support/spec_helpers/simple_machinable'
2
+
3
+ module CircuitBlueprints
4
+ def ensure_blueprints
5
+ SimpleMachinable.ensure_machinable(Circuit::Site, Circuit::Node)
6
+
7
+ if Circuit::Site.blueprint.nil?
8
+ Circuit::Site.blueprint do
9
+ host { 'example.org' }
10
+ aliases { %w[www.example.org subdomain.example.com] }
11
+ end
12
+ end
13
+
14
+ if Circuit::Node.blueprint.nil?
15
+ Circuit::Node.blueprint do
16
+ slug { Faker::Lorem.words(rand(3) + 2).join('-') }
17
+ behavior_klass { "RenderOk" }
18
+ end
19
+ end
20
+ end
21
+ module_function :ensure_blueprints
22
+ end
23
+
24
+ CircuitBlueprints.ensure_blueprints
@@ -0,0 +1,14 @@
1
+ RSpec::Matchers.define :be_current_time do
2
+ match do |time|
3
+ # the to is is for rounding errors
4
+ time.try(:utc).to_i == Time.zone.now.utc.to_i
5
+ end
6
+
7
+ failure_message_for_should do |time|
8
+ "#{time.class} should be set to local time"
9
+ end
10
+
11
+ failure_message_for_should_not do |time|
12
+ "#{time.class} should not be set to local time"
13
+ end
14
+ end
@@ -0,0 +1,31 @@
1
+ RSpec::Matchers.define :have_key do |key|
2
+ chain :== do |value|
3
+ @value = value
4
+ end
5
+
6
+ match do |hash|
7
+ @has_key = hash.has_key?(key)
8
+
9
+ if @value
10
+ @has_key && hash[key] == @value
11
+ else
12
+ @has_key
13
+ end
14
+ end
15
+
16
+ failure_message_for_should do |hash|
17
+ if @value
18
+ "#{hash.inspect} should have key #{key.inspect} == #{value.inspect}"
19
+ else
20
+ "#{hash.inspect} should have key #{key.inspect}"
21
+ end
22
+ end
23
+
24
+ failure_message_for_should_not do |model|
25
+ if @value
26
+ "#{hash.inspect} should not have key #{key.inspect} == #{value.inspect}"
27
+ else
28
+ "#{hash.inspect} should not have key #{key.inspect}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :have_accessor do |attribute|
2
+ match do |model|
3
+ model.respond_to?(attribute) and model.respond_to?("#{attribute}=")
4
+ end
5
+
6
+ failure_message_for_should do |model|
7
+ "#{model.class} should have attribute accessor #{attribute.inspect}"
8
+ end
9
+
10
+ failure_message_for_should_not do |model|
11
+ "#{model.class} should not have attribute accessor #{attribute.inspect}"
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ RSpec::Matchers.define :have_attribute do |attribute|
2
+ match do |model|
3
+ if model.respond_to?(attribute) and model.respond_to?("#{attribute}=")
4
+ obj = Object.new
5
+ old = model.send(attribute)
6
+ model.send("#{attribute}=", obj)
7
+ result = (model.attributes[attribute] == obj)
8
+ model.send("#{attribute}=", old)
9
+ result
10
+ else
11
+ false
12
+ end
13
+ end
14
+
15
+ failure_message_for_should do |model|
16
+ "#{model.class} should have attribute accessor #{attribute.inspect}"
17
+ end
18
+
19
+ failure_message_for_should_not do |model|
20
+ "#{model.class} should not have attribute accessor #{attribute.inspect}"
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ RSpec::Matchers.define :have_block do |block_sym|
2
+ match do |template|
3
+ # the to is is for rounding errors
4
+ template.blocks[block_sym]
5
+ end
6
+
7
+ failure_message_for_should do |template|
8
+ "#{template.class} should have block(#{block_sym})"
9
+ end
10
+
11
+ failure_message_for_should_not do |template|
12
+ "#{template.class} should not have block(#{block_sym})"
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # http://solnic.eu/2011/01/14/custom-rspec-2-matchers.html
2
+ RSpec::Matchers.define :have_errors_on do |attribute|
3
+ chain :with_message do |message|
4
+ @message = message
5
+ end
6
+
7
+ match do |model|
8
+ model.valid?
9
+
10
+ @has_errors = !(model.errors[attribute].blank?)
11
+
12
+ if @message
13
+ @has_errors && model.errors[attribute].include?(@message)
14
+ else
15
+ @has_errors
16
+ end
17
+ end
18
+
19
+ failure_message_for_should do |model|
20
+ if @message
21
+ "Validation errors #{model.errors[attribute].inspect} should include #{@message.inspect}"
22
+ else
23
+ "#{model.class} should have errors on attribute #{attribute.inspect}"
24
+ end
25
+ end
26
+
27
+ failure_message_for_should_not do |model|
28
+ "#{model.class} should not have an error on attribute #{attribute.inspect}"
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :have_module do |mod_or_name|
2
+ match do |obj|
3
+ !!obj.included_modules.detect { |m| m.to_s == mod_or_name.to_s }
4
+ end
5
+
6
+ failure_message_for_should do |obj|
7
+ "expected %p with modules %p to include module '%s'"%[obj, obj.included_modules, mod_or_name]
8
+ end
9
+
10
+ failure_message_for_should_not do |obj|
11
+ "expected %p with modules %p to not include module '%s'"%[obj, obj.included_modules, mod_or_name]
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # http://solnic.eu/2011/01/14/custom-rspec-2-matchers.html
2
+ RSpec::Matchers.define :have_reader do |attribute|
3
+ match do |model|
4
+ model.respond_to?(attribute)
5
+ end
6
+
7
+ failure_message_for_should do |model|
8
+ if @message
9
+ "Validation errors #{model.errors[attribute].inspect} should include #{@message.inspect}"
10
+ else
11
+ "#{model.class} should have attribute reader #{attribute.inspect}"
12
+ end
13
+ end
14
+
15
+ failure_message_for_should_not do |model|
16
+ "#{model.class} should not have attribute reader #{attribute.inspect}"
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # http://solnic.eu/2011/01/14/custom-rspec-2-matchers.html
2
+ RSpec::Matchers.define :have_writer do |attribute|
3
+ match do |model|
4
+ model.respond_to?("#{attribute}=")
5
+ end
6
+
7
+ failure_message_for_should do |model|
8
+ if @message
9
+ "Validation errors #{model.errors[attribute].inspect} should include #{@message.inspect}"
10
+ else
11
+ "#{model.class} should have attribute writer #{attribute.inspect}"
12
+ end
13
+ end
14
+
15
+ failure_message_for_should_not do |model|
16
+ "#{model.class} should not have attribute writer #{attribute.inspect}"
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ RSpec::Matchers.define :have_instance_variable do |var_symbol|
2
+ chain :== do |value|
3
+ @value = value
4
+ end
5
+
6
+ match do |model|
7
+ @ivar = model.instance_variable_get("@#{var_symbol.to_s}")
8
+
9
+ if @value
10
+ @ivar == @value
11
+ else
12
+ @ivar
13
+ end
14
+ end
15
+
16
+ failure_message_for_should do |model|
17
+ if @message
18
+ "#{model.class} should have instance variable #{var_symbol.inspect} == #{value.inspect}"
19
+ else
20
+ "#{model.class} should have instance variable #{var_symbol.inspect}"
21
+ end
22
+ end
23
+
24
+ failure_message_for_should_not do |model|
25
+ if @message
26
+ "#{model.class} should not have instance variable #{var_symbol.inspect} == #{value.inspect}"
27
+ else
28
+ "#{model.class} should not have instance variable #{var_symbol.inspect}"
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ require 'active_support/concern'
2
+
3
+ module SpecHelpers
4
+ module BaseBehaviors
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+
9
+ class ::StatusOkBehavior
10
+ include Decks::Behavior
11
+
12
+ def self.call_with_downstream_app(env, app)
13
+ [200, {'Content-Type' => 'text/plain'}, ["OK"]]
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ # constructs 2 different trees of routing for our examples purposes.
2
+ #
3
+ # + root
4
+ # + child
5
+ # + grandchild
6
+ # + great_grandchild
7
+ #
8
+ # And a more complex version:
9
+ #
10
+ # + root_1
11
+ # + child_1
12
+ # + grandchild_1
13
+ # + grandchild_2
14
+ # + great_grandchild_1
15
+ # + great_grandchild_2
16
+ # + child_2
17
+ # + grandchild_3
18
+ # + great_grandchild_3
19
+ # + root_2
20
+ #
21
+ # This include doesn't take into account any slug information(as they are generated
22
+ # by the Circuit::Node blueprint already). Also, each of these is persisted, so if you want
23
+ # something faster or don't require routing tree, you should consider using a stub or
24
+ # `node_class.make` to generate a non-persisted record.
25
+ #
26
+ require 'active_support/concern'
27
+
28
+ module SpecHelpers
29
+ module BaseModels
30
+ extend ActiveSupport::Concern
31
+
32
+ included do
33
+ let(:node_class) { Circuit::Node }
34
+ let(:site_class) { Circuit::Site }
35
+
36
+ let(:site_1) { site_class.make! :host => "www.foo.com", :aliases => [] }
37
+ let(:root_1) { node_class.make! :slug => nil, :site => site_1 }
38
+ let(:child_1) { node_class.make! :parent => root_1 }
39
+ let(:grandchild_1) { node_class.make! :parent => child_1 }
40
+ let(:grandchild_2) { node_class.make! :parent => child_1 }
41
+ let(:great_grandchild_1) { node_class.make! :parent => grandchild_2 }
42
+ let(:great_grandchild_2) { node_class.make! :parent => grandchild_2 }
43
+ let(:child_2) { node_class.make! :parent => root_1 }
44
+ let(:grandchild_3) { node_class.make! :parent => child_2 }
45
+ let(:great_grandchild_3) { node_class.make! :parent => grandchild_3 }
46
+
47
+ let(:site_2) { site_class.make! :host => "www.bar.com", :aliases => [] }
48
+ let(:root_2) { node_class.make! :slug => nil, :site => site_2 }
49
+
50
+ let(:site) { site_class.make! }
51
+ let(:root) { node_class.make! :slug => nil, :site => site }
52
+ let(:child) { node_class.make! :parent => root }
53
+ let(:grandchild) { node_class.make! :parent => child }
54
+ let(:great_grandchild) { node_class.make! :parent => grandchild }
55
+
56
+ let(:dup_site_1) { site_class.make! :host => "dup1.com", :aliases => [] }
57
+ let(:dup_site_1_dup) { site_class.make! :host => "dup1.com", :aliases => %w[foo.com] }
58
+ let(:dup_site_2) { site_class.make! :host => "dup2.com", :aliases => [] }
59
+ let(:dup_site_2_dup) { site_class.make! :host => "bar.com", :aliases => %w[dup2.com] }
60
+ let(:dup_site_3) { site_class.make! :host => "wow1.com", :aliases => %w[dup3.com] }
61
+ let(:dup_site_3_dup) { site_class.make! :host => "wow2.com", :aliases => %w[dup3.com] }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,58 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ module SpecHelpers
5
+ module LoggerHelpers
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :default_logger, :use_logger
10
+
11
+ around :each do |example|
12
+ @default_logger = Circuit.logger
13
+
14
+ if clean_logger?
15
+ Circuit.logger = nil
16
+ elsif !default_logger?
17
+ @logger_sio = StringIO.new
18
+ Circuit.logger = Logger.new(@logger_sio)
19
+ end
20
+
21
+ example.run
22
+
23
+ if clean_logger?
24
+ clean_logger!(false)
25
+ elsif !default_logger?
26
+ @logger_sio.close
27
+ @logger_sio = nil
28
+ end
29
+
30
+ Circuit.logger = @default_logger
31
+ end
32
+ end
33
+
34
+ def use_logger!(key)
35
+ @use_logger = (key ? key.to_sym : nil)
36
+ end
37
+
38
+ def use_logger?(key)
39
+ @use_logger == key.to_sym
40
+ end
41
+
42
+ def clean_logger!(val=true)
43
+ use_logger!(val ? :clean : false)
44
+ end
45
+ def clean_logger?() use_logger?(:clean); end
46
+
47
+ def default_logger!(val=true)
48
+ use_logger!(val ? :default : false)
49
+ end
50
+ def default_logger?() use_logger?(:default); end
51
+
52
+ def logger_output
53
+ raise "Clean logger used" if clean_logger?
54
+ raise "Default logger used" if default_logger?
55
+ @logger_sio.string
56
+ end
57
+ end
58
+ end