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
data/Gemfile ADDED
@@ -0,0 +1,34 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem "rake", "~> 0.9.2"
5
+ gem "combustion", "= 0.3.2"
6
+ gem "rspec-rails", "~> 2.9.0"
7
+ gem "shoulda-matchers", "~> 1.1.0"
8
+ gem "database_cleaner", "~> 0.7.2"
9
+ gem "mocha", "~> 0.11.4"
10
+ gem "rspec-rails-mocha", "~> 0.3.2"
11
+ gem "delorean", "~> 1.2.0"
12
+ gem "machinist", "~> 2.0.0.beta2"
13
+ gem "faker", "~> 1.0.1"
14
+ gem "simplecov", "~> 0.6.4", :require => false
15
+ end
16
+
17
+ group :development do
18
+ gem "yard", "~> 0.8"
19
+ gem "redcarpet", "~> 2.1"
20
+ end
21
+
22
+ group :mongo do
23
+ gem "mongoid", "~> 2.4"
24
+ gem "mongoid-tree", "~> 0.7"
25
+ gem "bson_ext", "~> 1.4"
26
+ gem "mongoid-rspec", "= 1.4.4"
27
+ gem "machinist_mongo",
28
+ :require => "machinist/mongoid",
29
+ :git => "git://github.com/nmerouze/machinist_mongo.git",
30
+ :branch => "machinist2"
31
+ end
32
+
33
+ # Specify core dependencies in core.gemspec
34
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 MaxMedia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject
9
+ to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,161 @@
1
+ **Circuit is in heavy development and reorganization at the moment. We are planning to have this API and change
2
+ stabilized in the 0.4.0 release. 0.4.0 and future releases will include deprecation warning and a 0.4.0-stable
3
+ branch in GitHub.**
4
+
5
+ # Circuit
6
+
7
+ <<< description.md
8
+
9
+ **GitHub** http://github.com/maxmedia/circuit
10
+
11
+ **Issues** http://github.com/maxmedia/circuit/issues
12
+
13
+ **Travis-CI** http://travis-ci.org/maxmedia/circuit
14
+ [![Build Status](https://secure.travis-ci.org/maxmedia/circuit.png?branch=master)](http://travis-ci.org/maxmedia/circuit)
15
+
16
+ **Docs** http://rubydoc.info/gems/circuit
17
+
18
+ **RubyGems** http://rubygems.org/gems/circuit
19
+
20
+ ## Contributing
21
+
22
+ Anyone is welcome to contribute to circuit. Just [fork us on GitHub](https://github.com/maxmedia/circuit/fork_select)
23
+ and send a pull request when you are ready.
24
+
25
+ Please ensure the following compatibility requirements are met.
26
+
27
+ <<< docs/COMPATIBILITY.md
28
+
29
+ ## A common discussion ensues.
30
+
31
+ **Q: But why would someone need database powered Rack::Builder?**
32
+
33
+ Response: The rack ecosystem doesn't have a good way to handle dynamic http routing. Circuit meets the
34
+ needs of dynamic routing while preserving the functionality that Rack provides.
35
+
36
+ **Comment: That seems really... slow.**
37
+
38
+ Response: A valid point, but circuit isn't for every codebase. If you need dynamic routing (i.e.
39
+ using splat routing methods followed by a database lookup), you are going to see similar
40
+ performance to circuits backends. Along with that, circuit provides a number of useful features
41
+ that far outweigh performance costs.
42
+
43
+ **Comment: this isn't very _Railsy_**
44
+
45
+ Response: In practice, codebases that use circuit to solve dynamic routing are **more** railsy.
46
+ Let me explain:
47
+
48
+ If you are constructing an application in Rails that requires dynamic user specified slugs, you
49
+ probably have this line uncommented from your routes:
50
+
51
+ # yuck.
52
+ match '/*', to: 'site#proxy'
53
+
54
+ Say you are creating a CMS using this line. This means that that one action will handle all
55
+ requests that don't match the rest of your routes definition. Immediately this might seem like a good
56
+ idea, but it will inevitably lead to a very expensive Rails action that handles a large number of
57
+ requests on the site.
58
+
59
+ Circuit will allow you to remove this line and use middlewares to route requests to different
60
+ controllers (even rack applications), and control this logic via the backend of your choice.
61
+ Routes can be extended with their specified behaviors, allowing even more control over requests
62
+ than rails provides out of the box.
63
+
64
+ Using the Rerouting Middleware you can easily reroute downstream requests to standard-issue rails
65
+ controllers, enabling your dynamic requests to add middleware, modify the rack request object and
66
+ even change your downstream app (to another rack app).
67
+
68
+ ## A mapping graph to route rack requests.
69
+
70
+ TODO
71
+
72
+ ## Rackup-based behaviors.
73
+
74
+ Circuit uses behaviors to extend a requests functionality in the rack stack. Behaviors are written
75
+ and stored in circuit-rackup files (`.cru`). The difference between a normal rackup file and a
76
+ circuit-rackup file is that a circuit-rackup file does not have to have a `run` declaration; this
77
+ makes circuit-rackup files only *partially* rackup compliant. Below is an example of a behavior
78
+ that renders an ok response (this is a fully-compliant rackup file; however, we still use the
79
+ `.cru` extension for the behavior):
80
+
81
+ # ./app/behaviors/render_ok.cru
82
+
83
+ run proc {|env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
84
+
85
+
86
+ First, setup your `Site`:
87
+
88
+ $ > @site = Circuit::Site.new(host: "example.com", aliases: ["www.example.com"])
89
+ $ > @site.save
90
+ $ => true
91
+
92
+ Then, setup your `Node` and specify the behavior:
93
+
94
+ $ > @node = Circuit::Node.new(slug: "", behavior_klass: "RenderOk")
95
+ $ > @node.site = @site
96
+ $ > @node.save
97
+ $ => true
98
+ $ > @node.root? # the blank slug indicates the root
99
+ $ => true
100
+ $ > @node.behavior
101
+ $ => RenderOk
102
+ $ > @node.behavior.class
103
+ $ => Module
104
+ $ > @node.behavior.included_modules
105
+ $ => [Circuit::Behavior]
106
+
107
+ When your site is loaded, the render_ok.ru behavior will be executed.
108
+
109
+ Behaviors are loaded into memory during application initialization. Any modifications to behaviors
110
+ will require restarting your application.
111
+
112
+ ## Circuit::Middleware::Rewriter
113
+
114
+ One useful middleware baked into circuit is the `Rewriter`. The idea of this middleware is
115
+ to use circuit routing to route to dynamic content you have associated in you routing tree.
116
+
117
+ # ./app/behaviors/page.ru
118
+
119
+ use Circuit::Middleware::Rewriter do |request|
120
+ content = request.route.last.content
121
+ ["/#{content.class.to_s.underscore.pluralize}", "/#{content.id}"]
122
+ end
123
+
124
+ and the corresponding downstream rails file:
125
+
126
+ # ./config/routes.rb
127
+
128
+ Rails.application.routes.draw do
129
+ resources :contents
130
+ end
131
+
132
+ In the above example, we are assuming you have created a model that has a content object associated
133
+ to it. By doing this we have now created a small CMS.
134
+
135
+ ## Multi-Backend support
136
+
137
+ Circuit provides support for different backends to power this map tree. Currently, the gem includes a
138
+ Mongoid backend and a memory backend. Each backend is how Circuit is constructed to interface with your
139
+ persisted routing tree. Backends are ActiveModel based.
140
+
141
+ Currently, Backends are ActiveModel backed and within them they define the model that the router interacts
142
+ with. We are discussing the idea of separating the backends from the model (to drop the ActiveModel dependency).
143
+
144
+ **This is where circuit needs your help!** We would love to see backends for many common ODM's. Our
145
+ hit-list includes:
146
+
147
+ * <del>Mongoid</del>
148
+ * <del>Memory</del>
149
+ * Yaml
150
+ * Redis
151
+ * ActiveRecord
152
+ * RiakClient/Ripple
153
+
154
+ We will entertain pull requests from other commonly used libraries.
155
+
156
+ <<< docs/ROADMAP.md
157
+
158
+ -----------------------------------------------
159
+ ```
160
+ <<< LICENSE
161
+ ```
@@ -0,0 +1,27 @@
1
+ require "bundler/gem_tasks"
2
+ require "pathname"
3
+
4
+ $rake_root = Pathname(__FILE__).expand_path.dirname
5
+
6
+ Dir.glob($rake_root.join("**/*.rake").to_s).each do |fn|
7
+ load fn
8
+ end
9
+
10
+ task :default => [:spec]
11
+
12
+ namespace :clobber do
13
+ desc "Clobber Gem Package (pkg/)"
14
+ task :pkg do
15
+ $rake_root.join("pkg").rmtree rescue nil
16
+ end
17
+
18
+ desc "Clobber Coverage (coverage/)"
19
+ task :coverage do
20
+ $rake_root.join("coverage").rmtree rescue nil
21
+ end
22
+
23
+ task :doc => ["doc:clobber"]
24
+ end
25
+
26
+ desc "Clobber All"
27
+ task :clobber => ["doc:clobber", "clobber:pkg", "clobber:coverage"]
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize! :action_controller, :action_view, :sprockets
7
+ run Combustion::Application
@@ -0,0 +1,5 @@
1
+ Circuit is a rack application middleware that enables dynamic request mapping. Modeled after
2
+ [Rack::Builder](https://github.com/rack/rack/blob/master/lib/rack/builder.rb), Circuit provides
3
+ for a tree of url-mappings that is walked at request time. If you are interested in dynamically
4
+ loading middleware and functionality into requests, circuit is a viable solution to do that in a
5
+ maintainable way.
@@ -0,0 +1,14 @@
1
+ ## Compatibility
2
+
3
+ Circuit is compatible with the following versions of Ruby, Rack, and Rails components:
4
+
5
+ **Ruby** 1.8.7 and 1.9
6
+ *Ruby 1.8.7 support will be removed when Rails 4 support is added*
7
+
8
+ **Rack** 1.3 and 1.4
9
+
10
+ **Rails** 3.1 and 3.2
11
+ *ActiveSupport and ActiveModel are the only requirements from Rails*
12
+
13
+ **Mongoid** 2.4+
14
+ *MongoDB/Mongoid is not required*
@@ -0,0 +1,29 @@
1
+ ## Roadmap
2
+
3
+ ### 0.2.0
4
+
5
+ * <del>better readme</del>
6
+ * <del>Separate site and routing tree</del>
7
+ * <del>database site aliases</del>
8
+ * <del>multi-backend support</del>
9
+ * <del>travis-ci</del>
10
+ * <del>Move to github issues</del>
11
+ * <del>Remove rails dependencies (specifically ActionPack)</del>
12
+ * <del>Behaviors are specified in rackup files, instead of classes</del>
13
+ * <del>Behaviors support partial Rack::Builder: `run` and `use` methods<del>
14
+
15
+ ### 0.2.1
16
+ * Behaviors support full Rack::Builder: `run`, `use`, and `map` methods.
17
+
18
+ ### 0.3.0
19
+
20
+ * tree inheritable middlewares.
21
+ * full documentation.
22
+
23
+ ### 0.4.0
24
+
25
+ * API stability
26
+
27
+ ### In the future
28
+
29
+ * Goliath support
@@ -0,0 +1,125 @@
1
+ if Object.const_defined?(:Rails)
2
+ require "circuit/railtie"
3
+ end
4
+ require 'logger'
5
+ require 'circuit/version'
6
+ require 'circuit/compatibility'
7
+ require 'active_support/configurable'
8
+ require 'dionysus/configuration_callbacks'
9
+
10
+ module Circuit
11
+ autoload :Middleware, 'circuit/middleware'
12
+ autoload :Rack, 'circuit/rack'
13
+ autoload :Behavior, 'circuit/behavior'
14
+ autoload :Storage, 'circuit/storage'
15
+
16
+ # Top-level error class for Circuit errorsr
17
+ class CircuitError < StandardError; end
18
+
19
+ # @!attribute [r] config
20
+ # Maintains configuration values for `logger`, `cru_path`, `site_store`,
21
+ # and `node_store`
22
+ # @return [ActiveSupport::Configurable::Configuration] configuration
23
+ # @see http://rubydoc.info/gems/activesupport/ActiveSupport/Configurable/ClassMethods#config-instance_method
24
+
25
+ # @!method configure()
26
+ # Configure Circuit
27
+ # @yield [ActiveSupport::Configurable::Configuration] configuration object
28
+ # @see http://rubydoc.info/gems/activesupport/ActiveSupport/Configurable/ClassMethods#configure-instance_method
29
+ include ActiveSupport::Configurable
30
+ include Dionysus::ConfigurationCallbacks
31
+ config_accessor :logger, :cru_path, :site_store, :node_store,
32
+ :instance_reader => false,
33
+ :instance_writer => false
34
+
35
+ # @!method logger=(logger)
36
+ # @!scope class
37
+ # @param [Logger] logger for Circuit
38
+ # @return [Logger] logger for Circuit
39
+
40
+ # @!method logger
41
+ # @!scope class
42
+ # @return [Logger] logger for Circuit
43
+
44
+ # @!method cru_path=(pathname)
45
+ # @!scope class
46
+ # @param [Pathname,String] pathname directory for behaviors, .cru, and .ru files
47
+ # @return [Pathname] pathname directory for behaviors, .cru, and .ru files
48
+
49
+ # @!method cru_path
50
+ # @!scope class
51
+ # @return [Pathname] pathname directory for behaviors, .cru, and .ru files
52
+
53
+ # @!method site_store
54
+ # @!scope class
55
+ # @return [Storage::Sites::BaseStore] the Site storage instance
56
+
57
+ # @!method node_store
58
+ # @!scope class
59
+ # @return [Storage::Nodes::BaseStore] the Node storage instance
60
+ configure do |c|
61
+ c.logger = ::Logger.new($stdout)
62
+
63
+ c.after :set, :cru_path do
64
+ val = _get(:cru_path)
65
+ if val and !val.is_a?(Pathname)
66
+ _set(:cru_path, Pathname.new(val.to_s))
67
+ end
68
+ end
69
+
70
+ c.forward :get, :site_store, Storage::Sites, :instance
71
+ c.forward :get, :node_store, Storage::Nodes, :instance
72
+ end
73
+
74
+ config.instance_exec do
75
+ def site_store=(args)
76
+ Storage::Sites.set_instance(*args)
77
+ end
78
+ def node_store=(args)
79
+ Storage::Nodes.set_instance(*args)
80
+ end
81
+ end
82
+
83
+ # @overload set_site_store(instance)
84
+ # @param [Storage::Sites::BaseStore] instance for the Site store
85
+ # @example Set the Site store
86
+ # Circuit.set_site_store Circuit::Storage::Sites::MongoidStore.new
87
+ # @overload set_site_store(klass)
88
+ # @param [Class] klass to create instance for the Site store
89
+ # @example Set the Site store
90
+ # Circuit.set_site_store Circuit::Storage::Sites::MongoidStore
91
+ # @overload set_site_store(symbol)
92
+ # @param [Symbol] symbol for a Site store class under Circuit::Storage::Sites
93
+ # @example Set the Site store
94
+ # Circuit.set_site_store :mongoid_store
95
+ # @return [Storage::Sites::BaseStore] the Site storage instance
96
+ # @see Storage.set_instance
97
+ def self.set_site_store(*args)
98
+ config.site_store = args
99
+ end
100
+
101
+ # @overload set_node_store(instance)
102
+ # @param [Storage::Nodes::BaseStore] instance for the Node store
103
+ # @example Set the Node store
104
+ # Circuit.set_node_store Circuit::Storage::Nodes::MongoidStore.new
105
+ # @overload set_node_store(klass)
106
+ # @param [Class] klass to create instance for the Node store
107
+ # @example Set the Node store
108
+ # Circuit.set_node_store Circuit::Storage::Nodes::MongoidStore
109
+ # @overload set_node_store(symbol)
110
+ # @param [Symbol] symbol for a Node store class under Circuit::Storage::Nodes
111
+ # @example Set the Node store
112
+ # Circuit.set_node_store :mongoid_store
113
+ # @return [Storage::Nodes::BaseStore] the Node storage instance
114
+ # @see Storage.set_instance
115
+ def self.set_node_store(*args)
116
+ config.node_store = args
117
+ end
118
+
119
+ # @return [Pathname] path to the vendor directory
120
+ def self.vendor_path
121
+ Pathname.new(__FILE__).expand_path.dirname.join("..", "vendor")
122
+ end
123
+ end
124
+
125
+ Circuit::Compatibility.make_compatible
@@ -0,0 +1,99 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'pathname'
5
+
6
+ module Circuit
7
+ module Behavior
8
+ extend ActiveSupport::Concern
9
+
10
+ # Raised when the path for a rackup file is indeterminate.
11
+ class RackupPathError < Circuit::CircuitError; end
12
+
13
+ # @param [String] str name of Behavior constant to get
14
+ # @return [Behavior] Behavior constant
15
+ def self.get(str)
16
+ str.classify.constantize
17
+ rescue NameError
18
+ str = str.classify
19
+ nesting = str.deconstantize
20
+ nesting = (nesting.blank? ? Object : nesting.constantize)
21
+
22
+ mod = Module.new do
23
+ include Circuit::Behavior
24
+ self.cru_path = Circuit.cru_path.join(str.classify.underscore+".cru")
25
+ self.builder!
26
+ class_eval %(def self.to_s() "#{str}"; end)
27
+ end
28
+ nesting.const_set(str.demodulize, mod)
29
+ end
30
+
31
+ module ClassMethods
32
+ # Clones the builder into the subclass when using inheritance
33
+ def inherited(subclass)
34
+ subclass.builder = @builder.clone
35
+ end
36
+
37
+ # Indicates whether a Builder is defined or will be defined based on a rackup file
38
+ # @return [Boolean] false if there is no Builder and no rackup file to create one
39
+ def builder?
40
+ !!@builder or self.cru_path.file?
41
+ rescue RackupPathError
42
+ false
43
+ end
44
+
45
+ # Creates an empty Builder object or parses the rackup file if it exists
46
+ # @return [Builder] the Builder
47
+ def builder!
48
+ if self.cru_path.file?
49
+ @builder = Circuit::Rack::Builder.parse_file(self.cru_path)
50
+ else
51
+ @builder = Circuit::Rack::Builder.new
52
+ end
53
+ end
54
+
55
+ # Returns the Builder (and creates one if it doesn't exist yet)
56
+ # @return [Builder] the Builder
57
+ # @see #builder!
58
+ def builder
59
+ @builder ||= self.builder!
60
+ end
61
+
62
+ attr_writer :builder
63
+
64
+ # @return [Pathname] the path to the rackup file
65
+ # @raise [RackupPathError] if the rackup file path is indeterminate
66
+ def cru_path
67
+ if @cru_path.nil? and Circuit.cru_path.nil?
68
+ raise RackupPathError, "Rackup path cannot be determined for #{self.to_s}"
69
+ end
70
+ @cru_path ||= Circuit.cru_path.join(self.class.to_s.underscore)
71
+ end
72
+
73
+ # Sets the rackup file path and clears the #builder
74
+ # @param [Pathname,String] pathname of the rackup file
75
+ def cru_path=(pathname)
76
+ @cru_path = (pathname.is_a?(Pathname) ? pathname : Pathname.new(pathname.to_s))
77
+ self.builder = nil
78
+ end
79
+
80
+ # @!method use(middleware, *args, &block)
81
+ # @see http://rubydoc.info/gems/rack/Rack/Builder#use-instance_method
82
+ # ::Rack::Builder#use
83
+
84
+ # @!method run(app)
85
+ # @see http://rubydoc.info/gems/rack/Rack/Builder#run-instance_method
86
+ # ::Rack::Builder#run
87
+
88
+ # @!method map(path, &block)
89
+ # @see http://rubydoc.info/gems/rack/Rack/Builder#map-instance_method
90
+ # ::Rack::Builder#map
91
+
92
+ # @!method useto_app
93
+ # @see http://rubydoc.info/gems/rack/Rack/Builder#to_app-instance_method
94
+ # ::Rack::Builder#to_app
95
+
96
+ delegate :use, :run, :map, :to_app, :to => :builder
97
+ end
98
+ end
99
+ end