pancake 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,95 +1,284 @@
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...__
1
+ h1. Pancake
2
2
 
3
- h3. Whats this then
3
+ Pancake is primarily a tool for making rack applictions. "Rack":http://rack.github.com has come up in the Ruby web world as _the framework that matters_ when developing web applications. All the major frameworks use it, although so many application frameworks and middlewares are not really re-usable yet away from their specific implementations.
4
4
 
5
- A tool for making pancake stacks. Where, as you know, pancake == ruby web thing.
5
+ Pancake addresses this by making Rack the fundamental building block of an applicaiton. It provides very useful helpers on top of rack that assist in constructing rack stacks as mixins. Almost all aspects of web frameworks are covered as Pancake mixins to help you create your own re-usable Rack Stacks without worrying about the really low level plumbing.
6
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.
7
+ h2. Stacks
8
8
 
9
- h3. Why stack pancakes?
9
+ While Rack provides the framework for building web applications on. Pancake provides a stack as a place to start your application. A stack provides a whole bunch of behavior for you including a router and middleware stack. The stack will accept any valid rack application as the endpoint.
10
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:
11
+ The general form of a stack is as follows:
12
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
13
+ <pre>
14
+ -----------------------------------
15
+ | |
16
+ | Router |
17
+ | |
18
+ -----------------------------------
19
+ | Middleware |
20
+ -----------------------------------
21
+ | Middleware |
22
+ -----------------------------------
23
+ | Middleware |
24
+ -----------------------------------
25
+ | |
26
+ | Application |
27
+ | Endpoint |
28
+ | |
29
+ -----------------------------------
30
+ </pre>
15
31
 
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.
32
+ Stacks are designed to work together. It's trivially easy to mount one stack inside another, or just mount any valid rack application inside your stack. They're also designed to be gemed and used as libraries as full applications.
17
33
 
18
- Insert helpful diagram here...
34
+ <pre><code>MyStack.router do |r|
35
+ r.mount(OtherStack, "/stack/mount/point")
36
+ end</code></pre>
19
37
 
20
- h3. Getting Started
38
+ A stack doesn't have to mount other stacks. Pancake stacks are full applications in their own right and can be used standalone as Rack endpoints, or as middleware.
21
39
 
22
- h4. You'll need to have installed (as well as your usual ruby stuffs)...
40
+ Rails 3 is shipping with a pluggable router which can be the awesome "Usher":http://github.com/joshbuddy/usher router. If you use Usher in your Rails app, you'll be able to mount Pancake stacks directly. For now, you can mount them as Metals.
23
41
 
24
- * wycats-thor
25
- * Carllerches rack router
42
+ All stacks are namespaced. Pancake makes heavy use of namespacing to help construct applications concisely.
26
43
 
27
- h4. Installing
44
+ h2. Middleware Stacks
28
45
 
29
- <pre>
30
- $sudo gem install hassox-pancake
31
- </pre>
46
+ When you use Pancake middleware management, you can name middleware, set middleware to come before or after other middleware, or be included or not based on a stack type label. Lets have a look at that.
32
47
 
