diff --git a/README.md b/README.md index 90065b6b..5adb1967 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,15 @@ results tothe next filter. A pipeline has several kinds of filters available to You can assemble each sequence into a single pipeline, or choose to call each filter individually. -As an example, suppose we want to transform Commonmark source text into Markdown HTML. With the content, we also want to: +As an example, suppose we want to transform Commonmark source text into Markdown HTML: -- change every instance of `$NAME` to "`Johnny" +``` +Hey there, @gjtorikian +``` + +With the content, we also want to: + +- change every instance of `Hey` to `Hello` - strip undesired HTML - linkify @mention @@ -73,7 +79,7 @@ require 'html_pipeline' class HelloJohnnyFilter < HTMLPipelineFilter def call - text.gsub("$NAME", "Johnny") + text.gsub("Hey", "Hello") end end @@ -104,11 +110,21 @@ used to pass around arguments and metadata between filters in a pipeline. For example, if you want to disable footnotes in the `MarkdownFilter`, you can pass an option in the context hash: ```ruby -context = { markdown: { extensions: { footnotes: false } } } +context = { markdown: { extensions: { footnotes: false } } } filter = HTMLPipeline::ConvertFilter::MarkdownFilter.new(context: context) filter.call("Hi **world**!") ``` +Alternatively, you can construct a pipeline, and pass in a context during the call: + +```ruby +pipeline = HTMLPipeline.new( + convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new, + node_filters: [HTMLPipeline::NodeFilter::MentionFilter.new] +) +pipeline.call(user_supplied_text, context: { markdown: { extensions: { footnotes: false } } }) +``` + Please refer to the documentation for each filter to understand what configuration options are available. ### More Examples diff --git a/lib/html_pipeline.rb b/lib/html_pipeline.rb index 68e5b08d..c609f93b 100644 --- a/lib/html_pipeline.rb +++ b/lib/html_pipeline.rb @@ -171,12 +171,13 @@ def call(text, context: {}, result: {}) text else instrument("call_convert_filter.html_pipeline", payload) do - html = @convert_filter.call(text) + html = @convert_filter.call(text, context: context) end end unless @node_filters.empty? instrument("call_node_filters.html_pipeline", payload) do + @node_filters.each { |filter| filter.context = (filter.context || {}).merge(context) } result[:output] = Selma::Rewriter.new(sanitizer: @sanitization_config, handlers: @node_filters).rewrite(html) html = result[:output] payload = default_payload({ diff --git a/lib/html_pipeline/convert_filter/markdown_filter.rb b/lib/html_pipeline/convert_filter/markdown_filter.rb index ee172285..9bec0bcd 100644 --- a/lib/html_pipeline/convert_filter/markdown_filter.rb +++ b/lib/html_pipeline/convert_filter/markdown_filter.rb @@ -16,8 +16,8 @@ def initialize(context: {}, result: {}) end # Convert Commonmark to HTML using the best available implementation. - def call(text) - options = @context.fetch(:markdown, {}) + def call(text, context: @context) + options = context.fetch(:markdown, {}) plugins = options.fetch(:plugins, {}) Commonmarker.to_html(text, options: options, plugins: plugins).rstrip! end diff --git a/lib/html_pipeline/node_filter.rb b/lib/html_pipeline/node_filter.rb index fa6948e9..d318c4c0 100644 --- a/lib/html_pipeline/node_filter.rb +++ b/lib/html_pipeline/node_filter.rb @@ -4,6 +4,8 @@ class HTMLPipeline class NodeFilter < Filter + attr_accessor :context + def initialize(context: {}, result: {}) super(context: context, result: {}) send(:after_initialize) if respond_to?(:after_initialize) diff --git a/test/html_pipeline_test.rb b/test/html_pipeline_test.rb index cdaf4b48..f15f8269 100644 --- a/test/html_pipeline_test.rb +++ b/test/html_pipeline_test.rb @@ -116,4 +116,34 @@ def test_kitchen_sink assert_equal("

!>eeuqram/eeuqram< ees ot evoL .yllib@ ,ereht yeH

", result) end + + def test_context_is_carried_over_in_call + text = "yeH! I _think_ @gjtorikian is ~great~!" + + pipeline = HTMLPipeline.new( + text_filters: [YehBolderFilter.new], + convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new, + node_filters: [HTMLPipeline::NodeFilter::MentionFilter.new], + ) + result = pipeline.call(text)[:output] + + # note: + # - yeH is bolded + # - strikethroughs are rendered + # - mentions are not linked + assert_equal("

yeH! I think @gjtorikian is great!

", result) + + context = { + no_bolding: false, + markdown: { extension: { strikethrough: false } }, + base_url: "http://your-domain.com", + } + result_with_context = pipeline.call(text, context: context)[:output] + + # note: + # - yeH is not bolded + # - strikethroughs are not rendered + # - mentions are linked + assert_equal("

yeH! I think @gjtorikian is ~great~!

", result_with_context) + end end diff --git a/test/sanitization_filter_test.rb b/test/sanitization_filter_test.rb index ced532f1..fd81c04d 100644 --- a/test/sanitization_filter_test.rb +++ b/test/sanitization_filter_test.rb @@ -263,5 +263,31 @@ def test_sanitization_pipeline_can_be_removed assert_equal(result[:output].to_s, expected.chomp) end + + def test_sanitization_pipeline_does_not_need_node_filters + config = { + elements: ["p", "pre", "code"], + } + + pipeline = HTMLPipeline.new( + convert_filter: + HTMLPipeline::ConvertFilter::MarkdownFilter.new, + sanitization_config: config, + ) + + result = pipeline.call(<<~CODE) + This is *great*, @birdcar: + + some_code(:first) + CODE + + expected = <<~HTML +

This is great, @birdcar:

+
some_code(:first)
+        
+ HTML + + assert_equal(result[:output].to_s, expected.chomp) + end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 8c801f0d..b6a85ba0 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -25,6 +25,6 @@ def call(input, context: {}, result: {}) # bolds any instance of the word yeH class YehBolderFilter < HTMLPipeline::TextFilter def call(input, context: {}, result: {}) - input.gsub("yeH", "**yeH**") + input.gsub("yeH", "**yeH**") unless context[:no_bolding] == false end end