vanilla 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/README +52 -0
  2. data/Rakefile +118 -0
  3. data/bin/vanilla +9 -0
  4. data/config.example.yml +5 -0
  5. data/config.ru +9 -0
  6. data/lib/defensio.rb +59 -0
  7. data/lib/tasks/vanilla.rake +178 -0
  8. data/lib/vanilla/app.rb +87 -0
  9. data/lib/vanilla/console.rb +3 -0
  10. data/lib/vanilla/dynasnip.rb +110 -0
  11. data/lib/vanilla/dynasnips/comments.rb +108 -0
  12. data/lib/vanilla/dynasnips/current_snip.rb +32 -0
  13. data/lib/vanilla/dynasnips/debug.rb +13 -0
  14. data/lib/vanilla/dynasnips/edit.rb +63 -0
  15. data/lib/vanilla/dynasnips/edit_link.rb +24 -0
  16. data/lib/vanilla/dynasnips/index.rb +11 -0
  17. data/lib/vanilla/dynasnips/kind.rb +70 -0
  18. data/lib/vanilla/dynasnips/link_to.rb +14 -0
  19. data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
  20. data/lib/vanilla/dynasnips/login.rb +56 -0
  21. data/lib/vanilla/dynasnips/new.rb +14 -0
  22. data/lib/vanilla/dynasnips/notes.rb +42 -0
  23. data/lib/vanilla/dynasnips/pre.rb +19 -0
  24. data/lib/vanilla/dynasnips/rand.rb +27 -0
  25. data/lib/vanilla/dynasnips/raw.rb +19 -0
  26. data/lib/vanilla/dynasnips/url_to.rb +7 -0
  27. data/lib/vanilla/renderers/base.rb +78 -0
  28. data/lib/vanilla/renderers/bold.rb +9 -0
  29. data/lib/vanilla/renderers/erb.rb +16 -0
  30. data/lib/vanilla/renderers/markdown.rb +13 -0
  31. data/lib/vanilla/renderers/raw.rb +9 -0
  32. data/lib/vanilla/renderers/ruby.rb +35 -0
  33. data/lib/vanilla/renderers/textile.rb +13 -0
  34. data/lib/vanilla/request.rb +68 -0
  35. data/lib/vanilla/routes.rb +29 -0
  36. data/lib/vanilla/snip_handling.rb +33 -0
  37. data/lib/vanilla/snips/start.rb +18 -0
  38. data/lib/vanilla/snips/system.rb +76 -0
  39. data/lib/vanilla/snips/tutorial.rb +158 -0
  40. data/lib/vanilla/test_snips.rb +85 -0
  41. data/lib/vanilla.rb +20 -0
  42. data/spec/dynasnip_spec.rb +31 -0
  43. data/spec/renderers/base_renderer_spec.rb +40 -0
  44. data/spec/renderers/erb_renderer_spec.rb +27 -0
  45. data/spec/renderers/markdown_renderer_spec.rb +29 -0
  46. data/spec/renderers/raw_renderer_spec.rb +21 -0
  47. data/spec/renderers/ruby_renderer_spec.rb +42 -0
  48. data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
  49. data/spec/soup_test.db +0 -0
  50. data/spec/spec_helper.rb +64 -0
  51. data/spec/vanilla_app_spec.rb +38 -0
  52. data/spec/vanilla_presenting_spec.rb +84 -0
  53. data/spec/vanilla_request_spec.rb +73 -0
  54. data/spec/vanilla_snip_finding_spec.rb +28 -0
  55. 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,13 @@
1
+ require 'vanilla/renderers/base'
2
+
3
+ require 'rubygems'
4
+ gem 'RedCloth'
5
+ require 'redcloth'
6
+
7
+ module Vanilla::Renderers
8
+ class Textile < Base
9
+ def process_text(content)
10
+ RedCloth.new(content).to_html
11
+ end
12
+ end
13
+ 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> &rarr;
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>&#123;link\_to snip&#125;</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>&#123;link\_to snip&#125;</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>&#123;hello\_world Dave&#125;</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