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
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