eduardo-vanilla 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/README +53 -0
  2. data/Rakefile +121 -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.rb +20 -0
  9. data/lib/vanilla/app.rb +87 -0
  10. data/lib/vanilla/console.rb +3 -0
  11. data/lib/vanilla/dynasnip.rb +110 -0
  12. data/lib/vanilla/dynasnips/code.rb +16 -0
  13. data/lib/vanilla/dynasnips/comments.rb +108 -0
  14. data/lib/vanilla/dynasnips/current_snip.rb +32 -0
  15. data/lib/vanilla/dynasnips/debug.rb +13 -0
  16. data/lib/vanilla/dynasnips/edit.rb +63 -0
  17. data/lib/vanilla/dynasnips/edit_link.rb +24 -0
  18. data/lib/vanilla/dynasnips/index.rb +11 -0
  19. data/lib/vanilla/dynasnips/kind.rb +70 -0
  20. data/lib/vanilla/dynasnips/link_to.rb +14 -0
  21. data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
  22. data/lib/vanilla/dynasnips/login.rb +56 -0
  23. data/lib/vanilla/dynasnips/new.rb +14 -0
  24. data/lib/vanilla/dynasnips/notes.rb +42 -0
  25. data/lib/vanilla/dynasnips/pre.rb +19 -0
  26. data/lib/vanilla/dynasnips/rand.rb +27 -0
  27. data/lib/vanilla/dynasnips/raw.rb +19 -0
  28. data/lib/vanilla/dynasnips/url_to.rb +7 -0
  29. data/lib/vanilla/renderers/base.rb +78 -0
  30. data/lib/vanilla/renderers/bold.rb +9 -0
  31. data/lib/vanilla/renderers/erb.rb +16 -0
  32. data/lib/vanilla/renderers/markdown.rb +13 -0
  33. data/lib/vanilla/renderers/raw.rb +9 -0
  34. data/lib/vanilla/renderers/ruby.rb +35 -0
  35. data/lib/vanilla/renderers/textile.rb +13 -0
  36. data/lib/vanilla/request.rb +68 -0
  37. data/lib/vanilla/routes.rb +29 -0
  38. data/lib/vanilla/snip_handling.rb +33 -0
  39. data/lib/vanilla/snips/start.rb +18 -0
  40. data/lib/vanilla/snips/system.rb +76 -0
  41. data/lib/vanilla/snips/tutorial.rb +158 -0
  42. data/lib/vanilla/test_snips.rb +85 -0
  43. data/spec/dynasnip_spec.rb +31 -0
  44. data/spec/renderers/base_renderer_spec.rb +40 -0
  45. data/spec/renderers/erb_renderer_spec.rb +27 -0
  46. data/spec/renderers/markdown_renderer_spec.rb +29 -0
  47. data/spec/renderers/raw_renderer_spec.rb +21 -0
  48. data/spec/renderers/ruby_renderer_spec.rb +42 -0
  49. data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
  50. data/spec/soup_test.db +0 -0
  51. data/spec/spec_helper.rb +64 -0
  52. data/spec/vanilla_app_spec.rb +38 -0
  53. data/spec/vanilla_presenting_spec.rb +84 -0
  54. data/spec/vanilla_request_spec.rb +73 -0
  55. data/spec/vanilla_snip_finding_spec.rb +28 -0
  56. metadata +173 -0