33
- h4. Create your own stack
48
+ <pre><code>class MyStack < Pancake::Stack
49
+ stack(:session).use(Rack::Sessions::Cookie)
50
+ stack(:auth, :after => :session).use(Warden::Manager, #...)
34
51
 
35
- <pre>
36
- $pancake-gen stack yummy-app
37
- </pre>
52
+ stack(:bugz, :labels => [:development]).use(Rack::Bug)
38
53
 
39
- h4. Does it work?
54
+ use(AlwaysUse)
55
+ end
56
+ </code></pre>
40
57
 
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>
58
+ h2. What can a stack do
47
59
 
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>
60
+ Pancake provides a whole bunch of functionality as mixins.
59
61
 
60
- You should see 'NOT FOUND' in your browser window and something like this appear in your rackup console window.
62
+ * BootLoaders
63
+ * Router ("Usher":http://github.com/joshbuddy/usher Based. Please read.)
64
+ * Sophisticated Middlware Configuration
65
+ * Stack/Custom Configuration
66
+ * Path Management with multiple roots (makes working with gem stacks transparent)
67
+ * Content Type negotiation
68
+ * Inheritable Templating System
69
+ * Action Publish Control
61
70
 
62
- <pre>
63
- 127.0.0.1 - - [26/Jul/2009 15:42:55] "GET / HTTP/1.1" 404 9 0.0011
64
- </pre>
71
+ Stacks bundle these behaviors together into a cohesive unit.
65
72
 
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...
73
+ The true power of a Pancake stack is that it is fully inheritable.
67
74
 
68
- h4. Routing
75
+ h2. Stack Inheritance
69
76
 
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
77
+ When you inherit a stack, you're really inheriting a full applictation. All the features above are inherited along with the stack class.
78
+
79
+ The easiest way to explain stack inheritance is perhaps with an example.
80
+
81
+ Lets take the example of a basic application skeleton. Many times I've seen github repos with pre-generated applications with some basic functionality. This is then cloned as a start point for further app development. It's basically the same as a generator but a little more flexible. What if rather than generate an application skeleton with the basic functionality, you could construct a stack complete with router, default configuration, base templates, css and javascript, even mounted applications, and gem it up. Then when you want to start an application you just require the gem and inherit the stack class. This is what Pancake stacks provide.
82
+
83
+ <pre><code>class MyStack < SomeOtherStack; end # A full application </code></pre>
84
+
85
+ h3. Namespacing
86
+
87
+ Pancake makes heavy use of namespacing. Namespacing is a good idea to begin with, but inside mounted applications it's a must. By embracing the constraint of namespacing, there are many benefits. Unexpected name clashes of course being the big one.
88
+
89
+ h3. Inheritable Inner Classes
90
+
91
+ There a fairly unique issue with inheriting a full application. Model data clashes.
92
+
93
+ Normally when you inherit a class in Ruby, when you reference an inner class in the child, it's actually a reference to the inner class in the parent. Lets see why this is a problem.
94
+
95
+ <pre><code>class AStack < Pancake::Stack
96
+ class Post < ActiveRecord::Base
97
+ # post code
76
98
  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>
99
+ end
100
+
101
+ class AnotherStack < AStack; end
102
+ class ADifferentOne < AStack; end
103
+
104
+ AnotherStack::Post == ADifferentOne::Post == AStack::Post
105
+ </code></pre>
106
+
107
+ When we create posts in all 3 stacks, and then select the posts in AStack::Post.all we'll actually get the posts from all of the stacks. This referencing of inner classes is great for ruby efficiency, but not so good when we want to keep data seperate.
108
+
109
+ Awesomely some of the ORM's include STI (Single Table Inheritance) that we can make use of. When using Pancake, we can set an inner class to inherit along with the parent. Lets have another look at that example:
110
+
111
+ <pre><code>class AStack < Pancake::Stack
112
+ inheritable_inner_classes :Post
113
+
114
+ class Post < ActiveRecord::Base
115
+ # post code
116
+ end
117
+ end
118
+
119
+ class AnotherStack < AStack; end
120
+ class ADifferentOne < AStack; end
121
+
122
+ class FurtherDown < AnotherStack; end
123
+
124
+ # Results in
125
+
126
+ class AnotherStack::Post < AStack::Post; end
127
+ class ADifferentOne::Post < AStack::Post; end
128
+ class FurtherDown::Post < AnotherStack::Post; end
129
+ </code></pre>
130
+
131
+ In this case STI will be activated and each stack will use it's own sti version of the Post model, segregating the data between the stacks.
132
+
133
+ If your Data layer doesn't support STI there are on_inherit hooks for all classes provided by Pancake to make any changes you need to.
134
+
135
+ h2. Mounting Applications
136
+
137
+ You can mount any valid rack application inside pancake.
138
+
139
+ Stacks look inside their root(s) for a "mounts" directory. Where they in-turn look for a sub-directory containing the file "pancake.init". When you want to mount an application you just include that file (and any support files) and the pancake stack will load it.
140
+
141
+ <pre><code>my_stack
142
+ -- mounts
143
+ -- another_app
144
+ -- pancake.init # Called to initialize the mounted app
145
+ </code></pre>
146
+
147
+ h2. Inheritable Templates
148
+
149
+ Templates in Pancake are similar to Django templates. They define named content_blocks that can supply a default, but that can also be inherited from, and have their content either combined with some other content, or replaced. Unlike Django, templates are built on known templating languages like ERB and Haml. The templating system in Pancake is built on top of the great "Tilt":http://github.com/rtomayko/tilt project.
150
+
151
+ Really this is best demonstrated with an example:
152
+
153
+ <pre><code># base.html.erb
154
+ <html>
155
+ <head><title>Hey There</title></head>
156
+ <body>
157
+ <h1>My Page</h1>
158
+
159
+ <ul class='nav'>
160
+ <% content_block :navigation do -%>
161
+ <li><a href="/">Home</a></li>
162
+ <% end -%>
163
+ </ul>
164
+
165
+ <hr/>
166
+
167
+ <% content_block :content do- %>
168
+ <div class='emtpy'>Nothing Found</div>
169
+ <% end -%>
170
+
171
+ <% content_block :footer do -%>
172
+ Footer Text
173
+ <% end -%>
174
+ </html>
175
+
176
+ </code></pre>
177
+ <pre><code># index.html.erb
178
+
179
+ <% inherits_from :base %>
180
+
181
+ <% content_block :navigation do -%>
182
+ <%= content_block.super %>
183
+ <li><a href="/index">Home</a></li>
184
+ <% end -%>
185
+
186
+ <% content_block :content do -%>
187
+ # Stuff for the content
188
+ <% end -%>
189
+ </code></pre>
190
+
191
+ When you @render :index@ in your stack, the index template will be activated, rendering the :base template but replacing the content_block, :content, with new data. And appending to the :navigation block.
192
+
193
+ Templates can be inherited as many times as required, so you could inherit from the :index template to build on it's content.
194
+
195
+ When specifying which template to inherit from, you can dynamically specify which template you'd like to use if it makes sense to do so.
196
+
197
+ At the moment only ERB, Erubis and Haml are supported with inheritable templates. Other tilt templates are able to be rendered, but not inherited.
198
+
199
+ h2. Short Stack
200
+
201
+ As part of the development of Pancake, the initial milestone was to develop a stack based on the awesome "Sinatra":http://sinatrarb.com framework. This was done as a way to develop Pancake to a simple level, not as a way to replace Sinatra. Pancake is very happy to mount Sinatra applications inside and make use of them.
202
+
203
+ A short stack is a resource focused. It uses the HTTP verbs and routes to access the code that defines a "resource"
204
+
205
+ Short stacks make use of content negotiation and views in addition to the normal stack behavior.
206
+
207
+ h3. Get Started
208
+
209
+ h4. Install
210
+
211
+ @$ gem install pancake@
212
+
213
+ h4. Generate your stack
214
+
215
+ To start with a short stack use pancakes built in generator:
216
+
217
+ @$ pancake-gen short my_stack@
218
+
219
+ This will generate the basic stack for you. It's generated as a gem and based on Jeweler so you can gem it up super easy.
220
+
221
+ <pre><code>class MyStack < Pancake::Stacks::Short
222
+ add_root(__FILE__)
223
+
224
+ router.mount(UserStack, "/users")
225
+
226
+ provides :html, :xml, :json
227
+
228
+ get "(/)", :_name => :home do
229
+ v[:some_models] = SomeModel.all
230
+ render :welcome
231
+ end
232
+
233
+ get "/new", :_name => :new do
234
+ v[:some_model] = SomeModel.new(params[:some_model])
235
+ render :new
236
+ end
237
+
238
+ post "(/)" do
239
+ v[:some_model] = SomeModel.new(params[:some_model])
240
+ if v[:some_model].save
241
+ redirect url(:home)
242
+ else
243
+ render :new
244
+ end
245
+ end
246
+ end
247
+ </code></pre>
248
+
249
+ h4. The Vault
250
+
251
+ In the example above you can see the "v" method. This method accesses the vault. The vault is where per-request data may be stored for later use. In this case, the data is stored so that we can access it in the views.
252
+
253
+ This is a way to seperate the concerns of Controller and View. These contexts are supposed to be seperate. Breaking the encapsulation of instance variables does not need to be done in a short stack.
254
+
255
+ h4. Templates
256
+
257
+ Templates are searched for in (stack root)/views/<name>.<format>.<template_engine> and rendered.
258
+
259
+ h2. URL generation
260
+
261
+ When you're in a stack, you can call @url(named_url) to generate a url. This will generate the url, taking into account the mount path for the stack.
262
+
263
+ You can generate urls for other stacks also by using the global Pancake.url helper.
264
+
265
+ <pre><code>Pancake.url(SomeStack, :named_url)</code></pre>
266
+
267
+ There's a whole bunch more to Pancake than what I can go over here. But hopefully I've been able to provide a taste of what it is and why I think it's pretty awesome in the up-coming Rack landscape.
268
+
269
+ h4. Note on Patches/Pull Requests
270
+
271
+ * Fork the project.
272
+ * Make your feature addition or bug fix.
273
+ * Add tests for it. This is important so I don't break it in a
274
+ future version unintentionally.
275
+ * Commit, do not mess with rakefile, version, or history.
276
+ (if you want to have your own version, that is fine but
277
+ bump version in a commit by itself I can ignore when I pull)
278
+ * h4. me a pull request. Bonus points for topic branches.
89
279
 
90
- h4. Whats next:
280
+ h4. Copyright
91
281
 
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.
282
+ Copyright (c) 2009 Daniel Neighman. See LICENSE for details.
93
283
 
94
- To be continued...
95
284
 
@@ -4,9 +4,9 @@ require 'pancake'
4
4
  require ::File.join(::File.expand_path(::File.dirname(__FILE__)), "<%= stack_name %>")
5
5
 
6
6
  # get the application to run. The applicadtion in the Pancake.start block
7
- # is the master application. It will have all requests directed to it through the
7
+ # is the master application. It will have all requests directed to it through the
8
8
  # pancake middleware
9
9
  # This should be a very minimal file, but should be used when any stand alone code needs to be included
10
- app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup }
10
+ app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup(:master => true) }
11
11
 
