vanilla 1.0.2 → 1.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 (105) hide show
  1. data/Rakefile +112 -109
  2. data/bin/vanilla +35 -6
  3. data/lib/vanilla.rb +10 -14
  4. data/lib/vanilla/app.rb +109 -41
  5. data/lib/vanilla/console.rb +22 -2
  6. data/lib/vanilla/dynasnip.rb +4 -36
  7. data/lib/vanilla/renderers.rb +12 -0
  8. data/lib/vanilla/renderers/base.rb +58 -34
  9. data/lib/vanilla/renderers/bold.rb +0 -2
  10. data/lib/vanilla/renderers/erb.rb +1 -3
  11. data/lib/vanilla/renderers/haml.rb +13 -0
  12. data/lib/vanilla/renderers/markdown.rb +0 -2
  13. data/lib/vanilla/renderers/raw.rb +0 -2
  14. data/lib/vanilla/renderers/ruby.rb +12 -6
  15. data/lib/vanilla/renderers/textile.rb +0 -2
  16. data/lib/vanilla/request.rb +19 -17
  17. data/lib/vanilla/routes.rb +9 -20
  18. data/lib/vanilla/snip_reference_parser.rb +94 -0
  19. data/lib/vanilla/static.rb +28 -0
  20. data/pristine_app/Gemfile +3 -0
  21. data/pristine_app/Gemfile.lock +32 -0
  22. data/pristine_app/README +47 -0
  23. data/pristine_app/config.ru +26 -0
  24. data/pristine_app/public/vanilla.css +15 -0
  25. data/pristine_app/soups/base/layout.snip +18 -0
  26. data/pristine_app/soups/base/start.snip +19 -0
  27. data/pristine_app/soups/dynasnips/current_snip.rb +29 -0
  28. data/{lib/vanilla → pristine_app/soups}/dynasnips/debug.rb +5 -3
  29. data/pristine_app/soups/dynasnips/index.rb +12 -0
  30. data/{lib/vanilla → pristine_app/soups}/dynasnips/link_to.rb +4 -2
  31. data/pristine_app/soups/dynasnips/link_to_current_snip.rb +14 -0
  32. data/pristine_app/soups/dynasnips/page_title.rb +9 -0
  33. data/{lib/vanilla → pristine_app/soups}/dynasnips/pre.rb +7 -5
  34. data/{lib/vanilla → pristine_app/soups}/dynasnips/raw.rb +8 -5
  35. data/pristine_app/soups/extras/comments.rb +78 -0
  36. data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/kind.rb +19 -17
  37. data/{lib/vanilla/dynasnips → pristine_app/soups/extras}/rand.rb +2 -0
  38. data/pristine_app/soups/extras/url_to.rb +7 -0
  39. data/pristine_app/soups/tutorial/bad_dynasnip.snip +8 -0
  40. data/pristine_app/soups/tutorial/hello_world.snip +20 -0
  41. data/pristine_app/soups/tutorial/markdown_example.snip +13 -0
  42. data/pristine_app/soups/tutorial/snip.snip +9 -0
  43. data/pristine_app/soups/tutorial/soup.snip +3 -0
  44. data/pristine_app/soups/tutorial/test.snip +30 -0
  45. data/pristine_app/soups/tutorial/textile_example.snip +11 -0
  46. data/pristine_app/soups/tutorial/tutorial-another-snip.snip +1 -0
  47. data/pristine_app/soups/tutorial/tutorial-basic-snip-inclusion.snip +1 -0
  48. data/pristine_app/soups/tutorial/tutorial-dynasnips.snip.markdown +56 -0
  49. data/pristine_app/soups/tutorial/tutorial-layout.snip +56 -0
  50. data/pristine_app/soups/tutorial/tutorial-links.snip +4 -0
  51. data/pristine_app/soups/tutorial/tutorial-renderers.snip.markdown +77 -0
  52. data/pristine_app/soups/tutorial/tutorial.snip.markdown +69 -0
  53. data/pristine_app/soups/tutorial/vanilla-rb.snip +16 -0
  54. data/pristine_app/soups/tutorial/vanilla.snip +8 -0
  55. data/test/dynasnip_test.rb +42 -0
  56. data/test/dynasnips/link_to_current_snip_test.rb +19 -0
  57. data/test/dynasnips/link_to_test.rb +27 -0
  58. data/test/dynasnips/page_title_test.rb +19 -0
  59. data/test/renderers/base_renderer_test.rb +43 -0
  60. data/test/renderers/erb_renderer_test.rb +29 -0
  61. data/test/renderers/haml_renderer_test.rb +35 -0
  62. data/test/renderers/markdown_renderer_test.rb +31 -0
  63. data/test/renderers/raw_renderer_test.rb +23 -0
  64. data/test/renderers/ruby_renderer_test.rb +59 -0
  65. data/test/snip_inclusion_test.rb +56 -0
  66. data/test/snip_reference_parser_test.rb +123 -0
  67. data/test/test_helper.rb +75 -0
  68. data/test/vanilla_app_test.rb +83 -0
  69. data/test/vanilla_presenting_test.rb +125 -0
  70. data/test/vanilla_request_test.rb +87 -0
  71. metadata +179 -78
  72. data/config.example.yml +0 -5
  73. data/config.ru +0 -9
  74. data/lib/defensio.rb +0 -59
  75. data/lib/tasks/vanilla.rake +0 -177
  76. data/lib/vanilla/dynasnips/comments.rb +0 -108
  77. data/lib/vanilla/dynasnips/current_snip.rb +0 -32
  78. data/lib/vanilla/dynasnips/edit.rb +0 -63
  79. data/lib/vanilla/dynasnips/edit_link.rb +0 -24
  80. data/lib/vanilla/dynasnips/index.rb +0 -11
  81. data/lib/vanilla/dynasnips/link_to_current_snip.rb +0 -16
  82. data/lib/vanilla/dynasnips/login.rb +0 -56
  83. data/lib/vanilla/dynasnips/new.rb +0 -14
  84. data/lib/vanilla/dynasnips/notes.rb +0 -42
  85. data/lib/vanilla/dynasnips/url_to.rb +0 -7
  86. data/lib/vanilla/snip_handling.rb +0 -33
  87. data/lib/vanilla/snips/start.rb +0 -27
  88. data/lib/vanilla/snips/system.rb +0 -76
  89. data/lib/vanilla/snips/tutorial.rb +0 -157
  90. data/lib/vanilla/test_snips.rb +0 -85
  91. data/public/hatch.png +0 -0
  92. data/public/javascripts/jquery.js +0 -3549
  93. data/public/javascripts/vanilla.js +0 -21
  94. data/spec/dynasnip_spec.rb +0 -31
  95. data/spec/renderers/base_renderer_spec.rb +0 -40
  96. data/spec/renderers/erb_renderer_spec.rb +0 -27
  97. data/spec/renderers/markdown_renderer_spec.rb +0 -29
  98. data/spec/renderers/raw_renderer_spec.rb +0 -21
  99. data/spec/renderers/ruby_renderer_spec.rb +0 -42
  100. data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +0 -35
  101. data/spec/spec_helper.rb +0 -64
  102. data/spec/vanilla_app_spec.rb +0 -38
  103. data/spec/vanilla_presenting_spec.rb +0 -84
  104. data/spec/vanilla_request_spec.rb +0 -73
  105. data/spec/vanilla_snip_finding_spec.rb +0 -28
