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.
- data/Rakefile +61 -60
- data/bin/vanilla +6 -35
- data/config.example.yml +6 -0
- data/config.ru +10 -0
- data/lib/defensio.rb +59 -0
- data/lib/tasks/vanilla.rake +173 -0
- data/lib/vanilla.rb +3 -10
- data/lib/vanilla/app.rb +48 -104
- data/lib/vanilla/console.rb +5 -19
- data/lib/vanilla/dynasnips/comments.rb +108 -0
- data/lib/vanilla/dynasnips/current_snip.rb +32 -0
- data/{pristine_app/soups → lib/vanilla}/dynasnips/debug.rb +3 -5
- data/lib/vanilla/dynasnips/edit.rb +60 -0
- data/lib/vanilla/dynasnips/edit_link.rb +20 -0
- data/{pristine_app/soups → lib/vanilla}/dynasnips/index.rb +2 -4
- data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/kind.rb +12 -14
- data/{pristine_app/soups → lib/vanilla}/dynasnips/link_to.rb +0 -2
- data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
- data/lib/vanilla/dynasnips/login.rb +56 -0
- data/lib/vanilla/dynasnips/new.rb +14 -0
- data/lib/vanilla/dynasnips/notes.rb +42 -0
- data/{pristine_app/soups → lib/vanilla}/dynasnips/pre.rb +4 -6
- data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/rand.rb +0 -2
- data/{pristine_app/soups → lib/vanilla}/dynasnips/raw.rb +5 -8
- data/{pristine_app/soups/extras → lib/vanilla/dynasnips}/url_to.rb +0 -0
- data/lib/vanilla/renderers/base.rb +22 -32
- data/lib/vanilla/renderers/bold.rb +2 -0
- data/lib/vanilla/renderers/erb.rb +2 -0
- data/lib/vanilla/renderers/markdown.rb +2 -0
- data/lib/vanilla/renderers/raw.rb +2 -0
- data/lib/vanilla/renderers/ruby.rb +5 -9
- data/lib/vanilla/renderers/textile.rb +2 -0
- data/lib/vanilla/request.rb +15 -16
- data/lib/vanilla/routes.rb +18 -5
- data/lib/vanilla/snip_reference.rb +534 -0
- data/lib/vanilla/snip_reference.treetop +48 -0
- data/lib/vanilla/snip_reference_parser.rb +99 -82
- data/lib/vanilla/snips/start.rb +28 -0
- data/lib/vanilla/snips/system.rb +77 -0
- data/lib/vanilla/snips/tutorial.rb +244 -0
- data/lib/vanilla/soup_with_timestamps.rb +21 -0
- data/public/hatch.png +0 -0
- data/public/javascripts/jquery.autogrow-textarea.js +54 -0
- data/public/javascripts/jquery.js +4376 -0
- data/public/javascripts/vanilla.js +22 -0
- data/spec/dynasnip_spec.rb +28 -0
- data/spec/renderers/base_renderer_spec.rb +40 -0
- data/spec/renderers/erb_renderer_spec.rb +27 -0
- data/spec/renderers/markdown_renderer_spec.rb +29 -0
- data/spec/renderers/raw_renderer_spec.rb +21 -0
- data/spec/renderers/ruby_renderer_spec.rb +59 -0
- data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
- data/spec/spec_helper.rb +70 -0
- data/spec/tmp/config.yml +2 -0
- data/spec/tmp/soup/current_snip.yml +15 -0
- data/spec/tmp/soup/system.yml +5 -0
- data/spec/vanilla_app_spec.rb +38 -0
- data/spec/vanilla_presenting_spec.rb +84 -0
- data/spec/vanilla_request_spec.rb +73 -0
- metadata +79 -170
- data/lib/vanilla/renderers.rb +0 -12
- data/lib/vanilla/renderers/haml.rb +0 -13
- data/lib/vanilla/static.rb +0 -28
- data/pristine_app/Gemfile +0 -3
- data/pristine_app/Gemfile.lock +0 -32
- data/pristine_app/README +0 -47
- data/pristine_app/config.ru +0 -26
- data/pristine_app/public/vanilla.css +0 -15
- data/pristine_app/soups/base/layout.snip +0 -18
- data/pristine_app/soups/base/start.snip +0 -19
- data/pristine_app/soups/dynasnips/current_snip.rb +0 -29
- data/pristine_app/soups/dynasnips/link_to_current_snip.rb +0 -14
- data/pristine_app/soups/dynasnips/page_title.rb +0 -9
- data/pristine_app/soups/extras/comments.rb +0 -78
- data/pristine_app/soups/tutorial/bad_dynasnip.snip +0 -8
- data/pristine_app/soups/tutorial/hello_world.snip +0 -20
- data/pristine_app/soups/tutorial/markdown_example.snip +0 -13
- data/pristine_app/soups/tutorial/snip.snip +0 -9
- data/pristine_app/soups/tutorial/soup.snip +0 -3
- data/pristine_app/soups/tutorial/test.snip +0 -30
- data/pristine_app/soups/tutorial/textile_example.snip +0 -11
- data/pristine_app/soups/tutorial/tutorial-another-snip.snip +0 -1
- data/pristine_app/soups/tutorial/tutorial-basic-snip-inclusion.snip +0 -1
- data/pristine_app/soups/tutorial/tutorial-dynasnips.snip.markdown +0 -56
- data/pristine_app/soups/tutorial/tutorial-layout.snip +0 -56
- data/pristine_app/soups/tutorial/tutorial-links.snip +0 -4
- data/pristine_app/soups/tutorial/tutorial-renderers.snip.markdown +0 -77
- data/pristine_app/soups/tutorial/tutorial.snip.markdown +0 -69
- data/pristine_app/soups/tutorial/vanilla-rb.snip +0 -16
- data/pristine_app/soups/tutorial/vanilla.snip +0 -8
- data/test/dynasnip_test.rb +0 -42
- data/test/dynasnips/link_to_current_snip_test.rb +0 -19
- data/test/dynasnips/link_to_test.rb +0 -27
- data/test/dynasnips/page_title_test.rb +0 -19
- data/test/renderers/base_renderer_test.rb +0 -43
- data/test/renderers/erb_renderer_test.rb +0 -29
- data/test/renderers/haml_renderer_test.rb +0 -35
- data/test/renderers/markdown_renderer_test.rb +0 -31
- data/test/renderers/raw_renderer_test.rb +0 -23
- data/test/renderers/ruby_renderer_test.rb +0 -59
- data/test/snip_inclusion_test.rb +0 -56
- data/test/snip_reference_parser_test.rb +0 -123
- data/test/test_helper.rb +0 -75
- data/test/vanilla_app_test.rb +0 -83
- data/test/vanilla_presenting_test.rb +0 -125
- data/test/vanilla_request_test.rb +0 -87
data/lib/vanilla.rb
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
VERSION = "1.2"
|
|
1
|
+
require 'vanilla/app'
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
autoload :Dynasnip, "vanilla/dynasnip"
|
|
7
|
-
autoload :Request, "vanilla/request"
|
|
8
|
-
autoload :Routes, "vanilla/routes"
|
|
9
|
-
autoload :Static, "vanilla/static"
|
|
10
|
-
autoload :SnipReferenceParser, "vanilla/snip_reference_parser"
|
|
11
|
-
end
|
|
3
|
+
# Load all the other renderer subclasses
|
|
4
|
+
Dir[File.join(File.dirname(__FILE__), 'vanilla', 'renderers', '*.rb')].each { |f| require f }
|
|
12
5
|
|
|
13
6
|
# Load all the base dynasnip classes
|
|
14
7
|
Dir[File.join(File.dirname(__FILE__), 'vanilla', 'dynasnips', '*.rb')].each do |dynasnip|
|
data/lib/vanilla/app.rb
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'vanilla/request'
|
|
2
|
+
require 'vanilla/routes'
|
|
3
|
+
require 'vanilla/soup_with_timestamps'
|
|
4
|
+
|
|
5
|
+
# Require the base set of renderers
|
|
6
|
+
require 'vanilla/renderers/base'
|
|
7
|
+
require 'vanilla/renderers/raw'
|
|
8
|
+
require 'vanilla/renderers/erb'
|
|
9
|
+
|
|
2
10
|
|
|
3
11
|
module Vanilla
|
|
4
12
|
class App
|
|
5
|
-
include
|
|
6
|
-
|
|
13
|
+
include Routes
|
|
14
|
+
|
|
7
15
|
attr_reader :request, :response, :config, :soup
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# :soup - provide the path to the soup data
|
|
13
|
-
# :soups - provide an array of paths to soup data
|
|
14
|
-
# :renderers - a hash of names to classes
|
|
15
|
-
# :default_renderer - the class to use when no renderer is provided;
|
|
16
|
-
# defaults to 'Vanilla::Renderers::Base'
|
|
17
|
-
# :default_layout_snip - the snip to use as a layout when rendering to HTML;
|
|
18
|
-
# defaults to 'layout'
|
|
19
|
-
# :root_snip - the snip to load for the root ('/') url;
|
|
20
|
-
# defaults to 'start'
|
|
21
|
-
def initialize(config={})
|
|
22
|
-
@config = config
|
|
23
|
-
if @config[:soup].nil? && @config[:soups].nil?
|
|
24
|
-
@config.merge!(:soup => File.expand_path("soup"))
|
|
25
|
-
end
|
|
26
|
-
@soup = prepare_soup(config)
|
|
27
|
-
prepare_renderers(config[:renderers])
|
|
16
|
+
|
|
17
|
+
def initialize(config_file=nil)
|
|
18
|
+
prepare_configuration(config_file)
|
|
19
|
+
@soup = SoupWithTimestamps.new(config[:soup])
|
|
28
20
|
end
|
|
29
|
-
|
|
21
|
+
|
|
30
22
|
# Returns a Rack-appropriate 3-element array (via Rack::Response#finish)
|
|
31
23
|
def call(env)
|
|
32
|
-
env['vanilla.app'] = self
|
|
33
24
|
@request = Vanilla::Request.new(env, self)
|
|
34
25
|
@response = Rack::Response.new
|
|
35
26
|
|
|
@@ -37,9 +28,9 @@ module Vanilla
|
|
|
37
28
|
output = formatted_render(request.snip, request.part, request.format)
|
|
38
29
|
rescue => e
|
|
39
30
|
@response.status = 500
|
|
40
|
-
output = e.to_s
|
|
31
|
+
output = e.to_s
|
|
41
32
|
end
|
|
42
|
-
response_format = request.format
|
|
33
|
+
response_format = request.format
|
|
43
34
|
response_format = 'plain' if response_format == 'raw'
|
|
44
35
|
@response['Content-Type'] = "text/#{response_format}"
|
|
45
36
|
@response.write(output)
|
|
@@ -49,16 +40,11 @@ module Vanilla
|
|
|
49
40
|
def formatted_render(snip, part=nil, format=nil)
|
|
50
41
|
case format
|
|
51
42
|
when 'html', nil
|
|
52
|
-
|
|
53
|
-
if layout == snip
|
|
54
|
-
"Rendering of the current layout would result in infinite recursion."
|
|
55
|
-
else
|
|
56
|
-
render(layout)
|
|
57
|
-
end
|
|
43
|
+
Renderers::Erb.new(self).render(soup['system'], :main_template)
|
|
58
44
|
when 'raw', 'css', 'js'
|
|
59
|
-
Renderers::Raw.new(self).render(snip, part)
|
|
45
|
+
Renderers::Raw.new(self).render(snip, part || :content)
|
|
60
46
|
when 'text', 'atom', 'xml'
|
|
61
|
-
render(snip, part)
|
|
47
|
+
render(snip, part || :content)
|
|
62
48
|
else
|
|
63
49
|
raise "Unknown format '#{format}'"
|
|
64
50
|
end
|
|
@@ -73,82 +59,40 @@ module Vanilla
|
|
|
73
59
|
end
|
|
74
60
|
end
|
|
75
61
|
|
|
62
|
+
# Given the snip and parameters, yield an instance of the appropriate
|
|
63
|
+
# Vanilla::Render::Base subclass
|
|
64
|
+
def rendering(snip)
|
|
65
|
+
renderer_instance = renderer_for(snip).new(self)
|
|
66
|
+
yield renderer_instance
|
|
67
|
+
rescue Exception => e
|
|
68
|
+
"<pre>[Error rendering '#{snip.name}' - \"" +
|
|
69
|
+
e.message.gsub("<", "<").gsub(">", ">") + "\"]\n" +
|
|
70
|
+
e.backtrace.join("\n").gsub("<", "<").gsub(">", ">") + "</pre>"
|
|
71
|
+
end
|
|
72
|
+
|
|
76
73
|
# Returns the renderer class for a given snip
|
|
77
74
|
def renderer_for(snip)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
else
|
|
81
|
-
default_renderer
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def default_layout_snip
|
|
86
|
-
soup[config[:default_layout_snip] || 'layout']
|
|
75
|
+
return Renderers::Base unless snip.render_as && !snip.render_as.empty?
|
|
76
|
+
Vanilla::Renderers.const_get(snip.render_as)
|
|
87
77
|
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
else
|
|
93
|
-
default_layout_snip
|
|
94
|
-
end
|
|
78
|
+
|
|
79
|
+
# Other things can call this when a snip cannot be loaded.
|
|
80
|
+
def render_missing_snip(snip_name)
|
|
81
|
+
"[snip '#{snip_name}' cannot be found]"
|
|
95
82
|
end
|
|
96
|
-
|
|
83
|
+
|
|
97
84
|
def snip(attributes)
|
|
98
|
-
@soup
|
|
85
|
+
@soup.new_snip(attributes)
|
|
99
86
|
end
|
|
100
|
-
|
|
101
|
-
def register_renderer(klass, *types)
|
|
102
|
-
types.each do |type|
|
|
103
|
-
if klass.is_a?(String)
|
|
104
|
-
klass = klass.split("::").inject(Object) { |o, name| o.const_get(name) }
|
|
105
|
-
end
|
|
106
|
-
@renderers[type.to_s] = klass
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
87
|
+
|
|
110
88
|
private
|
|
111
|
-
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
@
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"erb" => Vanilla::Renderers::Erb,
|
|
119
|
-
"rb" => Vanilla::Renderers::Ruby,
|
|
120
|
-
"ruby" => Vanilla::Renderers::Ruby,
|
|
121
|
-
"haml" => Vanilla::Renderers::Haml,
|
|
122
|
-
"raw" => Vanilla::Renderers::Raw,
|
|
123
|
-
"textile" => Vanilla::Renderers::Textile
|
|
124
|
-
})
|
|
125
|
-
additional_renderers.each { |name, klass| register_renderer(klass, name) } if additional_renderers
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def find_renderer(name)
|
|
129
|
-
@renderers[(name ? name.downcase : nil)]
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def default_renderer
|
|
133
|
-
@renderers[nil]
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def rendering(snip)
|
|
137
|
-
renderer_instance = renderer_for(snip).new(self)
|
|
138
|
-
yield renderer_instance
|
|
139
|
-
rescue Exception => e
|
|
140
|
-
snip_name = snip ? snip.name : nil
|
|
141
|
-
"<pre>[Error rendering '#{snip_name}' - \"" +
|
|
142
|
-
e.message.gsub("<", "<").gsub(">", ">") + "\"]\n" +
|
|
143
|
-
e.backtrace.join("\n").gsub("<", "<").gsub(">", ">") + "</pre>"
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def prepare_soup(config)
|
|
147
|
-
if config[:soups]
|
|
148
|
-
backends = [config[:soups]].flatten.map { |path| ::Soup::Backends::FileBackend.new(path) }
|
|
149
|
-
::Soup.new(::Soup::Backends::MultiSoup.new(*backends))
|
|
150
|
-
else
|
|
151
|
-
::Soup.new(::Soup::Backends::FileBackend.new(config[:soup]))
|
|
89
|
+
|
|
90
|
+
def prepare_configuration(config_file)
|
|
91
|
+
config_file ||= "config.yml"
|
|
92
|
+
@config = YAML.load(File.open(config_file)) rescue {}
|
|
93
|
+
@config[:filename] = config_file
|
|
94
|
+
def @config.save!
|
|
95
|
+
File.open(self[:filename], 'w') { |f| f.puts self.to_yaml }
|
|
152
96
|
end
|
|
153
97
|
end
|
|
154
98
|
end
|
data/lib/vanilla/console.rb
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
require 'vanilla'
|
|
2
|
+
require 'irb'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
app # return it
|
|
7
|
-
end
|
|
8
|
-
def use(*args)
|
|
9
|
-
# ignore
|
|
10
|
-
end
|
|
11
|
-
def get_binding
|
|
12
|
-
binding
|
|
13
|
-
end
|
|
14
|
-
end
|
|
4
|
+
def app(reload=false)
|
|
5
|
+
@__vanilla_console_app = nil if reload
|
|
6
|
+
@__vanilla_console_app ||= Vanilla::App.new(ENV['VANILLA_CONFIG'])
|
|
15
7
|
end
|
|
16
8
|
|
|
17
|
-
|
|
18
|
-
if !@__vanilla_console_app || reload
|
|
19
|
-
shim_binding = Vanilla::RackShim.new.get_binding
|
|
20
|
-
@__vanilla_console_app = eval File.read("config.ru"), shim_binding
|
|
21
|
-
end
|
|
22
|
-
@__vanilla_console_app
|
|
23
|
-
end
|
|
9
|
+
puts "The Soup is simmering."
|
|
@@ -0,0 +1,108 @@
|
|
|
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 = app.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="#{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 = app.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
|
+
app.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(app.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
|
|
@@ -0,0 +1,32 @@
|
|
|
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,15 +1,13 @@
|
|
|
1
1
|
require 'vanilla/dynasnip'
|
|
2
|
-
require 'cgi'
|
|
3
2
|
|
|
4
3
|
# If the dynasnip is a subclass of Dynasnip, it has access to the request hash
|
|
5
4
|
# (or whatever - access to some object outside of the snip itself.)
|
|
6
5
|
class Debug < Dynasnip
|
|
7
6
|
def get(*args)
|
|
8
|
-
|
|
7
|
+
app.request.inspect
|
|
9
8
|
end
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
def post(*args)
|
|
12
|
-
"You posted! " +
|
|
11
|
+
"You posted! " + app.request.inspect
|
|
13
12
|
end
|
|
14
|
-
self
|
|
15
13
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
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 = app.soup[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 = app.soup[snip_attributes[:name]]
|
|
23
|
+
snip_attributes.each do |name, value|
|
|
24
|
+
snip.__send__(:set_value, name, value)
|
|
25
|
+
end
|
|
26
|
+
snip.save
|
|
27
|
+
%{Saved snip #{link_to snip_attributes[:name]} ok}
|
|
28
|
+
rescue Exception => e
|
|
29
|
+
app.soup << snip_attributes
|
|
30
|
+
%{Created snip #{link_to snip_attributes[:name]} ok}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def edit(snip)
|
|
34
|
+
renderer = Vanilla::Renderers::Erb.new(app)
|
|
35
|
+
renderer.instance_eval { @snip_to_edit = snip } # hacky!
|
|
36
|
+
snip_in_edit_template = renderer.render_without_including_snips(app.soup['edit'], :template)
|
|
37
|
+
prevent_snip_inclusion(snip_in_edit_template)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def prevent_snip_inclusion(content)
|
|
43
|
+
content.gsub("{", "{").gsub("}" ,"}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
attribute :template, %{
|
|
47
|
+
<form action="<%= url_to 'edit' %>" method="post">
|
|
48
|
+
<dl class="attributes">
|
|
49
|
+
<% @snip_to_edit.attributes.each do |name, value| %>
|
|
50
|
+
<dt><%= name %></dt>
|
|
51
|
+
<dd><textarea name="<%= name %>" class="<%= name %>"><%=h value %></textarea></dd>
|
|
52
|
+
<% end %>
|
|
53
|
+
<dt><input class="attribute_name" type="text"></dt>
|
|
54
|
+
<dd><textarea></textarea></dd>
|
|
55
|
+
</dl>
|
|
56
|
+
<a href="#" id="add">Add</a>
|
|
57
|
+
<button name='save_button'>Save</button>
|
|
58
|
+
</form>
|
|
59
|
+
}
|
|
60
|
+
end
|