12
- run app
12
+ run app
@@ -18,6 +18,9 @@ rescue LoadError
18
18
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
19
  end
20
20
 
21
+ require File.join(File.dirname(__FILE__), "lib", "<%= stack_name %>")
22
+ <%= stack_name.camel_case %>.load_rake_tasks!
23
+
21
24
  require 'spec/rake/spectask'
22
25
  Spec::Rake::SpecTask.new(:spec) do |spec|
23
26
  spec.libs << 'lib' << 'spec'
@@ -5,6 +5,6 @@ require ::File.join(::File.expand_path(::File.dirname(__FILE__)), "..", "<%= sta
5
5
  # is the master application. It will have all requests directed to it through the
6
6
  # pancake middleware
7
7
  # This should be a very minimal file, but should be used when any stand alone code needs to be included
8
- app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup }
8
+ app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup(:master => true) }
9
9
 
10
10
  run app
@@ -18,6 +18,9 @@ rescue LoadError
18
18
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
19
  end
20
20
 
21
+ require File.join(File.dirname(__FILE__), "lib", "<%= stack_name %>")
22
+ <%= stack_name.camel_case %>.load_rake_tasks!
23
+
21
24
  require 'spec/rake/spectask'
22
25
  Spec::Rake::SpecTask.new(:spec) do |spec|
