redcarpet 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of redcarpet might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 794753edf10a2310e7bcb5cdef1cf39f69c20a0f
4
- data.tar.gz: 9b2baf785cca27311b402c760b8a3d453acee40e
3
+ metadata.gz: add57c5fb7ad56babf9ce372000b7ce5f86dda6f
4
+ data.tar.gz: b58fe24ba222fcec7225082dacd7942fc187a405
5
5
  SHA512:
6
- metadata.gz: ddd0407e934865e45d5c2914f1b396054c16db71b854d77db320905c92b343b41d8ad23fed0db1cd591cfbb10d27fc1ba0a559c6985fe3e5c4ef3ffe072458cb
7
- data.tar.gz: 701d3101a85b4e716ae0734dca3cd77ae8d4636540ffc05eaad2cfe7262aebcb18c0fac7b4642341e83c28142a9073408c94535a5c9aae8ed9221a671bad0df5
6
+ metadata.gz: 6d8ba8386fbaf42f8de15624e3ec892f67d685d3b5a0d5b2f4e1855a8d23107631a2eb99f86757de22477b045fa20d793d4339a270c2b98a30e7f6b4ba3a83e1
7
+ data.tar.gz: 72ac393c6d785ba58c0c4cf69a167ed042dfd07cc1f86bedeb7854684e357c9d8f5156399ab57f9b19905991b694b499083c2d85169654d673a4a7a451a8fb03
data/Gemfile CHANGED
@@ -3,11 +3,7 @@ source "https://rubygems.org/"
3
3
  gemspec
4
4
 
5
5
  group :benchmark do
6
+ gem "benchmark-ips", "~> 1.2.0"
6
7
  gem "bluecloth", "~> 2.2.0"
7
8
  gem "kramdown", "~> 1.0.2"
8
9
  end
9
-
10
- platforms :rbx do
11
- gem "rubysl"
12
- gem "racc"
13
- end
data/README.markdown CHANGED
@@ -1,9 +1,9 @@
1
1
  Redcarpet is written with sugar, spice and everything nice
2
2
  ============================================================
3
3
 
