mint 0.7.4 → 0.8.1

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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -14
  3. data/LICENSE +22 -0
  4. data/README.md +68 -79
  5. data/bin/mint +47 -10
  6. data/bin/mint-epub +1 -4
  7. data/config/templates/base/style.css +187 -0
  8. data/config/templates/default/layout.erb +10 -0
  9. data/config/templates/default/style.css +237 -0
  10. data/config/templates/garden/layout.erb +38 -0
  11. data/config/templates/garden/style.css +303 -0
  12. data/config/templates/nord/layout.erb +11 -0
  13. data/config/templates/nord/style.css +339 -0
  14. data/config/templates/nord-dark/layout.erb +11 -0
  15. data/config/templates/nord-dark/style.css +339 -0
  16. data/config/templates/zen/layout.erb +11 -0
  17. data/config/templates/zen/style.css +114 -0
  18. data/lib/mint/command_line.rb +253 -111
  19. data/lib/mint/css.rb +11 -4
  20. data/lib/mint/css_parser.rb +76 -0
  21. data/lib/mint/css_template.rb +37 -0
  22. data/lib/mint/document.rb +203 -43
  23. data/lib/mint/helpers.rb +50 -10
  24. data/lib/mint/layout.rb +2 -3
  25. data/lib/mint/markdown_template.rb +47 -0
  26. data/lib/mint/mint.rb +181 -114
  27. data/lib/mint/plugin.rb +3 -3
  28. data/lib/mint/plugins/epub.rb +1 -2
  29. data/lib/mint/resource.rb +19 -9
  30. data/lib/mint/style.rb +10 -14
  31. data/lib/mint/version.rb +1 -1
  32. data/lib/mint.rb +1 -0
  33. data/man/mint.1 +135 -0
  34. data/spec/cli/README.md +99 -0
  35. data/spec/cli/argument_parsing_spec.rb +237 -0
  36. data/spec/cli/bin_integration_spec.rb +348 -0
  37. data/spec/cli/configuration_management_spec.rb +363 -0
  38. data/spec/cli/full_workflow_integration_spec.rb +527 -0
  39. data/spec/cli/publish_workflow_spec.rb +368 -0
  40. data/spec/cli/template_management_spec.rb +300 -0
  41. data/spec/css_parser_spec.rb +149 -0
  42. data/spec/css_spec.rb +1 -1
  43. data/spec/document_spec.rb +102 -69
  44. data/spec/helpers_spec.rb +42 -42
  45. data/spec/mint_spec.rb +104 -80
  46. data/spec/plugin_spec.rb +141 -143
  47. data/spec/run_cli_tests.rb +95 -0
  48. data/spec/spec_helper.rb +8 -1
  49. data/spec/style_spec.rb +18 -16
  50. data/spec/support/cli_helpers.rb +169 -0
  51. data/spec/support/fixtures/content-2.md +16 -0
  52. data/spec/support/matchers.rb +1 -1
  53. metadata +116 -224
  54. data/config/syntax.yaml +0 -71
  55. data/config/templates/base/style.sass +0 -144
  56. data/config/templates/default/css/style.css +0 -158
  57. data/config/templates/default/layout.haml +0 -8
  58. data/config/templates/default/style.sass +0 -36
  59. data/config/templates/protocol/layout.haml +0 -7
  60. data/config/templates/protocol/style.sass +0 -20
  61. data/config/templates/zen/css/style.css +0 -145
  62. data/config/templates/zen/layout.haml +0 -7
  63. data/config/templates/zen/style.sass +0 -24
  64. data/features/config.feature +0 -21
  65. data/features/plugins/epub.feature +0 -23
  66. data/features/publish.feature +0 -73
  67. data/features/support/env.rb +0 -15
  68. data/features/templates.feature +0 -79
  69. data/spec/command_line_spec.rb +0 -87
  70. data/spec/plugins/epub_spec.rb +0 -242
