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 +256 -67
- data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +3 -3
- data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +3 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +1 -1
- data/lib/pancake/generators/templates/stack/%stack_name%/Rakefile.tt +3 -0
- data/lib/pancake/generators/templates/stack/%stack_name%/lib/%stack_name%/config.ru.tt +1 -1
- data/lib/pancake/mixins/render/render.rb +2 -2
- data/lib/pancake/mixins/render.rb +2 -0
- data/lib/pancake/mixins/request_helper.rb +4 -2
- data/lib/pancake/paths.rb +2 -2
- data/lib/pancake/stack/bootloader.rb +12 -2
- data/lib/pancake/stack/defaults/public/favicon.ico +0 -0
- data/lib/pancake/stack/defaults/tasks/pancake.rake +9 -0
- data/lib/pancake/stack/stack.rb +67 -2
- data/lib/pancake/stacks/short/bootloaders.rb +0 -2
- data/spec/pancake/fixtures/tasks/root1/tasks/task1.rake +1 -0
- data/spec/pancake/fixtures/tasks/root1/tasks/task2.rake +1 -0
- data/spec/pancake/fixtures/tasks/root2/tasks/task1.rake +1 -0
- data/spec/pancake/fixtures/tasks/root2/tasks/task2.rake +1 -0
- data/spec/pancake/middlewares/static_spec.rb +4 -1
- data/spec/pancake/mixins/render_spec.rb +9 -0
- data/spec/pancake/stack/stack_spec.rb +60 -1
- metadata +8 -2
data/README.textile
CHANGED
@@ -1,95 +1,284 @@
|
|
1
|
-
|
1
|
+
h1. Pancake
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
7
|
+
h2. Stacks
|
8
8
|
|
9
|
-
|
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
|
-
|
11
|
+
The general form of a stack is as follows:
|
12
12
|
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
34
|
+
<pre><code>MyStack.router do |r|
|
35
|
+
r.mount(OtherStack, "/stack/mount/point")
|
36
|
+
end</code></pre>
|
19
37
|
|
20
|
-
|
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
|
-
|
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
|
-
|
25
|
-
* Carllerches rack router
|
42
|
+
All stacks are namespaced. Pancake makes heavy use of namespacing to help construct applications concisely.
|
26
43
|
|
27
|
-
|
44
|
+
h2. Middleware Stacks
|
28
45
|
|
29
|
-
|
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
|
-
|
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
|
-
|
36
|
-
$pancake-gen stack yummy-app
|
37
|
-
</pre>
|
52
|
+
stack(:bugz, :labels => [:development]).use(Rack::Bug)
|
38
53
|
|
39
|
-
|
54
|
+
use(AlwaysUse)
|
55
|
+
end
|
56
|
+
</code></pre>
|
40
57
|
|
41
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
73
|
+
The true power of a Pancake stack is that it is fully inheritable.
|
67
74
|
|
68
|
-
|
75
|
+
h2. Stack Inheritance
|
69
76
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
79
|
-
<
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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.
|
280
|
+
h4. Copyright
|
91
281
|
|
92
|
-
|
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
|
-
#
|
12
|
+
# vault[:user] = @user
|
13
|
+
# v[:user] == vault[:user]
|
13
14
|
# # This is now stored in the environment and is available later
|
14
|
-
def
|
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}
|
27
|
-
# ["#{root}
|
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(:
|
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
|
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|
|
Binary file
|
data/lib/pancake/stack/stack.rb
CHANGED
@@ -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 "
|
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
|
-
#
|
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
|
#
|
@@ -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
|
-
|
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.
|
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-
|
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
|