23
26
  spec.libs << 'lib' << 'spec'
@@ -7,6 +7,6 @@ require Pancake.get_root(__FILE__, "<%= stack_name %>")
7
7
  # is the master application. It will have all requests directed to it through the
8
8
  # pancake middleware
9
9
  # This should be a very minimal file, but should be used when any stand alone code needs to be included
10
- app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup }
10
+ app = Pancake.start(:root => Pancake.get_root(__FILE__)){ <%= stack_name.camel_case %>.stackup(:master => true) }
11
11
 
12
12
  run app
@@ -40,11 +40,11 @@ module Pancake
40
40
  _concat_methods[ Tilt::ErubisTemplate ] = :_erb_concat
41
41
 
42
42
  module Renderer
43
- def render(template, opts = {})
43
+ def render(template, opts = {}, &blk)
44
44
  template = _view_context_for.template(template)
45
45
  raise TemplateNotFound unless template
46
46
  result = _with_renderer template do
47
- _current_renderer.render(self, opts)
47
+ _current_renderer.render(self, opts, &blk) # only include the block once
48
48
  end
49
49
 
50
50
  if @_inherit_helper.inherits_from
@@ -74,6 +74,8 @@ module Pancake
74
74
  # Get the view context for the tempalte
75
75
  template, vc_class = self.class._renderer_and_view_context_class_for(template_name)