@@ -1,108 +0,0 @@
1
- require 'vanilla/dynasnip'
2
- require 'defensio'
3
- require 'date'
4
-
5
- class Comments < Dynasnip
6
- usage %|
7
- Embed comments within snips!
8
-
9
- {comments <snip-name>}
10
-
11
- This will embed a list of comments, and a comment form, in a snip
12
- If the snip is being rendered within another snip, it will show a link to the snip,
13
- with the number of comments.
14
- |
15
-
16
- def get(snip_name=nil, disable_new_comments=false)
17
- snip_name = snip_name || app.request.params[:snip]
18
- return usage if self.class.snip_name == snip_name
19
- comments = Soup.sieve(:commenting_on => snip_name)
20
- comments_html = if app.request.snip_name == snip_name
21
- rendered_comments = render_comments(comments)
22
- rendered_comments += comment_form.gsub('SNIP_NAME', snip_name) unless disable_new_comments
23
- rendered_comments
24
- else
25
- %{<a href="#{Vanilla::Routes.url_to(snip_name)}">#{comments.length} comments for #{snip_name}</a>}
26
- end
27
- return comments_html
28
- end
29
-
30
- def post(*args)
31
- snip_name = app.request.params[:snip]
32
- existing_comments = Soup.sieve(:commenting_on => snip_name)
33
- comment = app.request.params.reject { |k,v| ![:author, :email, :website, :content].include?(k) }
34
-
35
- return "You need to add some details!" if comment.empty?
36
-
37
- comment = check_for_spam(comment)
38
-
39
- if comment[:spam]
40
- "Sorry - your comment looks like spam, according to Defensio :("
41
- else
42
- return "No spam today, thanks anyway" unless app.request.params[:human] == 'human'
43
- Soup << comment.merge({
44
- :name => "#{snip_name}-comment-#{existing_comments.length + 1}",
45
- :commenting_on => snip_name,
46
- :created_at => Time.now
47
- })
48
- "Thanks for your comment! Back to {link_to #{snip_name}}"
49
- end
50
- end
51
-
52
- def render_comments(comments)
53
- "<h2>Comments</h2><ol class='comments'>" + comments.map do |comment|
54
- rendered_comment = comment_template.gsub('COMMENT_CONTENT', app.render(comment)).
55
- gsub('COMMENT_DATE', comment.created_at)
56
- author = comment.author
57
- author = "Anonymous" unless author && author != ""
58
- if comment.website && comment.website != ""
59
- rendered_comment.gsub!('COMMENT_AUTHOR', "<a href=\"#{comment.website}\">#{author}</a>")
60
- else
61
- rendered_comment.gsub!('COMMENT_AUTHOR', author)
62
- end
63
- rendered_comment
64
- end.join + "</ol>"
65
- end
66
-
67
- def check_for_spam(comment)
68
- snip_date = Date.parse(Soup[app.request.params[:snip]].updated_at)
69
- Defensio.configure(app.config[:defensio])
70
- defensio_params = {
71
- :comment_author_email => comment[:email],
72
- :comment_author => comment[:author],
73
- :comment_author_url => comment[:website],
74
- :comment_content => comment[:content],
75
- :comment_type => "comment",
76
- :user_ip => app.request.ip,
77
- :article_date => snip_date.strftime("%Y/%m/%d")
78
- }
79
- audit = Defensio.audit_comment(defensio_params)
80
-
81
- # Augment the comment hash
82
- comment[:user_ip] = app.request.ip
83
- comment[:spamminess] = audit["defensio_result"]["spaminess"]
84
- comment[:spam] = audit["defensio_result"]["spam"]
85
- comment[:defensio_signature] = audit["defensio_result"]["signature"]
86
- comment[:defensio_message] = audit["defensio_result"]["message"] if audit["defensio_result"]["message"]
87
- comment[:defensio_status] = audit["defensio_result"]["status"]
88
- comment
89
- end
90
-
91
- attribute :comment_template, %{
92
- <li>
93
- <p>COMMENT_AUTHOR (COMMENT_DATE)</p>
94
- <div>COMMENT_CONTENT</div>
95
- </li>
96
- }
97
-
98
- attribute :comment_form, %{
99
- <form class="comments" action="/#{snip_name}?snip=SNIP_NAME" method="POST">
100
- <label>Name: <input type="text" name="author"></input></label>
101
- <label>Email: <input type="text" name="email"></input></label>
102
- <label>Website: <input type="text" name="website"></input></label>
103
- <textarea name="content"></textarea>
104
- <label class="human">Type 'human' if you are one: <input type="text" name="human"></input></label>
105
- <button>Submit</button>
106
- </form>
107
- }
108
- end
@@ -1,32 +0,0 @@
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 the name of the current snip in a consistent way:
10
-
11
- {current_snip name}
12
-
13
- will output the name of the current snip, or the name of the snip currently being edited.
14
- |
15
-
16
- def handle(*args)
17
- if args[0] == 'name'
18
- if app.request.snip_name == 'edit' # we're editing so don't use this name
19
- app.request.params[:snip_to_edit]
20
- else
21
- app.request.snip_name
22
- end
23
- else
24
- if app.request.snip
25
- app.render(app.request.snip, app.request.part)
26
- else
27
- app.response.status = 404
28
- "Couldn't find snip {link_to #{app.request.snip_name}}"
29
- end
30
- end
31
- end
32
- end
@@ -1,63 +0,0 @@
1
- require 'vanilla/dynasnip'
2
- require 'vanilla/dynasnips/login'
3
-
4
- # The edit dyna will load the snip given in the 'snip_to_edit' part of the
5
- # params
6
- class EditSnip < Dynasnip
7
- include Login::Helper
8
-
9
- snip_name "edit"
10
-
11
- def get(snip_name=nil)
12
- return login_required unless logged_in?
13
- snip = Vanilla.snip(snip_name || app.request.params[:name])
14
- edit(snip)
15
- end
16
-
17
- def post(*args)
18
- return login_required unless logged_in?
19
- snip_attributes = cleaned_params
20
- snip_attributes.delete(:save_button)
21
- return 'no params' if snip_attributes.empty?
22
- snip = Vanilla.snip(snip_attributes[:name])
23
- snip_attributes[:updated_at] = Time.now
24
- snip_attributes.each do |name, value|
25
- snip.__send__(:set_value, name, value)
26
- end
27
- snip.save
28
- %{Saved snip #{Vanilla::Routes.link_to snip_attributes[:name]} ok}
29
- rescue Exception => e
30
- snip_attributes[:created_at] ||= Time.now
31
- Soup << snip_attributes
32
- %{Created snip #{Vanilla::Routes.link_to snip_attributes[:name]} ok}
33
- end
34
-
35
- def edit(snip)
36
- renderer = Vanilla::Renderers::Erb.new(app)
37
- renderer.instance_eval { @snip_to_edit = snip } # hacky!
38
- snip_in_edit_template = renderer.render_without_including_snips(Vanilla.snip('edit'), :template)
39
- prevent_snip_inclusion(snip_in_edit_template)
40
- end
41
-
42
- private
43
-
44
- def prevent_snip_inclusion(content)
45
- content.gsub("{", "&#123;").gsub("}" ,"&#125;")
46
- end
47
-
48
- attribute :template, %{
49
- <form action="<%= Vanilla::Routes.url_to 'edit' %>" method="post">
50
- <dl class="attributes">
51
- <% @snip_to_edit.attributes.each do |name, value| %>
52
- <dt><%= name %></dt>
53
- <% num_rows = (value || "").split("\n").length + 1 %>
54
- <dd><textarea name="<%= name %>" class="<%= name %>" rows="<%= num_rows %>"><%=h value %></textarea></dd>
55
- <% end %>
56
- <dt><input class="attribute_name" type="text"></dt>
57
- <dd><textarea></textarea></dd>
58
- </dl>
59
- <a href="#" id="add">Add</a>
60
- <button name='save_button'>Save</button>
61
- </form>
62
- }
63
- end
@@ -1,24 +0,0 @@
1
- require 'vanilla/dynasnip'
2
-
3
- class EditLink < Dynasnip
4
- usage %|
5
- You can use the edit_link snip to insert links for editing other snips. For example:
6
-
7
- &#123;edit_link blah&#125;
8
-
9
- would insert a link to an editor for the blah snip.
10
-
11
- You can also give a custom piece of text for the link, like this:
12
-
13
- &#123;edit_link blah,link-name&#125;|
14
-
15
- def handle(*args)
16
- if args.length < 2
17
- show_usage
18
- else
19
- snip_name = args[0]
20
- link_text = args[1]
21
- Vanilla::Routes.edit_link(snip_name, link_text)
22
- end
23
- end
24
- end
@@ -1,11 +0,0 @@
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
@@ -1,16 +0,0 @@
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
@@ -1,56 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,42 +0,0 @@
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
@@ -1,7 +0,0 @@
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
@@ -1,33 +0,0 @@
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
@@ -1,27 +0,0 @@
1
- start = Snip.new(:name => 'start')
2
- start.content = <<-START
3
- Welcome to Vanilla.rb
4
- =============
5
-
6
- Vanilla.rb is a web-experiment (a _websperiment_?) about storing and reusing data on a website. It can also function as a [bliki](http://www.wikipedia.com/wiki/Bliki).
7
-
8
- The fundamental building block is the 'snip', which is a malleable object that can store content and arbitrary metadata.
9
-
10
- ---
11
-
12
- This is the {link_to start} snip, which is the default home page.
13
-
14
- You might want to check out the {link_to vanilla-rb-tutorial} snip.
15
-
16
- In fact - I'll include it here for you:
17
-
18
- {vanilla-rb-tutorial}
19
-
20
- ---
21
-
22
- 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.
23
-
24
- Anyway, welcome.
25
- START
26
- start.render_as = "Markdown"
27
- start.save