From 5b08241935e8a15c6656c7c62d607f6204e2fa20 Mon Sep 17 00:00:00 2001 From: Simon Courtois Date: Tue, 30 Jul 2013 14:56:59 +0200 Subject: [PATCH] Moving all the logic from the helper to the Mustdown module (ref #2) --- app/helpers/mustdown/mustdown_helper.rb | 31 ++-- lib/mustdown.rb | 219 +++++++++++++++++++++--- spec/mustdown_spec.rb | 112 ++++++++++++ 3 files changed, 325 insertions(+), 37 deletions(-) create mode 100644 spec/mustdown_spec.rb diff --git a/app/helpers/mustdown/mustdown_helper.rb b/app/helpers/mustdown/mustdown_helper.rb index 9d89e73..172ac05 100644 --- a/app/helpers/mustdown/mustdown_helper.rb +++ b/app/helpers/mustdown/mustdown_helper.rb @@ -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 diff --git a/lib/mustdown.rb b/lib/mustdown.rb index a9b1c04..7a3a3ef 100644 --- a/lib/mustdown.rb +++ b/lib/mustdown.rb @@ -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) +# # => '

This is bold and this is italic

' +# +# # 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') +# # => '

More about Tom

' 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) + # # => '

Hello world

' + # + # tpl = 'http://example.com [example](http://example.com)' + # Mustdown.markdown(tpl, autolink: false) + # # => '

http://example.com example

' + # + # tpl = 'http://example.com [example](http://example.com)' + # Mustdown.markdown(tpl, { autolink: false }, { no_links: true }) + # # => '

http://example.com [example](http://example.com)

' + # + # 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') + # # => '

Hello Tom

' + # + # tpl = '{{url}}' + # website = { url: 'http://example.com' } + # Mustdown.markdown(tpl, website, autolink: false) + # # => '

http://example.com

' + # + # tpl = '[{{title}}]({{url}})' + # website = { title: 'Example', url: 'http://example.com' } + # Mustdown.markdown(tpl, website, { autolink: false }, { no_links: true }) + # # => '

[Example](http://example.com)

' + # + # 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 diff --git a/spec/mustdown_spec.rb b/spec/mustdown_spec.rb new file mode 100644 index 0000000..1163eb8 --- /dev/null +++ b/spec/mustdown_spec.rb @@ -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("

Test

\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(/test\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