Skip to content

Commit

Permalink
Moving all the logic from the helper to the Mustdown module (ref #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Courtois committed Jul 30, 2013
1 parent 06e36ee commit 5b08241
Show file tree
Hide file tree
Showing 3 changed files with 325 additions and 37 deletions.
31 changes: 19 additions & 12 deletions app/helpers/mustdown/mustdown_helper.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
# Public: Wrapper class for Mustdown.
#
# This module is a Rails helper that provides an easy access to the Mustdown
# rendering methods.
module Mustdown
module MustdownHelper
def markdown(content, markdown_extensions = {}, renderer_options = {})
md_exts = Mustdown.markdown_extensions.merge(markdown_extensions)
renderer_opts = Mustdown.renderer_options.merge(renderer_options)

renderer = Mustdown.renderer.new(renderer_opts)
markdown = Redcarpet::Markdown.new(renderer, md_exts)

markdown.render(content).html_safe
# Public: Calls Mustdown.markdown.
#
# See Mustdown.markdown.
def markdown(*args)
Mustdown.markdown(*args).html_safe
end

def mustache(template, resource)
Mustache.render(template, resource).html_safe
# Public: Calls Mustdown.mustache.
#
# See Mustdown.mustache.
def mustache(*args)
Mustdown.mustache(*args).html_safe
end

def mustdown(template, resource, *markdown_args)
markdown mustache(template, resource), *markdown_args
# Public: Calls Mustdown.mustdown.
#
# See Mustdown.mustdown.
def mustdown(*args)
Mustdown.mustdown(*args).html_safe
end
end
end
219 changes: 194 additions & 25 deletions lib/mustdown.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,206 @@
require 'redcarpet'
require 'mustache'

require 'mustdown/engine'
require 'mustdown/engine' if defined?(Rails)

# Public: Provides a simple way to render markdown, mustache and
# mustache+markdown templates.
#
# Examples
#
# # Rendering a markdown template.
# tpl = 'This is **bold** and this is *italic*'
# Mustdown.markdown(tpl)
# # => '<p>This is <strong>bold</strong> and this is <em>italic</em></p>'
#
# # Rendering a mustache template.
# tpl = 'My name is {{name}} and I am {{age}}'
# Mustdown.mustache(tpl, name: 'Tom', age: 20)
# # => 'My name is Tom and I am 20'
#
# # Rendering a mustache+markdown template.
# tpl = '# More about {{name}}'
# Mustdown.mustdown(tpl, name: 'Tom')
# # => '<h1>More about Tom</h1>'
module Mustdown
class << self
attr_accessor :markdown_extensions
attr_accessor :renderer_options
attr_accessor :renderer
extend self

def configure
yield self
end
# Public: Sets the Hash of markdown extensions.
attr_writer :markdown_extensions

def markdown_extensions
@markdown_extensions ||= {
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
autolink: true,
strikethrough: true
}
end
# Public: Sets the Hash of markdown renderer options.
attr_writer :renderer_options

def renderer_options
@renderer_options ||= {
no_styles: true,
safe_links_only: true
}
end
# Public: Sets the markdown renderer (a Redcarpet compatible renderer).
#
# Note: It accepts a class or an instance.
attr_writer :markdown_renderer

# Public: Provides a way to configure Mustdown.
#
# Yields itself.
#
# Examples
#
# # Configuring Mustdown
# Mustdown.configure do |config|
# config.markdown_renderer = MyCustomRenderer
# end
#
# Returns nothing.
def configure
yield self
end

# Public: Returns the default markdown extensions
#
# Returns a Hash.
def markdown_extensions
@markdown_extensions ||= {
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
autolink: true,
strikethrough: true
}
end

# Public: Returns the default markdown renderer options
#
# Returns a Hash.
def renderer_options
@renderer_options ||= {
no_styles: true,
safe_links_only: true
}
end

# Public: Returns the markdown renderer.
#
# This can be a class or an object. When it's a class, a new instance will be
# created when rendering.
#
# Defaults to Redcarpet::Render::HTML
#
# Returns an Object.
def markdown_renderer
@markdown_renderer ||= Redcarpet::Render::HTML
end

# Deprecated: Returns the markdown renderer.
#
# Use markdown_renderer instead.
#
# Returns an Object.
def renderer
warn "Mustdown.renderer is deprecated. Use Mustdown.markdown_renderer instead."
self.markdown_renderer
end

# Deprecated: Sets the markdown renderer
#
# Use markdown_renderer= instead.
#
# Returns nothing.
def renderer=(value)
warn "Mustdown.renderer= is deprecated. Use Mustdown.markdown_renderer= instead."
self.markdown_renderer = value
end

# Public: Renders a markdown template.
#
# template - The String template containing markdown template.
# markdown_extensions - A Hash of additional extensions that will be merged
# with the default ones.
# renderer_options - A Hash of additional markdown renderer options that
# will be merged with the default ones.
#
# Examples
#
# tpl = '# Hello world'
# Mustdown.markdown(tpl)
# # => '<h1>Hello world</h1>'
#
# tpl = 'http://example.com [example](http://example.com)'
# Mustdown.markdown(tpl, autolink: false)
# # => '<p>http://example.com <a href="http://example.com">example</a></p>'
#
# tpl = 'http://example.com [example](http://example.com)'
# Mustdown.markdown(tpl, { autolink: false }, { no_links: true })
# # => '<p>http://example.com [example](http://example.com)</p>'
#
# Returns the rendered markdown String.
def markdown(template, markdown_extensions = {}, renderer_options = {})
exts = markdown_extensions.merge(markdown_extensions)
opts = renderer_options.merge(renderer_options)

renderer = markdown_renderer.new(opts)
markdown = Redcarpet::Markdown.new(renderer, exts)

markdown.render(template)
end

# Public: Renders a mustache template.
#
# template - The String template containing mustache template.
# resource - The Hash or Object used as binding for the template.
#
# Examples
#
# tpl = 'Hello {{name}}'
# Mustdown.mustache(tpl, name: 'Tom')
# # => 'Hello Tom'
#
# tpl = 'Hello {{name}}'
# user = User.find(1) # A user named Tom
# Mustdown.mustache(tpl, user)
# # => 'Hello Tom'
#
# Returns the rendered mustache String.
def mustache(template, resource)
Mustache.render(template, resource)
end

# Public: Renders a mustache+markdown template.
#
# template - The String template containing mustache+markdown template.
# resource - The Hash or Object used as binding for the template.
# markdown_args - Zero, one or two Hash objects that will be passed to the
# markdown method.
#
# Examples
#
# tpl = '# Hello {{name}}'
# Mustdown.mustdown(tpl, name: 'Tom')
# # => '<h1>Hello Tom</h1>'
#
# tpl = '{{url}}'
# website = { url: 'http://example.com' }
# Mustdown.markdown(tpl, website, autolink: false)
# # => '<p>http://example.com</p>'
#
# tpl = '[{{title}}]({{url}})'
# website = { title: 'Example', url: 'http://example.com' }
# Mustdown.markdown(tpl, website, { autolink: false }, { no_links: true })
# # => '<p>[Example](http://example.com)</p>'
#
# Returns the rendered mustache+arkdown String.
def mustdown(template, resource, *markdown_args)
markdown mustache(template, resource), *markdown_args
end

def renderer
@renderer ||= Redcarpet::Render::HTML
# Private: Outputs a warning message.
#
# In a Rails app uses Rails.logger, $stderr otherwise.
#
# message - The String message to display as a warning.
#
# Returns nothing.
def warn(message)
if defined?(Rails)
Rails.logger.warn message
else
$stderr.puts "WARNING: #{message}"
end
end
end
112 changes: 112 additions & 0 deletions spec/mustdown_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require 'spec_helper'

class MustdownIncluder
include Mustdown
end

shared_examples_for "Mustdown" do
let(:binding_object) do
{ name: "test" }
end

describe "configure" do
it "yields with subject as argument" do
subject.configure { |c| c.markdown_extensions = "configure" }
subject.markdown_extensions.should eq("configure")
end
end

describe "markdown_extensions=" do
it "sets the markdown extensions of subject" do
subject.markdown_extensions = "extensions"
subject.markdown_extensions.should eq("extensions")
end
end

describe "renderer_options=" do
it "sets the markdown renderer options of subject" do
subject.renderer_options = "options"
subject.renderer_options.should eq("options")
end
end

describe "markdown_renderer=" do
it "sets the markdown renderer of subject" do
subject.markdown_renderer = "md renderer"
subject.markdown_renderer.should eq("md renderer")
end
end

describe "markdown" do
it "process a template into HTML" do
output = subject.markdown('# Test')
output.should eq("<h1>Test</h1>\n")
end

it "uses configured renderer class" do
rndr_class = Class.new(Redcarpet::Render::HTML) do
def header(*args); super; end
end

rndr = rndr_class.new
rndr_class.stub(:new).and_return(rndr)

subject.markdown_renderer = rndr_class

rndr.should_receive(:header)
subject.markdown('# Test')
end

context "when overriding autolink to true" do
it "generates autolinks" do
output = subject.markdown("http://test.com", { autolink: true })
output.should match(/<a /)
end
end

context "when overriding autolink to false" do
it "doesn't generate any autolink" do
output = subject.markdown("http://test.com", { autolink: false })
output.should_not match(/<a /)
end
end

context "when overriding no_links to true" do
it "doesn't generate any link" do
output = subject.markdown("[a](test)", {}, { no_links: true })
output.should_not match(/<a /)
end
end

context "when overriding no_links to false" do
it "doesn't generate any link" do
output = subject.markdown("[a](test)", {}, { no_links: false })
output.should match(/<a /)
end
end
end

describe "mustache" do
it "passes the binding object to the template" do
output = subject.mustache("{{name}}", binding_object)
output.should eq("test")
end
end

describe "mustdown" do
it "renders mustache and then markdown" do
output = subject.mustdown("# {{name}}", binding_object)
output.should eq("<h1>test</h1>\n")
end
end
end

describe Mustdown do
subject { Mustdown.dup }
it_behaves_like "Mustdown"
end

describe MustdownIncluder do
subject { MustdownIncluder.new }
it_behaves_like "Mustdown"
end

0 comments on commit 5b08241

Please sign in to comment.