76
76
 
77
+ yield v if block_given?
78
+
77
79
  view_context = vc_class.new(env, self)
78
80
  view_context_before_render(view_context)
79
81
  view_context.render(template, opts)
@@ -9,12 +9,14 @@ module Pancake
9
9
  # Stores the data in session for the length of the request.
10
10
  #
11
11
  # @example
12
- # v[:user] = @user
12
+ # vault[:user] = @user
13
+ # v[:user] == vault[:user]
13
14
  # # This is now stored in the environment and is available later
14
- def v
15
+ def vault
15
16
  env[VAULT_KEY] ||= {}
16
17
  env[VAULT_KEY]
17
18
  end
19
+ alias_method :v, :vault
18
20
 
19
21
  # Generate a url for the current stacks router.
20
22
  #
data/lib/pancake/paths.rb CHANGED
@@ -23,8 +23,8 @@ module Pancake
23
23
  #
24
24
  # Foo.dirs_and_glob_for(:model) == [
25
25
  # ["#{root}/relative/path/to/models", "**/*.rb"],
26
- # ["#{root}//another/path/to/models", "**/*.rb"],
27
- # ["#{root}//yet/another", nil]
26
+ # ["#{root}/another/path/to/models", "**/*.rb"],
27
+ # ["#{root}/yet/another", nil]
28
28
  # ]
29
29
  #
30
30
  # Foo.paths_for(:model) == [
@@ -24,7 +24,17 @@ Pancake::Stack::BootLoader.add(:load_mounted_inits, :level => :init) do
24
24
  end
25
25
  end
26
26
 
27
- Pancake::Stack::BootLoader.add(:load_configuration, :level => :init) do
27
+ Pancake::Stack::BootLoader.add(:set_as_master) do
28
+ def run!
29
+ return unless config[:master]
30
+ roots = stack_class.roots.dup
31
+ roots.each do |root|
32
+ stack_class.roots << File.join(root, "master")
33
+ end
34
+ end
35
+ end
36
+
37
+ Pancake::Stack::BootLoader.add(:load_configuration) do
28
38
  def run!
29
39
  stack_class.roots.each do |root|
30
40
  stack_class.paths_for(:config).each{|f| require f.join}
@@ -32,7 +42,7 @@ Pancake::Stack::BootLoader.add(:load_configuration, :level => :init) do
32
42
  end
