├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── Guardfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── html_pipeline_rails.gemspec ├── lib ├── html_pipeline_rails.rb └── html_pipeline_rails │ ├── configuration.rb │ ├── handler.rb │ └── version.rb └── spec ├── configuration_spec.rb ├── fixtures └── templates │ ├── _fake_md.html │ └── _simple_md.html.md ├── html_pipeline_rails_spec.rb ├── markdown_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | script: bundle exec rspec 3 | rvm: 4 | - 2.1.0 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in html_pipeline_rails.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/}) { 'spec' } 4 | watch('spec/spec_helper.rb') { 'spec' } 5 | end 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Aidan Feldman 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTML::Pipeline for Rails [![Build Status](https://travis-ci.org/afeld/html_pipeline_rails.png?branch=master)](https://travis-ci.org/afeld/html_pipeline_rails) 2 | 3 | Adds support for rendering views via [HTML::Pipeline](https://github.com/jch/html-pipeline) in Rails. [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown) in your Rails app! 4 | 5 | ## Installation 6 | 7 | Add this line to your Gemfile: 8 | 9 | ```ruby 10 | gem 'html_pipeline_rails' 11 | ``` 12 | 13 | and then run: 14 | 15 | $ bundle 16 | 17 | All views ending in `.md` will then be rendered as HTML, with support for ERB. 18 | 19 | ## Example 20 | 21 | `app/views/layouts/application.html.erb`: 22 | 23 | ```erb 24 | 27 | ``` 28 | 29 | `app/views/shared/footer.md`: 30 | 31 | ```markdown 32 | ## Copyright <%= Time.now.year %> **BigCorp** 33 | 34 | All rights reserved. 35 | ``` 36 | 37 | Cool, eh? 38 | 39 | ## Customization 40 | 41 | By default, `.md` views will run through ERB, and then the `MarkdownFilter` pipeline. Note that this means anything output from your ERB tags in a `.md` view will be parsed as Markdown. You can customize the render pipeline like so: 42 | 43 | ```ruby 44 | # config/initializers/html_pipeline.rb 45 | HtmlPipelineRails.config do |c| 46 | c.pipeline = HTML::Pipeline.new([ 47 | HTML::Pipeline::MarkdownFilter, 48 | HTML::Pipeline::MentionFilter 49 | ]) 50 | end 51 | ``` 52 | 53 | In this case, `@mention`s will now be converted to links. See [the HTML::Pipeline documentation](https://github.com/jch/html-pipeline#usage) to learn about the different options. 54 | 55 | For the `HTML::Pipeline::EmojiFilter`, follow the Rake task instructions in the [gemoji](https://github.com/github/gemoji) gem, and set the `:asset_root` in your config like so: 56 | 57 | ```ruby 58 | HtmlPipelineRails.config do |c| 59 | context = { 60 | asset_root: '/images', 61 | # ... 62 | } 63 | 64 | c.pipeline = HTML::Pipeline.new([ 65 | HTML::Pipeline::EmojiFilter 66 | # ... 67 | ], context) 68 | end 69 | ``` 70 | 71 | ## Contributing 72 | 73 | Run tests with: 74 | 75 | ```bash 76 | bundle 77 | # then 78 | bundle exec rspec 79 | # or 80 | bundle exec guard 81 | ``` 82 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /html_pipeline_rails.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'html_pipeline_rails/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "html_pipeline_rails" 8 | spec.version = HtmlPipelineRails::VERSION 9 | spec.authors = ["Aidan Feldman"] 10 | spec.email = ["aidan.feldman@gmail.com"] 11 | spec.summary = %q{Render Markdown via HTML::Pipeline in Rails.} 12 | spec.homepage = "https://github.com/afeld/html_pipeline_rails/" 13 | spec.license = "MIT" 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_dependency "github-markdown" 21 | spec.add_dependency "html-pipeline" 22 | 23 | spec.add_development_dependency "bundler", "~> 1.5" 24 | spec.add_development_dependency "rake" 25 | spec.add_development_dependency "actionpack", ">= 3.0" 26 | spec.add_development_dependency "rspec", "~> 2.14" 27 | 28 | spec.add_development_dependency "guard" 29 | spec.add_development_dependency "guard-rspec" 30 | end 31 | -------------------------------------------------------------------------------- /lib/html_pipeline_rails.rb: -------------------------------------------------------------------------------- 1 | require 'html_pipeline_rails/configuration' 2 | require 'html_pipeline_rails/handler' 3 | require 'html_pipeline_rails/version' 4 | 5 | require 'active_support/core_ext/module/attribute_accessors' 6 | 7 | module HtmlPipelineRails 8 | mattr_accessor :configuration 9 | self.configuration = Configuration.new 10 | 11 | def self.config 12 | yield self.configuration 13 | end 14 | end 15 | 16 | ActionView::Template.register_template_handler(:md, :mdown, :markdown, HtmlPipelineRails::Handler.new) 17 | -------------------------------------------------------------------------------- /lib/html_pipeline_rails/configuration.rb: -------------------------------------------------------------------------------- 1 | module HtmlPipelineRails 2 | class Configuration 3 | attr_accessor :pipeline 4 | 5 | def initialize 6 | self.reset! 7 | end 8 | 9 | def reset! 10 | self.pipeline = HTML::Pipeline.new([ 11 | HTML::Pipeline::MarkdownFilter 12 | ]) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/html_pipeline_rails/handler.rb: -------------------------------------------------------------------------------- 1 | require 'html/pipeline' 2 | 3 | module HtmlPipelineRails 4 | class Handler 5 | def default_format 6 | :html 7 | end 8 | 9 | def call(template) 10 | compiled_source = self.class.erb.call(template) 11 | 12 | <<-END 13 | pipeline = HtmlPipelineRails.configuration.pipeline 14 | result = pipeline.call(begin;#{compiled_source};end) 15 | result[:output].to_s 16 | END 17 | end 18 | 19 | # via http://stackoverflow.com/a/10131299/358804 20 | def self.erb 21 | @erb ||= ActionView::Template.registered_template_handler(:erb) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/html_pipeline_rails/version.rb: -------------------------------------------------------------------------------- 1 | module HtmlPipelineRails 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /spec/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HtmlPipelineRails::Configuration do 4 | describe '#reset' do 5 | let(:config) { HtmlPipelineRails::Configuration.new } 6 | 7 | it "sets the pipeline to the default" do 8 | config.pipeline = HTML::Pipeline.new([ 9 | HTML::Pipeline::MarkdownFilter, 10 | HTML::Pipeline::MentionFilter 11 | ]) 12 | config.reset! 13 | expect(config.pipeline.filters).to eq([HTML::Pipeline::MarkdownFilter]) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/templates/_fake_md.html: -------------------------------------------------------------------------------- 1 | # Fake Heading 2 | -------------------------------------------------------------------------------- /spec/fixtures/templates/_simple_md.html.md: -------------------------------------------------------------------------------- 1 | # A Heading 2 | -------------------------------------------------------------------------------- /spec/html_pipeline_rails_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HtmlPipelineRails do 4 | describe '.config' do 5 | it "runs the config block" do 6 | block_run = false 7 | HtmlPipelineRails.config do |c| 8 | block_run = true 9 | end 10 | expect(block_run).to be_true 11 | end 12 | 13 | it "defaults to include the MarkdownFilter" do 14 | HtmlPipelineRails.config do |c| 15 | pipeline = c.pipeline 16 | expect(pipeline).to be_an(HTML::Pipeline) 17 | expect(pipeline.filters).to eq([HTML::Pipeline::MarkdownFilter]) 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/markdown_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "markdown views" do 4 | before do 5 | setup_view_instance 6 | end 7 | 8 | def render_md(template) 9 | @view.render(inline: template, type: :md) 10 | end 11 | 12 | def expect_rendered(template) 13 | result = render_md(template) 14 | expect(result) 15 | end 16 | 17 | it "inline rendering" do 18 | expect_rendered('# A Heading').to eq('

A Heading

') 19 | end 20 | 21 | it "doesn't affect normal HTML rendering" do 22 | template = '# A Heading' 23 | result = @view.render(inline: template) 24 | expect(result).to eq(template) 25 | end 26 | 27 | it "renders external templates" do 28 | result = @view.render('simple_md') 29 | expect(result).to eq("

A Heading

") 30 | end 31 | 32 | it "doesn't affect normal external HTML templates" do 33 | result = @view.render('fake_md') 34 | expect(result).to eq("# Fake Heading\n") 35 | end 36 | 37 | it "interprets ERB" do 38 | # not sure why it gets wrapped... 39 | expect_rendered('<%= 1 + 1 %>').to eq('

2

') 40 | end 41 | 42 | it "interprets Markdown passed through ERB tags" do 43 | expect_rendered('<%= "# The Title" %>').to eq('

The Title

') 44 | end 45 | 46 | it "uses the configured pipeline" do 47 | HtmlPipelineRails.config do |c| 48 | c.pipeline = HTML::Pipeline.new([ 49 | HTML::Pipeline::MarkdownFilter, 50 | HTML::Pipeline::MentionFilter 51 | ]) 52 | end 53 | 54 | expect_rendered('@afeld').to eq('

@afeld

') 55 | end 56 | 57 | it "inline rendering with mdown extension" do 58 | template = '# A Heading' 59 | result = @view.render(inline: template, type: :mdown) 60 | expect(result).to eq('

A Heading

') 61 | end 62 | 63 | it "inline rendering with markdown extension" do 64 | template = '# A Heading' 65 | result = @view.render(inline: template, type: :markdown) 66 | expect(result).to eq('

A Heading

') 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper"` to ensure that it is only 4 | # loaded once. 5 | 6 | # workaround for https://github.com/rails/rails/issues/14686 7 | require 'action_dispatch/http/mime_type' 8 | 9 | require 'action_view' 10 | require_relative '../lib/html_pipeline_rails' 11 | 12 | 13 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 14 | RSpec.configure do |config| 15 | config.treat_symbols_as_metadata_keys_with_true_values = true 16 | config.run_all_when_everything_filtered = true 17 | config.filter_run :focus 18 | 19 | # Run specs in random order to surface order dependencies. If you find an 20 | # order dependency and want to debug it, you can fix the order by providing 21 | # the seed, which is printed after each run. 22 | # --seed 1234 23 | config.order = 'random' 24 | 25 | config.before do 26 | HtmlPipelineRails.config do |c| 27 | c.reset! 28 | end 29 | end 30 | end 31 | 32 | 33 | def setup_view_instance 34 | @view = ActionView::Base.new 35 | @view.view_paths << File.join(File.dirname(__FILE__), 'fixtures', 'templates') 36 | end 37 | --------------------------------------------------------------------------------