vanilla 1.2 → 1.9.9

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 (106) hide show
  1. data/Rakefile +61 -60
  2. data/bin/vanilla +6 -35
  3. data/config.example.yml +6 -0
  4. data/config.ru +10 -0
  5. data/lib/defensio.rb +59 -0
  6. data/lib/tasks/vanilla.rake +173 -0
  7. data/lib/vanilla.rb +3 -10
  8. data/lib/vanilla/app.rb +48 -104
  9. data/lib/vanilla/console.rb +5 -19
  10. data/lib/vanilla/dynasnips/comments.rb +108 -0
  11. data/lib/vanilla/dynasnips/current_snip.rb +32 -0
  12. data/{pristine_app/soups → lib/vanilla}/dynasnips/debug.rb +3 -5
  13. data/lib/vanilla/dynasnips/edit.rb +60 -0
  14. data/lib/vanilla/dynasnips/edit_link.rb +20 -0
  15. data/{pristine_app/soups → lib/vanilla}/dynasnips/index.rb +2 -4
  16. data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/kind.rb +12 -14
  17. data/{pristine_app/soups → lib/vanilla}/dynasnips/link_to.rb +0 -2
  18. data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
  19. data/lib/vanilla/dynasnips/login.rb +56 -0
  20. data/lib/vanilla/dynasnips/new.rb +14 -0
  21. data/lib/vanilla/dynasnips/notes.rb +42 -0
  22. data/{pristine_app/soups → lib/vanilla}/dynasnips/pre.rb +4 -6
  23. data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/rand.rb +0 -2
  24. data/{pristine_app/soups → lib/vanilla}/dynasnips/raw.rb +5 -8
  25. data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/url_to.rb +0 -0
  26. data/lib/vanilla/renderers/base.rb +22 -32
  27. data/lib/vanilla/renderers/bold.rb +2 -0
  28. data/lib/vanilla/renderers/erb.rb +2 -0
  29. data/lib/vanilla/renderers/markdown.rb +2 -0
  30. data/lib/vanilla/renderers/raw.rb +2 -0
  31. data/lib/vanilla/renderers/ruby.rb +5 -9
  32. data/lib/vanilla/renderers/textile.rb +2 -0
  33. data/lib/vanilla/request.rb +15 -16
  34. data/lib/vanilla/routes.rb +18 -5
  35. data/lib/vanilla/snip_reference.rb +534 -0
  36. data/lib/vanilla/snip_reference.treetop +48 -0
  37. data/lib/vanilla/snip_reference_parser.rb +99 -82
  38. data/lib/vanilla/snips/start.rb +28 -0
  39. data/lib/vanilla/snips/system.rb +77 -0
  40. data/lib/vanilla/snips/tutorial.rb +244 -0
  41. data/lib/vanilla/soup_with_timestamps.rb +21 -0
  42. data/public/hatch.png +0 -0
  43. data/public/javascripts/jquery.autogrow-textarea.js +54 -0
  44. data/public/javascripts/jquery.js +4376 -0
  45. data/public/javascripts/vanilla.js +22 -0
  46. data/spec/dynasnip_spec.rb +28 -0
  47. data/spec/renderers/base_renderer_spec.rb +40 -0
  48. data/spec/renderers/erb_renderer_spec.rb +27 -0
  49. data/spec/renderers/markdown_renderer_spec.rb +29 -0
  50. data/spec/renderers/raw_renderer_spec.rb +21 -0
  51. data/spec/renderers/ruby_renderer_spec.rb +59 -0
  52. data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
  53. data/spec/spec_helper.rb +70 -0
  54. data/spec/tmp/config.yml +2 -0
  55. data/spec/tmp/soup/current_snip.yml +15 -0
  56. data/spec/tmp/soup/system.yml +5 -0
  57. data/spec/vanilla_app_spec.rb +38 -0
  58. data/spec/vanilla_presenting_spec.rb +84 -0
  59. data/spec/vanilla_request_spec.rb +73 -0
  60. metadata +79 -170
  61. data/lib/vanilla/renderers.rb +0 -12
  62. data/lib/vanilla/renderers/haml.rb +0 -13
  63. data/lib/vanilla/static.rb +0 -28
  64. data/pristine_app/Gemfile +0 -3
  65. data/pristine_app/Gemfile.lock +0 -32
  66. data/pristine_app/README +0 -47
  67. data/pristine_app/config.ru +0 -26
  68. data/pristine_app/public/vanilla.css +0 -15
  69. data/pristine_app/soups/base/layout.snip +0 -18
  70. data/pristine_app/soups/base/start.snip +0 -19
  71. data/pristine_app/soups/dynasnips/current_snip.rb +0 -29
  72. data/pristine_app/soups/dynasnips/link_to_current_snip.rb +0 -14
  73. data/pristine_app/soups/dynasnips/page_title.rb +0 -9
  74. data/pristine_app/soups/extras/comments.rb +0 -78
  75. data/pristine_app/soups/tutorial/bad_dynasnip.snip +0 -8
  76. data/pristine_app/soups/tutorial/hello_world.snip +0 -20
  77. data/pristine_app/soups/tutorial/markdown_example.snip +0 -13
  78. data/pristine_app/soups/tutorial/snip.snip +0 -9
  79. data/pristine_app/soups/tutorial/soup.snip +0 -3
  80. data/pristine_app/soups/tutorial/test.snip +0 -30
  81. data/pristine_app/soups/tutorial/textile_example.snip +0 -11
  82. data/pristine_app/soups/tutorial/tutorial-another-snip.snip +0 -1
  83. data/pristine_app/soups/tutorial/tutorial-basic-snip-inclusion.snip +0 -1
  84. data/pristine_app/soups/tutorial/tutorial-dynasnips.snip.markdown +0 -56
  85. data/pristine_app/soups/tutorial/tutorial-layout.snip +0 -56
  86. data/pristine_app/soups/tutorial/tutorial-links.snip +0 -4
  87. data/pristine_app/soups/tutorial/tutorial-renderers.snip.markdown +0 -77
  88. data/pristine_app/soups/tutorial/tutorial.snip.markdown +0 -69
  89. data/pristine_app/soups/tutorial/vanilla-rb.snip +0 -16
  90. data/pristine_app/soups/tutorial/vanilla.snip +0 -8
  91. data/test/dynasnip_test.rb +0 -42
  92. data/test/dynasnips/link_to_current_snip_test.rb +0 -19
  93. data/test/dynasnips/link_to_test.rb +0 -27
  94. data/test/dynasnips/page_title_test.rb +0 -19
  95. data/test/renderers/base_renderer_test.rb +0 -43
  96. data/test/renderers/erb_renderer_test.rb +0 -29
  97. data/test/renderers/haml_renderer_test.rb +0 -35
  98. data/test/renderers/markdown_renderer_test.rb +0 -31
  99. data/test/renderers/raw_renderer_test.rb +0 -23
  100. data/test/renderers/ruby_renderer_test.rb +0 -59
  101. data/test/snip_inclusion_test.rb +0 -56
  102. data/test/snip_reference_parser_test.rb +0 -123
  103. data/test/test_helper.rb +0 -75
  104. data/test/vanilla_app_test.rb +0 -83
  105. data/test/vanilla_presenting_test.rb +0 -125
  106. data/test/vanilla_request_test.rb +0 -87