33
43
  end
34
44
 
35
- Pancake::Stack::BootLoader.add(:load_application, :level => :init) do
45
+ Pancake::Stack::BootLoader.add(:load_application) do
36
46
  def run!
37
47
  stack_class.roots.each do |root|
38
48
  [:models, :controllers].each do |type|
@@ -0,0 +1,9 @@
1
+ namespace :pancake do
2
+ desc "symlink all public files to the current public directory"
3
+ task :symlink_to_public do
4
+ puts "Symlinking files to public"
5
+ THIS_STACK.stackup
6
+ THIS_STACK.symlink_public_files!
7
+ puts "Done"
8
+ end
9
+ end
@@ -13,10 +13,13 @@ module Pancake
13
13
  push_paths(:models, "app/models", "**/*.rb")
14
14
  push_paths(:controllers, "app/controllers", "**/*.rb")
15
15
  push_paths(:router, "config", "router.rb")
16
+ push_paths(:rake_tasks, "tasks", "**/*.rake")
17
+ push_paths(:public, "public", "**/*")
18
+
16
19
 
17
20
  #Iterates the list of roots in the stack, and initializes the app found their
18
21
  def self.initialize_stack
19
- raise "Application root not set" if roots.empty?
22
+ raise "Stack root not set" if roots.empty?
20
23
 
21
24
  # Run any :init level bootloaders for this stack
22
25
  self::BootLoader.run!(:stack_class => self, :only => {:level => :init})
@@ -32,6 +35,8 @@ module Pancake
32
35
  def self.add_root(*args)
33
36
  roots << Pancake.get_root(*args)
34
37
  end
38
+ add_root __FILE__, "defaults"
39
+
35
40
 
36
41
  def self.initialized?
37
42
  !!@initialized
@@ -57,12 +62,72 @@ module Pancake
57
62
  end
58
63
 
59
64
  # Construct a stack using the application, wrapped in the middlewares
60
- # :api: public
65
+ # @api public
61
66
  def self.stackup(opts = {}, &block)
62
67
  app = new(nil, opts, &block)
63
68
  Pancake.configuration.configs[app.app_name].router
64
69
  end # stackup
65
70
 
71
+ # Loads the rake task for this stack, and all mounted stacks
72
+ #
73
+ # To have your rake task loaded include a "tasks" director in the stack root
74
+ #
75
+ # Tasks found in all stack roots are loaded in the order of the stack roots declearations
76
+ #
77
+ # @api public
78
+ def self.load_rake_tasks!(opts={})
79
+ stackup(opts) # load the application
80
+ opts[:_rake_files_loaded] ||= []
81
+
82
+ # For each mounted application, load the rake tasks
83
+ self::Router.mounted_applications.each do |app|
84
+ if app.mounted_app.respond_to?(:load_rake_tasks!)
85
+ app.mounted_app.load_rake_tasks!(opts)
86
+ end
87
+ end
88
+ paths_for(:rake_tasks).each do |f|
89
+ path = File.join(*f)
90
+ unless opts[:_rake_files_loaded].include?(path)
91
+ load path
92
+ opts[:_rake_files_loaded] << path
93
+ end
94
+ end
95
+ end
96
+
97
+ # Symlinks files in the public roots of this stack and all mounted stacks.
98
+ # Provided a mounted application responds to the +symlink_public_files!+ method then it will be called.
99
+ # symlinks public files from all roots of the stacks to Pancake.root/public
100
+ #
101
+ # @api public
102
+ # @author Daniel Neighman
103
+ def self.symlink_public_files!
104
+ raise "Pancake root not set" unless Pancake.root
105
+
106
+ public_root = File.join(Pancake.root, "public")
107
+ mount_point = configuration.router.base_url
108
+
109
+ unique_paths_for(:public).sort_by{|(r,p)| p}.each do |(r,p)|
110
+ # don't try to symlink the symlinks
111
+ origin_path = File.join(r, p)
112
+ next if r == public_root || FileTest.directory?(origin_path)
113
+
114
+ output_path = File.join(public_root, mount_point, p)
115
+
116
+ unless File.exists?(File.dirname(output_path))
117
+ FileUtils.mkdir_p(File.dirname(output_path))
118
+ end
119
+ # unless the dir exists... create it
120
+ puts "Linking #{output_path}"
121
+ FileUtils.ln_s(origin_path, output_path, :force => true)
122
+ end
123
+
124
+ router.mounted_applications.each do |s|
125
+ if s.mounted_app.respond_to?(:symlink_public_files!)
126
+ s.mounted_app.symlink_public_files!
127
+ end
128
+ end
129
+ end
130
+
66
131
  # Creates a bootloader hook(s) of the given name. That are inheritable
