vanilla 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +52 -0
- data/Rakefile +118 -0
- data/bin/vanilla +9 -0
- data/config.example.yml +5 -0
- data/config.ru +9 -0
- data/lib/defensio.rb +59 -0
- data/lib/tasks/vanilla.rake +178 -0
- data/lib/vanilla/app.rb +87 -0
- data/lib/vanilla/console.rb +3 -0
- data/lib/vanilla/dynasnip.rb +110 -0
- data/lib/vanilla/dynasnips/comments.rb +108 -0
- data/lib/vanilla/dynasnips/current_snip.rb +32 -0
- data/lib/vanilla/dynasnips/debug.rb +13 -0
- data/lib/vanilla/dynasnips/edit.rb +63 -0
- data/lib/vanilla/dynasnips/edit_link.rb +24 -0
- data/lib/vanilla/dynasnips/index.rb +11 -0
- data/lib/vanilla/dynasnips/kind.rb +70 -0
- data/lib/vanilla/dynasnips/link_to.rb +14 -0
- data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
- data/lib/vanilla/dynasnips/login.rb +56 -0
- data/lib/vanilla/dynasnips/new.rb +14 -0
- data/lib/vanilla/dynasnips/notes.rb +42 -0
- data/lib/vanilla/dynasnips/pre.rb +19 -0
- data/lib/vanilla/dynasnips/rand.rb +27 -0
- data/lib/vanilla/dynasnips/raw.rb +19 -0
- data/lib/vanilla/dynasnips/url_to.rb +7 -0
- data/lib/vanilla/renderers/base.rb +78 -0
- data/lib/vanilla/renderers/bold.rb +9 -0
- data/lib/vanilla/renderers/erb.rb +16 -0
- data/lib/vanilla/renderers/markdown.rb +13 -0
- data/lib/vanilla/renderers/raw.rb +9 -0
- data/lib/vanilla/renderers/ruby.rb +35 -0
- data/lib/vanilla/renderers/textile.rb +13 -0
- data/lib/vanilla/request.rb +68 -0
- data/lib/vanilla/routes.rb +29 -0
- data/lib/vanilla/snip_handling.rb +33 -0
- data/lib/vanilla/snips/start.rb +18 -0
- data/lib/vanilla/snips/system.rb +76 -0
- data/lib/vanilla/snips/tutorial.rb +158 -0
- data/lib/vanilla/test_snips.rb +85 -0
- data/lib/vanilla.rb +20 -0
- data/spec/dynasnip_spec.rb +31 -0
- data/spec/renderers/base_renderer_spec.rb +40 -0
- data/spec/renderers/erb_renderer_spec.rb +27 -0
- data/spec/renderers/markdown_renderer_spec.rb +29 -0
- data/spec/renderers/raw_renderer_spec.rb +21 -0
- data/spec/renderers/ruby_renderer_spec.rb +42 -0
- data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
- data/spec/soup_test.db +0 -0
- data/spec/spec_helper.rb +64 -0
- data/spec/vanilla_app_spec.rb +38 -0
- data/spec/vanilla_presenting_spec.rb +84 -0
- data/spec/vanilla_request_spec.rb +73 -0
- data/spec/vanilla_snip_finding_spec.rb +28 -0
- metadata +122 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'vanilla/renderers/base'
|
2
|
+
|
3
|
+
module Vanilla::Renderers
|
4
|
+
# Snips that render_as "Ruby" should define a class.
|
5
|
+
# The class should have instance methods for any HTTP request methods that the dynasnip
|
6
|
+
# should respond to, i.e. get(), post(), and so on.
|
7
|
+
# Alternatively, it can respond to 'handle'.
|
8
|
+
#
|
9
|
+
# The result of the method invocation always has #to_s called on it.
|
10
|
+
# The last line of the content should be the name of that class, so that it
|
11
|
+
# is returned by "eval" and we can instantiate it.
|
12
|
+
# If the dynasnip needs access to the 'context' (i.e. probably the request
|
13
|
+
# itself), it should be a subclass of Dynasnip (or define an initializer
|
14
|
+
# that accepts the context as its first argument).
|
15
|
+
class Ruby < Base
|
16
|
+
def prepare(snip, part=:content, args=[])
|
17
|
+
@args = args
|
18
|
+
@snip = snip
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_text(content)
|
22
|
+
handler_klass = eval(content, binding, @snip.name)
|
23
|
+
instance = if handler_klass.ancestors.include?(Vanilla::Renderers::Base)
|
24
|
+
handler_klass.new(app)
|
25
|
+
else
|
26
|
+
handler_klass.new
|
27
|
+
end
|
28
|
+
if (method = app.request.method) && instance.respond_to?(method)
|
29
|
+
instance.send(method, *@args).to_s
|
30
|
+
else
|
31
|
+
instance.handle(*@args).to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module Vanilla
|
4
|
+
# Create a request with symbolised access to the params, and some special
|
5
|
+
# accessors to the snip, part and format based on our routing.
|
6
|
+
class Request
|
7
|
+
attr_reader :snip_name, :part, :format, :method
|
8
|
+
|
9
|
+
def initialize(env)
|
10
|
+
@rack_request = Rack::Request.new(env)
|
11
|
+
determine_request_uri_parts
|
12
|
+
end
|
13
|
+
|
14
|
+
def params
|
15
|
+
# Don't you just love how terse functional programming tends to look like maths?
|
16
|
+
@symbolised_params ||= @rack_request.params.inject({}) { |p, (k,v)| p[k.to_sym] = v; p }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the snip referenced by the request's URL. Performs no exception
|
20
|
+
# handling, so if the snip does not exist, an exception will be thrown.
|
21
|
+
def snip
|
22
|
+
Vanilla.snip(snip_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cookies
|
26
|
+
@rack_request.cookies
|
27
|
+
end
|
28
|
+
|
29
|
+
def ip
|
30
|
+
@rack_request.env["REMOTE_ADDR"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def session
|
34
|
+
@rack_request.env["rack.session"]
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def determine_request_uri_parts
|
40
|
+
@snip_name, @part, @format = request_uri_parts(@rack_request)
|
41
|
+
@format ||= 'html'
|
42
|
+
@method = (params.delete(:_method) || @rack_request.request_method).downcase
|
43
|
+
end
|
44
|
+
|
45
|
+
def uri_path(request)
|
46
|
+
request.path_info
|
47
|
+
end
|
48
|
+
|
49
|
+
URL_ROOT = /\A\/\Z/ # i.e. /
|
50
|
+
URL_SNIP = /\A\/([\w\-\s]+)(\/|\.(\w+))?\Z/ # i.e. /start, /start.html
|
51
|
+
URL_SNIP_AND_PART = /\A\/([\w\-\s]+)\/([\w\-\s]+)(\/|\.(\w+))?\Z/ # i.e. /blah/part, /blah/part.raw
|
52
|
+
|
53
|
+
# Returns an array of the requested snip, part and format
|
54
|
+
def request_uri_parts(request)
|
55
|
+
case CGI.unescape(uri_path(request))
|
56
|
+
when URL_ROOT
|
57
|
+
['start', nil, 'html']
|
58
|
+
when URL_SNIP
|
59
|
+
[$1, nil, $3]
|
60
|
+
when URL_SNIP_AND_PART
|
61
|
+
[$1, $2, $4]
|
62
|
+
else
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Vanilla
|
2
|
+
module Routes
|
3
|
+
def link_to(link_text, snip_name=link_text, part=nil)
|
4
|
+
%{<a href="#{Vanilla::Routes.url_to(snip_name, part)}">#{link_text}</a>}
|
5
|
+
end
|
6
|
+
|
7
|
+
def url_to(snip_name, part=nil)
|
8
|
+
url = "/#{snip_name}"
|
9
|
+
url += "/#{part}" if part
|
10
|
+
url
|
11
|
+
end
|
12
|
+
|
13
|
+
def url_to_raw(snip_name, part=nil)
|
14
|
+
url = "/#{snip_name}"
|
15
|
+
url += "/#{part}" if part
|
16
|
+
url += ".raw"
|
17
|
+
end
|
18
|
+
|
19
|
+
def edit_link(snip_name, link_text)
|
20
|
+
%[<a href="/edit?name=#{snip_name}">#{link_text}</a>]
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_link(snip_name="New")
|
24
|
+
%[<a href="/new?name=#{snip_name}" class="new">#{snip_name}</a>]
|
25
|
+
end
|
26
|
+
|
27
|
+
extend self
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Vanilla
|
2
|
+
module SnipHandling
|
3
|
+
class MissingSnipException < Exception; end
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def snip(name)
|
11
|
+
snip = Soup[name]
|
12
|
+
if snip.is_a?(Array) && snip.empty?
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
snip
|
16
|
+
end
|
17
|
+
|
18
|
+
def snip!(name)
|
19
|
+
snip = snip(name)
|
20
|
+
raise MissingSnipException, "can't find '#{name}'" unless snip
|
21
|
+
end
|
22
|
+
|
23
|
+
def snip_exists?(name)
|
24
|
+
snip = Soup[name]
|
25
|
+
if snip.is_a?(Array) && snip.empty?
|
26
|
+
false
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
start = Snip.new(:name => 'start')
|
2
|
+
start.content = <<-START
|
3
|
+
This is the {link_to start} snip, which is the default home page.
|
4
|
+
|
5
|
+
You might want to check out the {link_to vanilla-rb-tutorial} snip.
|
6
|
+
|
7
|
+
In fact - I'll include it here for you:
|
8
|
+
|
9
|
+
{vanilla-rb-tutorial}
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
That was the end of the tutorial snip. We're back in the start snip now. There's also the {link_to test} snip, that might be interesting.
|
14
|
+
|
15
|
+
Anyway, welcome.
|
16
|
+
START
|
17
|
+
start.render_as = "Markdown"
|
18
|
+
start.save
|
@@ -0,0 +1,76 @@
|
|
1
|
+
system = Snip.new(:name => "system")
|
2
|
+
system.content = "You're in the system snip now. You probably want to {edit_link system,edit} it though."
|
3
|
+
|
4
|
+
system.main_template = <<-HTML
|
5
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
6
|
+
<html lang="en">
|
7
|
+
<head>
|
8
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
9
|
+
<title>{current_snip name}</title>
|
10
|
+
<script language="javascript" src="/public/javascripts/jquery-1.2.3.js"></script>
|
11
|
+
<script language="javascript" src="/public/javascripts/vanilla.js"></script>
|
12
|
+
<link rel="stylesheet" type="text/css" media="screen" href="<%= Vanilla::Routes.url_to("system", "css.css") %>" />
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<div id="content">
|
16
|
+
<div id="controls">
|
17
|
+
<strong><a href="/">home</a></strong>,
|
18
|
+
<%= Vanilla::Routes.new_link %> ::
|
19
|
+
<strong>{link_to_current_snip}</strong> →
|
20
|
+
<%= Vanilla::Routes.edit_link("{current_snip name}", "Edit") %>
|
21
|
+
</div>
|
22
|
+
{current_snip}
|
23
|
+
</div>
|
24
|
+
</body>
|
25
|
+
</html>
|
26
|
+
HTML
|
27
|
+
|
28
|
+
system.css = <<-CSS
|
29
|
+
body {
|
30
|
+
font-family: Helvetica;
|
31
|
+
background-color: #666;
|
32
|
+
margin: 0;
|
33
|
+
padding: 0;
|
34
|
+
}
|
35
|
+
|
36
|
+
div#content {
|
37
|
+
width: 800px;
|
38
|
+
margin: 0 auto;
|
39
|
+
background-color: #fff;
|
40
|
+
padding: 1em;
|
41
|
+
}
|
42
|
+
|
43
|
+
div#controls {
|
44
|
+
font-size: 80%;
|
45
|
+
padding-bottom: 1em;
|
46
|
+
margin-bottom: 1em;
|
47
|
+
border-bottom: 1px solid #999;
|
48
|
+
}
|
49
|
+
|
50
|
+
textarea {
|
51
|
+
width: 100%;
|
52
|
+
}
|
53
|
+
|
54
|
+
textarea.content {
|
55
|
+
min-height: 10em;
|
56
|
+
}
|
57
|
+
|
58
|
+
pre {
|
59
|
+
background-color: #f6f6f6;
|
60
|
+
border: 1px solid #ccc;
|
61
|
+
padding: 1em;
|
62
|
+
}
|
63
|
+
|
64
|
+
a.new {
|
65
|
+
background-color: #f0f0f0;
|
66
|
+
text-decoration: none;
|
67
|
+
color: #999;
|
68
|
+
padding: 0.2em;
|
69
|
+
}
|
70
|
+
|
71
|
+
a.new:hover {
|
72
|
+
text-decoration: underline;
|
73
|
+
}
|
74
|
+
CSS
|
75
|
+
|
76
|
+
system.save
|
@@ -0,0 +1,158 @@
|
|
1
|
+
tutorial = Snip.new(:name => 'vanilla-rb-tutorial')
|
2
|
+
tutorial.render_as = "Markdown"
|
3
|
+
tutorial.content = <<-END_OF_TUTORIAL
|
4
|
+
Basic Concepts
|
5
|
+
------------
|
6
|
+
|
7
|
+
Every piece of information displayed here is stored as a {link_to snip}. Snips, within their contents, can also reference other snips.
|
8
|
+
|
9
|
+
For example, consider the snip {link_to tutorial-basic-snip-inclusion}:
|
10
|
+
|
11
|
+
{raw tutorial-basic-snip-inclusion}
|
12
|
+
|
13
|
+
is rendered into:
|
14
|
+
|
15
|
+
{tutorial-basic-snip-inclusion}
|
16
|
+
|
17
|
+
Notice the use of curly brackets to reference one snip from inside another. Vanilla.rb finds these references to snips, then renders that snip and replaces it in the first snip. Neat!
|
18
|
+
|
19
|
+
|
20
|
+
Renderers
|
21
|
+
--------
|
22
|
+
|
23
|
+
The way that a snip is rendered depends on whether or not it has a `render_as` attribute set. For instance, the `render_as` property of this snip ({link_to vanilla-rb}) is "Markdown". This means that the `content` of this snip will be passed through `Vanilla::Renderers::Markdown` before it is then rendered to the page. There are several different renders provided by Vanilla.rb at the moment:
|
24
|
+
|
25
|
+
* Markdown - as described above
|
26
|
+
* Textile - which performs similarly for Textile. This means that you can mix how you write the content of snips!
|
27
|
+
* Raw - which simply returns the content of the snip, as-is. If you attach a `.raw` extension to this url, you'll see it in action
|
28
|
+
* Bold - simply wraps the content in bold. It's a demo, essentially.
|
29
|
+
* Erb - passes the snip contents through Ruby's `Erb` library. It also makes some information available for use by ruby code within the snip's contents
|
30
|
+
* Ruby - parses the snips content as Ruby itself.
|
31
|
+
|
32
|
+
It's using this last renderer that a second concept of Vanilla is implemented - dynasnips.
|
33
|
+
|
34
|
+
|
35
|
+
Dynasnips
|
36
|
+
--------
|
37
|
+
|
38
|
+
Because the curly braces simply cause a snip to be rendered, we can use this in conjunction with the Ruby renderer to run actual code. For instance, in the snip above:
|
39
|
+
|
40
|
+
{raw tutorial-basic-snip-inclusion}
|
41
|
+
|
42
|
+
we can see a reference to the `link_to` snip - <tt>{link\_to snip}</tt>.
|
43
|
+
|
44
|
+
Lets look at the raw content of `link_to`:
|
45
|
+
|
46
|
+
{raw link_to}
|
47
|
+
|
48
|
+
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. So, in the case of <tt>{link\_to snip}</tt>, the only argument is `snip`.
|
49
|
+
|
50
|
+
Vanilla.rb includes a number of dynasnips by default. Here are a couple:
|
51
|
+
|
52
|
+
* `rand`, which generates a random number (a silly demo really); {link_to rand}, or an example: {rand}
|
53
|
+
* `link_to`, to produce a link to another snip
|
54
|
+
* `kind`, which selects and renders sets of snips based on their `kind` attribute (this is how the blog is currently implemented)
|
55
|
+
* `raw`, which displays the raw contents of a snip
|
56
|
+
* `edit`, which implements the editor
|
57
|
+
* `index`, which shows all of the available snips: {link_to index}
|
58
|
+
* ... and several others.
|
59
|
+
|
60
|
+
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 {link_to hello\_world}:
|
61
|
+
|
62
|
+
{raw hello_world}
|
63
|
+
|
64
|
+
which, when rendered, gives:
|
65
|
+
|
66
|
+
{hello_world}
|
67
|
+
|
68
|
+
Note that the `handle` method can take one (optional) argument. Lets try including it with <tt>{hello\_world Dave}</tt>:
|
69
|
+
|
70
|
+
{hello_world Dave}
|
71
|
+
|
72
|
+
Anyway - that should be enough to get you started.
|
73
|
+
END_OF_TUTORIAL
|
74
|
+
|
75
|
+
tutorial.save
|
76
|
+
|
77
|
+
tutorial_basic_snip_inclusion = Snip.new(:name => 'tutorial-basic-snip-inclusion')
|
78
|
+
tutorial_basic_snip_inclusion.content = <<-EOS
|
79
|
+
This is a snip, which includes another {link_to snip}: {tutorial-another-snip}
|
80
|
+
EOS
|
81
|
+
tutorial_basic_snip_inclusion.save
|
82
|
+
|
83
|
+
tutorial_another_snip = Snip.new(:name => 'tutorial-another-snip')
|
84
|
+
tutorial_another_snip.content = "this is another snip!"
|
85
|
+
tutorial_another_snip.save
|
86
|
+
|
87
|
+
hello_world = Snip.new(:name => 'hello_world', :render_as => "Ruby")
|
88
|
+
hello_world.content = <<-END_OF_RUBY
|
89
|
+
class HelloWorld
|
90
|
+
# although the name doesn't need to match the snip name,
|
91
|
+
# it's simple to follow that convention where appropriate
|
92
|
+
|
93
|
+
def handle(name=nil)
|
94
|
+
if name
|
95
|
+
"Hey \#{name} - Hello World!"
|
96
|
+
else
|
97
|
+
"Hello World!"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# note that this code must evaluate to a class. One way of achieving that is by
|
102
|
+
# putting 'self' at the end of the class definition.
|
103
|
+
self
|
104
|
+
end
|
105
|
+
# Another way is by referring to the class at the end of the content. Either works fine.
|
106
|
+
HelloWorld
|
107
|
+
END_OF_RUBY
|
108
|
+
hello_world.save
|
109
|
+
|
110
|
+
snip = Snip.new(:name => 'snip', :render_as => "Markdown")
|
111
|
+
snip.content = <<-EOS
|
112
|
+
A snip is the basic building block of information for {link_to vanilla-rb}. Essentially, it is a piece of content with arbitrary attributes. Vanilla anticipates the presence of some key attributes:
|
113
|
+
|
114
|
+
* `name` - the name of the snip, which is how it will be referred to. The `name` of this snip is _snip_.
|
115
|
+
* `content` - the default part of the snip to render. You can see the `content` of this snip <a href="/snip/content.raw">here</a>.
|
116
|
+
* `render_as` - the name of the renderer to use when rendering the content. The `render_as` of this snip is {snip.render_as}.
|
117
|
+
|
118
|
+
One implementation of the snip store is {link_to soup}.
|
119
|
+
EOS
|
120
|
+
snip.save
|
121
|
+
|
122
|
+
soup = Snip.new(:name => 'soup')
|
123
|
+
soup.content = <<-EOS
|
124
|
+
Soup is a data store supporting the {link_to snip}-space that {link_to vanilla-rb} expects.
|
125
|
+
|
126
|
+
It's hosted on github <a href="http://github.com/lazyatom/soup">here</a>.
|
127
|
+
EOS
|
128
|
+
soup.save
|
129
|
+
|
130
|
+
vanilla_rb = Snip.new(:name => 'vanilla-rb', :render_as => "Markdown")
|
131
|
+
vanilla_rb.content = <<-EOS
|
132
|
+
Vanilla.rb is the software powering this site. It's a sort-of wiki/bliki thing, based on {link_to vanilla}.
|
133
|
+
|
134
|
+
Here's the [introductory blog post][3].
|
135
|
+
|
136
|
+
It's developed on [github][1], and has a [lighthouse bug tracker][2]. At the moment it's not very well documented, since I'm exploring how the concept might work and the internals are subject to change. However, please do play around with it.
|
137
|
+
|
138
|
+
Here's the tutorial (helpfully included from {link_to vanilla-rb-tutorial}).
|
139
|
+
|
140
|
+
{vanilla-rb-tutorial}
|
141
|
+
|
142
|
+
|
143
|
+
[1]: http://github.com/lazyatom/vanilla
|
144
|
+
[2]: http://lazyatom.lighthouseapp.com/projects/11797-vanilla/tickets
|
145
|
+
[3]: http://interblah.net/introducing-vanilla-rb
|
146
|
+
EOS
|
147
|
+
vanilla_rb.save
|
148
|
+
|
149
|
+
vanilla = Snip.new(:name => 'vanilla')
|
150
|
+
vanilla.content = <<-EOS
|
151
|
+
The bliki upon which {link_to vanilla-rb} is based, writen by [Christian Langreiter][1]
|
152
|
+
|
153
|
+
[Official Web HQ][2]
|
154
|
+
|
155
|
+
[1]: http://www.langreiter.com
|
156
|
+
[2]: http://www.vanillasite.at
|
157
|
+
EOS
|
158
|
+
vanilla.save
|
@@ -0,0 +1,85 @@
|
|
1
|
+
test = Snip.new(:name => "test")
|
2
|
+
test.content =<<EOF
|
3
|
+
Linking is good: {link_to bold}
|
4
|
+
Here's a bold snip: {bold}
|
5
|
+
|
6
|
+
- Here's a random number between 5 and 15: {rand 5,15}
|
7
|
+
- Here's a random number between 1 and 90 (the default min): {rand 90}
|
8
|
+
- Here's a random number between 1 and 100 (the default range): {rand}
|
9
|
+
|
10
|
+
And lets include some textile:
|
11
|
+
|
12
|
+
{textile_example}
|
13
|
+
|
14
|
+
The source for that was
|
15
|
+
|
16
|
+
{pre textile_example}
|
17
|
+
|
18
|
+
And lets include some markdown!:
|
19
|
+
|
20
|
+
{markdown_example}
|
21
|
+
|
22
|
+
The source for that was
|
23
|
+
|
24
|
+
{pre markdown_example}
|
25
|
+
|
26
|
+
How about some {link_to debug} information: {debug}
|
27
|
+
|
28
|
+
What about a missing snip? Lets try to include one: {monkey}
|
29
|
+
|
30
|
+
And an error when running? {bad_dynasnip}
|
31
|
+
|
32
|
+
EOF
|
33
|
+
test.render_as = "Markdown"
|
34
|
+
test.save
|
35
|
+
|
36
|
+
bold = Snip.new(:name => "bold")
|
37
|
+
bold.content =<<EOF
|
38
|
+
Snip2 in da house!
|
39
|
+
EOF
|
40
|
+
bold.render_as = "Bold"
|
41
|
+
bold.save
|
42
|
+
|
43
|
+
textile = Snip.new(:name => "textile_example")
|
44
|
+
textile.content =<<EOF
|
45
|
+
|
46
|
+
# testing lists
|
47
|
+
# because lists are common things
|
48
|
+
|
49
|
+
monkey
|
50
|
+
|
51
|
+
what the *hell* are __you__ looking at?
|
52
|
+
|
53
|
+
"Beyotch":http://example.com
|
54
|
+
|
55
|
+
EOF
|
56
|
+
textile.render_as = "Textile"
|
57
|
+
textile.save
|
58
|
+
|
59
|
+
textile = Snip.new(:name => "markdown_example")
|
60
|
+
textile.content =<<EOF
|
61
|
+
|
62
|
+
# testing header
|
63
|
+
|
64
|
+
so, how are you?
|
65
|
+
|
66
|
+
- item one
|
67
|
+
- item two
|
68
|
+
- item three
|
69
|
+
|
70
|
+
|
71
|
+
what the *hell* are looking at, [beyotch](http://example.com)?
|
72
|
+
EOF
|
73
|
+
textile.render_as = "Markdown"
|
74
|
+
textile.save
|
75
|
+
|
76
|
+
bad_dynasnip = Snip.new(:name => "bad_dynasnip", :render_as => "Ruby")
|
77
|
+
bad_dynasnip.content = <<EOF
|
78
|
+
class BadDynasnip
|
79
|
+
def get(*args)
|
80
|
+
raise "Oh no"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
BadDynasnip
|
84
|
+
EOF
|
85
|
+
bad_dynasnip.save
|
data/lib/vanilla.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'soup', '>= 0.1.4'
|
4
|
+
require 'soup'
|
5
|
+
require 'vanilla/snip_handling'
|
6
|
+
|
7
|
+
module Vanilla
|
8
|
+
include SnipHandling
|
9
|
+
end
|
10
|
+
|
11
|
+
# Load all the other renderer subclasses
|
12
|
+
Dir[File.join(File.dirname(__FILE__), 'vanilla', 'renderers', '*.rb')].each { |f| require f }
|
13
|
+
|
14
|
+
# Load the routing information
|
15
|
+
require 'vanilla/routes'
|
16
|
+
|
17
|
+
# Load all the base dynasnip classes
|
18
|
+
Dir[File.join(File.dirname(__FILE__), 'vanilla', 'dynasnips', '*.rb')].each do |dynasnip|
|
19
|
+
require dynasnip
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require 'vanilla/dynasnip'
|
3
|
+
|
4
|
+
describe Dynasnip, "when storing attributes" do
|
5
|
+
class TestDyna < Dynasnip
|
6
|
+
attribute :test_attribute, "test attribute content"
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@fake_app = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should make the attribute available as an instance method" do
|
14
|
+
TestDyna.new(@fake_app).test_attribute.should == "test attribute content"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should store the attribute in the soup" do
|
18
|
+
TestDyna.persist!
|
19
|
+
Soup['test_dyna'].test_attribute.should == "test attribute content"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should allow the attribute to be overriden by the soup contents" do
|
23
|
+
TestDyna.persist!
|
24
|
+
snip = Soup['test_dyna']
|
25
|
+
snip.test_attribute = "altered content"
|
26
|
+
snip.save
|
27
|
+
|
28
|
+
TestDyna.new(@fake_app).test_attribute.should == "altered content"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe Vanilla::Renderers::Base do
|
4
|
+
describe "in general" do
|
5
|
+
before(:each) do
|
6
|
+
create_snip(:name => "test", :content => "content content", :part => "part content")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should render the contents part of the snip as it is" do
|
10
|
+
response_body_for("/test").should == "content content"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should render the specified part of the snip" do
|
14
|
+
response_body_for("/test/part").should == "part content"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should include the contents of a referenced snip" do
|
18
|
+
create_snip(:name => "snip_with_inclusions", :content => "loading {test}")
|
19
|
+
response_body_for("/snip_with_inclusions").should == "loading content content"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should perform snip inclusion when rendering a part" do
|
23
|
+
create_snip(:name => "snip_with_inclusions", :content => "other content", :part => "loading {test}")
|
24
|
+
response_body_for("/snip_with_inclusions/part").should == "loading content content"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should include other snips using their renderers" do
|
28
|
+
create_snip(:name => "including_snip", :content => "lets include {another_snip}")
|
29
|
+
create_snip(:name => "another_snip", :content => "blah", :render_as => "Bold")
|
30
|
+
response_body_for("/including_snip").gsub(/\s+/, ' ').should == "lets include <b>blah</b>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when trying to include a missing snip" do
|
35
|
+
it "should return a string describing the missing snip" do
|
36
|
+
create_snip(:name => 'blah', :content => 'include a {missing_snip}')
|
37
|
+
response_body_for("/blah").should == "include a [snip 'missing_snip' cannot be found]"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe Vanilla::Renderers::Erb, "when rendering" do
|
4
|
+
def erb_snip(params)
|
5
|
+
create_snip(params.merge(:render_as => "Erb"))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should insert evaluated Erb content into the snip" do
|
9
|
+
erb_snip(:name => "test", :content => "<%= 1 + 2 %>")
|
10
|
+
response_body_for("/test").should == "3"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should evaluate Erb content in the snip" do
|
14
|
+
erb_snip(:name => "test", :content => "<% if false %>monkey<% else %>donkey<% end %>")
|
15
|
+
response_body_for("/test").should == "donkey"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should expose the snip as an instance variable" do
|
19
|
+
erb_snip(:name => "test", :content => "<%= @snip.name %>")
|
20
|
+
response_body_for("/test").should == "test"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "shoudl expose the app as an instance variable" do
|
24
|
+
erb_snip(:name => "test", :content => "<%= @app.class.name %>")
|
25
|
+
response_body_for("/test").should == "Vanilla::App"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe Vanilla::Renderers::Markdown, "when rendering" do
|
4
|
+
def markdown_snip(attributes)
|
5
|
+
create_snip(attributes.merge(:render_as => "Markdown"))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should return the snip contents rendered via Markdown" do
|
9
|
+
content = <<Markdown
|
10
|
+
# markdown
|
11
|
+
|
12
|
+
* totally
|
13
|
+
* [rocks](http://www.example.com)!
|
14
|
+
Markdown
|
15
|
+
markdown_snip(:name => "test", :content => content)
|
16
|
+
response_body_for("/test").should == BlueCloth.new(content).to_html
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should include other snips using their renderers" do
|
20
|
+
markdown_snip(:name => "test", :content => <<-Markdown
|
21
|
+
# markdown
|
22
|
+
|
23
|
+
and so lets include {another_snip}
|
24
|
+
Markdown
|
25
|
+
)
|
26
|
+
create_snip(:name => "another_snip", :content => "blah", :render_as => "Bold")
|
27
|
+
response_body_for("/test").gsub(/\s+/, ' ').should == "<h1>markdown</h1> <p>and so lets include <b>blah</b> </p>"
|
28
|
+
end
|
29
|
+
end
|