@@ -0,0 +1,37 @@
1
+ require 'tilt/template'
2
+
3
+ module Mint
4
+ class CSSTemplate < Tilt::Template
5
+ self.default_mime_type = 'text/css'
6
+
7
+ def prepare
8
+ @data = data
9
+ end
10
+
11
+ def evaluate(scope, locals, &block)
12
+ process_imports(@data, File.dirname(file))
13
+ end
14
+
15
+ def process_imports(css_content, base_dir)
16
+ css_content.gsub(/@import\s+["']([^"']+)["'];?/) do |match|
17
+ import_path = $1
18
+
19
+ # If we find a relative path, resolve it
20
+ if import_path.start_with?('../', './')
21
+ full_path = File.expand_path(import_path, base_dir)
22
+ else
23
+ full_path = File.join(base_dir, import_path)
24
+ end
25
+
26
+ full_path += '.css' unless full_path.end_with?('.css')
27
+
28
+ if File.exist?(full_path)
29
+ imported_content = File.read(full_path)
30
+ process_imports(imported_content, File.dirname(full_path))
31
+ else
32
+ match
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/mint/document.rb CHANGED
@@ -1,57 +1,81 @@
1
1
  require "mint/resource"
2
2
  require "mint/layout"
3
3
  require "mint/style"
4
+ require "mint/css_parser"
4
5
 
5
6
  module Mint
6
7
  class Document < Resource
7
8
  METADATA_DELIM = "\n\n"
8
9
 
9
10
  # Creates a new Mint Document object. Can be block initialized.
10
- # Accepts source and options. Block initialization occurs after
11
- # all defaults are set, so not all options must be specified.
12
- def initialize(source, opts={})
13
- options = Mint.default_options.merge opts
11
+ def initialize(source,
12
+ root: nil,
13
+ destination: nil,
14
+ context: nil,
15
+ name: nil,
16
+ style_mode: :inline,
17
+ style_destination: nil,
18
+ layout: nil,
19
+ style: nil,
20
+ template: nil,
21
+ layout_or_style_or_template: [:template, 'default'],
22
+ all_files: nil,
23
+ &block)
24
+
25
+ destination = preserve_folder_structure!(source, root, destination)
26
+
27
+ @all_files = all_files if all_files && all_files.any?
28
+
29
+ style_mode = :external if style_destination && style_mode == :inline
14
30
 
15
31
  # Loads source and destination, which will be used for
16
32
  # all source_* and destination_* virtual attributes.
17
- super(source, options)
18
- self.type = :document
33
+ super(source, root: root, destination: destination, context: context, name: name)
34
+ self.type = :document
19
35
 
20
36
  # Each of these should invoke explicitly defined method
21
- self.content = source
22
- self.layout = options[:layout]
23
- self.style = options[:style]
24
- self.style_destination = options[:style_destination]
37
+ self.content = source
38
+ self.style_mode = style_mode
39
+ self.style_destination = style_destination
40
+
41
+ if layout_or_style_or_template
42
+ type, name = layout_or_style_or_template
43
+ case type
44
+ when :template
45
+ self.template = name
46
+ when :layout
47
+ self.layout = name
48
+ when :style
49
+ self.style = name
50
+ end
51
+ end
52
+
53
+ # Individual layout/style options can override the above
54
+ self.layout = layout if layout
55
+ self.style = style if style
25
56
 
26
57
  # The template option will override layout and style choices
27
- self.template = options[:template]
58
+ self.template = template if template
28
59
 
29
60
  # Yield self to block after all other parameters are loaded,
30
61
  # so we only have to tweak. (We don't have to give up our
31
62
  # defaults or re-test blocks beyond them being tweaked.)
32
- yield self if block_given?
63
+ yield self if block
33
64
  end
34
65
 
35
66
  # Renders content in the context of layout and returns as a String.
36
67
  def render(args={})
37
68
  intermediate_content = layout.render self, args
38
- Mint.after_render(intermediate_content)
69
+ Mint.after_render(intermediate_content, {})
39
70
  end
40
71
 
41
72
  # Writes all rendered content where a) possible, b) required,
42
73
  # and c) specified. Outputs to specified file.
43
- def publish!(opts={})
74
+ def publish!(opts={})
44
75
  options = { :render_style => true }.merge(opts)
45
- FileUtils.mkdir_p self.destination_directory
46
- File.open(self.destination_file, "w+") do |f|
47
- f << self.render
48
- end
76
+ super
49
77
 
50
- # Only renders style if a) it's specified by the options path and
51
- # b) it actually needs rendering (i.e., it's in template form and
52
- # not raw, browser-parseable CSS) or it if it doesn't need
53
- # rendering but there is an explicit style_destination.
54
- if options[:render_style]
78
+ if @style_mode == :external && options[:render_style]
55
79
  FileUtils.mkdir_p style_destination_directory