67
132
  # This will create hooks for use in a bootloader (but will not create the bootloader itself!)
68
133
  #
@@ -1,7 +1,5 @@
1
1
  Pancake::Stacks::Short::BootLoader.add(:paths, :before => :load_mounted_inits ) do
2
2
  def run!
3
- stack_class.push_paths :public, "public"
4
-
5
3
  stack_class::Controller.push_paths :views, ["app/views", "views"], "**/*"
6
4
  end
7
5
  end
@@ -0,0 +1 @@
1
+ $captures << "root1/tasks/task1.rake"
@@ -0,0 +1 @@
1
+ $captures << "root1/tasks/task2.rake"
@@ -0,0 +1 @@
1
+ $captures << "root2/tasks/task1.rake"
@@ -0,0 +1 @@
1
+ $captures << "root2/tasks/task2.rake"
@@ -54,7 +54,10 @@ describe Pancake::Middlewares::Static do
54
54
  it "should return a 404 if the file requested is outside the root directory" do
55
55
  static = Pancake::Middlewares::Static.new(@app, FooBar)
56
56
  file = "/../../../middlewares/static_spec.rb"
57
- File.exists?(File.join(FooBar.dirs_for(:public).first, file)).should be_true
57
+ result = FooBar.dirs_for(:public).map do |r|
58
+ File.exists?(File.join(r, file))
59
+ end
60
+ result.should include(true)
58
61
  env = Rack::MockRequest.env_for(file)
59
62
  result = static.call(env)
60
63
  result[2].body.map{|e| e}.join.should_not == "OK"
@@ -53,4 +53,13 @@ describe Pancake::Mixins::Render do
53
53
  $captures.first.should be_a_kind_of(Pancake::Mixins::Render::ViewContext)
54
54
  end
55
55
 
56
+ it "should yield v when rendering" do
57
+ mock_v = {}
58
+ mock_v.should_receive(:in_the_block)
59
+ @render.should_receive(:v).and_return(mock_v)
60
+ @render.render(:haml_template) do |v|
61
+ v.should be_a_kind_of(Hash)
62
+ v.in_the_block
63
+ end
64
+ end
56
65
  end
@@ -4,10 +4,11 @@ describe "Pancake::Stack" do
4
4
  before(:each) do
5
5
  class ::StackSpecStack < Pancake::Stack
6
6
  end
7
+ StackSpecStack.roots.clear
7
8
  end
8
9
 
9
10
  after(:each) do
10
- clear_constants(:StackSpecStack)
11
+ clear_constants(:StackSpecStack, :FooSpecStack)
11
12
  end
12
13
 
13
14
  describe "roots" do
@@ -57,4 +58,62 @@ describe "Pancake::Stack" do
57
58
  StackSpecStack.should_not be_initialized
58
59
  end
59
60
  # end
