motion-html-pipeline 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +379 -0
- data/lib/motion-html-pipeline.rb +14 -0
- data/lib/motion-html-pipeline/document_fragment.rb +27 -0
- data/lib/motion-html-pipeline/pipeline.rb +153 -0
- data/lib/motion-html-pipeline/pipeline/absolute_source_filter.rb +45 -0
- data/lib/motion-html-pipeline/pipeline/body_content.rb +42 -0
- data/lib/motion-html-pipeline/pipeline/disabled/@mention_filter.rb +140 -0
- data/lib/motion-html-pipeline/pipeline/disabled/autolink_filter.rb +27 -0
- data/lib/motion-html-pipeline/pipeline/disabled/camo_filter.rb +93 -0
- data/lib/motion-html-pipeline/pipeline/disabled/email_reply_filter.rb +66 -0
- data/lib/motion-html-pipeline/pipeline/disabled/emoji_filter.rb +125 -0
- data/lib/motion-html-pipeline/pipeline/disabled/markdown_filter.rb +37 -0
- data/lib/motion-html-pipeline/pipeline/disabled/plain_text_input_filter.rb +13 -0
- data/lib/motion-html-pipeline/pipeline/disabled/sanitization_filter.rb +137 -0
- data/lib/motion-html-pipeline/pipeline/disabled/syntax_highlight_filter.rb +44 -0
- data/lib/motion-html-pipeline/pipeline/disabled/toc_filter.rb +67 -0
- data/lib/motion-html-pipeline/pipeline/filter.rb +163 -0
- data/lib/motion-html-pipeline/pipeline/https_filter.rb +27 -0
- data/lib/motion-html-pipeline/pipeline/image_filter.rb +17 -0
- data/lib/motion-html-pipeline/pipeline/image_max_width_filter.rb +37 -0
- data/lib/motion-html-pipeline/pipeline/text_filter.rb +14 -0
- data/lib/motion-html-pipeline/pipeline/version.rb +5 -0
- data/spec/motion-html-pipeline/_helpers/mock_instumentation_service.rb +19 -0
- data/spec/motion-html-pipeline/pipeline/absolute_source_filter_spec.rb +47 -0
- data/spec/motion-html-pipeline/pipeline/disabled/auto_link_filter_spec.rb +33 -0
- data/spec/motion-html-pipeline/pipeline/disabled/camo_filter_spec.rb +75 -0
- data/spec/motion-html-pipeline/pipeline/disabled/email_reply_filter_spec.rb +64 -0
- data/spec/motion-html-pipeline/pipeline/disabled/emoji_filter_spec.rb +92 -0
- data/spec/motion-html-pipeline/pipeline/disabled/markdown_filter_spec.rb +112 -0
- data/spec/motion-html-pipeline/pipeline/disabled/plain_text_input_filter_spec.rb +20 -0
- data/spec/motion-html-pipeline/pipeline/disabled/sanitization_filter_spec.rb +164 -0
- data/spec/motion-html-pipeline/pipeline/disabled/syntax_highlighting_filter_spec.rb +59 -0
- data/spec/motion-html-pipeline/pipeline/disabled/toc_filter_spec.rb +137 -0
- data/spec/motion-html-pipeline/pipeline/https_filter_spec.rb +52 -0
- data/spec/motion-html-pipeline/pipeline/image_filter_spec.rb +37 -0
- data/spec/motion-html-pipeline/pipeline/image_max_width_filter_spec.rb +57 -0
- data/spec/motion-html-pipeline/pipeline_spec.rb +80 -0
- data/spec/spec_helper.rb +48 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91205fa1d79198a224c28464e12946d1294ac518124dec20b536e86d77d91a49
|
4
|
+
data.tar.gz: 6388828a786942a593664a2759daec71b61582d283a692ffcd0c5d5b1f9f70d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 12a542cb6b5380268836031a1cfabde939bd32dc3bdb9b71ace8060a97f9c1e170d49ed88c06f79e64e62350eb09b68ec7e10952a21e2547d6c5d8d7db28a10c
|
7
|
+
data.tar.gz: 86091ee0ccca5c6e97ec59a04aca17e23f66a56ab28c5f696d4fd8f6145a021cb946d5e216d321cd28bede723e6df773565387313c4b274fb607504d661eda8b
|
data/README.md
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
# motion-html-pipeline
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/motion-html-pipeline.svg)](http://badge.fury.io/rb/motion-html-pipeline)
|
4
|
+
[![Build Status](https://travis-ci.org/digitalmoksha/motion-html-pipeline.svg?branch=master)](https://travis-ci.org/digitalmoksha/motion-html-pipeline)
|
5
|
+
|
6
|
+
This gem is a port of the [`html-pipeline` gem](https://github.com/jch/html-pipeline) to RubyMotion, for use on iOS and macOS. Currently synced with `html-pipeline` release [`v.2.11.0`](https://github.com/jch/html-pipeline/releases/tag/v2.11.0)
|
7
|
+
|
8
|
+
Several of the standard filters, such as `AutolinkFilter` and `EmojiFilter`, are initially disabled, as they rely on other Ruby gems that don't have RubyMotion equivalents. Please feel free to submit a pull request that enables any of them.
|
9
|
+
|
10
|
+
We use [HTMLKit](https://github.com/iabudiab/HTMLKit) to take the place of Nokogiri.
|
11
|
+
|
12
|
+
Below is a copy of the original README file, until I'm able to flesh this one out more.
|
13
|
+
|
14
|
+
---
|
15
|
+
# HTML::Pipeline
|
16
|
+
|
17
|
+
GitHub HTML processing filters and utilities. This module includes a small
|
18
|
+
framework for defining DOM based content filters and applying them to user
|
19
|
+
provided content. Read an introduction about this project in
|
20
|
+
[this blog post](https://github.com/blog/1311-html-pipeline-chainable-content-filters).
|
21
|
+
|
22
|
+
- [Installation](#installation)
|
23
|
+
- [Usage](#usage)
|
24
|
+
- [Examples](#examples)
|
25
|
+
- [Filters](#filters)
|
26
|
+
- [Dependencies](#dependencies)
|
27
|
+
- [Documentation](#documentation)
|
28
|
+
- [Extending](#extending)
|
29
|
+
- [3rd Party Extensions](#3rd-party-extensions)
|
30
|
+
- [Instrumenting](#instrumenting)
|
31
|
+
- [Contributing](#contributing)
|
32
|
+
- [Contributors](#contributors)
|
33
|
+
- [Releasing A New Version](#releasing-a-new-version)
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'html-pipeline'
|
41
|
+
```
|
42
|
+
|
43
|
+
And then execute:
|
44
|
+
|
45
|
+
```sh
|
46
|
+
$ bundle
|
47
|
+
```
|
48
|
+
|
49
|
+
Or install it yourself as:
|
50
|
+
|
51
|
+
```sh
|
52
|
+
$ gem install html-pipeline
|
53
|
+
```
|
54
|
+
|
55
|
+
## Usage
|
56
|
+
|
57
|
+
This library provides a handful of chainable HTML filters to transform user
|
58
|
+
content into markup. A filter takes an HTML string or
|
59
|
+
`Nokogiri::HTML::DocumentFragment`, optionally manipulates it, and then
|
60
|
+
outputs the result.
|
61
|
+
|
62
|
+
For example, to transform Markdown source into Markdown HTML:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require 'html/pipeline'
|
66
|
+
|
67
|
+
filter = HTML::Pipeline::MarkdownFilter.new("Hi **world**!")
|
68
|
+
filter.call
|
69
|
+
```
|
70
|
+
|
71
|
+
Filters can be combined into a pipeline which causes each filter to hand its
|
72
|
+
output to the next filter's input. So if you wanted to have content be
|
73
|
+
filtered through Markdown and be syntax highlighted, you can create the
|
74
|
+
following pipeline:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
pipeline = HTML::Pipeline.new [
|
78
|
+
HTML::Pipeline::MarkdownFilter,
|
79
|
+
HTML::Pipeline::SyntaxHighlightFilter
|
80
|
+
]
|
81
|
+
result = pipeline.call <<-CODE
|
82
|
+
This is *great*:
|
83
|
+
|
84
|
+
some_code(:first)
|
85
|
+
|
86
|
+
CODE
|
87
|
+
result[:output].to_s
|
88
|
+
```
|
89
|
+
|
90
|
+
Prints:
|
91
|
+
|
92
|
+
```html
|
93
|
+
<p>This is <em>great</em>:</p>
|
94
|
+
|
95
|
+
<pre><code>some_code(:first)
|
96
|
+
</code></pre>
|
97
|
+
```
|
98
|
+
|
99
|
+
To generate CSS for HTML formatted code, use the [Rouge CSS Theme](https://github.com/jneen/rouge#css-theme-options) `#css` method. `rouge` is a dependency of the `SyntaxHighlightFilter`.
|
100
|
+
|
101
|
+
Some filters take an optional **context** and/or **result** hash. These are
|
102
|
+
used to pass around arguments and metadata between filters in a pipeline. For
|
103
|
+
example, if you don't want to use GitHub formatted Markdown, you can pass an
|
104
|
+
option in the context hash:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
filter = HTML::Pipeline::MarkdownFilter.new("Hi **world**!", :gfm => false)
|
108
|
+
filter.call
|
109
|
+
```
|
110
|
+
|
111
|
+
### Examples
|
112
|
+
|
113
|
+
We define different pipelines for different parts of our app. Here are a few
|
114
|
+
paraphrased snippets to get you started:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
# The context hash is how you pass options between different filters.
|
118
|
+
# See individual filter source for explanation of options.
|
119
|
+
context = {
|
120
|
+
:asset_root => "http://your-domain.com/where/your/images/live/icons",
|
121
|
+
:base_url => "http://your-domain.com"
|
122
|
+
}
|
123
|
+
|
124
|
+
# Pipeline providing sanitization and image hijacking but no mention
|
125
|
+
# related features.
|
126
|
+
SimplePipeline = Pipeline.new [
|
127
|
+
SanitizationFilter,
|
128
|
+
TableOfContentsFilter, # add 'name' anchors to all headers and generate toc list
|
129
|
+
CamoFilter,
|
130
|
+
ImageMaxWidthFilter,
|
131
|
+
SyntaxHighlightFilter,
|
132
|
+
EmojiFilter,
|
133
|
+
AutolinkFilter
|
134
|
+
], context
|
135
|
+
|
136
|
+
# Pipeline used for user provided content on the web
|
137
|
+
MarkdownPipeline = Pipeline.new [
|
138
|
+
MarkdownFilter,
|
139
|
+
SanitizationFilter,
|
140
|
+
CamoFilter,
|
141
|
+
ImageMaxWidthFilter,
|
142
|
+
HttpsFilter,
|
143
|
+
MentionFilter,
|
144
|
+
EmojiFilter,
|
145
|
+
SyntaxHighlightFilter
|
146
|
+
], context.merge(:gfm => true) # enable github formatted markdown
|
147
|
+
|
148
|
+
|
149
|
+
# Define a pipeline based on another pipeline's filters
|
150
|
+
NonGFMMarkdownPipeline = Pipeline.new(MarkdownPipeline.filters,
|
151
|
+
context.merge(:gfm => false))
|
152
|
+
|
153
|
+
# Pipelines aren't limited to the web. You can use them for email
|
154
|
+
# processing also.
|
155
|
+
HtmlEmailPipeline = Pipeline.new [
|
156
|
+
PlainTextInputFilter,
|
157
|
+
ImageMaxWidthFilter
|
158
|
+
], {}
|
159
|
+
|
160
|
+
# Just emoji.
|
161
|
+
EmojiPipeline = Pipeline.new [
|
162
|
+
PlainTextInputFilter,
|
163
|
+
EmojiFilter
|
164
|
+
], context
|
165
|
+
```
|
166
|
+
|
167
|
+
## Filters
|
168
|
+
|
169
|
+
* `MentionFilter` - replace `@user` mentions with links
|
170
|
+
* `AbsoluteSourceFilter` - replace relative image urls with fully qualified versions
|
171
|
+
* `AutolinkFilter` - auto_linking urls in HTML
|
172
|
+
* `CamoFilter` - replace http image urls with [camo-fied](https://github.com/atmos/camo) https versions
|
173
|
+
* `EmailReplyFilter` - util filter for working with emails
|
174
|
+
* `EmojiFilter` - everyone loves [emoji](http://www.emoji-cheat-sheet.com/)!
|
175
|
+
* `HttpsFilter` - HTML Filter for replacing http github urls with https versions.
|
176
|
+
* `ImageMaxWidthFilter` - link to full size image for large images
|
177
|
+
* `MarkdownFilter` - convert markdown to html
|
178
|
+
* `PlainTextInputFilter` - html escape text and wrap the result in a div
|
179
|
+
* `SanitizationFilter` - whitelist sanitize user markup
|
180
|
+
* `SyntaxHighlightFilter` - code syntax highlighter
|
181
|
+
* `TextileFilter` - convert textile to html
|
182
|
+
* `TableOfContentsFilter` - anchor headings with name attributes and generate Table of Contents html unordered list linking headings
|
183
|
+
|
184
|
+
## Dependencies
|
185
|
+
|
186
|
+
Filter gem dependencies are not bundled; you must bundle the filter's gem
|
187
|
+
dependencies. The below list details filters with dependencies. For example,
|
188
|
+
`SyntaxHighlightFilter` uses [rouge](https://github.com/jneen/rouge)
|
189
|
+
to detect and highlight languages. For example, to use the `SyntaxHighlightFilter`,
|
190
|
+
add the following to your Gemfile:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
gem 'rouge'
|
194
|
+
```
|
195
|
+
|
196
|
+
* `AutolinkFilter` - `rinku`
|
197
|
+
* `EmailReplyFilter` - `escape_utils`, `email_reply_parser`
|
198
|
+
* `EmojiFilter` - `gemoji`
|
199
|
+
* `MarkdownFilter` - `commonmarker`
|
200
|
+
* `PlainTextInputFilter` - `escape_utils`
|
201
|
+
* `SanitizationFilter` - `sanitize`
|
202
|
+
* `SyntaxHighlightFilter` - `rouge`
|
203
|
+
* `TableOfContentsFilter` - `escape_utils`
|
204
|
+
* `TextileFilter` - `RedCloth`
|
205
|
+
|
206
|
+
_Note:_ See [Gemfile](/Gemfile) `:test` block for version requirements.
|
207
|
+
|
208
|
+
## Documentation
|
209
|
+
|
210
|
+
Full reference documentation can be [found here](http://rubydoc.info/gems/html-pipeline/frames).
|
211
|
+
|
212
|
+
## Extending
|
213
|
+
To write a custom filter, you need a class with a `call` method that inherits
|
214
|
+
from `HTML::Pipeline::Filter`.
|
215
|
+
|
216
|
+
For example this filter adds a base url to images that are root relative:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
require 'uri'
|
220
|
+
|
221
|
+
class RootRelativeFilter < HTML::Pipeline::Filter
|
222
|
+
|
223
|
+
def call
|
224
|
+
doc.search("img").each do |img|
|
225
|
+
next if img['src'].nil?
|
226
|
+
src = img['src'].strip
|
227
|
+
if src.start_with? '/'
|
228
|
+
img["src"] = URI.join(context[:base_url], src).to_s
|
229
|
+
end
|
230
|
+
end
|
231
|
+
doc
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
237
|
+
Now this filter can be used in a pipeline:
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
Pipeline.new [ RootRelativeFilter ], { :base_url => 'http://somehost.com' }
|
241
|
+
```
|
242
|
+
|
243
|
+
### 3rd Party Extensions
|
244
|
+
|
245
|
+
If you have an idea for a filter, propose it as
|
246
|
+
[an issue](https://github.com/jch/html-pipeline/issues) first. This allows us discuss
|
247
|
+
whether the filter is a common enough use case to belong in this gem, or should be
|
248
|
+
built as an external gem.
|
249
|
+
|
250
|
+
Here are some extensions people have built:
|
251
|
+
|
252
|
+
* [html-pipeline-asciidoc_filter](https://github.com/asciidoctor/html-pipeline-asciidoc_filter)
|
253
|
+
* [jekyll-html-pipeline](https://github.com/gjtorikian/jekyll-html-pipeline)
|
254
|
+
* [nanoc-html-pipeline](https://github.com/burnto/nanoc-html-pipeline)
|
255
|
+
* [html-pipeline-bitly](https://github.com/dewski/html-pipeline-bitly)
|
256
|
+
* [html-pipeline-cite](https://github.com/lifted-studios/html-pipeline-cite)
|
257
|
+
* [tilt-html-pipeline](https://github.com/bradgessler/tilt-html-pipeline)
|
258
|
+
* [html-pipeline-wiki-link'](https://github.com/lifted-studios/html-pipeline-wiki-link) - WikiMedia-style wiki links
|
259
|
+
* [task_list](https://github.com/github/task_list) - GitHub flavor Markdown Task List
|
260
|
+
* [html-pipeline-nico_link](https://github.com/rutan/html-pipeline-nico_link) - An HTML::Pipeline filter for [niconico](http://www.nicovideo.jp) description links
|
261
|
+
* [html-pipeline-gitlab](https://gitlab.com/gitlab-org/html-pipeline-gitlab) - This gem implements various filters for html-pipeline used by GitLab
|
262
|
+
* [html-pipeline-youtube](https://github.com/st0012/html-pipeline-youtube) - An HTML::Pipeline filter for YouTube links
|
263
|
+
* [html-pipeline-flickr](https://github.com/st0012/html-pipeline-flickr) - An HTML::Pipeline filter for Flickr links
|
264
|
+
* [html-pipeline-vimeo](https://github.com/dlackty/html-pipeline-vimeo) - An HTML::Pipeline filter for Vimeo links
|
265
|
+
* [html-pipeline-hashtag](https://github.com/mr-dxdy/html-pipeline-hashtag) - An HTML::Pipeline filter for hashtags
|
266
|
+
* [html-pipeline-linkify_github](https://github.com/jollygoodcode/html-pipeline-linkify_github) - An HTML::Pipeline filter to autolink GitHub urls
|
267
|
+
* [html-pipeline-redcarpet_filter](https://github.com/bmikol/html-pipeline-redcarpet_filter) - Render Markdown source text into Markdown HTML using Redcarpet
|
268
|
+
* [html-pipeline-typogruby_filter](https://github.com/bmikol/html-pipeline-typogruby_filter) - Add Typogruby text filters to your HTML::Pipeline
|
269
|
+
* [korgi](https://github.com/jodeci/korgi) - HTML::Pipeline filters for links to Rails resources
|
270
|
+
|
271
|
+
|
272
|
+
## Instrumenting
|
273
|
+
|
274
|
+
Filters and Pipelines can be set up to be instrumented when called. The pipeline
|
275
|
+
must be setup with an
|
276
|
+
[ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html)
|
277
|
+
compatible service object and a name. New pipeline objects will default to the
|
278
|
+
`HTML::Pipeline.default_instrumentation_service` object.
|
279
|
+
|
280
|
+
``` ruby
|
281
|
+
# the AS::Notifications-compatible service object
|
282
|
+
service = ActiveSupport::Notifications
|
283
|
+
|
284
|
+
# instrument a specific pipeline
|
285
|
+
pipeline = HTML::Pipeline.new [MarkdownFilter], context
|
286
|
+
pipeline.setup_instrumentation "MarkdownPipeline", service
|
287
|
+
|
288
|
+
# or set default instrumentation service for all new pipelines
|
289
|
+
HTML::Pipeline.default_instrumentation_service = service
|
290
|
+
pipeline = HTML::Pipeline.new [MarkdownFilter], context
|
291
|
+
pipeline.setup_instrumentation "MarkdownPipeline"
|
292
|
+
```
|
293
|
+
|
294
|
+
Filters are instrumented when they are run through the pipeline. A
|
295
|
+
`call_filter.html_pipeline` event is published once the filter finishes. The
|
296
|
+
`payload` should include the `filter` name. Each filter will trigger its own
|
297
|
+
instrumentation call.
|
298
|
+
|
299
|
+
``` ruby
|
300
|
+
service.subscribe "call_filter.html_pipeline" do |event, start, ending, transaction_id, payload|
|
301
|
+
payload[:pipeline] #=> "MarkdownPipeline", set with `setup_instrumentation`
|
302
|
+
payload[:filter] #=> "MarkdownFilter"
|
303
|
+
payload[:context] #=> context Hash
|
304
|
+
payload[:result] #=> instance of result class
|
305
|
+
payload[:result][:output] #=> output HTML String or Nokogiri::DocumentFragment
|
306
|
+
end
|
307
|
+
```
|
308
|
+
|
309
|
+
The full pipeline is also instrumented:
|
310
|
+
|
311
|
+
``` ruby
|
312
|
+
service.subscribe "call_pipeline.html_pipeline" do |event, start, ending, transaction_id, payload|
|
313
|
+
payload[:pipeline] #=> "MarkdownPipeline", set with `setup_instrumentation`
|
314
|
+
payload[:filters] #=> ["MarkdownFilter"]
|
315
|
+
payload[:doc] #=> HTML String or Nokogiri::DocumentFragment
|
316
|
+
payload[:context] #=> context Hash
|
317
|
+
payload[:result] #=> instance of result class
|
318
|
+
payload[:result][:output] #=> output HTML String or Nokogiri::DocumentFragment
|
319
|
+
end
|
320
|
+
```
|
321
|
+
|
322
|
+
## FAQ
|
323
|
+
|
324
|
+
### 1. Why doesn't my pipeline work when there's no root element in the document?
|
325
|
+
|
326
|
+
To make a pipeline work on a plain text document, put the `PlainTextInputFilter`
|
327
|
+
at the beginning of your pipeline. This will wrap the content in a `div` so the
|
328
|
+
filters have a root element to work with. If you're passing in an HTML fragment,
|
329
|
+
but it doesn't have a root element, you can wrap the content in a `div`
|
330
|
+
yourself. For example:
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
EmojiPipeline = Pipeline.new [
|
334
|
+
PlainTextInputFilter, # <- Wraps input in a div and escapes html tags
|
335
|
+
EmojiFilter
|
336
|
+
], context
|
337
|
+
|
338
|
+
plain_text = "Gutentag! :wave:"
|
339
|
+
EmojiPipeline.call(plain_text)
|
340
|
+
|
341
|
+
html_fragment = "This is outside of an html element, but <strong>this isn't. :+1:</strong>"
|
342
|
+
EmojiPipeline.call("<div>#{html_fragment}</div>") # <- Wrap your own html fragments to avoid escaping
|
343
|
+
```
|
344
|
+
|
345
|
+
### 2. How do I customize a whitelist for `SanitizationFilter`s?
|
346
|
+
|
347
|
+
`SanitizationFilter::WHITELIST` is the default whitelist used if no `:whitelist`
|
348
|
+
argument is given in the context. The default is a good starting template for
|
349
|
+
you to add additional elements. You can either modify the constant's value, or
|
350
|
+
re-define your own constant and pass that in via the context.
|
351
|
+
|
352
|
+
## Contributing
|
353
|
+
|
354
|
+
Please review the [Contributing Guide](https://github.com/jch/html-pipeline/blob/master/CONTRIBUTING.md).
|
355
|
+
|
356
|
+
1. [Fork it](https://help.github.com/articles/fork-a-repo)
|
357
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
358
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
359
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
360
|
+
5. Create new [Pull Request](https://help.github.com/articles/using-pull-requests)
|
361
|
+
|
362
|
+
To see what has changed in recent versions, see the [CHANGELOG](https://github.com/jch/html-pipeline/blob/master/CHANGELOG.md).
|
363
|
+
|
364
|
+
### Contributors
|
365
|
+
|
366
|
+
Thanks to all of [these contributors](https://github.com/jch/html-pipeline/graphs/contributors).
|
367
|
+
|
368
|
+
Project is a member of the [OSS Manifesto](http://ossmanifesto.org/).
|
369
|
+
|
370
|
+
### Releasing A New Version
|
371
|
+
|
372
|
+
This section is for gem maintainers to cut a new version of the gem.
|
373
|
+
|
374
|
+
* create a new branch named `release-x.y.z` where `x.y.z` follows [semver](http://semver.org)
|
375
|
+
* update lib/html/pipeline/version.rb to next version number X.X.X
|
376
|
+
* update CHANGELOG.md. Prepare a draft with `script/changelog`
|
377
|
+
* push branch and create a new pull request
|
378
|
+
* after tests are green, merge to master
|
379
|
+
* on the master branch, run `script/release`
|
@@ -0,0 +1,14 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'motion-cocoapods'
|
6
|
+
|
7
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
8
|
+
Motion::Project::App.setup do |app|
|
9
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-html-pipeline/**/*.rb")))
|
10
|
+
|
11
|
+
app.pods do
|
12
|
+
pod 'HTMLKit', '~> 2.1'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Gives a Nokogiri type of interface, but uses
|
2
|
+
# HTMLKit (https://github.com/iabudiab/HTMLKit)
|
3
|
+
#
|
4
|
+
# Not meant to duplicate Nokogiri completely, just add a similar interface
|
5
|
+
#------------------------------------------------------------------------------
|
6
|
+
module MotionHTMLPipeline
|
7
|
+
class DocumentFragment < HTMLDocument
|
8
|
+
attr_reader :document
|
9
|
+
|
10
|
+
def self.parse(html)
|
11
|
+
DocumentFragment.new(html)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(html)
|
15
|
+
@document = HTMLDocument.documentWithString(html)
|
16
|
+
end
|
17
|
+
|
18
|
+
def css(query)
|
19
|
+
document.querySelectorAll(query)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_html
|
23
|
+
document.body.innerHTML
|
24
|
+
end
|
25
|
+
alias :to_s :to_html
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# require 'nokogiri'
|
2
|
+
# require 'active_support/xml_mini/nokogiri' # convert Documents to hashes
|
3
|
+
|
4
|
+
module MotionHTMLPipeline
|
5
|
+
# GitHub HTML processing filters and utilities. This module includes a small
|
6
|
+
# framework for defining DOM based content filters and applying them to user
|
7
|
+
# provided content.
|
8
|
+
#
|
9
|
+
# See MotionHTMLPipeline::Pipeline::Filter for information on building filters.
|
10
|
+
#
|
11
|
+
# Construct a Pipeline for running multiple HTML filters. A pipeline is created once
|
12
|
+
# with one to many filters, and it then can be `call`ed many times over the course
|
13
|
+
# of its lifetime with input.
|
14
|
+
#
|
15
|
+
# filters - Array of Filter objects. Each must respond to call(doc,
|
16
|
+
# context) and return the modified DocumentFragment or a
|
17
|
+
# String containing HTML markup. Filters are performed in the
|
18
|
+
# order provided.
|
19
|
+
# default_context - The default context hash. Values specified here will be merged
|
20
|
+
# into values from the each individual pipeline run. Can NOT be
|
21
|
+
# nil. Default: empty Hash.
|
22
|
+
# result_class - The default Class of the result object for individual
|
23
|
+
# calls. Default: Hash. Protip: Pass in a Struct to get
|
24
|
+
# some semblance of type safety.
|
25
|
+
class Pipeline
|
26
|
+
# Parse a String into a DocumentFragment object. When a DocumentFragment is
|
27
|
+
# provided, return it verbatim.
|
28
|
+
def self.parse(document_or_html)
|
29
|
+
document_or_html ||= ''
|
30
|
+
if document_or_html.is_a?(String)
|
31
|
+
DocumentFragment.parse(document_or_html)
|
32
|
+
else
|
33
|
+
document_or_html
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Returns an Array of Filter objects for this Pipeline.
|
38
|
+
attr_reader :filters
|
39
|
+
|
40
|
+
# Public: Instrumentation service for the pipeline.
|
41
|
+
# Set an ActiveSupport::Notifications compatible object to enable.
|
42
|
+
attr_accessor :instrumentation_service
|
43
|
+
|
44
|
+
# Public: String name for this Pipeline. Defaults to Class name.
|
45
|
+
attr_writer :instrumentation_name
|
46
|
+
def instrumentation_name
|
47
|
+
return @instrumentation_name if defined?(@instrumentation_name)
|
48
|
+
@instrumentation_name = self.class.name
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
# Public: Default instrumentation service for new pipeline objects.
|
53
|
+
attr_accessor :default_instrumentation_service
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(filters, default_context = {}, result_class = nil)
|
57
|
+
raise ArgumentError, 'default_context cannot be nil' if default_context.nil?
|
58
|
+
@filters = filters.flatten.freeze
|
59
|
+
@default_context = default_context.freeze
|
60
|
+
@result_class = result_class || Hash
|
61
|
+
@instrumentation_service = self.class.default_instrumentation_service
|
62
|
+
end
|
63
|
+
|
64
|
+
# Apply all filters in the pipeline to the given HTML.
|
65
|
+
#
|
66
|
+
# html - A String containing HTML or a DocumentFragment object.
|
67
|
+
# context - The context hash passed to each filter. See the Filter docs
|
68
|
+
# for more info on possible values. This object MUST NOT be modified
|
69
|
+
# in place by filters. Use the Result for passing state back.
|
70
|
+
# result - The result Hash passed to each filter for modification. This
|
71
|
+
# is where Filters store extracted information from the content.
|
72
|
+
#
|
73
|
+
# Returns the result Hash after being filtered by this Pipeline. Contains an
|
74
|
+
# :output key with the DocumentFragment or String HTML markup based on the
|
75
|
+
# output of the last filter in the pipeline.
|
76
|
+
def call(html, context = {}, result = nil)
|
77
|
+
context = @default_context.merge(context)
|
78
|
+
context = context.freeze
|
79
|
+
result ||= @result_class.new
|
80
|
+
payload = default_payload filters: @filters.map(&:name),
|
81
|
+
context: context, result: result
|
82
|
+
instrument 'call_pipeline.html_pipeline', payload do
|
83
|
+
result[:output] =
|
84
|
+
@filters.inject(html) do |doc, filter|
|
85
|
+
perform_filter(filter, doc, context, result)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
result
|
89
|
+
end
|
90
|
+
|
91
|
+
# Internal: Applies a specific filter to the supplied doc.
|
92
|
+
#
|
93
|
+
# The filter is instrumented.
|
94
|
+
#
|
95
|
+
# Returns the result of the filter.
|
96
|
+
def perform_filter(filter, doc, context, result)
|
97
|
+
payload = default_payload filter: filter.name,
|
98
|
+
context: context, result: result
|
99
|
+
instrument 'call_filter.html_pipeline', payload do
|
100
|
+
filter.call(doc, context, result)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Like call but guarantee the value returned is a DocumentFragment.
|
105
|
+
# Pipelines may return a DocumentFragment or a String. Callers that need a
|
106
|
+
# DocumentFragment should use this method.
|
107
|
+
def to_document(input, context = {}, result = nil)
|
108
|
+
result = call(input, context, result)
|
109
|
+
MotionHTMLPipeline::Pipeline.parse(result[:output])
|
110
|
+
end
|
111
|
+
|
112
|
+
# Like call but guarantee the value returned is a string of HTML markup.
|
113
|
+
def to_html(input, context = {}, result = nil)
|
114
|
+
result = call(input, context, result = nil)
|
115
|
+
output = result[:output]
|
116
|
+
if output.respond_to?(:to_html)
|
117
|
+
output.to_html
|
118
|
+
else
|
119
|
+
output.to_s
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Public: setup instrumentation for this pipeline.
|
124
|
+
#
|
125
|
+
# Returns nothing.
|
126
|
+
def setup_instrumentation(name = nil, service = nil)
|
127
|
+
self.instrumentation_name = name
|
128
|
+
self.instrumentation_service =
|
129
|
+
service || self.class.default_instrumentation_service
|
130
|
+
end
|
131
|
+
|
132
|
+
# Internal: if the `instrumentation_service` object is set, instruments the
|
133
|
+
# block, otherwise the block is ran without instrumentation.
|
134
|
+
#
|
135
|
+
# Returns the result of the provided block.
|
136
|
+
def instrument(event, payload = nil)
|
137
|
+
payload ||= default_payload
|
138
|
+
return yield(payload) unless instrumentation_service
|
139
|
+
instrumentation_service.instrument event, payload do |payload|
|
140
|
+
yield payload
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Internal: Default payload for instrumentation.
|
145
|
+
#
|
146
|
+
# Accepts a Hash of additional payload data to be merged.
|
147
|
+
#
|
148
|
+
# Returns a Hash.
|
149
|
+
def default_payload(payload = {})
|
150
|
+
{ pipeline: instrumentation_name }.merge(payload)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|