4
- [![Build Status](https://travis-ci.org/vmg/redcarpet.png?branch=master)](https://travis-ci.org/vmg/redcarpet)
4
+ [![Build Status](https://travis-ci.org/vmg/redcarpet.svg?branch=master)](https://travis-ci.org/vmg/redcarpet)
5
5
 
6
- Redcarpet is Ruby library for Markdown processing that smells like
6
+ Redcarpet is a Ruby library for Markdown processing that smells like
7
7
  butterflies and popcorn.
8
8
 
9
9
  This library is written by people
@@ -78,8 +78,8 @@ be added at the end of the opening fence for the code block.
78
78
 
79
79
  * `:autolink`: parse links even when they are not enclosed in `<>`
80
80
  characters. Autolinks for the http, https and ftp protocols will be
81
- automatically detected. Email addresses are also handled, and http
82
- links without protocol, but starting with `www`.
81
+ automatically detected. Email addresses and http links without protocol,
82
+ but starting with `www` are also handled.
83
83
 
84
84
  * `:disable_indented_code_blocks`: do not parse usual markdown
85
85
  code blocks. Markdown converts text with four spaces at
@@ -149,6 +149,10 @@ Initializes an HTML renderer. The following flags are available:
149
149
 
150
150
  * `:no_styles`: do not generate any `<style>` tags.
151
151
 
152
+ * `:escape_html`: escape any HTML tags. This option has precedence over
153
+ `:no_styles`, `:no_links`, `:no_images` and `:filter_html` which means
154
+ that any existing tag will be escaped instead of being removed.
155
+
152
156
  * `:safe_links_only`: only generate links for protocols which are considered
153
157
  safe.
154
158
 
@@ -216,8 +220,9 @@ The following instance methods may be implemented by the renderer:
216
220
  ### Block-level calls
217
221
 
218
222
  If the return value of the method is `nil`, the block will be skipped.
219
- If the method for a document element is not implemented, the block will
220
- be skipped.
223
+ Therefore, make sure that your renderer has at least a `paragraph` method
224
+ implemented. If the method for a document element is not implemented, the
225
+ block will be skipped.
221
226
 
222
227
  Example:
223
228
 
@@ -266,9 +271,9 @@ be copied verbatim:
266
271
  * footnote_ref(number)
267
272
 
268
273
  **Note**: When overriding a renderer's method, be sure to return a HTML
269
- element with a level that match the level of that method (e.g. return a block
270
- element when overriding a block-level callback). Otherwise, the output may
271
- be unexpected.
274
+ element with a level that matches the level of that method (e.g. return a
275
+ block element when overriding a block-level callback). Otherwise, the output
276
+ may be unexpected.
272
277
 
273
278
  ### Low level rendering
274
279
 
@@ -327,47 +332,10 @@ SmartyPants works on top of already-rendered HTML, and will ignore replacements
327
332
  inside the content of HTML tags and inside specific HTML blocks such as
328
333
  `<code>` or `<pre>`.
329
334
 
330
- What? You really want to mix Markdown renderers?
331
- ------------------------------------------------
332
-
333
- Redcarpet used to be a drop-in replacement for Redcloth. This is no longer the
334
- case since version 2 -- it now has its own API, but retains the old name. Yes,
335
- that does mean that Redcarpet is not backwards-compatible with the 1.X
336
- versions.
337
-
338
- Each renderer has its own API and its own set of extensions: you should choose one
339
- (it doesn't have to be Redcarpet, though that would be great!), write your
340
- software accordingly, and force your users to install it. That's the
341
- only way to have reliable and predictable Markdown output on your program.
342
-
343
- Markdown is already ill-specified enough; if you create software that is
344
- renderer-independent, the results will be completely unreliable!
345
-
346
- Still, if major forces (let's say, tornadoes or other natural disasters) force you
347
- to keep a Markdown-compatibility layer, Redcarpet also supports this:
348
-
349
- ~~~~~ ruby
350
- require 'redcarpet/compat'
351
- ~~~~~
352
-
353
- Requiring the compatibility library will declare a `Markdown` class with the
354
- classical RedCloth API, e.g.
355
-
356
- ~~~~~ ruby
357
- Markdown.new('this is my text').to_html
358
- ~~~~~
359
-
360
- This class renders 100% standards compliant Markdown with 0 extensions. Nada.
361
- Don't even try to enable extensions with a compatibility layer, because
362
- that's a maintenance nightmare and won't work.
363
-
364
- On a related topic: if your Markdown gem has a `lib/markdown.rb` file that
365
- monkeypatches the Markdown class, you're a terrible human being. Just saying.
366
-
367
335
  Boring legal stuff
368
336
  ------------------
369
337
 
370
- Copyright (c) 2011-2013, Vicent Martí
338
+ Copyright (c) 2011-2014, Vicent Martí
371
339
 
372
340
  Permission to use, copy, modify, and/or distribute this software for any
373
341
  purpose with or without fee is hereby granted, provided that the above
data/Rakefile CHANGED
@@ -27,13 +27,13 @@ end
27
27
 
28
28
  task 'test:unit' => :compile
29
29
 
30
- desc 'Run conformance tests (MARKDOWN_TEST_VER=1.0)'
30
+ desc 'Run conformance tests (MARKDOWN_TEST_VER=1.0.3)'
31
31
  task 'test:conformance' => :compile do |t|
32
- script = "#{pwd}/bin/redcarpet"
33
- test_version = ENV['MARKDOWN_TEST_VER'] || '1.0.3'
32
+ script = "#{pwd}/bin/redcarpet"
33
+ version = ENV['MARKDOWN_TEST_VER'] || '1.0.3'
34
34
  lib_dir = "#{pwd}/lib"
35
35
 
36
- chdir("test/MarkdownTest_#{test_version}") do
36
+ chdir("test/MarkdownTest_#{version}") do
37
37
  sh "RUBYLIB=#{lib_dir} ./MarkdownTest.pl --script='#{script}' --tidy"
38
38
  end
39
39
  end
@@ -29,9 +29,9 @@
29
29
  int
30
30
  sd_autolink_issafe(const uint8_t *link, size_t link_len)
31
31
  {
32
- static const size_t valid_uris_count = 5;
32
+ static const size_t valid_uris_count = 6;
33
33
  static const char *valid_uris[] = {
34
- "/", "http://", "https://", "ftp://", "mailto:"
34
+ "#", "/", "http://", "https://", "ftp://", "mailto:"
35
35
  };
36
36
 
37
37
  size_t i;
@@ -1,4 +1,4 @@
1
- /* C code produced by gperf version 3.0.3 */
1
+ /* C code produced by gperf version 3.0.4 */
2
2
  /* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt */
3
3
  /* Computed positions: -k'1-2' */
4
4
 
@@ -29,7 +29,7 @@
29
29
  error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
30
30
  #endif
31
31
 
32
- /* maximum key range = 37, duplicates = 0 */
32
+ /* maximum key range = 67, duplicates = 0 */
33
33
 
34
34
  #ifndef GPERF_DOWNCASE
35
35
  #define GPERF_DOWNCASE 1
@@ -93,32 +93,32 @@ hash_block_tag (str, len)
93
93
  {
94
94
  static const unsigned char asso_values[] =
95
95
  {
96
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
97
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
98
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
99
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
100
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
101
- 8, 30, 25, 20, 15, 10, 38, 38, 38, 38,
102
- 38, 38, 38, 38, 38, 38, 0, 38, 0, 38,
103
- 5, 5, 5, 15, 0, 38, 38, 0, 15, 10,
104
- 0, 38, 38, 15, 0, 5, 38, 38, 38, 38,
105
- 38, 38, 38, 38, 38, 38, 38, 38, 0, 38,
106
- 0, 38, 5, 5, 5, 15, 0, 38, 38, 0,
107
- 15, 10, 0, 38, 38, 15, 0, 5, 38, 38,
108
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
109
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
110
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
111
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
112
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
113
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
114
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
115
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
116
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
117
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
118
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
119
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
120
- 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
121
- 38, 38, 38, 38, 38, 38, 38
96
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
97
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
98
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
99
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
100
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
101
+ 55, 50, 45, 40, 35, 30, 68, 68, 68, 68,
102
+ 68, 68, 68, 68, 68, 15, 10, 5, 15, 15,
103
+ 0, 20, 10, 10, 5, 68, 68, 0, 20, 25,
104
+ 0, 68, 68, 0, 25, 0, 15, 68, 68, 68,
105
+ 68, 68, 68, 68, 68, 68, 68, 15, 10, 5,
106
+ 15, 15, 0, 20, 10, 10, 5, 68, 68, 0,
107
+ 20, 25, 0, 68, 68, 0, 25, 0, 15, 68,
108
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
109
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
110
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
111
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
112
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
113
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
114
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
115
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
116
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
117
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
118
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
119
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
120
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
121
+ 68, 68, 68, 68, 68, 68, 68
122
122
  };
123
123
  register int hval = len;
124
124
 
@@ -136,7 +136,7 @@ hash_block_tag (str, len)
136
136
 
137
137
  #ifdef __GNUC__
138
138
  __inline
139
- #ifdef __GNUC_STDC_INLINE__
139
+ #if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
140
140
  __attribute__ ((__gnu_inline__))
141
141
  #endif
142
142
  #endif
@@ -147,47 +147,68 @@ find_block_tag (str, len)
147
147
  {
148
148
  enum
149
149
  {
150
- TOTAL_KEYWORDS = 24,
150
+ TOTAL_KEYWORDS = 40,
151
151
  MIN_WORD_LENGTH = 1,
152
152
  MAX_WORD_LENGTH = 10,
153
153
  MIN_HASH_VALUE = 1,
154
- MAX_HASH_VALUE = 37
154
+ MAX_HASH_VALUE = 67
155
155
  };
156
156
 
157
157
  static const char * const wordlist[] =
158
158
  {
159
159
  "",
160
160
  "p",
161
- "dl",
162
- "div",
163
- "math",
164
- "table",
165
- "",
166
161
  "ul",
167
- "del",
162
+ "pre",
168
163
  "form",
169
- "blockquote",
164
+ "style",
165
+ "footer",
166
+ "section",
167
+ "", "", "",
170
168
  "figure",
171
- "ol",
169
+ "hr",
172
170
  "fieldset",
171
+ "math",
172
+ "figcaption",
173
+ "header",
174
+ "dl",
175
+ "del",
173
176
  "",
174
- "h1",
177
+ "blockquote",
178
+ "canvas",
179
+ "article",
180
+ "div",
181
+ "abbr",
182
+ "video",
183
+ "hgroup",
184
+ "ol",
185
+ "noscript",
186
+ "", "", "",
187
+ "dd",
188
+ "nav",
189
+ "",
190
+ "audio",
191
+ "iframe",
192
+ "address",
193
+ "ins",
194
+ "",
195
+ "table",
175
196
  "",
176
197
  "h6",
177
- "pre",
178
198
  "", "",
179
- "script",
199
+ "aside",
200
+ "output",
180
201
  "h5",
181
- "noscript",
202
+ "", "",
203
+ "tfoot",
182
204
  "",
183
- "style",
184
- "iframe",
185
205
  "h4",
186
- "ins",
187
- "", "", "",
206
+ "", "", "", "",
188
207
  "h3",
189
208
  "", "", "", "",
190
- "h2"
209
+ "h2",
210
+ "", "", "", "",
211
+ "h1"
191
212
  };
192
213
 
193
214
  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -23,7 +23,7 @@
23
23
  #include <ctype.h>
24
24
 
25
25
  #if defined(_WIN32)
26
- #define snprintf _snprintf
26
+ #define snprintf _snprintf
27
27
  #endif
28
28
 
29
29
  struct smartypants_data {
@@ -139,6 +139,12 @@ smartypants_squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previo
139
139
  return next_squote_len;
140
140
  }
141
141
 
142
+ // trailing single quotes: students', tryin'
143
+ if (word_boundary(t1)) {
144
+ BUFPUTSL(ob, "&rsquo;");
145
+ return 0;
146
+ }
147
+
142
148
  // Tom's, isn't, I'm, I'd
143
149
  if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&
144
150
  (size == 3 || word_boundary(text[2]))) {
@@ -509,14 +509,14 @@ find_emph_char(uint8_t *data, size_t size, uint8_t c)
509
509
  if (i == size)
510
510
  return 0;
511
511
 
512
- if (data[i] == c)
513
- return i;
514
-
515
512
  /* not counting escaped chars */
516
513
  if (i && data[i - 1] == '\\') {
517
514
  i++; continue;
518
515
  }
519
516
 
517
+ if (data[i] == c)
518
+ return i;
519
+
520
520
  if (data[i] == '`') {
521
521
  size_t span_nb = 0, bt;
522
522
  size_t tmp_i = 0;
@@ -845,7 +845,7 @@ char_quote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offse
845
845
  static size_t
846
846
  char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
847
847
  {
848
- static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";
848
+ static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~=";
849
849
  struct buf work = { 0, 0, 0, 0 };
850
850
 
851
851
  if (size > 1) {
@@ -1624,7 +1624,7 @@ static size_t
1624
1624
  parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1625
1625
  {
1626
1626
  size_t i = 0, end = 0;
1627
- int level = 0;
1627
+ int level = 0, last_is_empty = 1;
1628
1628
  struct buf work = { data, 0, 0, 0 };
1629
1629
 
1630
1630
  while (i < size) {
@@ -1633,9 +1633,11 @@ parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
1633
1633
  if (is_empty(data + i, size - i))
1634
1634
  break;
1635
1635
 
1636
- if ((level = is_headerline(data + i, size - i)) != 0)
1636
+ if (!last_is_empty && (level = is_headerline(data + i, size - i)) != 0)
1637
1637
  break;
1638
1638
 
1639
+ last_is_empty = 0;
1640
+
1639
1641
  if (is_atxheader(rndr, data + i, size - i) ||
1640
1642
  is_hrule(data + i, size - i) ||
1641
1643
  prefix_quote(data + i, size - i)) {
data/lib/redcarpet.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'redcarpet.so'
2
2
 
3
3
  module Redcarpet
4
- VERSION = '3.1.2'
4
+ VERSION = '3.2.0'
5
5
 
6
6
  class Markdown
7
7
  attr_reader :renderer
@@ -11,8 +11,8 @@ module Redcarpet
11
11
 
12
12
  # XHTML Renderer
13
13
  class XHTML < HTML
14
- def initialize(extensions={})
15
- super(extensions.merge(:xhtml => true))
14
+ def initialize(extensions = {})
15
+ super(extensions.merge(xhtml: true))
16
16
  end
17
17
  end
18
18
 
@@ -21,6 +21,43 @@ module Redcarpet
21
21
  include SmartyPants
22
22
  end
23
23
 
24
+ # A renderer object you can use to deal with users' input. It
25
+ # enables +escape_html+ and +safe_links_only+ by default.
26
+ #
27
+ # The +block_code+ callback is also overriden not to include
28
+ # the lang's class as the user can basically specify anything
29
+ # with the vanilla one.
30
+ class Safe < HTML
31
+ def initialize(extensions = {})
32
+ super({
33
+ escape_html: true,
34
+ safe_links_only: true
35
+ }.merge(extensions))
36
+ end
37
+
38
+ def block_code(code, lang)
39
+ "<pre>" \
40
+ "<code>#{html_escape(code)}</code>" \
41
+ "</pre>"
42
+ end
43
+
44
+ private
45
+
46
+ # TODO: This is far from ideal to have such method as we
47
+ # are duplicating existing code from Houdini. This method
48
+ # should be defined at the C level.
49
+ def html_escape(string)
50
+ string.gsub(/['&\"<>\/]/, {
51
+ '&' => '&amp;',
52
+ '<' => '&lt;',
53
+ '>' => '&gt;',
54
+ '"' => '&quot;',
55
+ "'" => '&#x27;',
56
+ "/" => '&#x2F',
57
+ })
58
+ end
59
+ end
60
+
24
61
  # SmartyPants Mixin module
25
62
  #
26
63
  # Implements SmartyPants.postprocess, which
@@ -52,74 +89,3 @@ module Redcarpet
52
89
  end
53
90
  end
54
91
  end
55
-
56
- # Compatibility class;
57
- # Creates an instance of Redcarpet with the RedCloth API.
58
- class RedcarpetCompat
59
- attr_accessor :text
60
-
61
- def initialize(text, *exts)
62
- exts_hash, render_hash = *parse_extensions_and_renderer_options(exts)
63
- @text = text
64
- renderer = Redcarpet::Render::HTML.new(render_hash)
65
- @markdown = Redcarpet::Markdown.new(renderer, exts_hash)
66
- end
67
-
68
- def to_html(*_dummy)
69
- @markdown.render(@text)
70
- end
71
-
72
- private
73
-
74
- EXTENSION_MAP = {
75
- # old name => new name
76
- :autolink => :autolink,
77
- :fenced_code => :fenced_code_blocks,
78
- :filter_html => :filter_html,
79
- :hard_wrap => :hard_wrap,
80
- :prettify => :prettify,
81
- :lax_htmlblock => :lax_spacing,
82
- :no_image => :no_images,
83
- :no_intraemphasis => :no_intra_emphasis,
84
- :no_links => :no_links,
85
- :filter_styles => :no_styles,
86
- :safelink => :safe_links_only,
87
- :space_header => :space_after_headers,
88
- :strikethrough => :strikethrough,
89
- :tables => :tables,
90
- :generate_toc => :with_toc_data,
91
- :xhtml => :xhtml,
92
- # old names with no new mapping
93
- :gh_blockcode => nil,
94
- :no_tables => nil,
95
- :smart => nil,
96
- :strict => nil
97
- }
98
-
99
- RENDERER_OPTIONS = [:filter_html, :no_images, :no_links, :no_styles,
100
- :safe_links_only, :with_toc_data, :hard_wrap, :prettify, :xhtml]
101
-
102
- def rename_extensions(exts)
103
- exts.map do |old_name|
104
- if new_name = EXTENSION_MAP[old_name]
105
- new_name
106
- else
107
- old_name
108
- end
109
- end.compact
110
- end
111
-
112
- # Returns two hashes, the extensions and renderer options
113
- # given the extension list
114
- def parse_extensions_and_renderer_options(exts)
115
- exts = rename_extensions(exts)
116
- exts.partition {|ext| !RENDERER_OPTIONS.include?(ext) }.
117
- map {|list| list_to_truthy_hash(list) }
118
- end
119
-
120
- # Turns a list of symbols into a hash of <tt>symbol => true</tt>.
121
- def list_to_truthy_hash(list)
122
- list.inject({}) {|h, k| h[k] = true; h }
123
- end
124
- end
125
-
@@ -1,3 +1,76 @@
1
1
  require 'redcarpet'
2
2
 
3
+ # Deprecated: Please use the default API to parse Markdown
4
+ # documents ; this layer will be removed in Redcarpet 4.0.
5
+ #
6
+ # Creates an instance of Redcarpet with the RedCloth API.
7
+ class RedcarpetCompat
8
+ attr_accessor :text
9
+
10
+ def initialize(text, *exts)
11
+ exts_hash, render_hash = *parse_extensions_and_renderer_options(exts)
12
+ @text = text
13
+ renderer = Redcarpet::Render::HTML.new(render_hash)
14
+ @markdown = Redcarpet::Markdown.new(renderer, exts_hash)
15
+ end
16
+
17
+ def to_html(*_dummy)
18
+ @markdown.render(text)
19
+ end
20
+
21
+ private
22
+
23
+ EXTENSION_MAP = {
24
+ # old name => new name
25
+ :autolink => :autolink,
26
+ :fenced_code => :fenced_code_blocks,
27
+ :filter_html => :filter_html,
28
+ :hard_wrap => :hard_wrap,
29
+ :prettify => :prettify,
30
+ :lax_htmlblock => :lax_spacing,
31
+ :no_image => :no_images,
32
+ :no_intraemphasis => :no_intra_emphasis,
33
+ :no_links => :no_links,
34
+ :filter_styles => :no_styles,
35
+ :safelink => :safe_links_only,
36
+ :space_header => :space_after_headers,
37
+ :strikethrough => :strikethrough,
38
+ :tables => :tables,
39
+ :generate_toc => :with_toc_data,
40
+ :xhtml => :xhtml,
41
+
42
+ # old names with no new mapping
43
+ :gh_blockcode => nil,
44
+ :no_tables => nil,
45
+ :smart => nil,
46
+ :strict => nil
47
+ }
48
+
49
+ RENDERER_OPTIONS = [:filter_html, :no_images, :no_links, :no_styles,
50
+ :safe_links_only, :with_toc_data, :hard_wrap, :prettify, :xhtml]
51
+
52
+ def rename_extensions(exts)
53
+ exts.map do |old_name|
54
+ if new_name = EXTENSION_MAP[old_name]
55
+ new_name
56
+ else
57
+ old_name
58
+ end
59
+ end.compact
60
+ end
61
+
62
+ # Returns two hashes, the extensions and renderer options
63
+ # given the extension list
64
+ def parse_extensions_and_renderer_options(exts)
65
+ exts = rename_extensions(exts)
66
+ exts.partition {|ext| !RENDERER_OPTIONS.include?(ext) }.
67
+ map {|list| list_to_truthy_hash(list) }
68
+ end
69
+
70
+ # Turns a list of symbols into a hash of <tt>symbol => true</tt>.
71
+ def list_to_truthy_hash(list)
72
+ list.inject({}) {|h, k| h[k] = true; h }
73
+ end
74
+ end
75
+
3
76
  Markdown = RedcarpetCompat unless defined? Markdown
@@ -26,9 +26,9 @@ module Redcarpet
26
26
  end
27
27
  end
28
28
 
29
- # Other methods where the text content is in another argument
29
+ # Other methods where we don't return only a specific argument
30
30
  def link(link, title, content)
31
- content
31
+ "#{content} (#{link})"
32
32
  end
33
33
 
34
34
  def image(link, title, content)
data/redcarpet.gemspec CHANGED
@@ -1,10 +1,10 @@
1
1
  # encoding: utf-8
2
2
  Gem::Specification.new do |s|
3
3
  s.name = 'redcarpet'
4
- s.version = '3.1.2'
4
+ s.version = '3.2.0'
5
5
  s.summary = "Markdown that smells nice"
6
6
  s.description = 'A fast, safe and extensible Markdown to (X)HTML parser'
7
- s.date = '2014-05-10'
7
+ s.date = '2014-10-11'
8
8
  s.email = 'vicent@github.com'
9
9
  s.homepage = 'http://github.com/vmg/redcarpet'
10
10
  s.authors = ["Natacha Porté", "Vicent Martí"]
@@ -41,16 +41,19 @@ Gem::Specification.new do |s|
41
41
  lib/redcarpet/render_man.rb
42
42
  lib/redcarpet/render_strip.rb
43
43
  redcarpet.gemspec
44
- test/test_helper.rb
44
+ test/benchmark.rb
45
45
  test/custom_render_test.rb
46
+ test/html5_test.rb
46
47
  test/html_render_test.rb
47
48
  test/html_toc_render_test.rb
48
49
  test/markdown_test.rb
49
50
  test/pathological_inputs_test.rb
50
51
  test/redcarpet_compat_test.rb
52
+ test/safe_render_test.rb
51
53
  test/smarty_html_test.rb
52
54
  test/smarty_pants_test.rb
53
55
  test/stripdown_render_test.rb
56
+ test/test_helper.rb
54
57
  ]
55
58
  # = MANIFEST =
56
59
  s.test_files = s.files.grep(%r{^test/})
data/test/benchmark.rb ADDED
@@ -0,0 +1,24 @@
1
+ # coding: UTF-8
2
+ # Thanks Kramdown for the inspiration!
3
+ require 'benchmark/ips'
4
+
5
+ require 'redcarpet'
6
+ require 'bluecloth'
7
+ require 'kramdown'
8
+
9
+ markdown = File.read(File.join(File.dirname(__FILE__), "fixtures/benchmark.md"))
10
+
11
+ # Let's bench!
12
+ Benchmark.ips do |bench|
13
+ bench.report("Redcarpet") do
14
+ Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(markdown)
15
+ end
16
+
17
+ bench.report("BlueCloth") do
18
+ BlueCloth.new(markdown).to_html
19
+ end
20
+
21
+ bench.report("Kramdown") do
22
+ Kramdown::Document.new(markdown).to_html
23
+ end
24
+ end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class CustomRenderTest < Test::Unit::TestCase
4
+ class CustomRenderTest < Redcarpet::TestCase
5
5
  class SimpleRender < Redcarpet::Render::HTML
6
6
  def emphasis(text)
7
7
  "<em class=\"cool\">#{text}</em>"
@@ -0,0 +1,60 @@
1
+ require 'test_helper'
2
+
3
+ class HTML5Test < Redcarpet::TestCase
4
+ def test_that_html5_works
5
+ section = <<EOS
6
+ <section>
7
+ <p>The quick brown fox jumps over the lazy dog.</p>
8
+ </section>
9
+ EOS
10
+
11
+ figure = <<EOS
12
+ <figure>
13
+ <img src="http://example.org/image.jpg" alt="">
14
+ <figcaption>
15
+ <p>Hello world!</p>
16
+ </figcaption>
17
+ </figure>
18
+ EOS
19
+
20
+ assert_renders section, section
21
+ assert_renders figure, figure
22
+ end
23
+
24
+ def test_that_html5_works_with_code_blocks
25
+ section = <<EOS
26
+ \t<section>
27
+ \t\t<p>The quick brown fox jumps over the lazy dog.</p>
28
+ \t</section>
29
+ EOS
30
+
31
+ section_expected = <<EOE
32
+ <pre><code>&lt;section&gt;
33
+ &lt;p&gt;The quick brown fox jumps over the lazy dog.&lt;/p&gt;
34
+ &lt;/section&gt;
35
+ </code></pre>
36
+ EOE
37
+
38
+ header = <<EOS
39
+ <header>
40
+ <hgroup>
41
+ <h1>Section heading</h1>
42
+ <h2>Subhead</h2>
43
+ </hgroup>
44
+ </header>
45
+ EOS
46
+
47
+ header_expected = <<EOE
48
+ <pre><code>&lt;header&gt;
49
+ &lt;hgroup&gt;
50
+ &lt;h1&gt;Section heading&lt;/h1&gt;
51
+ &lt;h2&gt;Subhead&lt;/h2&gt;
52
+ &lt;/hgroup&gt;
53
+ &lt;/header&gt;
54
+ </code></pre>
55
+ EOE
56
+
57
+ assert_renders section_expected, section
58
+ assert_renders header_expected, header
59
+ end
60
+ end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class HTMLRenderTest < Test::Unit::TestCase
4
+ class HTMLRenderTest < Redcarpet::TestCase
5
5
  def setup
6
6
  @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
7
7
  @rndr = {
@@ -220,4 +220,21 @@ Markdown
220
220
 
221
221
  assert output.include?("<code class=\"prettyprint ruby\">")
222
222
  end
223
+
224
+ def test_safe_links_only_with_anchors
225
+ markdown = "An [anchor link](#anchor) on a page."
226
+
227
+ renderer = Redcarpet::Markdown.new(@rndr[:safe_links])
228
+ output = renderer.render(markdown)
229
+
230
+ assert_match %r{<a href="#anchor">anchor link</a>}, output
231
+ end
232
+
233
+ def test_autolink_with_link_attributes
234
+ render = Redcarpet::Render::HTML.new(link_attributes: {rel: "nofollow"})
235
+ parser = Redcarpet::Markdown.new(render, autolink: true)
236
+
237
+ output = parser.render("https://github.com/")
238
+ assert_match %r{rel="nofollow"}, output
239
+ end
223
240
  end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class HTMLTOCRenderTest < Test::Unit::TestCase
4
+ class HTMLTOCRenderTest < Redcarpet::TestCase
5
5
  def setup
6
6
  @render = Redcarpet::Render::HTML_TOC
7
7
  @markdown = "# A title \n## A __nice__ subtitle\n## Another one \n### A sub-sub-title"
@@ -14,8 +14,8 @@ class HTMLTOCRenderTest < Test::Unit::TestCase
14
14
  assert output.start_with?("<ul>")
15
15
  assert output.end_with?("</ul>")
16
16
 
17
- assert_equal 4, output.split("<ul>").length
18
- assert_equal 5, output.split("<li>").length
17
+ assert_equal 3, output.scan("<ul>").length
18
+ assert_equal 4, output.scan("<li>").length
19
19
  end
20
20
 
21
21
  def test_granular_toc_render
@@ -25,7 +25,7 @@ class HTMLTOCRenderTest < Test::Unit::TestCase
25
25
  assert output.start_with?("<ul>")
26
26
  assert output.end_with?("</ul>")
27
27
 
28
- assert_equal 4, output.split("<li>").length
28
+ assert_equal 3, output.scan("<li>").length
29
29
  assert !output.include?("A sub-sub title")
30
30
  end
31
31
 
@@ -38,4 +38,12 @@ class HTMLTOCRenderTest < Test::Unit::TestCase
38
38
  assert_match /another-one/, output
39
39
  assert_match /a-sub-sub-title/, output
40
40
  end
41
+
42
+ def test_toc_heading_with_hyphen_and_equal
43
+ renderer = Redcarpet::Markdown.new(@render)
44
+ output = renderer.render("# Hello World\n\n-\n\n=")
45
+
46
+ assert_equal 1, output.scan("<li>").length
47
+ assert !output.include?('<a href=\"#\"></a>')
48
+ end
41
49
  end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class MarkdownTest < Test::Unit::TestCase
4
+ class MarkdownTest < Redcarpet::TestCase
5
5
 
6
6
  def setup
7
7
  @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
@@ -149,6 +149,11 @@ HTML
149
149
  header
150
150
  end
151
151
 
152
+ def test_a_hyphen_and_a_equal_should_not_be_converted_to_heading
153
+ html_equal "<p>-</p>\n", @markdown.render("-")
154
+ html_equal "<p>=</p>\n", @markdown.render("=")
155
+ end
156
+
152
157
  def test_that_tables_flag_works
153
158
  text = <<EOS
154
159
  aaa | bbbb
@@ -280,6 +285,17 @@ text
280
285
  assert_equal html, render_with({:no_intra_emphasis => true}, markdown)
281
286
  end
282
287
 
288
+ def test_emphasis_escaping
289
+ markdown = @markdown.render("**foo\\*** _dd\\_dd_")
290
+ html_equal "<p><strong>foo*</strong> <em>dd_dd</em></p>\n", markdown
291
+ end
292
+
293
+ def test_char_escaping_when_highlighting
294
+ markdown = "==attribute\\==="
295
+ output = render_with({highlight: true}, markdown)
296
+ html_equal "<p><mark>attribute=</mark></p>\n", output
297
+ end
298
+
283
299
  def test_ordered_lists_with_lax_spacing
284
300
  markdown = "Foo:\n1. Foo\n2. Bar"
285
301
  output = render_with({lax_spacing: true}, markdown)
@@ -290,6 +306,6 @@ text
290
306
 
291
307
  def test_references_with_tabs_after_colon
292
308
  markdown = @markdown.render("[Link][id]\n[id]:\t\t\thttp://google.es")
293
- html_equal '<p><a href="http://google.es">Link</a></p>', markdown
309
+ html_equal "<p><a href=\"http://google.es\">Link</a></p>\n", markdown
294
310
  end
295
311
  end
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
 
4
4
  # Disabled by default
5
5
  # (these are the easy ones -- the evil ones are not disclosed)
6
- class PathologicalInputsTest # < Test::Unit::TestCase
6
+ class PathologicalInputsTest # < Redcarpet::TestCase
7
7
  def setup
8
8
  @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
9
9
  end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class RedcarpetCompatTest < Test::Unit::TestCase
4
+ class RedcarpetCompatTest < Redcarpet::TestCase
5
5
  def test_simple_compat_api
6
6
  html = RedcarpetCompat.new("This is_just_a test").to_html
7
7
  html_equal "<p>This is<em>just</em>a test</p>\n", html
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class SafeRenderTest < Redcarpet::TestCase
4
+ def setup
5
+ @render = Redcarpet::Render::Safe
6
+ @parser = Redcarpet::Markdown.new(@render, fenced_code_blocks: true)
7
+ end
8
+
9
+ def test_safe_links_only_is_enabled_by_default
10
+ markdown = "[foo](javascript:alert('foo'))"
11
+ output = @parser.render(markdown)
12
+
13
+ assert_not_match %r{a href}, output
14
+ end
15
+
16
+ def test_escape_html_is_enabled_by_default
17
+ markdown = "<p>Hello world!</p>"
18
+ output = @parser.render(markdown)
19
+
20
+ assert_match %r{&lt;}, output
21
+ end
22
+
23
+ def test_html_escaping_in_code_blocks
24
+ markdown = "~~~\n<p>Hello!</p>\n~~~"
25
+ output = @parser.render(markdown)
26
+
27
+ assert_match %r{&lt;p&gt;}, output
28
+ end
29
+
30
+ def test_lang_class_is_removed
31
+ markdown = "~~~ruby\nclass Foo; end\n~~~"
32
+ output = @parser.render(markdown)
33
+
34
+ assert_not_match %r{ruby}, output
35
+ end
36
+ end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class SmartyHTMLTest < Test::Unit::TestCase
4
+ class SmartyHTMLTest < Redcarpet::TestCase
5
5
  def setup
6
6
  @smarty_markdown = Redcarpet::Markdown.new(Redcarpet::Render::SmartyHTML)
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class SmartyPantsTest < Test::Unit::TestCase
4
+ class SmartyPantsTest < Redcarpet::TestCase
5
5
  def setup
6
6
  @pants = Redcarpet::Render::SmartyPants
7
7
  end
@@ -40,4 +40,9 @@ class SmartyPantsTest < Test::Unit::TestCase
40
40
  rd = @pants.render("<p>single `backticks` in HTML should be preserved</p>")
41
41
  assert_equal "<p>single `backticks` in HTML should be preserved</p>", rd
42
42
  end
43
+
44
+ def test_that_smart_converts_trailing_single_quotes_to_curly_quotes
45
+ rd = @pants.render("<p>Hopin' that this bug gets some fixin'.</p>")
46
+ assert_equal "<p>Hopin&rsquo; that this bug gets some fixin&rsquo;.</p>", rd
47
+ end
43
48
  end
@@ -1,42 +1,40 @@
1
1
  # coding: UTF-8
2
2
  require 'test_helper'
3
3
 
4
- class StripDownRender < Test::Unit::TestCase
5
-
4
+ class StripDownRender < Redcarpet::TestCase
6
5
  def setup
7
- @markdown = Redcarpet::Markdown.new(Redcarpet::Render::StripDown)
6
+ @parser = Redcarpet::Markdown.new(Redcarpet::Render::StripDown)
8
7
  end
9
8
 
10
- def test_basics
11
- markdown = <<-Markdown
12
- # [Foo bar](https://github.com)
13
- Markdown
14
- html = @markdown.render(markdown)
15
- html_equal "Foo bar\n", html
9
+ def test_titles
10
+ markdown = "# Foo bar"
11
+ output = @parser.render(markdown)
12
+
13
+ assert_equal "Foo bar\n", output
16
14
  end
17
15
 
18
- def test_insert_new_lines_char
19
- markdown = <<-Markdown
20
- # Foo bar
16
+ def test_code_blocks
17
+ markdown = "\tclass Foo\n\tend"
18
+ output = @parser.render(markdown)
19
+
20
+ assert_equal "class Foo\nend\n", output
21
+ end
21
22
 
22
- Hello world! Please visit [this site](https://github.com/).
23
+ def test_images
24
+ markdown = "Look at this ![picture](http://example.org/picture.png)\n" \
25
+ "And this: ![](http://example.org/image.jpg)"
26
+ expected = "Look at this picture http://example.org/picture.png\n" \
27
+ "And this: http://example.org/image.jpg\n"
28
+ output = @parser.render(markdown)
23
29
 
24
- class Foo
25
- end
30
+ assert_equal expected, output
31
+ end
26
32
 
27
- Look at this ![picture](http://example.org/picture.png)
28
- And this: ![](http://example.org/image.jpg)
29
- Markdown
30
- plaintext = <<-Plaintext
31
- Foo bar
32
- Hello world! Please visit this site.
33
- class Foo
34
- end
35
- Look at this picture http://example.org/picture.png
36
- And this: http://example.org/image.jpg
37
- Plaintext
33
+ def test_links
34
+ markdown = "Here's an [example](https://github.com)"
35
+ expected = "Here's an example (https://github.com)\n"
36
+ output = @parser.render(markdown)
38
37
 
39
- html = @markdown.render(markdown)
40
- html_equal plaintext, html
38
+ assert_equal expected, output
41
39
  end
42
40
  end
data/test/test_helper.rb CHANGED
@@ -9,8 +9,21 @@ require 'nokogiri'
9
9
  require 'redcarpet'
10
10
  require 'redcarpet/render_strip'
11
11
  require 'redcarpet/render_man'
12
+ require 'redcarpet/compat'
12
13
 
13
- def html_equal(html_a, html_b)
14
- assert_equal Nokogiri::HTML::DocumentFragment.parse(html_a).to_html,
15
- Nokogiri::HTML::DocumentFragment.parse(html_b).to_html
14
+ class Redcarpet::TestCase < Test::Unit::TestCase
15
+ def html_equal(html_a, html_b)
16
+ assert_equal Nokogiri::HTML::DocumentFragment.parse(html_a).to_html,
17
+ Nokogiri::HTML::DocumentFragment.parse(html_b).to_html
18
+ end
19
+
20
+ def assert_renders(html, markdown)
21
+ html_equal html, parser.render(markdown)
22
+ end
23
+
24
+ private
25
+
26
+ def parser
27
+ @parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML)
28
+ end
16
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redcarpet
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Natacha Porté
@@ -9,48 +9,48 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-10 00:00:00.000000000 Z
12
+ date: 2014-10-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ~>
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
20
  version: 1.6.0
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ~>
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 1.6.0
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rake-compiler
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ~>
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
34
  version: 0.8.3
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ~>
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: 0.8.3
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: test-unit
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ~>
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
48
  version: 2.5.4
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ~>
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 2.5.4
56
56
  description: A fast, safe and extensible Markdown to (X)HTML parser
@@ -91,16 +91,19 @@ files:
91
91
  - lib/redcarpet/render_man.rb
92
92
  - lib/redcarpet/render_strip.rb
93
93
  - redcarpet.gemspec
94
- - test/test_helper.rb
94
+ - test/benchmark.rb
95
95
  - test/custom_render_test.rb
96
+ - test/html5_test.rb
96
97
  - test/html_render_test.rb
97
98
  - test/html_toc_render_test.rb
98
99
  - test/markdown_test.rb
99
100
  - test/pathological_inputs_test.rb
100
101
  - test/redcarpet_compat_test.rb
102
+ - test/safe_render_test.rb
101
103
  - test/smarty_html_test.rb
102
104
  - test/smarty_pants_test.rb
103
105
  - test/stripdown_render_test.rb
106
+ - test/test_helper.rb
104
107
  homepage: http://github.com/vmg/redcarpet
105
108
  licenses:
106
109
  - MIT
@@ -111,28 +114,31 @@ require_paths:
111
114
  - lib
112
115
  required_ruby_version: !ruby/object:Gem::Requirement
113
116
  requirements:
114
- - - '>='
117
+ - - ">="
115
118
  - !ruby/object:Gem::Version
116
119
  version: 1.9.2
117
120
  required_rubygems_version: !ruby/object:Gem::Requirement
118
121
  requirements:
119
- - - '>='
122
+ - - ">="
120
123
  - !ruby/object:Gem::Version
121
124
  version: '0'
122
125
  requirements: []
123
126
  rubyforge_project:
124
- rubygems_version: 2.0.3
127
+ rubygems_version: 2.2.2
125
128
  signing_key:
126
129
  specification_version: 4
127
130
  summary: Markdown that smells nice
128
131
  test_files:
129
- - test/test_helper.rb
132
+ - test/benchmark.rb
130
133
  - test/custom_render_test.rb
134
+ - test/html5_test.rb
131
135
  - test/html_render_test.rb
132
136
  - test/html_toc_render_test.rb
133
137
  - test/markdown_test.rb
134
138
  - test/pathological_inputs_test.rb
135
139
  - test/redcarpet_compat_test.rb
140
+ - test/safe_render_test.rb
136
141
  - test/smarty_html_test.rb
137
142
  - test/smarty_pants_test.rb
138
143
  - test/stripdown_render_test.rb
144
+ - test/test_helper.rb