61
+ describe "master stack" do
62
+ it "should set the stack to be master, and include the master dir in each root" do
63
+ StackSpecStack.add_root(__FILE__)
64
+ before_roots = StackSpecStack.roots.dup
65
+ s = StackSpecStack.stackup(:master => true)
66
+ before_roots.each do |r|
67
+ StackSpecStack.roots.should include(File.join(r, "master"))
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "loading rake tasks" do
73
+ before do
74
+ $captures = []
75
+ StackSpecStack.add_root(__FILE__, "..", "fixtures", "tasks", "root1")
76
+ end
77
+
78
+ it "should load the rake tasks in the stacks root" do
79
+ StackSpecStack.load_rake_tasks!
80
+ $captures.should == ["root1/tasks/task1.rake", "root1/tasks/task2.rake"]
81
+ end
82
+
83
+ it "should load the rake tasks in order of the roots" do
84
+ StackSpecStack.add_root(__FILE__, "..", "fixtures", "tasks", "root2")
85
+ StackSpecStack.load_rake_tasks!
86
+ $captures.should == [
87
+ "root1/tasks/task1.rake",
88
+ "root1/tasks/task2.rake",
89
+ "root2/tasks/task1.rake",
90
+ "root2/tasks/task2.rake"
91
+ ]
92
+ end
93
+
94
+ it "should load rake tasks for mounted applications" do
95
+ class ::FooSpecStack < Pancake::Stacks::Short
96
+ add_root(__FILE__, "..", "fixtures", "tasks", "root2")
97
+ end
98
+ StackSpecStack.router.mount(FooSpecStack, "/foo")
99
+ StackSpecStack.load_rake_tasks!
100
+ # Mounted stacks should have their rake tasks loaded first so they can be overwritten
101
+ $captures.should == [
102
+ "root2/tasks/task1.rake",
103
+ "root2/tasks/task2.rake",
104
+ "root1/tasks/task1.rake",
105
+ "root1/tasks/task2.rake"
106
+ ]
107
+ end
108
+
109
+ it "should not try to load rake tasks of a mounted app that does not respond to load_rake_tasks!" do
110
+ class ::FooSpecStack < Object
111
+ def self.call(env); Rack::Response.new("OK").finish; end
112
+ end
113
+ StackSpecStack.router.mount(FooSpecStack, "/foo/spec")
114
+ lambda do
115
+ StackSpecStack.load_rake_tasks!
116
+ end.should_not raise_error
117
+ end
118
+ end
60
119
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pancake
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Neighman
@@ -9,7 +9,7 @@ autorequire: pancake
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-30 00:00:00 +11:00
12
+ date: 2009-11-09 00:00:00 +11:00
13
13
  default_executable: pancake-gen
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -175,6 +175,8 @@ files:
175
175
  - lib/pancake/stack/app.rb
176
176
  - lib/pancake/stack/bootloader.rb
177
177
  - lib/pancake/stack/configuration.rb
178
+ - lib/pancake/stack/defaults/public/favicon.ico
179
+ - lib/pancake/stack/defaults/tasks/pancake.rake
178
180
  - lib/pancake/stack/middleware.rb
179
181
  - lib/pancake/stack/router.rb
180
182
  - lib/pancake/stack/stack.rb
@@ -249,6 +251,10 @@ files:
249
251
  - spec/pancake/fixtures/stacks/short/foobar/views/inherited_from_base.html.haml
250
252
  - spec/pancake/fixtures/stacks/short/foobar/views/template.html.haml
251
253
  - spec/pancake/fixtures/stacks/short/foobar/views/vault.html.haml
254
+ - spec/pancake/fixtures/tasks/root1/tasks/task1.rake
255
+ - spec/pancake/fixtures/tasks/root1/tasks/task2.rake
256
+ - spec/pancake/fixtures/tasks/root2/tasks/task1.rake
257
+ - spec/pancake/fixtures/tasks/root2/tasks/task2.rake
252
258
  - spec/pancake/hooks/on_inherit_spec.rb
253
259
  - spec/pancake/inheritance_spec.rb
254
260
  - spec/pancake/middleware_spec.rb