acts_as_markup_extended 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ require 'maruku'
2
+
3
+ class Maruku
4
+ include Stringlike
5
+
6
+ attr_reader :text
7
+
8
+ def initialize(s=nil, meta={})
9
+ super(nil)
10
+ self.attributes.merge! meta
11
+ if s
12
+ @text = s
13
+ parse_doc(s)
14
+ end
15
+ end
16
+
17
+ # Used to get the original Markdown text.
18
+ def to_s
19
+ @text
20
+ end
21
+
22
+ # used to be compatable with Rails/ActiveSupport
23
+ def blank?
24
+ @text.blank?
25
+ end
26
+
27
+ end
28
+
29
+ class String
30
+ alias_method :to_html, :to_s
31
+
32
+ def to_xml
33
+ REXML::Text.new(self)
34
+ end
35
+ end
36
+
37
+ module MaRuKu # :nodoc:
38
+ module Out # :nodoc:
39
+ module HTML
40
+
41
+ # We patch this method to play nicely with our own modifications of String.
42
+ #
43
+ # It originally used a +to_html+ method on String we've swapped this out for a +to_xml+
44
+ # method because we need +to_html+ to return the original text on plain text fields of
45
+ # the variable language option on +acts_as_markup+
46
+ def array_to_html(array)
47
+ elements = []
48
+ array.each do |item|
49
+ method = item.kind_of?(MDElement) ? "to_html_#{item.node_type}" : "to_xml"
50
+ unless item.respond_to?(method)
51
+ next
52
+ end
53
+
54
+ html_text = item.send(method)
55
+ if html_text.nil?
56
+ raise "Nil html created by method #{method}:\n#{html_text.inspect}\n for object #{item.inspect[0,300]}"
57
+ end
58
+
59
+ if html_text.kind_of?Array
60
+ elements = elements + html_text
61
+ else
62
+ elements << html_text
63
+ end
64
+ end
65
+ elements
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1 @@
1
+ Object.send :alias_method, :to_html, :to_s
@@ -0,0 +1,10 @@
1
+ require 'peg_markdown'
2
+
3
+ class PEGMarkdown
4
+ include Stringlike
5
+
6
+ # used to be compatable with Rails/ActiveSupport
7
+ def blank?
8
+ self.text.blank?
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ require 'rdiscount'
2
+
3
+ class RDiscount
4
+ include Stringlike
5
+
6
+ # Used to get the original Markdown text.
7
+ def to_s
8
+ self.text
9
+ end
10
+
11
+ # used to be compatable with Rails/ActiveSupport
12
+ def blank?
13
+ self.text.blank?
14
+ end
15
+ end
@@ -0,0 +1,101 @@
1
+ require 'rdoc/markup/simple_markup'
2
+ require 'rdoc/markup/simple_markup/to_html'
3
+
4
+ class RDocWithHyperlinkToHtml < SM::ToHtml
5
+
6
+ # Generate a hyperlink for url, labeled with text. Handle the
7
+ # special cases for img: and link: described under handle_special_HYPEDLINK
8
+ #
9
+ def gen_url(url, text)
10
+ if url =~ /([A-Za-z]+):(.*)/
11
+ type = $1
12
+ path = $2
13
+ else
14
+ type = "http"
15
+ path = url
16
+ url = "http://#{url}"
17
+ end
18
+
19
+ if type == "link"
20
+ if path[0,1] == '#' # is this meaningful?
21
+ url = path
22
+ else
23
+ url = HTMLGenerator.gen_url(@from_path, path)
24
+ end
25
+ end
26
+
27
+ if (type == "http" || type == "link") && url =~ /\.(gif|png|jpg|jpeg|bmp)$/
28
+ "<img src=\"#{url}\" />"
29
+ else
30
+ "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
31
+ end
32
+ end
33
+
34
+ # And we're invoked with a potential external hyperlink mailto:
35
+ # just gets inserted. http: links are checked to see if they
36
+ # reference an image. If so, that image gets inserted using an
37
+ # <img> tag. Otherwise a conventional <a href> is used. We also
38
+ # support a special type of hyperlink, link:, which is a reference
39
+ # to a local file whose path is relative to the --op directory.
40
+ #
41
+ def handle_special_HYPERLINK(special)
42
+ url = special.text
43
+ gen_url(url, url)
44
+ end
45
+
46
+ # Here's a hypedlink where the label is different to the URL
47
+ # <label>[url]
48
+ #
49
+ def handle_special_TIDYLINK(special)
50
+ text = special.text
51
+ unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
52
+ return text
53
+ end
54
+ label = $1
55
+ url = $2
56
+ gen_url(url, label)
57
+ end
58
+
59
+ end
60
+
61
+ # This allows a us to create a wrapper object similar to those provided by the
62
+ # Markdown and Textile libraries. It stores the original and formated HTML text
63
+ # in instance variables. It also stores the SimpleMarkup parser objects in
64
+ # instance variables.
65
+ #
66
+ class RDocText < String
67
+ attr_reader :text
68
+ attr_reader :html
69
+ attr_reader :markup
70
+ attr_reader :html_formater
71
+
72
+ def initialize(str)
73
+ super(str)
74
+ @text = str.to_s
75
+ @markup = SM::SimpleMarkup.new
76
+
77
+ # external hyperlinks
78
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
79
+
80
+ # and links of the form <text>[<url>]
81
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
82
+
83
+ # Convert leading comment markers to spaces, but only
84
+ # if all non-blank lines have them
85
+
86
+ if str =~ /^(?>\s*)[^\#]/
87
+ content = str
88
+ else
89
+ content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
90
+ end
91
+
92
+ @html_formatter = RDocWithHyperlinkToHtml.new
93
+
94
+ @html = @markup.convert(@text, @html_formatter)
95
+ end
96
+
97
+ def to_html
98
+ @html
99
+ end
100
+ end
101
+
@@ -0,0 +1,20 @@
1
+ require 'wikitext'
2
+
3
+ # This allows a us to create a wrapper object similar to those provided by the
4
+ # Markdown and Textile libraries. It stores the original and formated HTML text
5
+ # in instance variables.
6
+ #
7
+ class WikitextString < String
8
+ attr_reader :text
9
+ attr_reader :html
10
+
11
+ def initialize(str, *options)
12
+ super(str)
13
+ @text = str.to_s
14
+ @html = Wikitext::Parser.new(*options).parse(@text)
15
+ end
16
+
17
+ def to_html
18
+ @html
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # This mixin allows our markup objects (like RDiscount or RedCloth) to have
2
+ # all the normal string methods that are available.
3
+ module Stringlike
4
+ def method_missing(method, *params)
5
+ self.to_s.send(method, *params)
6
+ end
7
+ end
@@ -0,0 +1,79 @@
1
+ require 'set'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+
5
+ module ActsAsMarkup
6
+ # :stopdoc:
7
+ VERSION = '1.3.4'.freeze
8
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
9
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
10
+ # :startdoc:
11
+
12
+ # This exception is raised when an unsupported markup language is supplied to acts_as_markup.
13
+ class UnsupportedMarkupLanguage < ArgumentError
14
+ end
15
+ # This exception is raised when an unsupported Markdown library is set to the config value.
16
+ class UnsportedMarkdownLibrary < ArgumentError
17
+ end
18
+ # this extension is raised when an extension method doesn't exist
19
+ class UnknownExtensionMethod < ArgumentError
20
+ end
21
+ DEFAULT_MARKDOWN_LIB = :rdiscount
22
+
23
+ MARKDOWN_LIBS = { :rdiscount => {:class_name => "RDiscount",
24
+ :lib_name => "rdiscount"},
25
+ :bluecloth => {:class_name => "BlueCloth",
26
+ :lib_name => "bluecloth"},
27
+ :rpeg => {:class_name => "PEGMarkdown",
28
+ :lib_name => "peg_markdown"},
29
+ :maruku => {:class_name => "Maruku",
30
+ :lib_name => "maruku"} }
31
+
32
+ LIBRARY_EXTENSIONS = ::Set.new(Dir[ActsAsMarkup::LIBPATH + 'acts_as_markup/exts/*.rb'].map {|file| File.basename(file, '.rb')}).delete('string')
33
+
34
+ @@markdown_library = DEFAULT_MARKDOWN_LIB
35
+ mattr_accessor :markdown_library
36
+
37
+ # :stopdoc:
38
+ # Returns the version string for the library.
39
+ #
40
+ def self.version
41
+ VERSION
42
+ end
43
+
44
+ # Returns the library path for the module. If any arguments are given,
45
+ # they will be joined to the end of the library path using
46
+ # <tt>File.join</tt>.
47
+ #
48
+ def self.libpath( *args )
49
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, *args)
50
+ end
51
+
52
+ # Returns the path for the module. If any arguments are given,
53
+ # they will be joined to the end of the path using
54
+ # <tt>File.join</tt>.
55
+ #
56
+ def self.path( *args )
57
+ args.empty? ? PATH : ::File.join(PATH, *args)
58
+ end
59
+
60
+ # Utility method used to rquire all files ending in .rb that lie in the
61
+ # directory below this file that has the same name as the filename passed
62
+ # in. Optionally, a specific _directory_ name can be passed in such that
63
+ # the _filename_ does not have to be equivalent to the directory.
64
+ #
65
+ def self.require_all_libs_relative_to( fname, dir = nil )
66
+ dir ||= ::File.basename(fname, '.*')
67
+ search_me = ::File.expand_path(
68
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
69
+
70
+ Dir.glob(search_me).sort.each {|rb| require rb}
71
+ end
72
+ # :startdoc:
73
+
74
+ end # module ActsAsMarkup
75
+
76
+ require 'acts_as_markup/exts/object'
77
+ require 'acts_as_markup/stringlike'
78
+ require 'markup_extensions'
79
+ ActsAsMarkup.require_all_libs_relative_to __FILE__, 'acts'
@@ -0,0 +1,24 @@
1
+ ActsAsMarkup.require_all_libs_relative_to __FILE__, 'markup_methods'
2
+ ActsAsMarkup.require_all_libs_relative_to "lib/.", 'markup_methods'
3
+
4
+ module MarkupExtensions
5
+ class MString < String
6
+ include MarkupExtensionMethods
7
+
8
+ def run_methods(methods)
9
+ (methods == :all ?
10
+ MarkupExtensionMethods.instance_methods :
11
+ methods
12
+ ).inject(self) { |mstring, meth|
13
+ if mstring.respond_to? meth
14
+ MString.new(mstring.send(meth))
15
+ else
16
+ raise ActsAsMarkup::UnknownExtensionMethod,
17
+ "Extension method \"#{meth.to_s}\" is not defined "
18
+ MString.new(mstring)
19
+ end
20
+ }
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module MarkupExtensionMethods
2
+
3
+ # This sample extension replaces URL strings in the form:
4
+ # "http://..."
5
+ # with links:
6
+ # "<a href="http://..."
7
+ #
8
+ # Strings that are already part of
9
+ # <a href= ...> or <img src= ...>
10
+ # or inside
11
+ # <code> or <pre>
12
+ # tags will remain unchanged
13
+ #
14
+ # (This extension can be used for any markup language.)
15
+ #
16
+ def ahttp
17
+ gsub( /http:\/\/[^\s><"']+/ ) {
18
+ match = $&; before = $`; after = $'
19
+ before !~ /\s+(href|src|)\s*=\s*['"]*$/ &&
20
+ after !~ /^[^<]*<\/a>/ &&
21
+ before !~ /(<code>|<pre>).*$/ ?
22
+ %Q~<a href="#{match}">#{match}</a>~ :
23
+ match
24
+ }
25
+ end
26
+
27
+ end
@@ -0,0 +1,30 @@
1
+ module MarkupExtensionMethods
2
+
3
+ # This sample extension allows list elements to act as a checklist.
4
+ # If the markup list indicator is followed by a disposition character
5
+ # and a space, this method will make checklist substitutions.
6
+ #
7
+ # For example, when this method sees this in a Markdown list:
8
+ # '- x item one'
9
+ # or this in a Textile list:
10
+ # '* x item one"
11
+ # it will substitute:
12
+ # '<li <strong>(x)</strong> item one'.
13
+ #
14
+ # This method recognizes these disposition characters.
15
+ # x - item completed
16
+ # 0 - item dropped
17
+ # d - item deferred
18
+ # @ - item in progress
19
+ #
20
+ # (this extension can be used for any markup language.)
21
+ #
22
+ def checklist
23
+ gsub(/<li>([x0d@]) (\S)/) {
24
+ m1 = $1;m2 = $2
25
+ m1 = ">" if m1 == "d"
26
+ "<li><strong>(#{m1.upcase}) </strong>#{m2}"
27
+ }
28
+ end
29
+
30
+ end
@@ -0,0 +1,27 @@
1
+ module MarkupExtensionMethods
2
+
3
+ # This sample extension inserts a company logo.
4
+ # If Markdown contains:
5
+ # "[logo](home)"
6
+ # or Textile contains:
7
+ # "logo":home
8
+ # The generated HTML contains the company logo and links to the home page.
9
+ #
10
+ # (This extension can be used for any markup language)
11
+ #
12
+ def logo
13
+ gsub( /<a href="home">logo<\/a>/ ) {
14
+ match = $&; before = $`
15
+ if before !~ /(<code>|<pre>).*$/
16
+ %Q~<a href="http://www.google.com">\n~ +
17
+ %Q~<img id="logo" alt="Google"~ +
18
+ %Q~ src="http://www.google.com/images/logos/google_logo.gif"~ +
19
+ %Q~ style="border: 0 none;">\n~ +
20
+ %Q~</a>\n~
21
+ else
22
+ match
23
+ end
24
+ }
25
+ end
26
+
27
+ end
@@ -0,0 +1,298 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ActsAsMarkdownTest < ActsAsMarkupTestCase
4
+ context 'acts_as_markdown' do
5
+ setup do
6
+ @markdown_text = '## Markdown Test Text'
7
+ end
8
+
9
+ context 'using RDiscount' do
10
+ setup do
11
+ ActsAsMarkup.markdown_library = :rdiscount
12
+ class ::Post < ActiveRecord::Base
13
+ acts_as_markdown :body
14
+ end
15
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
16
+ end
17
+
18
+ should_act_like_a_string
19
+
20
+ should "have a RDiscount object returned for the column value" do
21
+ assert_kind_of RDiscount, @post.body
22
+ end
23
+
24
+ should "return original markdown text for a `to_s` method call on the column value" do
25
+ assert_equal @markdown_text, @post.body.to_s
26
+ end
27
+
28
+ should 'return false for .blank?' do
29
+ assert !@post.body.blank?
30
+ end
31
+
32
+ should "return formatted html for a `to_html` method call on the column value" do
33
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
34
+ end
35
+
36
+ should "not return escaped html" do
37
+ @post.body = "## Markdown <i>Test</i> Text"
38
+ assert_match(/<i>Test<\/i>/, @post.body.to_html)
39
+ end
40
+
41
+ context "changing value of markdown field should return new markdown object" do
42
+ setup do
43
+ @old_body = @post.body
44
+ @post.body = "`@count = 20`"
45
+ end
46
+
47
+ should "still have an RDiscount object but not the same object" do
48
+ assert_kind_of RDiscount, @post.body
49
+ assert_not_same @post.body, @old_body
50
+ end
51
+
52
+ should "return correct text for `to_s`" do
53
+ assert_equal "`@count = 20`", @post.body.to_s
54
+ end
55
+
56
+ should "return correct HTML for the `to_html` method" do
57
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
58
+ end
59
+
60
+ teardown do
61
+ @old_body = nil
62
+ end
63
+ end
64
+
65
+ teardown do
66
+ @post = nil
67
+ Post.delete_all
68
+ end
69
+ end
70
+
71
+ context 'using RDiscount with options' do
72
+ setup do
73
+ class ::Post
74
+ acts_as_markdown :body, :markdown_options => [ :filter_html ]
75
+ end
76
+ @post = Post.new(:title => 'Blah')
77
+ end
78
+
79
+ should "return escaped html because of :filter_html" do
80
+ @post.body = "## Markdown <i>Test</i> Text"
81
+ assert_match(/&lt;i>Test&lt;\/i>/, @post.body.to_html)
82
+ end
83
+ end
84
+
85
+ context 'using Ruby PEG Markdown' do
86
+ setup do
87
+ ActsAsMarkup.markdown_library = :rpeg
88
+ class ::Post < ActiveRecord::Base
89
+ acts_as_markdown :body
90
+ end
91
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
92
+ end
93
+
94
+ should_act_like_a_string
95
+
96
+ should "have a Ruby PEG Markdown object returned for the column value" do
97
+ assert_kind_of PEGMarkdown, @post.body
98
+ end
99
+
100
+ should "return original markdown text for a `to_s` method call on the column value" do
101
+ assert_equal @markdown_text, @post.body.to_s
102
+ end
103
+
104
+ should 'return false for .blank?' do
105
+ assert !@post.body.blank?
106
+ end
107
+
108
+ should "return formated html for a `to_html` method call on the column value" do
109
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
110
+ end
111
+
112
+ should "not return escaped html" do
113
+ @post.body = "## Markdown <i>Test</i> Text"
114
+ assert_match(/<i>Test<\/i>/, @post.body.to_html)
115
+ end
116
+
117
+ context "changing value of markdown field should return new markdown object" do
118
+ setup do
119
+ @old_body = @post.body
120
+ @post.body = "`@count = 20`"
121
+ end
122
+
123
+ should "still have an PEGMarkdown object but not the same object" do
124
+ assert_kind_of PEGMarkdown, @post.body
125
+ assert_not_same @post.body, @old_body
126
+ end
127
+
128
+ should "return correct text for `to_s`" do
129
+ assert_equal "`@count = 20`", @post.body.to_s
130
+ end
131
+
132
+ should "return correct HTML for the `to_html` method" do
133
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
134
+ end
135
+
136
+ teardown do
137
+ @old_body = nil
138
+ end
139
+ end
140
+
141
+ teardown do
142
+ @post = nil
143
+ Post.delete_all
144
+ end
145
+ end
146
+
147
+ context 'using Ruby PEG Markdown with options' do
148
+ setup do
149
+ class ::Post
150
+ acts_as_markdown :body, :markdown_options => [ :filter_html ]
151
+ end
152
+ @post = Post.new(:title => 'Blah')
153
+ end
154
+
155
+ should "return no html because of :filter_html" do
156
+ @post.body = "## Markdown <i>Test</i> Text"
157
+ assert_match(/Markdown Test Text/, @post.body.to_html)
158
+ end
159
+ end
160
+
161
+ context 'using BlueCloth' do
162
+ setup do
163
+ ActsAsMarkup.markdown_library = :bluecloth
164
+ class ::Post < ActiveRecord::Base
165
+ acts_as_markdown :body
166
+ end
167
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
168
+ end
169
+
170
+ should_act_like_a_string
171
+
172
+ should "have a BlueCloth object returned for the column value" do
173
+ assert_kind_of BlueCloth, @post.body
174
+ end
175
+
176
+ should "return original markdown text for a `to_s` method call on the column value" do
177
+ assert_equal @markdown_text, @post.body.to_s
178
+ end
179
+
180
+ should 'return false for .blank?' do
181
+ assert !@post.body.blank?
182
+ end
183
+
184
+ should "return formated html for a `to_html` method call on the column value" do
185
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
186
+ end
187
+
188
+ should "not return escaped html" do
189
+ @post.body = "## Markdown <i>Test</i> Text"
190
+ assert_match(/<i>Test<\/i>/, @post.body.to_html)
191
+ end
192
+
193
+ context "changing value of markdown field should return new markdown object" do
194
+ setup do
195
+ @old_body = @post.body
196
+ @post.body = "`@count = 20`"
197
+ end
198
+
199
+ should "still have an BlueCloth object but not the same object" do
200
+ assert_kind_of BlueCloth, @post.body
201
+ assert_not_same @post.body, @old_body
202
+ end
203
+
204
+ should "return correct text for `to_s`" do
205
+ assert_equal "`@count = 20`", @post.body.to_s
206
+ end
207
+
208
+ should "return correct HTML for the `to_html` method" do
209
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
210
+ end
211
+
212
+ teardown do
213
+ @old_body = nil
214
+ end
215
+ end
216
+
217
+ teardown do
218
+ @post = nil
219
+ Post.delete_all
220
+ end
221
+ end
222
+
223
+ context 'using BlueCloth with options' do
224
+ setup do
225
+ class ::Post
226
+ acts_as_markdown :body, :markdown_options => [ :filter_html ]
227
+ end
228
+ @post = Post.new(:title => 'Blah')
229
+ end
230
+
231
+ should "return escaped html because of :filter_html" do
232
+ @post.body = "## Markdown <i>Test</i> Text"
233
+ assert_match(/&lt;i&gt;Test&lt;\/i&gt;/, @post.body.to_html)
234
+ end
235
+ end
236
+
237
+ context 'using Maruku' do
238
+ setup do
239
+ ActsAsMarkup.markdown_library = :maruku
240
+ class ::Post < ActiveRecord::Base
241
+ acts_as_markdown :body
242
+ end
243
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
244
+ end
245
+
246
+ should_act_like_a_string
247
+
248
+ should "have a Maruku object returned for the column value" do
249
+ assert_kind_of Maruku, @post.body
250
+ end
251
+
252
+ should "return original markdown text for a `to_s` method call on the column value" do
253
+ assert_equal @markdown_text, @post.body.to_s
254
+ end
255
+
256
+ should 'return false for .blank?' do
257
+ assert !@post.body.blank?
258
+ end
259
+
260
+ should "return formated html for a `to_html` method call on the column value" do
261
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
262
+ end
263
+
264
+ context "changing value of markdown field should return new markdown object" do
265
+ setup do
266
+ @old_body = @post.body
267
+ @post.body = "`@count = 20`"
268
+ end
269
+
270
+ should "still have an Maruku object but not the same object" do
271
+ assert_kind_of Maruku, @post.body
272
+ assert_not_same @post.body, @old_body
273
+ end
274
+
275
+ should "return correct text for `to_s`" do
276
+ assert_equal "`@count = 20`", @post.body.to_s
277
+ end
278
+
279
+ should "return correct HTML for the `to_html` method" do
280
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
281
+ end
282
+
283
+ teardown do
284
+ @old_body = nil
285
+ end
286
+ end
287
+
288
+ teardown do
289
+ @post = nil
290
+ Post.delete_all
291
+ end
292
+ end
293
+
294
+ teardown do
295
+ @markdown_text = nil
296
+ end
297
+ end
298
+ end