56
80
  File.open(self.style_destination_file, "w+") do |f|
57
81
  f << self.style.render
@@ -63,7 +87,12 @@ module Mint
63
87
 
64
88
  # Implicit readers are paired with explicit accessors. This
65
89
  # allows for processing variables before storing them.
66
- attr_reader :content, :metadata, :layout, :style
90
+ attr_reader :metadata, :layout, :style
91
+
92
+ # Returns HTML content marked as safe for template rendering
93
+ def content
94
+ @content.html_safe
95
+ end
67
96
 
68
97
  # Passes content through a renderer before assigning it to be
69
98
  # the Document's content
@@ -76,7 +105,8 @@ module Mint
76
105
  original_content = File.read content
77
106
 
78
107
  @metadata, text = Document.parse_metadata_from original_content
79
- intermediate_content = Mint.before_render text
108
+ text_with_links = Helpers.transform_markdown_links text
109
+ intermediate_content = Mint.before_render text_with_links, {}
80
110
 
81
111
  File.open(tempfile, "w") do |file|
82
112
  file << intermediate_content
@@ -91,33 +121,29 @@ module Mint
91
121
  # @param [String, Layout, #render] layout a Layout object or name
92
122
  # of a layout to be looked up
93
123
  # @return [void]
94
- def layout=(layout)
95
- @layout =
124
+ def layout=(layout)
125
+ @layout =
96
126
  if layout.respond_to? :render
97
127
  layout
98
128
  else
99
- layout_file = Mint.lookup_template layout, :layout
100
- Layout.new layout_file
129
+ layout_file = Mint.lookup_layout layout
130
+ Layout.new(layout_file, root: self.root, destination: self.destination, context: self.context)
101
131
  end
102
- rescue TemplateNotFoundException
103
- abort "Template '#{layout}' does not exist."
104
132
  end
105
-
133
+
106
134
  # Sets layout to an existing Style object or looks it up by name
107
135
  #
108
136
  # @param [String, Style, #render] layout a Layout object or name
109
137
  # of a layout to be looked up
110
138
  # @return [void]
111
139
  def style=(style)
112
- @style =
140
+ @style =
113
141
  if style.respond_to? :render
114
142
  style
115
143
  else
116
- style_file = Mint.lookup_template style, :style
117
- Style.new style_file
144
+ style_file = Mint.lookup_style style
145
+ Style.new(style_file, root: self.root, destination: self.destination, context: self.context)
118
146
  end
119
- rescue TemplateNotFoundException
120
- abort "Template '#{style}' does not exist."
121
147
  end
122
148
 
123
149
  # Overrides layout and style settings with named template.
@@ -148,8 +174,14 @@ module Mint
148
174
  #
149
175
  # The style_destination attribute is lazy. It's exposed via
150
176
  # virtual attributes like #style_destination_file.
151
- attr_reader :style_destination
152
-
177
+ attr_reader :style_destination, :style_mode
178
+
179
+ # @param [Symbol] style_mode how styles should be incorporated (:inline or :external)
180
+ # @return [void]
181
+ def style_mode=(style_mode)
182
+ @style_mode = style_mode
183
+ end
184
+
153
185
  # @param [String] style_destination the subdirectory into
154
186
  # which styles will be rendered or copied
155
187
  # @return [void]
@@ -163,7 +195,7 @@ module Mint
163
195
  def style_destination_file_path
164
196
  if style_destination
165
197
  path = Pathname.new style_destination
166
- dir = path.absolute? ?
198
+ dir = path.absolute? ?
167
199
  path : destination_directory_path + path
168
200
  dir + style.name
169
201
  else
@@ -197,16 +229,140 @@ module Mint
197
229
  # Returns a relative path from the document to its stylesheet. Can
198
230
  # be called directly from inside a layout template.
199
231
  def stylesheet