@@ -0,0 +1,20 @@
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
+ snip_name = args[0] || app.request.snip_name
17
+ link_text = args[1] || "Edit"
18
+ edit_link(snip_name, link_text)
19
+ end
20
+ end
@@ -2,11 +2,9 @@ require 'vanilla/dynasnip'
2
2
 
3
3
  class Index < Dynasnip
4
4
  def get(*args)
5
- list = app.soup.instance_eval { @backend }.send(:all_snips).sort_by { |a| a.updated_at || Time.at(0) }.reverse.map { |snip|
5
+ list = app.soup.all_snips.sort_by { |a| a.updated_at || Time.at(0) }.reverse.map { |snip|
6
6
  "<li>#{link_to snip.name}</li>"
7
7
  }
8
8
  "<ol>#{list}</ol>"
9
9
  end
10
-
11
- self
12
- end
10
+ end
@@ -5,15 +5,15 @@ require 'date'
5
5
  class Kind < Dynasnip
6
6
  def handle(kind, limit=10, as=:html)
7
7
  as = as.to_sym
8
- snips = app.soup.with(:kind => kind)
9
- entries = snips.sort_by { |s| s.created_at || Time.at(0) }.reverse[0...limit.to_i].map do |snip|
8
+ snips = app.soup.sieve(:kind => kind)
9
+ entries = snips.sort_by { |s| s.created_at || '' }.reverse[0...limit.to_i].map do |snip|
10
10
  render_entry_in_template(snip, as, kind)
11
11
  end
12
12
  render_entry_collection(snips, entries, as, kind)
13
13
  end
14
-
14
+
15
15
  def render_entry_in_template(snip, as, kind)
16
- rendered_contents = externalise_links(prepare_snip_contents(snip))
16
+ rendered_contents = prepare_snip_contents(snip)
17
17
  case as
18
18
  when :html
19
19
  snip_template.
@@ -29,19 +29,17 @@ class Kind < Dynasnip
29
29
  e.title = snip.name
30
30
  e.authors = [Atom::Person.new(:name => snip.author || domain)]
31
31
  e.links << Atom::Link.new(:href => "http://#{domain}#{url_to(snip.name)}")
32
- e.id = "tag:#{domain},#{(snip.created_at.to_s || Date.today.to_s).split[0]}:/#{snip.name}"
32
+ e.id = "tag:#{domain},#{(snip.created_at || Date.today.to_s).split[0]}:/#{snip.name}"
33
33
  end
34
34
  end
35
35
  end
36
-
36
+
37
37
  def prepare_snip_contents(snip)
38
- app.render(snip)
39
- end
40
-
41
- def externalise_links(content)
42
- content.gsub(/href="\//, "href=\"http://#{domain}/").gsub(/src="\//, "src=\"http://#{domain}/")
38
+ rendered_snip = app.render(snip)
39
+ # make all the links absolute
40
+ rendered_snip.gsub(/href="\//, "href=\"http://#{domain}/")
43
41
  end
44
-
42
+
45
43
  def render_entry_collection(snips, entries, as, kind)
46
44
  case as
47
45
  when :html
@@ -49,13 +47,13 @@ class Kind < Dynasnip
49
47
  when :xml
50
48
  Atom::Feed.new do |f|
51
49
  f.title = feed_title
52
- f.updated = snips[0].updated_at
50
+ f.updated = DateTime.parse(snips[0].updated_at)
53
51
  f.id = "tag:#{domain},2008-06-01:kind/#{kind}"
54
52
  f.entries = entries
55
53
  end.to_xml
56
54
  end
57
55
  end
58
-
56
+
59
57
  attribute :feed_title, "Your Blog"
60
58
  attribute :domain, "yourdomain.com"
61
59
  attribute :snip_template, %{
@@ -11,6 +11,4 @@ would insert a link to the blah snip.|
11
11
  def handle(snip_name, link_text=snip_name, part=nil)
12
12
  link_to(link_text, snip_name, part)
13
13
  end
14
-
15
- self
16
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
+ link_to app.request.params[:name]
12
+ else
13
+ 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 = 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
@@ -5,17 +5,15 @@ class ShowContentInPreTag < Dynasnip
5
5
 
6
6
  usage %|
7
7
  Wraps the contents of the given snip in &lt;pre&gt; 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
21
19
  end
@@ -24,6 +24,4 @@ class RandomNumber < Dynasnip
24
24
  max = max.to_i
25
25
  (rand(max-min) + min)
26
26
  end
27
-
28
- self
29
27
  end
@@ -1,22 +1,19 @@
1
1
  require 'vanilla/dynasnip'
2
- require 'cgi'
3
2
 
4
3
  class ShowRawContent < Dynasnip
5
4
  snip_name "raw"
6
5
 
7
6
  usage %|
8
7
  Displays the raw contents of a snip in &lt;pre&gt; tags, e.g.
9
-
8
+
10
9
  {raw my_snip}
11
-
10
+
12
11
  You can specify a part to show, should you wish:
13
-
12
+
14
13
  {raw my_snip,specific_part}
15
14
  |
16
-
15
+
17
16
  def handle(snip_name, part=:content)
18
- %{#{Dynasnip.escape_curly_braces(CGI.escapeHTML(app.soup[snip_name].__send__(part || :content)))}}
17
+ %{<pre>#{Dynasnip.escape_curly_braces(app.soup[snip_name].__send__(part || :content))}</pre>}
19
18
  end
20
-
21
- self
22
19
  end
@@ -1,42 +1,35 @@
1
+ require 'vanilla/app'
1
2
  require 'vanilla/snip_reference_parser'
2
3
 
3
4
  module Vanilla
4
5
  module Renderers
5
6
  class Base
6
7
  include Routes
7
-
8
+
8
9
  # Render a snip.
9
10
  def self.render(snip, part=:content)
10
11
  new(app).render(snip, part)
11
12
  end
12
-
13
+
13
14
  def self.escape_curly_braces(str)
14
15
  str.gsub("{", "&#123;").gsub("}", "&#125;")
15
16
  end
16
-
17
+
17
18
  attr_reader :app
18
-
19
+
19
20
  def initialize(app)
20
21
  @app = app
21
22
  end
22
-
23
+
23
24
  # defined for the routes
24
25
  def soup
25
26
  @app.soup
26
27
  end
27
-
28
+
28
29
  def self.snip_regexp
29
- %r{(\{[\w\-_\d\.\"\'\s]+(\s+[^\}.]+)?\})}
30
- end
31
-
32
- def default_layout_snip
33
- app.default_layout_snip
34
- end
35
-
36
- def layout_for(snip)
37
- layout_snip = (snip && snip.layout) ? soup[snip.layout] : default_layout_snip
30
+ %r{(\{[^\}.]+\})}
38
31
  end
39
-
32
+
40
33
  # Default behaviour to include a snip's content
41
34
  def include_snips(content, enclosing_snip)
42
35
  content.gsub(Vanilla::Renderers::Base.snip_regexp) do
@@ -45,54 +38,51 @@ module Vanilla
45
38
  snip_name = snip_tree.snip
46
39
  snip_attribute = snip_tree.attribute
47
40
  snip_args = snip_tree.arguments
48
-
41
+
49
42
  # Render the snip or snip part with the given args, and the current
50
43
  # context, but with the default renderer for that snip. We dispatch
51
44
  # *back* out to the root Vanilla.render method to do this.
52
- if snip = soup[snip_name]
45
+ snip = soup[snip_name]
46
+ if snip
53
47
  app.render(snip, snip_attribute, snip_args, enclosing_snip)
54
48
  else
55
- render_missing_snip(snip_name)
49
+ app.render_missing_snip(snip_name)
56
50
  end
57
51
  else
58
52
  "malformed snip reference: #{$1.inspect}"
59
53
  end
60
54
  end
61
55
  end
62
-
56
+
63
57
  def parse_snip_reference(string)
64
- @parser ||= Vanilla::SnipReferenceParser.new
58
+ @parser ||= SnipReferenceParser.new
65
59
  @parser.parse(string)
66
60
  end
67
-
68
- def render_missing_snip(snip_name)
69
- "[snip '#{snip_name}' cannot be found]"
70
- end
71
-
61
+
72
62
  # Default rendering behaviour. Subclasses shouldn't really need to touch this.
73
63
  def render(snip, part=:content, args=[], enclosing_snip=snip)
74
64
  prepare(snip, part, args, enclosing_snip)
75
65
  processed_text = render_without_including_snips(snip, part)
76
66
  include_snips(processed_text, snip)
77
67
  end
78
-
68
+
79
69
  # Subclasses should override this to perform any actions required before
80
70
  # rendering
81
71
  def prepare(snip, part, args, enclosing_snip)
82
72
  # do nothing, by default
83
73
  end
84
-
74
+
85
75
  def render_without_including_snips(snip, part=:content)
86
76
  process_text(raw_content(snip, part))
87
77
  end
88
-
89
- # Handles processing the text of the content.
90
- # Subclasses should override this method to do fancy text processing
78
+
79
+ # Handles processing the text of the content.
80
+ # Subclasses should override this method to do fancy text processing
91
81
  # like markdown, or loading the content as Ruby code.
92
82
  def process_text(content)
93
83
  content
94
84
  end
95
-
85
+
96
86
  # Returns the raw content for the selected part of the selected snip
97
87
  def raw_content(snip, part)
98
88
  snip.__send__((part || :content).to_sym)
@@ -1,3 +1,5 @@
1
+ require 'vanilla/renderers/base'
2
+
1
3
  module Vanilla::Renderers
2
4
  class Bold < Base
3
5
  def process_text(content)
@@ -1,3 +1,5 @@
1
+ require 'vanilla/renderers/base'
2
+
1
3
  require 'erb'
2
4
  include ERB::Util
3
5
 
@@ -1,3 +1,5 @@
1
+ require 'vanilla/renderers/base'
2
+
1
3
  require 'rubygems'
2
4
  gem 'BlueCloth' # from http://www.deveiate.org/projects/BlueCloth
3
5
  require 'bluecloth'
@@ -1,3 +1,5 @@
1
+ require 'vanilla/renderers/base'
2
+
1
3
  module Vanilla::Renderers
2
4
  class Raw < Base
3
5
  def render(snip, part=:content)