vanilla 1.2 → 1.9.9

Sign up to get free protection for your applications and to get access to all the features.
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)