acts_as_markup_on 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ class BlueClothText < BlueCloth
2
+ include Stringlike
3
+
4
+ def initialize(string, options = BlueCloth::DEFAULT_OPTIONS)
5
+ @string = string
6
+ super
7
+ end
8
+
9
+ def to_s
10
+ @string
11
+ end
12
+
13
+ def blank?
14
+ @string.blank?
15
+ end
16
+ end
@@ -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_on+
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,100 @@
1
+ require 'rdoc'
2
+
3
+ class RDocWithHyperlinkToHtml < RDoc::Markup::ToHtml
4
+
5
+ # Generate a hyperlink for url, labeled with text. Handle the
6
+ # special cases for img: and link: described under handle_special_HYPEDLINK
7
+ #
8
+ def gen_url(url, text)
9
+ if url =~ /([A-Za-z]+):(.*)/
10
+ type = $1
11
+ path = $2
12
+ else
13
+ type = "http"
14
+ path = url
15
+ url = "http://#{url}"
16
+ end
17
+
18
+ if type == "link"
19
+ if path[0,1] == '#' # is this meaningful?
20
+ url = path
21
+ else
22
+ url = HTMLGenerator.gen_url(@from_path, path)
23
+ end
24
+ end
25
+
26
+ if (type == "http" || type == "link") && url =~ /\.(gif|png|jpg|jpeg|bmp)$/
27
+ "<img src=\"#{url}\" />"
28
+ else
29
+ "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
30
+ end
31
+ end
32
+
33
+ # And we're invoked with a potential external hyperlink mailto:
34
+ # just gets inserted. http: links are checked to see if they
35
+ # reference an image. If so, that image gets inserted using an
36
+ # <img> tag. Otherwise a conventional <a href> is used. We also
37
+ # support a special type of hyperlink, link:, which is a reference
38
+ # to a local file whose path is relative to the --op directory.
39
+ #
40
+ def handle_special_HYPERLINK(special)
41
+ url = special.text
42
+ gen_url(url, url)
43
+ end
44
+
45
+ # Here's a hypedlink where the label is different to the URL
46
+ # <label>[url]
47
+ #
48
+ def handle_special_TIDYLINK(special)
49
+ text = special.text
50
+ unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
51
+ return text
52
+ end
53
+ label = $1
54
+ url = $2
55
+ gen_url(url, label)
56
+ end
57
+
58
+ end
59
+
60
+ # This allows a us to create a wrapper object similar to those provided by the
61
+ # Markdown and Textile libraries. It stores the original and formated HTML text
62
+ # in instance variables. It also stores the SimpleMarkup parser objects in
63
+ # instance variables.
64
+ #
65
+ class RDocText < String
66
+ attr_reader :text
67
+ attr_reader :html
68
+ attr_reader :markup
69
+ attr_reader :html_formater
70
+
71
+ def initialize(str)
72
+ super(str)
73
+ @text = str.to_s
74
+ @markup = RDoc::Markup.new
75
+
76
+ # external hyperlinks
77
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
78
+
79
+ # and links of the form <text>[<url>]
80
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
81
+
82
+ # Convert leading comment markers to spaces, but only
83
+ # if all non-blank lines have them
84
+
85
+ if str =~ /^(?>\s*)[^\#]/
86
+ content = str
87
+ else
88
+ content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
89
+ end
90
+
91
+ @html_formatter = RDocWithHyperlinkToHtml.new(RDoc::Options.new)
92
+
93
+ @html = @markup.convert(@text, @html_formatter)
94
+ end
95
+
96
+ def to_html
97
+ @html
98
+ end
99
+ end
100
+
@@ -0,0 +1,9 @@
1
+ class RedcarpetText < Redcarpet::Markdown
2
+ include Stringlike
3
+
4
+ alias_method :to_s, :text
5
+
6
+ def blank?
7
+ text.blank?
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ require 'wikicloth'
2
+
3
+ class WikiClothText < WikiCloth::Parser
4
+ include Stringlike
5
+
6
+ def initialize(string)
7
+ @text = string
8
+ super({:data => string.to_s})
9
+ end
10
+
11
+ def to_s
12
+ @text.to_s
13
+ end
14
+
15
+ def blank?
16
+ @text.blank?
17
+ end
18
+ end
@@ -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,26 @@
1
+ module ActsAsMarkupOn
2
+ class Railtie < Rails::Railtie
3
+ config.acts_as_markup_on = ActiveSupport::OrderedOptions.new
4
+
5
+ initializer 'acts_as_markup_on.set_config', :after => 'active_record.initialize_database' do |app|
6
+ ActiveSupport.on_load(:acts_as_markup_on) do
7
+ self.markdown_library = app.config.acts_as_markup_on.markdown_library
8
+ self.mediawiki_library = app.config.acts_as_markup_on.mediawiki_library
9
+ end
10
+ end
11
+
12
+ initializer 'acts_as_markup_on.extend_active_record', :after => 'acts_as_markup_on.set_config' do |app|
13
+ ActiveSupport.on_load(:active_record) do
14
+ require 'acts_as_markup_on/exts/object'
15
+ require 'acts_as_markup_on/stringlike'
16
+ require 'acts_as_markup_on/active_record_extension'
17
+ self.send :include, ActsAsMarkupOn::ActiveRecordExtension
18
+ end
19
+ end
20
+
21
+ config.before_configuration do
22
+ config.acts_as_markup_on['markdown_library'] ||= :rdiscount
23
+ config.acts_as_markup_on['mediawiki_library'] ||= :wikicloth
24
+ end
25
+ end
26
+ 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,3 @@
1
+ module ActsAsMarkupOn
2
+ VERSION = '1.0.o'
3
+ end
@@ -0,0 +1,361 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ActsAsMarkdownTest < ActsAsMarkupOnTestCase
4
+ context 'acts_as_markdown_on' do
5
+ setup do
6
+ @markdown_text = '## Markdown Test Text'
7
+ end
8
+
9
+ context 'using RDiscount' do
10
+ setup do
11
+ ActsAsMarkupOn.markdown_library = :rdiscount
12
+ class ::Post < ActiveRecord::Base
13
+ acts_as_markdown_on :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_on :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
+ ActsAsMarkupOn.markdown_library = :rpeg
88
+ class ::Post < ActiveRecord::Base
89
+ acts_as_markdown_on :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_on :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
+ ActsAsMarkupOn.markdown_library = :bluecloth
164
+ class ::Post < ActiveRecord::Base
165
+ acts_as_markdown_on :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_on :body, :markdown_options => [{:escape_html => true}]
227
+ end
228
+ @post = Post.new(:title => 'Blah')
229
+ end
230
+
231
+ # TODO: This test is broken because BleuCloth's behavior is broken.
232
+ should "return escaped html because of :escape_html" do
233
+ @post.body = "## Markdown <i>Test</i> Text"
234
+ assert_match(/&lt;i&gt;Test&lt;\/i&gt;/, @post.body.to_html)
235
+ end
236
+ end
237
+
238
+ context 'using Maruku' do
239
+ setup do
240
+ ActsAsMarkupOn.markdown_library = :maruku
241
+ class ::Post < ActiveRecord::Base
242
+ acts_as_markdown_on :body
243
+ end
244
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
245
+ end
246
+
247
+ should_act_like_a_string
248
+
249
+ should "have a Maruku object returned for the column value" do
250
+ assert_kind_of Maruku, @post.body
251
+ end
252
+
253
+ should "return original markdown text for a `to_s` method call on the column value" do
254
+ assert_equal @markdown_text, @post.body.to_s
255
+ end
256
+
257
+ should 'return false for .blank?' do
258
+ assert !@post.body.blank?
259
+ end
260
+
261
+ should "return formated html for a `to_html` method call on the column value" do
262
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
263
+ end
264
+
265
+ context "changing value of markdown field should return new markdown object" do
266
+ setup do
267
+ @old_body = @post.body
268
+ @post.body = "`@count = 20`"
269
+ end
270
+
271
+ should "still have an Maruku object but not the same object" do
272
+ assert_kind_of Maruku, @post.body
273
+ assert_not_same @post.body, @old_body
274
+ end
275
+
276
+ should "return correct text for `to_s`" do
277
+ assert_equal "`@count = 20`", @post.body.to_s
278
+ end
279
+
280
+ should "return correct HTML for the `to_html` method" do
281
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
282
+ end
283
+
284
+ teardown do
285
+ @old_body = nil
286
+ end
287
+ end
288
+
289
+ teardown do
290
+ @post = nil
291
+ Post.delete_all
292
+ end
293
+ end
294
+
295
+ context 'using Redcarpet' do
296
+ setup do
297
+ ActsAsMarkupOn.markdown_library = :redcarpet
298
+ class ::Post < ActiveRecord::Base
299
+ acts_as_markdown_on :body
300
+ end
301
+ @post = Post.create!(:title => 'Blah', :body => @markdown_text)
302
+ end
303
+
304
+ should_act_like_a_string
305
+
306
+ should "have a Redcarpet object returned for the column value" do
307
+ assert_kind_of Redcarpet, @post.body
308
+ end
309
+
310
+ should "return original markdown text for a `to_s` method call on the column value" do
311
+ assert_equal @markdown_text, @post.body.to_s
312
+ end
313
+
314
+ should 'return false for .blank?' do
315
+ assert !@post.body.blank?
316
+ end
317
+
318
+ should "return formatted html for a `to_html` method call on the column value" do
319
+ assert_match(/<h2(\s\w+\=['"]\w*['"])*\s*>\s*Markdown Test Text\s*<\/h2>/, @post.body.to_html)
320
+ end
321
+
322
+ should "not return escaped html" do
323
+ @post.body = "## Markdown <i>Test</i> Text"
324
+ assert_match(/<i>Test<\/i>/, @post.body.to_html)
325
+ end
326
+
327
+ context "changing value of markdown field should return new markdown object" do
328
+ setup do
329
+ @old_body = @post.body
330
+ @post.body = "`@count = 20`"
331
+ end
332
+
333
+ should "still have an Redcarpet object but not the same object" do
334
+ assert_kind_of Redcarpet, @post.body
335
+ assert_not_same @post.body, @old_body
336
+ end
337
+
338
+ should "return correct text for `to_s`" do
339
+ assert_equal "`@count = 20`", @post.body.to_s
340
+ end
341
+
342
+ should "return correct HTML for the `to_html` method" do
343
+ assert_match(/<code>\s*\@count\s\=\s20\s*<\/code>/, @post.body.to_html)
344
+ end
345
+
346
+ teardown do
347
+ @old_body = nil
348
+ end
349
+ end
350
+
351
+ teardown do
352
+ @post = nil
353
+ Post.delete_all
354
+ end
355
+ end
356
+
357
+ teardown do
358
+ @markdown_text = nil
359
+ end
360
+ end
361
+ end