Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filters Manage Dependencies #80

Merged
merged 10 commits into from
Oct 23, 2013
21 changes: 18 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
source 'https://rubygems.org'
source "https://rubygems.org"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor pet peeve, but it's nice to keep pull requests focused on the changes described. These formatting edits make it trickier to review PRs. Don't worry about it for this PR, but just a heads up 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry and sure thing!


# Specify your gem's dependencies in html-pipeline.gemspec
gemspec

group :development do
gem 'bundler'
gem 'rake'
gem "bundler"
gem "rake"
end

group :test do
gem "rinku", "~> 1.7", :require => false
gem "gemoji", "~> 1.0", :require => false
gem "RedCloth", "~> 4.2.9", :require => false
gem "escape_utils", "~> 0.3", :require => false
gem "github-linguist", "~> 2.6.2", :require => false
gem "github-markdown", "~> 0.5", :require => false

if RUBY_VERSION < "1.9.2"
gem "sanitize", ">= 2", "< 2.0.4", :require => false
else
gem "sanitize", "~> 2.0", :require => false
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move these into their own platform blocks. Search for :platforms in the Gemfile documentation for details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried platforms two ways:

  platforms :ruby_18 do
    gem "sanitize", ">= 2", "< 2.0.4", :require => false
  end

  platforms :ruby_19, :ruby_20 do
    gem "sanitize", "~> 2.0", :require => false
  end
  gem "sanitize", ">= 2", "< 2.0.4", :require => false, :platforms => :ruby_18
  gem "sanitize", "~> 2.0",          :require => false, :platforms => [:ruby_19, :ruby_20]

Bundler doesn't like either way.

❯ bundle install
You cannot specify the same gem twice with different version requirements. 
You specified: sanitize (< 2.0.4, >= 2) and sanitize (~> 2.0)

I think we should stick with the RUBY_VERSION conditional. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference: rubygems/bundler#751

end
36 changes: 23 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ filter.call
* `TextileFilter` - convert textile to html
* `TableOfContentsFilter` - anchor headings with name attributes and generate Table of Contents html unordered list linking headings

## Dependencies

