hassox-pancake 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +95 -0
  3. data/Rakefile +56 -0
  4. data/TODO +18 -0
  5. data/bin/jeweler +19 -0
  6. data/bin/pancake-gen +17 -0
  7. data/bin/rubyforge +19 -0
  8. data/lib/pancake.rb +48 -0
  9. data/lib/pancake/bootloaders.rb +180 -0
  10. data/lib/pancake/configuration.rb +140 -0
  11. data/lib/pancake/core_ext/class.rb +44 -0
  12. data/lib/pancake/core_ext/object.rb +22 -0
  13. data/lib/pancake/core_ext/symbol.rb +15 -0
  14. data/lib/pancake/errors.rb +61 -0
  15. data/lib/pancake/generators.rb +8 -0
  16. data/lib/pancake/generators/base.rb +12 -0
  17. data/lib/pancake/generators/flat_generator.rb +17 -0
  18. data/lib/pancake/generators/short_generator.rb +17 -0
  19. data/lib/pancake/generators/stack_generator.rb +17 -0
  20. data/lib/pancake/generators/templates/common/dotgitignore +22 -0
  21. data/lib/pancake/generators/templates/common/dothtaccess +17 -0
  22. data/lib/pancake/generators/templates/flat/%stack_name%/%stack_name%.rb.tt +8 -0
  23. data/lib/pancake/generators/templates/flat/%stack_name%/.gitignore +21 -0
  24. data/lib/pancake/generators/templates/flat/%stack_name%/config.ru.tt +12 -0
  25. data/lib/pancake/generators/templates/flat/%stack_name%/pancake.init.tt +1 -0
  26. data/lib/pancake/generators/templates/flat/%stack_name%/public/.empty_directory +0 -0
  27. data/lib/pancake/generators/templates/flat/%stack_name%/tmp/.empty_directory +0 -0
  28. data/lib/pancake/generators/templates/short/%stack_name%/.gitignore +21 -0
  29. data/lib/pancake/generators/templates/short/%stack_name%/LICENSE.tt +20 -0
  30. data/lib/pancake/generators/templates/short/%stack_name%/README.tt +7 -0
  31. data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +48 -0
  32. data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +1 -0
  33. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +5 -0
  34. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +6 -0
  35. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
  36. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
  37. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  38. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  39. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  40. data/lib/pancake/generators/templates/short/%stack_name%/pancake.init.tt +1 -0
  41. data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  42. data/lib/pancake/generators/templates/short/%stack_name%/spec/spec_helper.rb.tt +9 -0
  43. data/lib/pancake/generators/templates/stack/%stack_name%/.gitignore +21 -0
  44. data/lib/pancake/generators/templates/stack/%stack_name%/LICENSE.tt +20 -0
  45. data/lib/pancake/generators/templates/stack/%stack_name%/README.tt +7 -0
  46. data/lib/pancake/generators/templates/stack/%stack_name%/Rakefile.tt +48 -0
  47. data/lib/pancake/generators/templates/stack/%stack_name%/VERSION.tt +1 -0
  48. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%.rb.tt +3 -0
  49. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config.ru.tt +12 -0
  50. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +18 -0
  51. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +18 -0
  52. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config/router.rb.tt +6 -0
  53. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/gems/cache/.empty_directory +0 -0
  54. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  55. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  56. data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  57. data/lib/pancake/generators/templates/stack/%stack_name%/pancake.init.tt +1 -0
  58. data/lib/pancake/generators/templates/stack/%stack_name%/spec/%stack_name%_spec.rb.tt +7 -0
  59. data/lib/pancake/generators/templates/stack/%stack_name%/spec/spec_helper.rb.tt +9 -0
  60. data/lib/pancake/hooks/inheritable_inner_classes.rb +60 -0
  61. data/lib/pancake/hooks/on_inherit.rb +34 -0
  62. data/lib/pancake/master.rb +87 -0
  63. data/lib/pancake/middleware.rb +354 -0
  64. data/lib/pancake/mime_types.rb +265 -0
  65. data/lib/pancake/mixins/publish.rb +125 -0
  66. data/lib/pancake/mixins/publish/action_options.rb +104 -0
  67. data/lib/pancake/mixins/render.rb +61 -0
  68. data/lib/pancake/mixins/request_helper.rb +92 -0
  69. data/lib/pancake/mixins/stack_helper.rb +44 -0
  70. data/lib/pancake/mixins/url.rb +10 -0
  71. data/lib/pancake/more/controller.rb +4 -0
  72. data/lib/pancake/more/controller/base.rb +34 -0
  73. data/lib/pancake/paths.rb +218 -0
  74. data/lib/pancake/router.rb +99 -0
  75. data/lib/pancake/stack/app.rb +10 -0
  76. data/lib/pancake/stack/bootloader.rb +79 -0
  77. data/lib/pancake/stack/configuration.rb +44 -0
  78. data/lib/pancake/stack/middleware.rb +0 -0
  79. data/lib/pancake/stack/router.rb +18 -0
  80. data/lib/pancake/stack/stack.rb +57 -0
  81. data/lib/pancake/stacks/short.rb +2 -0
  82. data/lib/pancake/stacks/short/controller.rb +105 -0
  83. data/lib/pancake/stacks/short/stack.rb +194 -0
  84. data/spec/helpers/helpers.rb +20 -0
  85. data/spec/helpers/matchers.rb +25 -0
  86. data/spec/pancake/bootloaders_spec.rb +109 -0
  87. data/spec/pancake/configuration_spec.rb +177 -0
  88. data/spec/pancake/fixtures/foo_stack/pancake.init +0 -0
  89. data/spec/pancake/fixtures/paths/controllers/controller1.rb +0 -0
  90. data/spec/pancake/fixtures/paths/controllers/controller2.rb +0 -0
  91. data/spec/pancake/fixtures/paths/controllers/controller3.rb +0 -0
  92. data/spec/pancake/fixtures/paths/models/model1.rb +0 -0
  93. data/spec/pancake/fixtures/paths/models/model2.rb +0 -0
  94. data/spec/pancake/fixtures/paths/models/model3.rb +0 -0
  95. data/spec/pancake/fixtures/paths/stack/controllers/controller1.rb +0 -0
  96. data/spec/pancake/fixtures/paths/stack/models/model3.rb +0 -0
  97. data/spec/pancake/fixtures/paths/stack/views/view1.erb +0 -0
  98. data/spec/pancake/fixtures/paths/stack/views/view1.rb +0 -0
  99. data/spec/pancake/fixtures/paths/stack/views/view2.erb +0 -0
  100. data/spec/pancake/fixtures/paths/stack/views/view2.haml +0 -0
  101. data/spec/pancake/fixtures/render_templates/context_template.html.erb +1 -0
  102. data/spec/pancake/fixtures/render_templates/erb_template.html.erb +1 -0
  103. data/spec/pancake/fixtures/render_templates/erb_template.json.erb +1 -0
  104. data/spec/pancake/fixtures/render_templates/haml_template.html.haml +1 -0
  105. data/spec/pancake/fixtures/render_templates/haml_template.xml.haml +1 -0
  106. data/spec/pancake/hooks/on_inherit_spec.rb +65 -0
  107. data/spec/pancake/inheritance_spec.rb +100 -0
  108. data/spec/pancake/middleware_spec.rb +401 -0
  109. data/spec/pancake/mime_types_spec.rb +234 -0
  110. data/spec/pancake/mixins/publish_spec.rb +94 -0
  111. data/spec/pancake/mixins/render_spec.rb +55 -0
  112. data/spec/pancake/mixins/stack_helper_spec.rb +46 -0
  113. data/spec/pancake/pancake_spec.rb +31 -0
  114. data/spec/pancake/paths_spec.rb +210 -0
  115. data/spec/pancake/stack/app_spec.rb +28 -0
  116. data/spec/pancake/stack/bootloader_spec.rb +41 -0
  117. data/spec/pancake/stack/middleware_spec.rb +0 -0
  118. data/spec/pancake/stack/router_spec.rb +266 -0
  119. data/spec/pancake/stack/stack_configuration_spec.rb +101 -0
  120. data/spec/pancake/stack/stack_spec.rb +55 -0
  121. data/spec/pancake/stacks/short/controller_spec.rb +287 -0
  122. data/spec/pancake/stacks/short/router_spec.rb +132 -0
  123. data/spec/pancake/stacks/short/stack_spec.rb +40 -0
  124. data/spec/spec_helper.rb +21 -0
  125. metadata +238 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Daniel Neighman
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 to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included 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
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,95 @@
1
+ __This read me is getting thrown to get there in little bits of time snatched between other bits of snatched time... it will be magnificient, eventually... please bare with it...__
2
+
3
+ h3. Whats this then
4
+
5
+ A tool for making pancake stacks. Where, as you know, pancake == ruby web thing.
6
+
7
+ It essentially takes the awesome "rack":rack.github.com and "rack router":http://github.com/carllerche/rack-router/ and mixes it with flour and water and eggs to bind it together into a pancake thin layer of goodness providing a structured way of orgnaising (stacking) your assortment of ruby web things. In doing so, it allows you to provide each of your ruby web things (lets call them "middlewares") with some underlying contextual functionality.
8
+
9
+ h3. Why stack pancakes?
10
+
11
+ Well, simply put, reuse! I'm building something... and what do you know i need another forum/asset manager/bla thing etc. Typically I can:
12
+
13
+ * Write/include the functionality directly in to my current app.
14
+ * Add a second app, theme it up similarly and use apache/nginx/other configs to glue it up, and perhaps come up with a devious approach for auth with some cunning SSO... or not
15
+
16
+ What we'd like to facilitate with pancake is you take your existing much loved forum/asset manager/bla thing etc and mount it into pancake, sharing auth and other contextual information between apps.
17
+
18
+ Insert helpful diagram here...
19
+
20
+ h3. Getting Started
21
+
22
+ h4. You'll need to have installed (as well as your usual ruby stuffs)...
23
+
24
+ * wycats-thor
25
+ * Carllerches rack router
26
+
27
+ h4. Installing
28
+
29
+ <pre>
30
+ $sudo gem install hassox-pancake
31
+ </pre>
32
+
33
+ h4. Create your own stack
34
+
35
+ <pre>
36
+ $pancake-gen stack yummy-app
37
+ </pre>
38
+
39
+ h4. Does it work?
40
+
41
+ (I've used curl here, feel free to use your point your browser to http://localhost:9292/ instead)
42
+ <pre>
43
+ $cd yummy-app/lib/yummy-app
44
+ $rackup config.ru
45
+ Loading Development Environment
46
+ </pre>
47
+
48
+ Now in your browser, visit http://localhost:9292/
49
+ Or, with curl like here:
50
+ <pre>
51
+ $curl -I localhost:9292
52
+ 127.0.0.1 - - [26/Jul/2009 15:42:55] "HEAD / HTTP/1.1" 404 9 0.0011
53
+ HTTP/1.1 404 Not Found
54
+ Connection: close
55
+ Date: Sun, 26 Jul 2009 05:42:55 GMT
56
+ Content-Type: text/plain
57
+ Content-Length: 9
58
+ </pre>
59
+
60
+ You should see 'NOT FOUND' in your browser window and something like this appear in your rackup console window.
61
+
62
+ <pre>
63
+ 127.0.0.1 - - [26/Jul/2009 15:42:55] "GET / HTTP/1.1" 404 9 0.0011
64
+ </pre>
65
+
66
+ That 404 is good news! The stack is running.. we just haven't cooked any pancakes yet, theres nothing to find. Next step add something...
67
+
68
+ h4. Routing
69
+
70
+ Edit config/router.rb with:
71
+ <pre>
72
+ home = lambda {|e| [200,{"Content-Type" => "text/plain", "Content-Length" => "12"}, "Welcome home" ]}
73
+
74
+ YummyApp.add_routes do |r|
75
+ r.map "/", :to => home
76
+ end
77
+ </pre>
78
+ Now, restart the server (^C) and try again:
79
+ <pre>
80
+ $rackup config.ru
81
+ Loading Development Environment
82
+ </pre>
83
+ Reload you page and you should see "Welcome home".
84
+ And, in your console window, rack should log the request like so:
85
+ <pre>
86
+ 127.0.0.1 - - [26/Jul/2009 16:16:23] "GET / HTTP/1.1" 200 12 0.0012
87
+ 127.0.0.1 - - [26/Jul/2009 16:16:23] "GET /favicon.ico HTTP/1.1" 200 12 0.0012
88
+ </pre>
89
+
90
+ h4. Whats next:
91
+
92
+ Well we don't want to be writing our apps as lambdas jemmied into the router, instead next we start to build stacks, out of other middlewares... merbs, other pancake stacks, some sinatra, more rack ups or what ever.
93
+
94
+ To be continued...
95
+
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "pancake"
8
+ gem.summary = %Q{Eat Pancake Stacks for Breakfast}
9
+ gem.description = %Q{Eat Pancake Stacks for Breakfast}
10
+ gem.email = "has.sox@gmail.com"
11
+ gem.homepage = "http://github.com/hassox/pancake"
12
+ gem.authors = ["Daniel Neighman"]
13
+ gem.add_development_dependency "rspec"
14
+ gem.add_dependency "usher", ">=0.5.5"
15
+ gem.add_dependency "mynyml-rack-accept-media-types"
16
+ gem.require_path = 'lib'
17
+ gem.autorequire = 'pancake'
18
+ gem.bindir = "bin"
19
+ gem.executables = %w( pancake-gen )
20
+ gem.files = %w(LICENSE README.textile Rakefile TODO) + Dir.glob("{lib,spec,bin}/**/{*,.[a-z]*}")
21
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
+ end
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_opts = %w(--format progress --color)
31
+ spec.spec_files = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
35
+ spec.libs << 'lib' << 'spec'
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :spec => :check_dependencies
41
+
42
+ task :default => :spec
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "pancake #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/TODO ADDED
@@ -0,0 +1,18 @@
1
+ TODO:
2
+
3
+ Controller Publish Declaration:
4
+ Allow requirements to be declared for multiple controllers at once
5
+ Support syntax for coercing multiple values into a single param entry i.e. two date strings into a range
6
+ Default values should be generated with a method rather than a static value alone
7
+ The list of params should be able to be flagged as exclusive i.e. no additional parameters may be passed in
8
+ Handle nested params
9
+ Collect options suitable for a OPTIONS request from a client
10
+ Mechanism to whitelist fields from a model
11
+
12
+ Renderable Mixin
13
+ Design interface for adapters -- adapters allow support for different template libs
14
+ Compiled templates need to be cached
15
+ Options used by the mixin need to be configurable
16
+ Templates should be inheritable
17
+ The target class should be able to inject arbitrary helpers into the template context
18
+ Layouts, views and partials should be supported
@@ -0,0 +1,19 @@
1
+ #!/opt/local/bin/ruby
2
+ #
3
+ # This file was generated by RubyGems.
4
+ #
5
+ # The application 'jeweler' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'rubygems'
10
+
11
+ version = ">= 0"
12
+
13
+ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
14
+ version = $1
15
+ ARGV.shift
16
+ end
17
+
18
+ gem 'jeweler', version
19
+ load Gem.bin_path('jeweler', 'jeweler', version)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'thor'
4
+ require 'thor/runner'
5
+ require 'pancake/generators'
6
+
7
+ # TODO update for pancake to have a version
8
+ case ARGV.shift
9
+ when "stack"
10
+ Pancake::Generators::Stack.start
11
+ when "short"
12
+ Pancake::Generators::Short.start
13
+ when "flat"
14
+ Pancake::Generators::Flat.start
15
+ else
16
+ puts "Unknown Generator"
17
+ end
@@ -0,0 +1,19 @@
1
+ #!/opt/local/bin/ruby
2
+ #
3
+ # This file was generated by RubyGems.
4
+ #
5
+ # The application 'rubyforge' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'rubygems'
10
+
11
+ version = ">= 0"
12
+
13
+ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
14
+ version = $1
15
+ ARGV.shift
16
+ end
17
+
18
+ gem 'rubyforge', version
19
+ load Gem.bin_path('rubyforge', 'rubyforge', version)
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'extlib/class'
3
+ require 'extlib/boolean'
4
+ require 'extlib/module'
5
+ require 'extlib/nil'
6
+ require 'extlib/numeric'
7
+ require 'extlib/object'
8
+ require 'extlib/symbol'
9
+ require 'extlib/blank'
10
+ require "extlib/dictionary"
11
+ require 'usher'
12
+ require 'usher/interface/rack_interface'
13
+ require 'tilt'
14
+
15
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
16
+
17
+ require 'pancake/paths'
18
+ require 'pancake/hooks/on_inherit'
19
+ require 'pancake/hooks/inheritable_inner_classes'
20
+ require 'pancake/core_ext/class'
21
+ require 'pancake/core_ext/object'
22
+ require 'pancake/core_ext/symbol'
23
+ require 'pancake/configuration'
24
+ require 'pancake/bootloaders'
25
+ require 'pancake/mime_types'
26
+ require 'pancake/middleware'
27
+ require 'pancake/router'
28
+ require 'pancake/master'
29
+ require 'pancake/errors'
30
+ require 'pancake/stack/stack'
31
+ require 'pancake/stack/configuration'
32
+ require 'pancake/stack/router'
33
+ require 'pancake/stack/bootloader'
34
+ require 'pancake/stack/app'
35
+ require 'pancake/mixins/request_helper'
36
+
37
+ module Pancake
38
+
39
+ module Stacks
40
+ autoload :Short, "pancake/stacks/short"
41
+ end
42
+
43
+ module Mixins
44
+ autoload :Publish, "pancake/mixins/publish"
45
+ autoload :Render, "pancake/mixins/render"
46
+ autoload :StackHelper, "pancake/mixins/stack_helper"
47
+ end
48
+ end
@@ -0,0 +1,180 @@
1
+ require 'set'
2
+ module Pancake
3
+ module BootLoaderMixin
4
+ include Enumerable
5
+ class Base
6
+ # :api: :public
7
+ attr_accessor :config
8
+
9
+ # Sets options for the bootloder
10
+ # By including conditions in the bootloader when you declare it
11
+ # You can selectively run bootloaders later
12
+ # :api: private
13
+ def self.options=(opts={}) # :nodoc:
14
+ @options = opts
15
+ @options[:level] ||= :default
16
+ end
17
+
18
+ # Provides access to the bootloader options
19
+ # :api: private
20
+ def self.options # :nodoc:
21
+ @options ||= {}
22
+ end
23
+
24
+ def stack
25
+ raise "No Stack Configured" unless @config[:stack]
26
+ @config[:stack]
27
+ end
28
+
29
+ def stack_class
30
+ raise "No Stack Class Configured" unless @config[:stack_class]
31
+ @config[:stack_class]
32
+ end
33
+
34
+ def initialize(config)
35
+ @config = config
36
+ end
37
+
38
+ # Creates a new instance and runs it
39
+ # :api: private
40
+ def self.call(config)
41
+ new(config).run!
42
+ end
43
+
44
+ # Checks the conditions with the options of the bootloader
45
+ # To see if this one should be run
46
+ # Only the central bootloaders with the conditions will be checked
47
+ # :api: private
48
+ def self.run?(conditions = {})
49
+ opts = options
50
+ if conditions.keys.include?(:only)
51
+ return conditions[:only].all?{|k,v| opts[k] == v}
52
+ end
53
+ if conditions.keys.include?(:except)
54
+ return conditions[:except].all?{|k,v| opts[k] != v}
55
+ end
56
+ true
57
+ end
58
+ end
59
+
60
+ def self.extended(base)
61
+ base.class_eval do
62
+ class_inheritable_reader :_bootloaders, :_central_bootloaders, :_bootloader_map
63
+ @_bootloaders, @_central_bootloaders = {}, []
64
+ @_bootloader_map = Hash.new{|h,k| h[k] = {:before => [], :after => []}}
65
+ end
66
+ end
67
+
68
+ # Provides access to an individual bootloader
69
+ # :api: public
70
+ def [](name)
71
+ _bootloaders[name]
72
+ end
73
+
74
+ # Add a bootloader. Inside the block we're inside a class definition.
75
+ # Requirements: define a +run!+ method
76
+ #
77
+ # Example
78
+ # FooStack::BootLoader.add(:foo) do
79
+ # def run!
80
+ # # stuff
81
+ # end
82
+ # end
83
+ #
84
+ # :api: public
85
+ def add(name, opts = {}, &block)
86
+ _bootloaders[name] = Class.new(Pancake::BootLoaderMixin::Base, &block)
87
+ raise "You must declare a #run! method on your bootloader" unless _bootloaders[name].method_defined?(:run!)
88
+ before = opts[:before]
89
+ after = opts[:after]
90
+
91
+ if opts[:level]
92
+ levels << opts[:level]
93
+ levels.uniq!
94
+ end
95
+
96
+ # If there are no before or after keys, add it to the central bootloaders
97
+ if before
98
+ _bootloader_map[before][:before] << name
99
+ elsif after
100
+ _bootloader_map[after][:after] << name
101
+ else
102
+ _central_bootloaders << name unless _central_bootloaders.include?(name)
103
+ end
104
+ _bootloaders[name].options = opts
105
+ _bootloaders[name]
106
+ end
107
+
108
+ # Runs the bootloaders in order
109
+ # :api: private
110
+ def run!(options = {}) # :nodoc:
111
+ unless options.keys.include?(:only) || options.keys.include?(:except)
112
+ options[:only] = {:level => :default}
113
+ end
114
+ conditions = if options[:only]
115
+ {:only => options.delete(:only)}
116
+ else
117
+ {:except => options.delete(:except)}
118
+ end
119
+ options[:stack_class] ||= stack
120
+
121
+ each(conditions) do |name, bl|
122
+ bl.call(options)
123
+ end
124
+ end
125
+
126
+ # Set the stack that this bootloader is responsible for.
127
+ # :api: private
128
+ def stack=(stack) # :nodoc:
129
+ @stack = stack
130
+ end
131
+
132
+ # Access to the stack that this bootloader is responsible for
133
+ # :api: public
134
+ def stack
135
+ @stack ||= Object.full_const_get(self.name.split("::")[0..-2].join("::"))
136
+ end
137
+
138
+ # Resets the bootloaders on the stack
139
+ # :api: public
140
+ def reset!
141
+ _central_bootloaders.clear
142
+ _bootloaders.clear
143
+ _bootloader_map.clear
144
+ end
145
+
146
+ # Yields each bootloader in order along with it's name
147
+ #
148
+ # Example
149
+ # FooStack::BootLoader.each do |name, bootloader|
150
+ # # do stuff
151
+ # end
152
+ #
153
+ # :api: public
154
+ def each(conditions = {})
155
+ _map_bootloaders(_central_bootloaders, conditions).each do |n|
156
+ yield n, _bootloaders[n]
157
+ end
158
+ end
159
+
160
+ private
161
+ # Map out the bootloaders by name to run.
162
+ # :api: private
163
+ def _map_bootloaders(*names)
164
+ conditions = Hash === names.last ? names.pop : {}
165
+ names.flatten.map do |name|
166
+ if _bootloaders[name].run?(conditions)
167
+ r = []
168
+ r << _map_bootloaders(_bootloader_map[name][:before])
169
+ r << name
170
+ r << _map_bootloaders(_bootloader_map[name][:after])
171
+ end
172
+ end.flatten.compact
173
+ end
174
+
175
+ def levels
176
+ @levels ||= [:default]
177
+ end
178
+
179
+ end # BootLoaders
180
+ end # Pancake