200
- Helpers.normalize_path(self.style_destination_file,
232
+ tmp_style_dir = Mint.path_for_scope(:user) + "tmp"
233
+ tmp_style_file = tmp_style_dir + File.basename(style.name)
234
+ Helpers.normalize_path(tmp_style_file.to_s,
201
235
  self.destination_directory).to_s
202
236
  end
203
237
 
238
+ # Returns the rendered CSS content for inline inclusion
239
+ def inline_stylesheet
240
+ self.style.render
241
+ end
242
+
243
+ # Returns either inline CSS or stylesheet link based on style mode
244
+ # Use this helper in layouts instead of stylesheet or inline_stylesheet directly
245
+ def stylesheet_tag
246
+ case @style_mode
247
+ when :external
248
+ "<link rel=\"stylesheet\" href=\"#{stylesheet}\">".html_safe
249
+ when :original
250
+ original_stylesheet_tags.html_safe
251
+ else # :inline (default)
252
+ "<style>#{self.style.render}</style>".html_safe
253
+ end
254
+ end
255
+
256
+ # Generates stylesheet link tags for original mode
257
+ # Links directly to original CSS files without processing
258
+ def original_stylesheet_tags
259
+ return "" unless self.style&.template_path
260
+
261
+ main_css_path = self.style.template_path
262
+ html_output_path = self.output_file
263
+
264
+ # Only process .css files
265
+ return "" unless File.extname(main_css_path) == '.css'
266
+
267
+ css_file_paths = CssParser.resolve_css_files(main_css_path, html_output_path)
268
+ CssParser.generate_link_tags(css_file_paths)
269
+ end
270
+
271
+ # Parses styles defined in YAML metadata in content, including it
272
+ # in output CSS style
273
+ #
274
+ # TODO: Implement injection of these styles
204
275
  def inline_styles
205
276
  CSS.parse(metadata)
206
277
  end
207
278
 
279
+ # Returns information about all files for navigation in some templates (e.g., garden)
280
+ # Available when processing multiple files
281
+ def files
282
+ return [] unless @all_files
283
+
284
+ # Get the base directories
285
+ source_base_dir = Pathname.new(root_directory_path).expand_path
286
+
287
+ # Calculate where the current file will actually be placed
288
+ current_source_path = Pathname.new(source_file_path).expand_path
289
+ current_relative_to_source = current_source_path.relative_path_from(source_base_dir)
290
+ current_html_filename = current_relative_to_source.to_s.gsub(/\.(#{Mint::MARKDOWN_EXTENSIONS.join('|')})$/i, '.html')
291
+
292
+ dest_base = Pathname.new(root_directory_path).expand_path
293
+ if destination && !destination.empty?
294
+ dest_base = dest_base + destination
295
+ end
296
+
297
+ current_full_path = dest_base + current_html_filename
298
+ current_destination_dir = current_full_path.dirname
299
+
300
+ @all_files.map do |file|
301
+ title = extract_title_from_file(file)
302
+
303
+ # Calculate where this target file will be placed
304
+ file_path = Pathname.new(file).expand_path
305
+ relative_to_source = file_path.relative_path_from(source_base_dir)
306
+ html_filename = relative_to_source.to_s.gsub(/\.(#{Mint::MARKDOWN_EXTENSIONS.join('|')})$/i, '.html')
307
+
308
+ target_full_path = dest_base + html_filename
309
+
310
+ # Calculate the relative path from the current file's destination directory to the target file
311
+ relative_link = target_full_path.relative_path_from(current_destination_dir)
312
+
313
+ {
314
+ source_path: relative_to_source.to_s,
315
+ html_path: relative_link.to_s,
316
+ title: title,
317
+ depth: relative_to_source.to_s.count('/')
318
+ }
319
+ end.sort_by {|f| f[:source_path] }
320
+ end
321
+
208
322
  # Functions
209
323
 
324
+ private
325
+
326
+ # Extracts the title from a markdown file, trying H1 first, then filename
327
+ def extract_title_from_file(file)
328
+ content = File.read(file)
329
+
330
+ if content =~ /^#\s+(.+)$/
331
+ return $1.strip
332
+ end
333
+
334
+ File.basename(file, '.*').tr('_-', ' ').split.map(&:capitalize).join(' ')
335
+ rescue
336
+ File.basename(file, '.*').tr('_-', ' ').split.map(&:capitalize).join(' ')
337
+ end
338
+
339
+ # Preserves folder structure when --recursive is used
340
+ #
341
+ # @param [String] source the source file path
342
+ # @param [Hash] options the options hash to modify
343
+ def preserve_folder_structure!(source, root, destination)
344
+ source_path = Pathname.new(source).expand_path
345
+ root_path = Pathname.new(root || Dir.getwd).expand_path
346
+
347
+ relative_path = source_path.relative_path_from(root_path)
348
+
349
+ relative_dir = relative_path.dirname
350
+ filename = relative_path.basename
351
+
352
+ # Set destination to preserve directory structure
353
+ if relative_dir.to_s != "."
354
+ # Combine base destination with relative directory structure
355
+ base_destination = destination || ""
356
+ if base_destination.empty?
357
+ destination = relative_dir.to_s
358
+ else
359
+ destination = File.join(base_destination, relative_dir.to_s)
360
+ end
361
+ end
362
+
363
+ destination
364
+ end
365
+
210
366
  class << self
211
367
  def metadata_chunk(text)
212
368
  text.split(METADATA_DELIM).first
@@ -214,16 +370,20 @@ module Mint
214
370
 
215
371
  def metadata_from(text)
216
372
  raw_metadata = YAML.load metadata_chunk(text)
217
-
373
+
218
374
  case raw_metadata
219
375
  when String
220
376
  {}
221
377
  when false
222
378
  {}
379
+ when nil
380
+ {}
223
381
  else
224
382
  raw_metadata
225
383
  end
226
- rescue
384
+ rescue Psych::SyntaxError
385
+ {}
386
+ rescue Exception
227
387
  {}
228
388
  end
229
389
 
data/lib/mint/helpers.rb CHANGED
@@ -4,7 +4,7 @@ require "yaml"
4
4
  require "active_support/core_ext/string/inflections"
5
5
 
6
6
  module Mint
7
- module Helpers
7
+ module Helpers
8
8
  def self.underscore(obj, opts={})
9
9
  namespaces = obj.to_s.split("::").map do |namespace|
10
10
  if opts[:ignore_prefix]
@@ -38,7 +38,7 @@ module Mint
38
38
  def self.symbolize(obj)
39
39
  slugize(obj).gsub(/-/, "_").to_sym
40
40
  end
41
-
41
+
42
42
  # Transforms a String or Pathname into a fully expanded Pathname.
43
43
  #
44
44
  # @param [String, Pathname] str_or_path a path to be expanded
@@ -59,8 +59,8 @@ module Mint
59
59
  def self.symbolize_keys(map, opts={})
60
60
  transform = lambda {|x| opts[:downcase] ? x.downcase : x }
61
61
 
62
- map.reduce(Hash.new) do |syms,(k,v)|
63
- syms[transform[k].to_sym] =
62
+ map.reduce(Hash.new) do |syms,(k,v)|
63
+ syms[transform[k].to_sym] =
64
64
  case v
65
65
  when Hash
66
66
  self.symbolize_keys(v, opts)
@@ -107,19 +107,19 @@ module Mint
107
107
  Hash[*list1.zip(list2).flatten]
108
108
  end
109
109
 
110
- # Returns the relative path to to_directory from from_directory.
111
- # If to_directory and from_directory have no parents in common besides
110
+ # Returns the relative path to to_directory from from_directory.
111
+ # If to_directory and from_directory have no parents in common besides
112
112
  # /, returns the absolute directory of to_directory. Assumes no symlinks.
113
113
  #
114
114
  # @param [String, Pathname] to_directory the target directory
115
115
  # @param [String, Pathname] from_directory the starting directory
116
- # @return [Pathname] the relative path to to_directory from
116
+ # @return [Pathname] the relative path to to_directory from
117
117
  # from_directory, or an absolute path if they have no parents in common
118
118
  # other than /
119
119
  def self.normalize_path(to_directory, from_directory)
120
120
  to_path, from_path = [to_directory, from_directory].map {|d| pathize d }
121
121
  to_root, from_root = [to_path, from_path].map {|p| p.each_filename.first }
122
- to_root == from_root ?
122
+ to_root == from_root ?
123
123
  to_path.relative_path_from(from_path) :
124
124
  to_path
125
125
  end
@@ -129,15 +129,33 @@ module Mint
129
129
  #
130
130
  # @param [Hash, #[]] new_opts a set of options to add to the Yaml file
131
131
  # @param [Pathname, #exist] file a file to read from and write to
132
- # @return [void]
132
+ # @return [void]
133
133
  def self.update_yaml!(file, opts={})
134
- curr_opts = File.exist?(file) ? YAML.load_file(file) : {}
134
+ curr_opts = if File.exist?(file)
135
+ begin
136
+ YAML.load_file(file) || {}
137
+ rescue Psych::SyntaxError, StandardError
138
+ # Handle corrupted YAML gracefully by treating it as empty
139
+ {}
140
+ end
141
+ else
142
+ {}
143
+ end
135
144
 
136
145
  File.open file, "w" do |f|
137
146
  YAML.dump(curr_opts.merge(opts), f)
138
147
  end
139
148
  end
140
149
 
150
+ def self.create_temp_file!(basename, extension=nil, &block)
151
+ tmp_args = basename && extension ? [basename, extension] : basename
152
+ tempfile = Tempfile.new(tmp_args)
153
+ block.call(tempfile)
154
+ tempfile.flush
155
+ tempfile.close
156
+ tempfile.path
157
+ end
158
+
141
159
  def self.generate_temp_file!(file)
142
160
  basename = File.basename file
143
161
  extension = File.extname file
@@ -149,5 +167,27 @@ module Mint
149
167
  tempfile.close
150
168
  tempfile.path
151
169
  end
170
+
171
+ # Transforms markdown links from .md extensions to .html for digital gardens
172
+ #
173
+ # @param [String] text the markdown text containing links
174
+ # @return [String] the text with transformed links
175
+ def self.transform_markdown_links(text)
176
+ # Transform relative markdown links like [text](path/file.md) to [text](path/file.html)
177
+ text.gsub(/(\[([^\]]*)\]\()([^)]*\.md)(\))/) do |match|
178
+ link_start = $1
179
+ link_text = $2
180
+ link_url = $3
181
+ link_end = $4
182
+
183
+ # Only transform relative links (not absolute URLs)
184
+ if link_url !~ /^https?:\/\//
185
+ new_url = link_url.gsub(/\.md$/, '.html')
186
+ "#{link_start}#{new_url}#{link_end}"
187
+ else
188
+ match
189
+ end
190
+ end
191
+ end
152
192
  end
153
193
  end
data/lib/mint/layout.rb CHANGED
@@ -6,9 +6,8 @@ module Mint
6
6
  # and optional configuration options.
7
7
  #
8
8
  # @param [String] source the absolute or relative file path
9
- # @param [Hash, #[]] opts layout options
10
- def initialize(source, opts=Mint.default_options)
11
- super(source, opts)
9
+ def initialize(source, root: nil, destination: nil, context: nil, name: nil, &block)
10
+ super(source, root: root, destination: destination, context: context, name: name, &block)
12
11
  self.type = :layout
13
12
  end
14
13
  end
@@ -0,0 +1,47 @@
1
+ require 'tilt/template'
2
+ require 'redcarpet'
3
+
4
+ module Mint
5
+ class MarkdownTemplate < Tilt::Template
6
+ self.default_mime_type = 'text/html'
7
+
8
+ def prepare
9
+ @options = options.dup
10
+
11
+ renderer_options = {
12
+ filter_html: false,
13
+ no_images: false,
14
+ no_links: false,
15
+ no_styles: false,
16
+ escape_html: false,
17
+ safe_links_only: false,
18
+ with_toc_data: false,
19
+ hard_wrap: false,
20
+ prettify: false
21
+ }.merge(@options)
22
+
23
+ markdown_options = {
24
+ tables: true,
25
+ autolink: true,
26
+ no_intra_emphasis: true,
27
+ fenced_code_blocks: true,
28
+ lax_html_blocks: false,
29
+ strikethrough: true,
30
+ superscript: false,
31
+ footnotes: false,
32
+ highlight: false,
33
+ quote: false,
34
+ disable_indented_code_blocks: false,
35
+ space_after_headers: false,
36
+ underline: false
37
+ }
38
+
39
+ @renderer = @options.delete(:renderer) || Redcarpet::Render::HTML.new(renderer_options)
40
+ @markdown = Redcarpet::Markdown.new(@renderer, markdown_options)
41
+ end
42
+
43
+ def evaluate(scope, locals, &block)
44
+ @markdown.render(data)
45
+ end
46
+ end
47
+ end