Filter gem dependencies are not bundled; you must bundle the filter's gem
dependencies. The below list details filters with dependencies. For example,
`SyntaxHighlightFilter` uses [github-linguist](https://github.com/github/linguist)
to detect and highlight languages. For example, to use the `SyntaxHighlightFilter`,
add the following to your Gemfile:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, to use the SyntaxHighlightFilter, add the following to your Gemfile:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice edit.


```ruby
gem 'github-linguist'
```

* `AutolinkFilter` - `rinku`
* `EmailReplyFilter` - `escape_utils`
* `EmojiFilter` - `gemoji`
* `MarkdownFilter` - `github-markdown`
* `PlainTextInputFilter` - `escape_utils`
* `SanitizationFilter` - `sanitize`
* `SyntaxHighlightFilter` - `github-linguist`
* `TextileFilter` - `RedCloth`

_Note:_ See [Gemfile](https://github.com/jch/html-pipeline/blob/master/Gemfile) `:test` block for version requirements.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub markdown supports relative links now. So you can just do /Gemfile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.


## 3rd Party Extensions

If you have an idea for a filter, propose it as
Expand All @@ -107,19 +130,6 @@ built as an external gem.

* [html-pipeline-asciidoc_filter](https://github.com/asciidoctor/html-pipeline-asciidoc_filter) - asciidoc support


## Syntax highlighting

`SyntaxHighlightFilter` uses [github-linguist](https://github.com/github/linguist)
to detect and highlight languages. It isn't included as a dependency by default
because it's a large dependency and
[a hassle to build on heroku](https://github.com/jch/html-pipeline/issues/33).
To use the filter, add the following to your Gemfile:

```ruby
gem 'github-linguist'
```

## Examples

We define different pipelines for different parts of our app. Here are a few
Expand Down
4 changes: 1 addition & 3 deletions bin/html-pipeline
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ else
case name
when "Text"
raise NameError # Text filter doesn't work, no call method
when "Textile"
require "RedCloth" # Textile filter doesn't require RedCloth
end

HTML::Pipeline.const_get("#{name}Filter")
Expand All @@ -77,4 +75,4 @@ context = {
:gfm => true
}

puts HTML::Pipeline.new(filters, context).call(ARGF.read)[:output]
puts HTML::Pipeline.new(filters, context).call(ARGF.read)[:output]
18 changes: 10 additions & 8 deletions html-pipeline.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep(%r{^test})
gem.require_paths = ["lib"]

gem.add_dependency "gemoji", "~> 1.0"
gem.add_dependency "nokogiri", RUBY_VERSION < "1.9.2" ? [">= 1.4", "< 1.6"] : "~> 1.4"
gem.add_dependency "github-markdown", "~> 0.5"
gem.add_dependency "sanitize", RUBY_VERSION < "1.9.2" ? [">= 2", "< 2.0.4"] : "~> 2.0"
gem.add_dependency "rinku", "~> 1.7"
gem.add_dependency "escape_utils", "~> 0.3"
gem.add_dependency "activesupport", RUBY_VERSION < "1.9.3" ? [">= 2", "< 4"] : ">= 2"
gem.add_dependency "nokogiri", RUBY_VERSION < "1.9.2" ? [">= 1.4", "< 1.6"] : "~> 1.4"
gem.add_dependency "activesupport", RUBY_VERSION < "1.9.3" ? [">= 2", "< 4"] : ">= 2"

gem.add_development_dependency "github-linguist", "~> 2.6.2"
gem.post_install_message = <<msg
-------------------------------------------------
Thank you for installing html-pipeline!
You must bundle Filter gem dependencies.
See html-pipeline README.md for more details.
https://github.com/jch/html-pipeline#dependencies
-------------------------------------------------
msg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice touch.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, since we're not 1.x yet, I'd remove the Filter gem dependencies are no longer bundled.. I'd also update the hyperlink url to https://github.com/jch/html-pipeline#dependencies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I always forget this is available.

end
1 change: 0 additions & 1 deletion lib/html/pipeline.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require "nokogiri"
require "active_support/xml_mini/nokogiri" # convert Documents to hashes
require "escape_utils"

module HTML
# GitHub HTML processing filters and utilities. This module includes a small
Expand Down
7 changes: 6 additions & 1 deletion lib/html/pipeline/autolink_filter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
require 'rinku'
begin
require "rinku"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "rinku", e.backtrace
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's overkill to have a custom exception class here because we want to exit as early as possible. The stack trace useful because it's not a programming error, it's a installation error. I'd prefer having the error string directly and calling exit with a non-zero exit code:

begin
  require 'rinku'
rescue LoadError => e
  $stderr.puts "Missing dependency 'rinku' for AutolinkFilter. See README.md for details"
  exit 1
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this simpler:

begin
  require 'rinku'
rescue LoadError => e
  abort "Missing dependency 'rinku' for AutolinkFilter. See README.md for details"
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ learn me something new today.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jch Agreed. Glad we're going this route; it's straightforward.

@rsanheim Thanks for the abort tip!

end

module HTML
class Pipeline
Expand Down
9 changes: 8 additions & 1 deletion lib/html/pipeline/email_reply_filter.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
begin
require "escape_utils"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "escape_utils", e.backtrace
end

module HTML
class Pipeline
# HTML Filter that converts email reply text into an HTML DocumentFragment.
Expand Down Expand Up @@ -53,4 +60,4 @@ def call
end
end
end
end
end
9 changes: 7 additions & 2 deletions lib/html/pipeline/emoji_filter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
require 'emoji'
begin
require "gemoji"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gemoji.rb is just a stub for emoji.rb. Changing this back to require "emoji" will eliminate a require. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd leave it alone in case the gem ever decides to load more things in gemoji.rb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "gemoji", e.backtrace
end

module HTML
class Pipeline
Expand Down Expand Up @@ -51,4 +56,4 @@ def asset_root
end
end
end
end
end
17 changes: 17 additions & 0 deletions lib/html/pipeline/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ class Pipeline
# Each filter may define additional options and output values. See the class
# docs for more info.
class Filter
# Public: Custom Exception raised when a Filter dependency is not installed.
#
# Examples
#
# begin
# require "rinku"
# rescue LoadError => e
# missing = HTML::Pipeline::Filter::MissingDependencyException
# raise missing, missing::MESSAGE % "rinku", e.backtrace
# end
class MissingDependencyException < StandardError
# Public: Format String for MissingDependencyException message.
MESSAGE = "Missing html-pipeline dependency: " +
"Please add `%s` to your Gemfile; " +
"see html-pipeline Gemfile for version."
end

class InvalidDocumentException < StandardError; end

def initialize(doc, context = nil, result = nil)
Expand Down
9 changes: 7 additions & 2 deletions lib/html/pipeline/markdown_filter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
require 'github/markdown'
begin
require "github/markdown"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "github-markdown", e.backtrace
end

module HTML
class Pipeline
Expand Down Expand Up @@ -26,4 +31,4 @@ def call
end
end
end
end
end
9 changes: 8 additions & 1 deletion lib/html/pipeline/plain_text_input_filter.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
begin
require "escape_utils"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "escape_utils", e.backtrace
end

module HTML
class Pipeline
# Simple filter for plain text input. HTML escapes the text input and wraps it
Expand All @@ -8,4 +15,4 @@ def call
end
end
end
end
end
7 changes: 6 additions & 1 deletion lib/html/pipeline/sanitization_filter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
require 'sanitize'
begin
require "sanitize"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "sanitize", e.backtrace
end

module HTML
class Pipeline
Expand Down
7 changes: 4 additions & 3 deletions lib/html/pipeline/syntax_highlight_filter.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
begin
require 'linguist'
rescue LoadError
raise LoadError, "You need to install 'github-linguist' before using the SyntaxHighlightFilter. See README.md for details"
require "linguist"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "github-linguist", e.backtrace
end

module HTML
Expand Down
9 changes: 8 additions & 1 deletion lib/html/pipeline/textile_filter.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
begin
require "redcloth"
rescue LoadError => e
missing = HTML::Pipeline::Filter::MissingDependencyException
raise missing, missing::MESSAGE % "RedCloth", e.backtrace
end

module HTML
class Pipeline
# HTML Filter that converts Textile text into HTML and converts into a
Expand All @@ -18,4 +25,4 @@ def call
end
end
end
end
end
4 changes: 4 additions & 0 deletions test/html/pipeline/autolink_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
AutolinkFilter = HTML::Pipeline::AutolinkFilter

class HTML::Pipeline::AutolinkFilterTest < Test::Unit::TestCase
def test_dependency_management
assert_dependency "autolink_filter", "rinku"
end

def test_uses_rinku_for_autolinking
# just try to parse a complicated piece of HTML
# that Rails auto_link cannot handle
Expand Down
7 changes: 7 additions & 0 deletions test/html/pipeline/email_reply_filter_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class HTML::Pipeline::EmailReplyFilterTest < Test::Unit::TestCase
def test_dependency_management
assert_dependency "email_reply_filter", "escape_utils"
end
end
14 changes: 9 additions & 5 deletions test/html/pipeline/emoji_filter_test.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
require 'test_helper'
require "test_helper"

class HTML::Pipeline::EmojiFilterTest < Test::Unit::TestCase
EmojiFilter = HTML::Pipeline::EmojiFilter


def test_dependency_management
assert_dependency "emoji_filter", "gemoji"
end

def test_emojify
filter = EmojiFilter.new("<p>:shipit:</p>", {:asset_root => 'https://foo.com'})
filter = EmojiFilter.new("<p>:shipit:</p>", {:asset_root => "https://foo.com"})
doc = filter.call
assert_match "https://foo.com/emoji/shipit.png", doc.search('img').attr('src').value
assert_match "https://foo.com/emoji/shipit.png", doc.search("img").attr("src").value
end

def test_required_context_validation
Expand All @@ -15,4 +19,4 @@ def test_required_context_validation
}
assert_match /:asset_root/, exception.message
end
end
end
14 changes: 9 additions & 5 deletions test/html/pipeline/markdown_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ def setup
"```"
end

def test_dependency_management
assert_dependency "markdown_filter", "github-markdown"
end

def test_fails_when_given_a_documentfragment
body = "<p>heyo</p>"
doc = HTML::Pipeline.parse(body)
Expand All @@ -27,26 +31,26 @@ def test_fails_when_given_a_documentfragment
def test_gfm_enabled_by_default
doc = MarkdownFilter.to_document(@haiku, {})
assert doc.kind_of?(HTML::Pipeline::DocumentFragment)
assert_equal 2, doc.search('br').size
assert_equal 2, doc.search("br").size
end

def test_disabling_gfm
doc = MarkdownFilter.to_document(@haiku, :gfm => false)
assert doc.kind_of?(HTML::Pipeline::DocumentFragment)
assert_equal 0, doc.search('br').size
assert_equal 0, doc.search("br").size
end

def test_fenced_code_blocks
doc = MarkdownFilter.to_document(@code)
assert doc.kind_of?(HTML::Pipeline::DocumentFragment)
assert_equal 1, doc.search('pre').size
assert_equal 1, doc.search("pre").size
end

def test_fenced_code_blocks_with_language
doc = MarkdownFilter.to_document(@code.sub("```", "``` ruby"))
assert doc.kind_of?(HTML::Pipeline::DocumentFragment)
assert_equal 1, doc.search('pre').size
assert_equal 'ruby', doc.search('pre').first['lang']
assert_equal 1, doc.search("pre").size
assert_equal "ruby", doc.search("pre").first["lang"]
end
end

Expand Down
4 changes: 4 additions & 0 deletions test/html/pipeline/plain_text_input_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
class HTML::Pipeline::PlainTextInputFilterTest < Test::Unit::TestCase
PlainTextInputFilter = HTML::Pipeline::PlainTextInputFilter

def test_dependency_management
assert_dependency "plain_text_input_filter", "escape_utils"
end

def test_fails_when_given_a_documentfragment
body = "<p>heyo</p>"
doc = Nokogiri::HTML::DocumentFragment.parse(body)
Expand Down
4 changes: 4 additions & 0 deletions test/html/pipeline/sanitization_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
class HTML::Pipeline::SanitizationFilterTest < Test::Unit::TestCase
SanitizationFilter = HTML::Pipeline::SanitizationFilter

def test_dependency_management
assert_dependency "sanitization_filter", "sanitize"
end

def test_removing_script_tags
orig = %(<p><img src="https://github.com/img.png" /><script></script></p>)
html = SanitizationFilter.call(orig).to_s
Expand Down
4 changes: 4 additions & 0 deletions test/html/pipeline/syntax_highlight_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
SyntaxHighlightFilter = HTML::Pipeline::SyntaxHighlightFilter

class HTML::Pipeline::SyntaxHighlightFilterTest < Test::Unit::TestCase
def test_dependency_management
assert_dependency "syntax_highlight_filter", "github-linguist"
end

def test_highlight_default
filter = SyntaxHighlightFilter.new \
"<pre>hello</pre>", :highlight => "coffeescript"
Expand Down
Loading