@@ -0,0 +1,11 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class Index < Dynasnip
4
+ def get(*args)
5
+ # TODO: figure out a way around calling Soup/AR methods directly.
6
+ list = Soup.tuple_class.find_all_by_name('name').map { |tuple|
7
+ "<li>#{Vanilla::Routes.link_to tuple.value}</li>"
8
+ }
9
+ "<ol>#{list}</ol>"
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ require 'vanilla/dynasnip'
2
+ require 'atom'
3
+ require 'date'
4
+
5
+ class Kind < Dynasnip
6
+ def handle(kind, limit=10, as=:html)
7
+ as = as.to_sym
8
+ snips = Soup.sieve(:kind => kind)
9
+ entries = snips.sort_by { |s| s.created_at || '' }.reverse[0...limit.to_i].map do |snip|
10
+ render_entry_in_template(snip, as, kind)
11
+ end
12
+ render_entry_collection(snips, entries, as, kind)
13
+ end
14
+
15
+ def render_entry_in_template(snip, as, kind)
16
+ rendered_contents = prepare_snip_contents(snip)
17
+ case as
18
+ when :html
19
+ snip_template.
20
+ gsub('SNIP_KIND', kind).
21
+ gsub('SNIP_NAME', snip.name).
22
+ gsub('CREATED_AT', snip.created_at || '').
23
+ gsub('SNIP_CONTENT', rendered_contents)
24
+ when :xml
25
+ Atom::Entry.new do |e|
26
+ e.published = DateTime.parse(snip.created_at)
27
+ e.updated = DateTime.parse(snip.updated_at || snip.created_at)
28
+ e.content = Atom::Content::Html.new(rendered_contents)
29
+ e.title = snip.name
30
+ e.authors = [Atom::Person.new(:name => snip.author || domain)]
31
+ e.links << Atom::Link.new(:href => "http://#{domain}#{Vanilla::Routes.url_to(snip.name)}")
32
+ e.id = "tag:#{domain},#{(snip.created_at || Date.today.to_s).split[0]}:/#{snip.name}"
33
+ end
34
+ end
35
+ end
36
+
37
+ def prepare_snip_contents(snip)
38
+ rendered_snip = app.render(snip)
39
+ # make all the links absolute
40
+ rendered_snip.gsub(/href="\//, "href=\"http://#{domain}/")
41
+ end
42
+
43
+ def render_entry_collection(snips, entries, as, kind)
44
+ case as
45
+ when :html
46
+ entries.join
47
+ when :xml
48
+ Atom::Feed.new do |f|
49
+ f.title = feed_title
50
+ f.updated = DateTime.parse(snips[0].updated_at)
51
+ f.id = "tag:#{domain},2008-06-01:kind/#{kind}"
52
+ f.entries = entries
53
+ end.to_xml
54
+ end
55
+ end
56
+
57
+ attribute :feed_title, "Your Blog"
58
+ attribute :domain, "yourdomain.com"
59
+ attribute :snip_template, %{
60
+ <div class="snip SNIP_KIND">
61
+ <div class="details">
62
+ #{Vanilla::Routes.link_to '#', 'SNIP_NAME'}
63
+ <p class="created_at">CREATED_AT</p>
64
+ </div>
65
+ <div class="content">
66
+ SNIP_CONTENT
67
+ </div>
68
+ </div>
69
+ }
70
+ end
@@ -0,0 +1,14 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class LinkTo < Dynasnip
4
+ usage %|
5
+ The link_to dyna lets you create links between snips:
6
+
7
+ {link_to blah}
8
+
9
+ would insert a link to the blah snip.|
10
+
11
+ def handle(snip_name)
12
+ Vanilla.snip_exists?(snip_name) ? Vanilla::Routes.link_to(snip_name) : Vanilla::Routes.new_link(snip_name)
13
+ end
14
+ end
@@ -0,0 +1,16 @@
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
+ if app.request.snip_name == 'edit' # we're editing so don't use this name
11
+ Vanilla::Routes.link_to app.request.params[:snip_to_edit]
12
+ else
13
+ Vanilla::Routes.link_to app.request.snip_name
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ require 'vanilla/dynasnip'
2
+ require 'yaml'
3
+ require 'md5'
4
+
5
+ class Login < Dynasnip
6
+ module Helper
7
+ def logged_in?
8
+ !current_user.nil?
9
+ end
10
+
11
+ def current_user
12
+ app.request.session['logged_in_as']
13
+ end
14
+
15
+ def login_required
16
+ "You need to <a href='/login'>login</a> to do that."
17
+ end
18
+ end
19
+ include Helper
20
+
21
+ def get(*args)
22
+ if logged_in?
23
+ login_controls
24
+ else
25
+ render(self, 'template')
26
+ end
27
+ end
28
+
29
+ def post(*args)
30
+ if app.config[:credentials][cleaned_params[:name]] == MD5.md5(cleaned_params[:password]).to_s
31
+ app.request.session['logged_in_as'] = cleaned_params[:name]
32
+ login_controls
33
+ else
34
+ "login fail!"
35
+ end
36
+ end
37
+
38
+ def delete(*args)
39
+ app.request.session['logged_in_as'] = nil
40
+ "Logged out"
41
+ end
42
+
43
+ attribute :template, <<-EHTML
44
+ <form action='/login' method='post'>
45
+ <label>Name: <input type="text" name="name"></input></label>
46
+ <label>Password: <input type="password" name="password"></input></label>
47
+ <button>login</button>
48
+ </form>
49
+ EHTML
50
+
51
+ private
52
+
53
+ def login_controls
54
+ "logged in as {link_to #{app.request.session['logged_in_as']}}; <a href='/login?_method=delete'>logout</a>"
55
+ end
56
+ end
@@ -0,0 +1,14 @@
1
+ require 'vanilla/dynasnip'
2
+ require 'vanilla/dynasnips/login'
3
+
4
+ class NewSnip < Dynasnip
5
+ include Login::Helper
6
+
7
+ snip_name :new
8
+
9
+ def handle(*arg)
10
+ return login_required unless logged_in?
11
+ base_params = {:render_as => '', :content => '', :author => current_user}.update(app.request.params)
12
+ editor = EditSnip.new(app).edit(Snip.new(base_params))
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class Notes < Dynasnip
4
+ def get(*args)
5
+ all_notes_content = all_notes.map { |snip|
6
+ render_note(snip)
7
+ }.join("")
8
+ snip.main_template.gsub('[notes]', all_notes_content)
9
+ end
10
+
11
+ def post(*args)
12
+ new_note = Snip.new(cleaned_params)
13
+ new_note.name = "note_#{snip.next_note_id}"
14
+ new_note.kind = "note"
15
+ new_note.save
16
+ increment_next_id
17
+ get(*args)
18
+ end
19
+
20
+ private
21
+
22
+ def all_notes
23
+ Snip.with(:kind, "= 'note'")
24
+ end
25
+
26
+ def increment_next_id
27
+ s = snip
28
+ s.next_note_id = s.next_note_id.to_i + 1
29
+ s.save
30
+ end
31
+
32
+ def render_note(note)
33
+ note_link = Vanilla::Routes.link_to(note.name)
34
+ note_content = Vanilla.render(note.name, nil, context, [])
35
+ snip.note_template.gsub('[note]', note_content).gsub('[link]', note_link)
36
+ end
37
+
38
+ attribute :next_note_id, 1
39
+
40
+ attribute :note_template, %{<dt>[link]</dt><dd>[note]</dd>}
41
+ attribute :main_template, %{<dl>[notes]</dl>}
42
+ end
@@ -0,0 +1,19 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class ShowContentInPreTag < Dynasnip
4
+ snip_name "pre"
5
+
6
+ usage %|
7
+ Wraps the contents of the given snip in &lt;pre&gt; tags, e.g.
8
+
9
+ {pre my_snip}
10
+
11
+ You can specify a part to render in pre tags, should you wish:
12
+
13
+ {pre my_snip,specific_part}
14
+ |
15
+
16
+ def handle(snip_name, part=:content)
17
+ %{<pre>#{Vanilla.snip(snip_name).__send__(part || :content)}</pre>}
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class RandomNumber < Dynasnip
4
+ snip_name "rand"
5
+
6
+ usage %|
7
+ Returns a random number, normally between 1 and 100.
8
+ The range can be limited:
9
+
10
+ {rand 50} renders a random number between 1 and 50
11
+ {rand 2,19} renders a random number between 2 and 19
12
+ |
13
+
14
+ def handle(*args)
15
+ min = 1
16
+ max = 100
17
+ max = args[0] if args.length == 1
18
+ if args.length == 2
19
+ min = args[0]
20
+ max = args[1]
21
+ end
22
+ # arguments come in as strings, so we need to convert them.
23
+ min = min.to_i
24
+ max = max.to_i
25
+ (rand(max-min) + min)
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class ShowRawContent < Dynasnip
4
+ snip_name "raw"
5
+
6
+ usage %|
7
+ Displays the raw contents of a snip in &lt;pre&gt; tags, e.g.
8
+
9
+ {raw my_snip}
10
+
11
+ You can specify a part to show, should you wish:
12
+
13
+ {raw my_snip,specific_part}
14
+ |
15
+
16
+ def handle(snip_name, part=:content)
17
+ %{<pre>#{Dynasnip.escape_curly_braces(Vanilla.snip(snip_name).__send__(part || :content))}</pre>}
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'vanilla/dynasnip'
2
+
3
+ class UrlTo < Dynasnip
4
+ def handle(snip_name)
5
+ Snip[snip_name] ? Vanilla::Routes.url_to(snip_name) : "[Snip '#{snip_name}' not found]"
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ require 'vanilla/app'
2
+
3
+ module Vanilla
4
+ module Renderers
5
+ class Base
6
+
7
+ # Render a snip.
8
+ def self.render(snip, part=:content)
9
+ new(app).render(snip, part)
10
+ end
11
+
12
+ def self.escape_curly_braces(str)
13
+ str.gsub("{", "&#123;").gsub("}", "&#125;")
14
+ end
15
+
16
+ attr_reader :app
17
+
18
+ def initialize(app)
19
+ @app = app
20
+ end
21
+
22
+ def self.snip_regexp
23
+ %r{ \{
24
+ ([\w\-]+) (?: \.([\w\-]+) )?
25
+ (?: \s+ ([\w\-,]+) )?
26
+ \} }x
27
+ end
28
+
29
+ # Default behaviour to include a snip's content
30
+ def include_snips(content)
31
+ content.gsub(Vanilla::Renderers::Base.snip_regexp) do
32
+ snip_name = $1
33
+ snip_attribute = $2
34
+ snip_args = $3 ? $3.split(',') : []
35
+
36
+ # Render the snip or snip part with the given args, and the current
37
+ # context, but with the default renderer for that snip. We dispatch
38
+ # *back* out to the root Vanilla.render method to do this.
39
+ snip = Vanilla.snip(snip_name)
40
+ if snip
41
+ app.render(snip, snip_attribute, snip_args)
42
+ else
43
+ app.render_missing_snip(snip_name)
44
+ end
45
+ end
46
+ end
47
+
48
+ # Default rendering behaviour. Subclasses shouldn't really need to touch this.
49
+ def render(snip, part=:content, args=[])
50
+ prepare(snip, part, args)
51
+ processed_text = render_without_including_snips(snip, part)
52
+ include_snips(processed_text)
53
+ end
54
+
55
+ # Subclasses should override this to perform any actions required before
56
+ # rendering
57
+ def prepare(snip, part, args)
58
+ # do nothing, by default
59
+ end
60
+
61
+ def render_without_including_snips(snip, part=:content)
62
+ process_text(raw_content(snip, part))
63
+ end
64
+
65
+ # Handles processing the text of the content.
66
+ # Subclasses should override this method to do fancy text processing
67
+ # like markdown, or loading the content as Ruby code.
68
+ def process_text(content)
69
+ content
70
+ end
71
+
72
+ # Returns the raw content for the selected part of the selected snip
73
+ def raw_content(snip, part)
74
+ snip.__send__((part || :content).to_sym)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ require 'vanilla/renderers/base'
2
+
3
+ module Vanilla::Renderers
4
+ class Bold < Base
5
+ def process_text(content)
6
+ "<b>#{content}</b>"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ require 'vanilla/renderers/base'
2
+
3
+ require 'erb'
4
+ include ERB::Util
5
+
6
+ module Vanilla::Renderers
7
+ class Erb < Base
8
+ def prepare(snip, part=:content, args=[])
9
+ @snip = snip
10
+ end
11
+
12
+ def process_text(content)
13
+ ERB.new(content).result(binding)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ require 'vanilla/renderers/base'
2
+
3
+ require 'rubygems'
4
+ gem 'BlueCloth' # from http://www.deveiate.org/projects/BlueCloth
5
+ require 'bluecloth'
6
+
7
+ module Vanilla::Renderers
8
+ class Markdown < Base
9
+ def process_text(content)
10
+ BlueCloth.new(content).to_html
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'vanilla/renderers/base'
2
+
3
+ module Vanilla::Renderers
4
+ class Raw < Base
5
+ def render(snip, part=:content)
6
+ raw_content(snip, part)
7
+ end
8
+ end
9
+ end