vanilla 1.14.1 → 1.15
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -2
- data/bin/vanilla +35 -6
- data/lib/vanilla/app.rb +6 -1
- data/lib/vanilla/console.rb +17 -6
- data/lib/vanilla.rb +1 -1
- data/pristine_app/Gemfile +3 -0
- data/pristine_app/Gemfile.lock +32 -0
- data/pristine_app/README +36 -0
- data/pristine_app/config.ru +26 -0
- data/pristine_app/public/vanilla.css +15 -0
- data/pristine_app/soups/base/layout.snip +18 -0
- data/pristine_app/soups/base/start.snip +19 -0
- data/pristine_app/soups/dynasnips/current_snip.rb +29 -0
- data/{lib/vanilla → pristine_app/soups}/dynasnips/debug.rb +5 -3
- data/{lib/vanilla → pristine_app/soups}/dynasnips/index.rb +2 -0
- data/{lib/vanilla → pristine_app/soups}/dynasnips/link_to.rb +2 -0
- data/pristine_app/soups/dynasnips/link_to_current_snip.rb +14 -0
- data/pristine_app/soups/dynasnips/page_title.rb +9 -0
- data/{lib/vanilla → pristine_app/soups}/dynasnips/pre.rb +6 -4
- data/{lib/vanilla → pristine_app/soups}/dynasnips/raw.rb +8 -5
- data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/comments.rb +3 -1
- data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/kind.rb +0 -0
- data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/rand.rb +2 -0
- data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/url_to.rb +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/bad_dynasnip.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/hello_world.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/markdown_example.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/snip.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/soup.snip +1 -3
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/test.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/textile_example.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/tutorial-another-snip.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/tutorial-basic-snip-inclusion.snip +0 -0
- data/pristine_app/soups/tutorial/tutorial-dynasnips.snip.markdown +56 -0
- data/pristine_app/soups/tutorial/tutorial-layout.snip +56 -0
- data/pristine_app/soups/tutorial/tutorial-links.snip +4 -0
- data/pristine_app/soups/tutorial/tutorial-renderers.snip.markdown +77 -0
- data/{lib/vanilla/snips/tutorial/vanilla-rb-tutorial.snip → pristine_app/soups/tutorial/tutorial.snip.markdown} +17 -21
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/vanilla-rb.snip +0 -0
- data/{lib/vanilla/snips → pristine_app/soups}/tutorial/vanilla.snip +0 -0
- data/pristine_app/tmp/restart.txt +0 -0
- data/test/test_helper.rb +1 -1
- data/test/vanilla_app_test.rb +3 -3
- data/test/vanilla_presenting_test.rb +9 -2
- metadata +76 -73
- data/README_FOR_APP +0 -38
- data/config.example.yml +0 -7
- data/config.ru +0 -11
- data/lib/tasks/vanilla.rake +0 -75
- data/lib/vanilla/dynasnips/current_snip.rb +0 -32
- data/lib/vanilla/dynasnips/link_to_current_snip.rb +0 -16
- data/lib/vanilla/dynasnips/title.rb +0 -7
- data/lib/vanilla/snips/system/layout.snip +0 -19
- data/lib/vanilla/snips/system/start.snip +0 -25
- data/lib/vanilla/snips/system/system.snip +0 -17
- data/public/hatch.png +0 -0
data/Rakefile
CHANGED
@@ -2,7 +2,8 @@ require "rubygems"
|
|
2
2
|
require "rake/gempackagetask"
|
3
3
|
require "rake/rdoctask"
|
4
4
|
|
5
|
-
|
5
|
+
require "bundler/setup"
|
6
|
+
require "vanilla"
|
6
7
|
|
7
8
|
task :default => :test
|
8
9
|
|
@@ -35,7 +36,7 @@ if Object.const_defined?(:Gem)
|
|
35
36
|
s.rdoc_options = %w(--main README)
|
36
37
|
|
37
38
|
# Add any extra files to include in the gem
|
38
|
-
s.files = %w(
|
39
|
+
s.files = %w(Rakefile README) + Dir.glob("{test,lib,bin,pristine_app}/**/*")
|
39
40
|
s.executables = ['vanilla']
|
40
41
|
s.require_paths = ["lib"]
|
41
42
|
|
data/bin/vanilla
CHANGED
@@ -1,9 +1,38 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'rake'
|
4
|
-
load File.join(File.dirname(__FILE__), *%w[.. lib tasks vanilla.rake])
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
def create(new_app_dir)
|
4
|
+
require 'fileutils'
|
5
|
+
pristine_app = File.expand_path("../../pristine_app", __FILE__)
|
6
|
+
FileUtils.cp_r(pristine_app, new_app_dir)
|
7
|
+
FileUtils.mkdir(File.join(new_app_dir, "tmp"))
|
8
|
+
require 'vanilla'
|
9
|
+
File.open(File.join(new_app_dir, "Gemfile"), "w") do |f|
|
10
|
+
f.write "source :rubygems\n\n# Vanilla itself.\ngem 'vanilla', '#{Vanilla::VERSION}'"
|
11
|
+
end
|
12
|
+
puts File.readlines(File.join(new_app_dir, "README"))[0..16].join
|
13
|
+
end
|
8
14
|
|
9
|
-
|
15
|
+
def console
|
16
|
+
$LOAD_PATH << "lib"
|
17
|
+
require "rubygems"
|
18
|
+
require "bundler/setup"
|
19
|
+
require "irb"
|
20
|
+
require "vanilla/console"
|
21
|
+
ARGV.clear
|
22
|
+
puts "The Soup is simmering."
|
23
|
+
IRB.start
|
24
|
+
end
|
25
|
+
|
26
|
+
def upgrade
|
27
|
+
# TODO
|
28
|
+
puts "TODO, but should be easier thanks to multi-space soup."
|
29
|
+
end
|
30
|
+
|
31
|
+
case ARGV[0]
|
32
|
+
when "console"
|
33
|
+
console
|
34
|
+
when "upgrade"
|
35
|
+
upgrade
|
36
|
+
else
|
37
|
+
create(ARGV[0])
|
38
|
+
end
|
data/lib/vanilla/app.rb
CHANGED
@@ -49,7 +49,12 @@ module Vanilla
|
|
49
49
|
def formatted_render(snip, part=nil, format=nil)
|
50
50
|
case format
|
51
51
|
when 'html', nil
|
52
|
-
|
52
|
+
layout = layout_for(snip)
|
53
|
+
if layout == snip
|
54
|
+
"Rendering of the current layout would result in infinite recursion."
|
55
|
+
else
|
56
|
+
render(layout)
|
57
|
+
end
|
53
58
|
when 'raw', 'css', 'js'
|
54
59
|
Renderers::Raw.new(self).render(snip, part)
|
55
60
|
when 'text', 'atom', 'xml'
|
data/lib/vanilla/console.rb
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
require 'vanilla'
|
2
|
-
|
2
|
+
|
3
|
+
module Vanilla
|
4
|
+
class RackShim
|
5
|
+
def run(app)
|
6
|
+
app # return it
|
7
|
+
end
|
8
|
+
def use(*args)
|
9
|
+
# ignore
|
10
|
+
end
|
11
|
+
def get_binding
|
12
|
+
binding
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
3
16
|
|
4
17
|
def app(reload=false)
|
5
18
|
if !@__vanilla_console_app || reload
|
6
|
-
|
7
|
-
@__vanilla_console_app =
|
19
|
+
shim_binding = Vanilla::RackShim.new.get_binding
|
20
|
+
@__vanilla_console_app = eval File.read("config.ru"), shim_binding
|
8
21
|
end
|
9
22
|
@__vanilla_console_app
|
10
|
-
end
|
11
|
-
|
12
|
-
puts "The Soup is simmering."
|
23
|
+
end
|
data/lib/vanilla.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /Users/james/Code/lazyatom/vanilla-rb
|
3
|
+
specs:
|
4
|
+
vanilla (1.15)
|
5
|
+
BlueCloth (>= 1.0.0)
|
6
|
+
RedCloth (>= 4.1.1)
|
7
|
+
haml
|
8
|
+
rack (>= 0.9.1)
|
9
|
+
ratom (>= 0.3.5)
|
10
|
+
soup (>= 1.0.6)
|
11
|
+
treetop (>= 1.4.1)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
BlueCloth (1.0.1)
|
17
|
+
RedCloth (4.2.7)
|
18
|
+
haml (3.0.25)
|
19
|
+
libxml-ruby (2.0.2)
|
20
|
+
polyglot (0.3.1)
|
21
|
+
rack (1.2.2)
|
22
|
+
ratom (0.6.7)
|
23
|
+
libxml-ruby (>= 1.1.2)
|
24
|
+
soup (1.0.6)
|
25
|
+
treetop (1.4.9)
|
26
|
+
polyglot (>= 0.3.1)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
vanilla!
|
data/pristine_app/README
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
WELCOME IN VANILLA
|
2
|
+
Vanilla
|
3
|
+
vanilla
|
4
|
+
.........vanillaaaaaAAAAAAAAAAA
|
5
|
+
|
6
|
+
What you've got:
|
7
|
+
|
8
|
+
Gemfile - specifying your dependencies
|
9
|
+
config.ru - this is the rack configuration, which is used to start
|
10
|
+
the application by your webserver
|
11
|
+
soup/ - the default soup directory, where your snips are stored
|
12
|
+
|
13
|
+
|
14
|
+
For an overview of vanilla, start your site and look at the tutorial:
|
15
|
+
|
16
|
+
$ rackup # then open http://localhost:9292/tutorial
|
17
|
+
|
18
|
+
|
19
|
+
You can edit any file in the soup directory using your favourite editor,
|
20
|
+
and the changes will be reflected automatically. The snip files are
|
21
|
+
slightly modified YAML files. Here's an example, which you might save
|
22
|
+
in a file called 'soup.snip':
|
23
|
+
|
24
|
+
|
25
|
+
Soup is a data store supporting the {link_to snip}-space that
|
26
|
+
{link_to vanilla-rb} expects.
|
27
|
+
|
28
|
+
It's hosted on github [here](http://github.com/lazyatom/soup).
|
29
|
+
|
30
|
+
:created_at: 2011-05-23 14:14:16 +01:00
|
31
|
+
:updated_at: 2009-05-23 15:23:22 +01:00
|
32
|
+
:render_as: Markdown
|
33
|
+
|
34
|
+
|
35
|
+
The 'content' of the snip is at the top of the file, followed by the
|
36
|
+
rest of the snip attributes on lines starting with ':'.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[lib])
|
4
|
+
|
5
|
+
require 'vanilla'
|
6
|
+
|
7
|
+
# You can partition your snips into subdirectories to keep things tidy. This
|
8
|
+
# doesn't affect their URL structure on the site (everything is flat).
|
9
|
+
soups = [
|
10
|
+
"soups/base",
|
11
|
+
"soups/dynasnips"
|
12
|
+
]
|
13
|
+
|
14
|
+
# If you don't want the tutorial on your site, remove this and delete the directory
|
15
|
+
soups << "soups/tutorial"
|
16
|
+
|
17
|
+
# This is a dumping ground of ideas at the moment
|
18
|
+
# soups << "soups/extras"
|
19
|
+
|
20
|
+
app = Vanilla::App.new(:soups => soups)
|
21
|
+
|
22
|
+
# If you running your site under a proper webserver, you probably don't need this.
|
23
|
+
require 'vanilla/static'
|
24
|
+
use Vanilla::Static, File.join(File.dirname(__FILE__), 'public')
|
25
|
+
|
26
|
+
run app
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
5
|
+
<title>{page_title}</title>
|
6
|
+
<link rel="stylesheet" href="/vanilla.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<ul id="controls">
|
10
|
+
<li><a href="/">home</a></li>
|
11
|
+
<li>{link_to index}</li>
|
12
|
+
<li>you are viewing: {link_to_current_snip}</li>
|
13
|
+
</ul>
|
14
|
+
<div id="content">
|
15
|
+
{current_snip}
|
16
|
+
</div>
|
17
|
+
</body>
|
18
|
+
</html>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<h1>Welcome to Vanilla.rb</h1>
|
2
|
+
|
3
|
+
<p>Vanilla.rb is a web-experiment (a <em>websperiment</em>?) about storing and reusing data on a website. It can also function as a <a href="http://www.wikipedia.com/wiki/Bliki">bliki</a>.</p>
|
4
|
+
|
5
|
+
<p>From a technical perspective, vanilla is a Ruby rack application, and should play well within the rack ecosystem. It uses a simple document store called `soup` to store data.</p>
|
6
|
+
|
7
|
+
<h2>What now?</h2>
|
8
|
+
|
9
|
+
<p>If this is your first time using vanilla, you should investigate the {link_to tutorial} for some orientation.</p>
|
10
|
+
|
11
|
+
<h2>What next?</h2>
|
12
|
+
|
13
|
+
<p>This is the {link_to start} snip, which is the default home page. You can find this content in `start.snip`.</p>
|
14
|
+
|
15
|
+
<p>You should replace this content with whatever you want on your site's home page.</p>
|
16
|
+
|
17
|
+
<p>Once your site is ready to run, you'll probably want to remove the tutorial; you can do this by editing the configuration in `config.ru`, and deleting the `soups/tutorial` directory.</p>
|
18
|
+
|
19
|
+
<p>Good luck!</p>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'vanilla/dynasnip'
|
2
|
+
|
3
|
+
class CurrentSnip < Dynasnip
|
4
|
+
usage %|
|
5
|
+
The current_snip dyna normally returns the result of rendering the snip named by the
|
6
|
+
'snip' value in the parameters. This way, it can be used in templates to place the currently
|
7
|
+
requested snip, in its rendered form, within the page.
|
8
|
+
|
9
|
+
It can also be used to determine an attribute of the current snip in a consistent way:
|
10
|
+
|
11
|
+
{current_snip name}
|
12
|
+
|
13
|
+
will output the name of the current snip.
|
14
|
+
|
|
15
|
+
|
16
|
+
def handle(attribute=nil)
|
17
|
+
if attribute
|
18
|
+
app.request.snip.__send__(attribute)
|
19
|
+
else
|
20
|
+
if app.request.snip
|
21
|
+
app.render(app.request.snip, app.request.part)
|
22
|
+
else
|
23
|
+
app.response.status = 404
|
24
|
+
%{Couldn't find snip "#{app.request.snip_name}"}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'vanilla/dynasnip'
|
2
|
+
require 'cgi'
|
2
3
|
|
3
4
|
# If the dynasnip is a subclass of Dynasnip, it has access to the request hash
|
4
5
|
# (or whatever - access to some object outside of the snip itself.)
|
5
6
|
class Debug < Dynasnip
|
6
7
|
def get(*args)
|
7
|
-
app.request.inspect
|
8
|
+
CGI.escapeHTML(app.request.inspect)
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
def post(*args)
|
11
|
-
"You posted! " + app.request.inspect
|
12
|
+
"You posted! " + CGI.escapeHTML(app.request.inspect)
|
12
13
|
end
|
14
|
+
self
|
13
15
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'vanilla/dynasnip'
|
2
|
+
|
3
|
+
class LinkToCurrentSnip < Dynasnip
|
4
|
+
usage %|
|
5
|
+
Renders a link to the current snip, or the snip currently being edited
|
6
|
+
(if we're currently editing)
|
7
|
+
|
|
8
|
+
|
9
|
+
def handle(*args)
|
10
|
+
link_to app.request.snip_name
|
11
|
+
end
|
12
|
+
|
13
|
+
self
|
14
|
+
end
|
@@ -5,15 +5,17 @@ class ShowContentInPreTag < Dynasnip
|
|
5
5
|
|
6
6
|
usage %|
|
7
7
|
Wraps the contents of the given snip in <pre> tags, e.g.
|
8
|
-
|
8
|
+
|
9
9
|
{pre my_snip}
|
10
|
-
|
10
|
+
|
11
11
|
You can specify a part to render in pre tags, should you wish:
|
12
|
-
|
12
|
+
|
13
13
|
{pre my_snip,specific_part}
|
14
14
|
|
|
15
|
-
|
15
|
+
|
16
16
|
def handle(snip_name, part=:content)
|
17
17
|
%{<pre>#{app.soup[snip_name].__send__(part || :content)}</pre>}
|
18
18
|
end
|
19
|
+
|
20
|
+
self
|
19
21
|
end
|
@@ -1,19 +1,22 @@
|
|
1
1
|
require 'vanilla/dynasnip'
|
2
|
+
require 'cgi'
|
2
3
|
|
3
4
|
class ShowRawContent < Dynasnip
|
4
5
|
snip_name "raw"
|
5
6
|
|
6
7
|
usage %|
|
7
8
|
Displays the raw contents of a snip in <pre> tags, e.g.
|
8
|
-
|
9
|
+
|
9
10
|
{raw my_snip}
|
10
|
-
|
11
|
+
|
11
12
|
You can specify a part to show, should you wish:
|
12
|
-
|
13
|
+
|
13
14
|
{raw my_snip,specific_part}
|
14
15
|
|
|
15
|
-
|
16
|
+
|
16
17
|
def handle(snip_name, part=:content)
|
17
|
-
%{
|
18
|
+
%{#{Dynasnip.escape_curly_braces(CGI.escapeHTML(app.soup[snip_name].__send__(part || :content)))}}
|
18
19
|
end
|
20
|
+
|
21
|
+
self
|
19
22
|
end
|
@@ -64,7 +64,7 @@ class Comments < Dynasnip
|
|
64
64
|
}
|
65
65
|
|
66
66
|
attribute :comment_form, %{
|
67
|
-
<form class="comments" action="
|
67
|
+
<form class="comments" action="/comments?snip=SNIP_NAME" method="POST">
|
68
68
|
<label>Name: <input type="text" name="author"></input></label>
|
69
69
|
<label>Email: <input type="text" name="email"></input></label>
|
70
70
|
<label>Website: <input type="text" name="website"></input></label>
|
@@ -73,4 +73,6 @@ class Comments < Dynasnip
|
|
73
73
|
<button>Submit</button>
|
74
74
|
</form>
|
75
75
|
}
|
76
|
+
|
77
|
+
self
|
76
78
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,5 +1,3 @@
|
|
1
1
|
Soup is a data store supporting the {link_to snip}-space that {link_to vanilla-rb} expects.
|
2
2
|
|
3
|
-
It's hosted on github <a href="http://github.com/lazyatom/soup">here</a>.
|
4
|
-
|
5
|
-
:render_as: Markdown
|
3
|
+
It's hosted on github <a href="http://github.com/lazyatom/soup">here</a>.
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,56 @@
|
|
1
|
+
{tutorial-links}
|
2
|
+
|
3
|
+
Dynasnips
|
4
|
+
=========
|
5
|
+
|
6
|
+
As mentioned in the general {link_to tutorial}, dynamic content is built in vanilla using "dynasnips". These are snips who content depends on more than just their content.
|
7
|
+
|
8
|
+
Typically they are rendered by the `Vanilla::Renderers::Ruby` renderer. Lets look again at the raw content of `link_to`:
|
9
|
+
|
10
|
+
{raw link_to}
|
11
|
+
|
12
|
+
As you can see, it simply refers to the Ruby class `LinkTo`, which is contained within the vanilla-rb codebase. When the Ruby renderer is called, expects the given code to evaulate to a Ruby class. It then instantiates the class, and calls a `handle` method on the instance, passing it any other arguments from the snip inclusion.
|
13
|
+
|
14
|
+
You can pass arguments to dynasnips in a number of ways. All of the following are valid:
|
15
|
+
|
16
|
+
* {dynasnip apple}
|
17
|
+
* {dynasnip apple, banana}
|
18
|
+
* {dynasnip apple, big banana, cherry}
|
19
|
+
* {dynasnip apple, big banana, "lovely lucious cherry"}
|
20
|
+
* {dynasnip apple => true, banana => false}
|
21
|
+
* {dynasnip apple: true, banana: false}
|
22
|
+
|
23
|
+
Where a simple list of arguments is given, these will be passed to the `handle` method as an array. If a ruby-hash-like syntax is used, a hash of these options will be passed.
|
24
|
+
|
25
|
+
Of course, it depends entirely on the implementation of the dynasnip what arguments it expects and accepts; some may require a flat list, while others may require hash-like named arguments.
|
26
|
+
|
27
|
+
|
28
|
+
Writing your own Dynasnips
|
29
|
+
--------------------------
|
30
|
+
|
31
|
+
While dynasnip classes can be provided as part of the vanilla codebase, it's envisioned that much of these will be created by end users in their own sites, either by refering to local classes, or defining the classes directly as the content. Here's an example of that, as the raw content of `hello_world`:
|
32
|
+
|
33
|
+
{raw hello_world}
|
34
|
+
|
35
|
+
It's important that the contents of the snip evaluate to a Ruby class; this is easy to achieve by placing `self` as the last statement in the class definition, or referencing the class at the end of the snip; both are shown above.
|
36
|
+
|
37
|
+
If we include the dynasnip here as <tt>{hello\_world}</tt>, gives:
|
38
|
+
|
39
|
+
> {hello_world}
|
40
|
+
|
41
|
+
Note that the `handle` method can take one (optional) argument. Lets try including it with <tt>{hello\_world Dave}</tt>:
|
42
|
+
|
43
|
+
> {hello_world Dave}
|
44
|
+
|
45
|
+
|
46
|
+
HTTP Verbs
|
47
|
+
----------
|
48
|
+
|
49
|
+
By default, the Ruby renderer will attempt to call `handle` on a dynasnip instance, but if the instance responds to methods corresponding to the HTTP Verbs - `get`, `post`, `put`, or `delete` - then these methods will be called instead.
|
50
|
+
|
51
|
+
This means you can have a single dynasnip which responds differently when receiving a `POST` request - quite useful if you want to write dynasnips that generate and respond to forms, like the `comments` dynasnip in your `extras` soup directory.
|
52
|
+
|
53
|
+
There's really no limit to what you can do with dynasnips - only what you can imagine.
|
54
|
+
|
55
|
+
|
56
|
+
{tutorial-links}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
{tutorial-links}
|
2
|
+
|
3
|
+
Layouts
|
4
|
+
=======
|
5
|
+
|
6
|
+
Since you almost certainly want your site to look good, one of the first things you'll want to change in your vanilla site is the layout.
|
7
|
+
|
8
|
+
When the browser requests a snip, normally vanilla will present it within a _layout_ template. This would typically include a header, a footer, and any other peripheral markup that shouldn't be within the content of the snip itself. If you're familiar with the construction of web applications, this will be exactly as you expect.
|
9
|
+
|
10
|
+
Layouts are just like any other snip - they can be sent through a renderer, and include other snips. The default layout snip is called, predictable, `layout.snip`, and here's the content:
|
11
|
+
|
12
|
+
{raw layout}
|
13
|
+
|
14
|
+
When you request `/start`, this is the snip that's actually rendered first. If this snip was just text, that's all that would be returned; however, there are some dynasnip calls in here which help us actually return the content that the user requested.
|
15
|
+
|
16
|
+
|
17
|
+
`current_snip`
|
18
|
+
--------------
|
19
|
+
|
20
|
+
The most significant is the call to `current_snip`. This figures out what snip was actually requested (e.g. if the url is `/start`, it's the {link_to start} snip), and renders it in place.
|
21
|
+
|
22
|
+
Here's the source of `current_snip`:
|
23
|
+
|
24
|
+
{raw current_snip}
|
25
|
+
|
26
|
+
The default case, as in our layout, is `app.render(app.request.snip, app.request.part)` - it delegates rendering back to the application, which then takes care of processing `start` using the right renderer and so on. This method call returns the fully rendered string, and vanilla replaces the call to the dynasnip with that output, placing our snip in the appropriate place in the layout.
|
27
|
+
|
28
|
+
|
29
|
+
Other dynas
|
30
|
+
-----------
|
31
|
+
|
32
|
+
Of course, you can put other plain content in your layout, and other dynasnips too. In the provided layout there are calls to two other dynasnips.
|
33
|
+
|
34
|
+
The first is `page_title`, which simply places a (hopefully) meaningful string in the title element of the page. Snips can set the title to be used by defining a `:page_title` attribute. As usual, the source explains it more clearly:
|
35
|
+
|
36
|
+
{raw page_title}
|
37
|
+
|
38
|
+
The second dynasnip used is `link_to_current_snip`, which returns an HTML link to the snip that's currently being rendered. I'll let you figure out how to view the source yourself.
|
39
|
+
|
40
|
+
|
41
|
+
Other layouts
|
42
|
+
-------------
|
43
|
+
|
44
|
+
Vanilla looks for a snip called `layout` by default, but this can be changed by passing in a `:default_layout` option to `Vanilla::App.new`, e.g.
|
45
|
+
|
46
|
+
Vanilla::App.new(:default_layout => "my_layout")
|
47
|
+
|
48
|
+
|
49
|
+
You can also override the layout on a per-snip basis, simply by setting the `:layout` attribute of the snip to the name of the layout snip to use instead.
|
50
|
+
|
51
|
+
Finally, if you implement a custom renderer class (see {link_to tutorial-renderers, "the renderers tutorial"}), you can also specify a layout to be used when the requested snip invokes that renderer. This can be useful if you have a particular kind of content that requires a different layout entirely.
|
52
|
+
|
53
|
+
{tutorial-links}
|
54
|
+
|
55
|
+
:render_as: Markdown
|
56
|
+
:page_title: Tutorial - Layout
|