slodown 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 49f19f4efa582699624089df1ceb43dcb0f095a9
4
+ data.tar.gz: 473dba59b4920bf8bd07bec99c5b5e3bdb01ef46
5
+ SHA512:
6
+ metadata.gz: 018e3cbab23eada8eae8f8fc3fa805cb9b5333fb2a50473a2e7330badf61af9bbce253573517a8db0e9993f9f3222c082dfe464bafe14ef265cc6178dd5ca77c
7
+ data.tar.gz: 7110e04ac94168c0d14230c812a46fa6f914a1194e6869485052ef1292f2af416e2eaa63b10c2c19f6ba55df40c7bda435d5f800eddb1d82973e9cb9f77cb05c
data/README.md CHANGED
@@ -9,14 +9,13 @@
9
9
  Here's what slodown does by default:
10
10
 
11
11
  - **render extended Markdown into HTML**. It uses the [kramdown](http://kramdown.rubyforge.org/) library, so yes, footnotes are supported!
12
- - **adds syntax highlighting to Markdown code blocks** through [CodeRay](http://coderay.rubychan.de/).
13
- - **supports super-easy rich media embeds**, [sloblog.io-style](http://sloblog.io/~hmans/qhdsk2SMoAU). Just point the Markdown image syntax at, say, a Youtube video, and slodown will fetch the complete embed code through the magic of [ruby-oembed](https://github.com/judofyr/ruby-oembed).
12
+ - **add syntax highlighting to Markdown code blocks** through [CodeRay](http://coderay.rubychan.de/).
13
+ - **support super-easy rich media embeds**, [sloblog.io-style](http://sloblog.io/~hmans/qhdsk2SMoAU). Just point the Markdown image syntax at, say, a Youtube video, and slodown will fetch the complete embed code through the magic of [ruby-oembed](https://github.com/judofyr/ruby-oembed).
14
14
  - **auto-link contained URLs** using [Rinku](https://github.com/vmg/rinku), which is smart enough to not auto-link URLs contained in, say, code blocks.
15
15
  - **sanitize the generated HTML** using the white-list based [sanitize](https://github.com/rgrove/sanitize) gem.
16
16
 
17
17
  slodown is an extraction from [sloblog.io](http://sloblog.io). It is very easy to extend or modify, as it's just a plain old Ruby class you can inherit from.
18
18
 
19
-
20
19
  ## Installation
21
20
 
22
21
  Add this line to your application's Gemfile:
@@ -33,7 +32,7 @@ Or install it yourself as:
33
32
 
34
33
  ## Usage
35
34
 
36
- For every piece of user input that needs to be rendered, create an instance of `Slodown::Formatter` with the source text and use it to perform somre or all transformations on it. Finally, call `#to_s` to get the rendered output.
35
+ For every piece of user input that needs to be rendered, create an instance of `Slodown::Formatter` with the source text and use it to perform some or all transformations on it. Finally, call `#to_s` to get the rendered output.
37
36
 
38
37
  ### Examples:
39
38
 
@@ -41,6 +40,9 @@ For every piece of user input that needs to be rendered, create an instance of `
41
40
  # let's create an instance to work with
42
41
  formatter = Slodown::Formatter.new(text)
43
42
 
43
+ # just extract metadata
44
+ formatter.extract_metadata.to_s
45
+
44
46
  # just render Markdown to HTML
45
47
  formatter.markdown.to_s
46
48
 
@@ -54,12 +56,47 @@ formatter.sanitize.to_s
54
56
  formatter.markdown.sanitize.to_s
55
57
 
56
58
  # this is the whole deal:
57
- formatter.markdown.autolink.sanitize.to_s
59
+ formatter.extract_metadata.markdown.autolink.sanitize.to_s
58
60
 
59
61
  # which is the same as:
60
62
  formatter.complete.to_s
61
63
  ~~~
62
64
 
65
+ ### Metadata
66
+
67
+ Slodown allows metadata, such as the creation date, to be defined in the text to be processed:
68
+
69
+ ~~~markdown
70
+ #+title: Slodown
71
+ #+created_at: 2014-03-01 13:51:12 CET
72
+ # Installation
73
+
74
+ Add this line to your application's Gemfile:
75
+
76
+ gem 'slodown'
77
+
78
+ ...
79
+ ~~~
80
+
81
+ Metadata can be accessed with `Slodown::Formatter#metadata`:
82
+
83
+ ~~~ruby
84
+ formatter.metadata[:title] # => "Slodown"
85
+ ~~~
86
+
87
+ ## OEmbed support
88
+
89
+ Slodown extends the Markdown image syntax to support OEmbed-based embeds.
90
+ Anything supported by the great [OEmbed gem](https://github.com/judofyr/ruby-oembed) will work. Just supply the URL:
91
+
92
+ ~~~markdown
93
+ ![youtube video](https://www.youtube.com/watch?v=oHg5SJYRHA0)
94
+ ~~~
95
+
96
+ Some OEmbed providers will return IFRAME-based embeds. If you want to control
97
+ which hosts are allowed to have IFRAMEs on your site, override the `Formatter#allowed_iframe_hosts` method to return a regular expression that will be matched against the IFRAME source URL's host. Please note that this will also apply to
98
+ IFRAME HTML tags added by the user directly.
99
+
63
100
  ## Hints
64
101
 
65
102
  * If you want to add more transformations or change the behavior of the `#complete` method, just subclass `Slodown::Formatter` and go wild. :-)
@@ -73,8 +110,27 @@ formatter.complete.to_s
73
110
 
74
111
  ## Contributing
75
112
 
76
- 1. Fork it
77
- 2. Create your feature branch (`git checkout -b my-new-feature`)
78
- 3. Commit your changes (`git commit -am 'Add some feature'`)
79
- 4. Push to the branch (`git push origin my-new-feature`)
80
- 5. Create new Pull Request
113
+ Just like with my other gems, I am trying to keep slodown as sane (and small) as possible. If you
114
+ want to contribute code, **please talk to me before writing a patch or submitting
115
+ a pull request**! I'm serious about keeping things focused and would hate to cause
116
+ unnecessary disappointment. Thank you.
117
+
118
+ If you're still set on submitting a pull request, please consider the following:
119
+
120
+ 1. Create your pull request from a _feature branch_.
121
+ 2. The pull request must only contain changes _related to the feature_.
122
+ 3. Please include specs where it makes sense.
123
+ 4. Absolutely _no_ version bumps or similar.
124
+
125
+ ## Version History
126
+
127
+ ### 0.2.0
128
+
129
+ - Slodown is now whitelisting all domains for possible iframe/embed-based media embeds by default. If you don't want this, you can override `Formatter#allowed_iframe_hosts` to return a regular expression that will match against the embed URL's host.
130
+ - Bumped minimum required version of kramdown to 1.5.0 for all the nice new syntax highlighter integrations it offers (and changes required due to deprecated/changed options.)
131
+ - Support for Twitter oEmbed (using an unfortunately deprecated API, nonetheless.)
132
+ - Added `Slodown::Formatter#kramdown_options`, returning a hash of kramdown configuration options. Overload this in order to customize the formatter's behavior.
133
+
134
+ ### 0.1.3
135
+
136
+ - first public release
@@ -9,7 +9,7 @@ class Kramdown::Converter::SlodownHtml < Kramdown::Converter::Html
9
9
  def convert_img(el, indent)
10
10
  oembed = OEmbed::Providers.get(el.attr['src'])
11
11
  %q(<div class="embedded %s %s">%s</div>) % [oembed.type, oembed.provider_name.parameterize, oembed.html]
12
- rescue OEmbed::NotFound
12
+ rescue StandardError => e
13
13
  super
14
14
  end
15
15
  end
@@ -7,7 +7,6 @@ require 'sanitize'
7
7
  require "kramdown/converter/slodown_html"
8
8
  require "slodown/version"
9
9
  require "slodown/formatter"
10
- require "slodown/embed_transformer"
11
10
 
12
11
  # Register all known oEmbed providers.
13
12
  #
@@ -1,34 +1,61 @@
1
1
  module Slodown
2
+ # This is the base Formatter class provided by Slodown. It works right
3
+ # out of the box if you want to use exactly the functionality provided by
4
+ # it, but in most projects, you'll probably want to create a new class
5
+ # inheriting from this one.
6
+ #
2
7
  class Formatter
3
8
  def initialize(source)
4
9
  @current = @source = source.to_s
5
10
  end
6
11
 
7
- # Runs the entire pipeline.
12
+ # Run the entire pipeline in a sane order.
8
13
  #
9
14
  def complete
10
- markdown.autolink.sanitize
15
+ extract_metadata.markdown.autolink.sanitize
11
16
  end
12
17
 
13
18
  # Convert the current document state from Markdown into HTML.
14
19
  #
15
20
  def markdown
16
- @current = Kramdown::Document.new(@current).to_slodown_html
17
- self
21
+ convert do |current|
22
+ Kramdown::Document.new(current, kramdown_options).to_slodown_html
23
+ end
18
24
  end
19
25
 
20
26
  # Auto-link URLs through Rinku.
21
27
  #
22
28
  def autolink
23
- @current = Rinku.auto_link(@current)
24
- self
29
+ convert do |current|
30
+ Rinku.auto_link(current)
31
+ end
25
32
  end
26
33
 
27
34
  # Sanitize HTML tags.
28
35
  #
29
36
  def sanitize
30
- @current = Sanitize.clean(@current, sanitize_config)
31
- self
37
+ convert do |current|
38
+ Sanitize.clean(current, sanitize_config)
39
+ end
40
+ end
41
+
42
+ def extract_metadata
43
+ @metadata = {}
44
+
45
+ convert do |current|
46
+ current.each_line.drop_while do |line|
47
+ next false if line !~ /^#\+([a-z_]+): (.*)/
48
+
49
+ key, value = $1, $2
50
+ @metadata[key.to_sym] = value
51
+ end.join('')
52
+ end
53
+ end
54
+
55
+ # Return a hash with the extracted metadata
56
+ #
57
+ def metadata
58
+ @metadata
32
59
  end
33
60
 
34
61
  def to_s
@@ -37,12 +64,27 @@ module Slodown
37
64
 
38
65
  private
39
66
 
67
+ # Applies a conversion of the current text state.
68
+ #
69
+ def convert(&blk)
70
+ @current = blk.call(@current)
71
+ self
72
+ end
73
+
74
+ def kramdown_options
75
+ {
76
+ syntax_highlighter: 'coderay',
77
+ syntax_highlighter_opts: {
78
+ }
79
+ }
80
+ end
81
+
40
82
  def sanitize_config
41
83
  {
42
84
  elements: %w(
43
- p a span sub sup strong em div hr abbr
85
+ p br a span sub sup strong em div hr abbr s
44
86
  ul ol li
45
- blockquote pre code
87
+ blockquote pre code kbd
46
88
  h1 h2 h3 h4 h5 h6
47
89
  img object param del
48
90
  ),
@@ -66,8 +108,43 @@ module Slodown
66
108
  'li' => {'id' => ['fn']},
67
109
  'sup' => {'id' => ['fnref']}
68
110
  },
69
- transformers: EmbedTransformer
111
+ transformers: transformers
70
112
  }
71
113
  end
114
+
115
+ def allowed_iframe_hosts
116
+ # By default, allow everything. Override this to return a regular expression
117
+ # that will be matched against the iframe/embed's src URL's host.
118
+ /.*/
119
+ end
120
+
121
+ def transformers
122
+ [embed_transformer]
123
+ end
124
+
125
+ def embed_transformer
126
+ lambda do |env|
127
+ node = env[:node]
128
+ node_name = env[:node_name]
129
+
130
+ # We're fine with a bunch of stuff -- but not <iframe> and <embed> tags.
131
+ return if env[:is_whitelisted] || !env[:node].element?
132
+ return unless %w[iframe embed].include? env[:node_name]
133
+
134
+ # We're dealing with an <iframe> or <embed> tag! Let's check its src attribute.
135
+ # If its host name matches our regular expression, we can whitelist it.
136
+ uri = URI(env[:node]['src'])
137
+ return unless uri.host =~ allowed_iframe_hosts
138
+
139
+ Sanitize.clean_node!(node, {
140
+ elements: %w[iframe embed],
141
+ attributes: {
142
+ all: %w[allowfullscreen frameborder height src width]
143
+ }
144
+ })
145
+
146
+ { node_whitelist: [node] }
147
+ end
148
+ end
72
149
  end
73
150
  end
@@ -1,3 +1,3 @@
1
1
  module Slodown
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -10,18 +10,18 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["hendrik@mans.de"]
11
11
  gem.description = %q{Markdown + oEmbed + Sanitize + CodeRay = the ultimate user input rendering pipeline.}
12
12
  gem.summary = %q{Markdown + oEmbed + Sanitize + CodeRay = the ultimate user input rendering pipeline.}
13
- gem.homepage = "http://github.com/hmans/slodown"
13
+ gem.homepage = "https://github.com/hmans/slodown"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency 'kramdown', '>= 0.14.0'
20
+ gem.add_dependency 'kramdown', '>= 1.5.0'
21
21
  gem.add_dependency 'coderay', '>= 1.0.0'
22
22
  gem.add_dependency 'sanitize', '>= 2.0.0'
23
23
  gem.add_dependency 'rinku', '>= 1.7.0'
24
- gem.add_dependency 'ruby-oembed', '~> 0.8.8'
24
+ gem.add_dependency 'ruby-oembed', '>= 0.8.8'
25
25
 
26
26
  gem.add_development_dependency 'rspec', '>= 2.12.0'
27
27
  gem.add_development_dependency 'rspec-html-matchers'
@@ -9,6 +9,6 @@ describe 'basic formatting syntax' do
9
9
  # to test the whole of kramdown here. :)~
10
10
  #
11
11
  it "renders **this** as bold text" do
12
- expect(render "**foo**").to eq "<p><strong>foo</strong></p>"
12
+ expect(render "**foo**").to eq "<p><strong>foo</strong></p>\n"
13
13
  end
14
14
  end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#metadata' do
4
+ let(:text) do
5
+ <<-EOF.gsub(/^\t/, '')
6
+ #+title: A document with metadata
7
+ #+created_at: 2014-03-01 12:56:31 CET
8
+ # The first headline
9
+
10
+ A paragraph.
11
+ #+with: no metadata
12
+ EOF
13
+ end
14
+
15
+ let(:formatter) { Slodown::Formatter.new(text).complete }
16
+
17
+ it 'returns metadata as a hash' do
18
+ expect(formatter.metadata).to be_a(Hash)
19
+ end
20
+
21
+ it 'contains every listed key' do
22
+ expect(formatter.metadata.keys).to match_array([:title, :created_at])
23
+ end
24
+
25
+ it 'contains every listed value' do
26
+ expect(formatter.metadata.values).to match_array(['A document with metadata',
27
+ '2014-03-01 12:56:31 CET'])
28
+ end
29
+
30
+ it 'removes metadata from the source' do
31
+ expect(formatter.to_s).to_not match(/created_at/)
32
+ end
33
+
34
+ describe 'keys occuring more than once' do
35
+ let(:text) do
36
+ <<-EOF.gsub(/^\t/, '')
37
+ #+title: ignored
38
+ #+title: foo
39
+ EOF
40
+ end
41
+
42
+ it 'uses the last definition' do
43
+ expect(formatter.metadata.fetch(:title)).to eql 'foo'
44
+ end
45
+ end
46
+ end
metadata CHANGED
@@ -1,142 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slodown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Hendrik Mans
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-19 00:00:00.000000000 Z
11
+ date: 2016-02-22 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: kramdown
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
- version: 0.14.0
19
+ version: 1.5.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: 0.14.0
26
+ version: 1.5.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: coderay
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.0.0
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.0.0
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: sanitize
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: 2.0.0
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - ">="
60
53
  - !ruby/object:Gem::Version
61
54
  version: 2.0.0
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rinku
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - ">="
68
60
  - !ruby/object:Gem::Version
69
61
  version: 1.7.0
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - ">="
76
67
  - !ruby/object:Gem::Version
77
68
  version: 1.7.0
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: ruby-oembed
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ~>
73
+ - - ">="
84
74
  - !ruby/object:Gem::Version
85
75
  version: 0.8.8
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ~>
80
+ - - ">="
92
81
  - !ruby/object:Gem::Version
93
82
  version: 0.8.8
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rspec
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - ">="
100
88
  - !ruby/object:Gem::Version
101
89
  version: 2.12.0
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - ">="
108
95
  - !ruby/object:Gem::Version
109
96
  version: 2.12.0
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rspec-html-matchers
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - ">="
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: rake
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
- - - ! '>='
115
+ - - ">="
132
116
  - !ruby/object:Gem::Version
133
117
  version: '0'
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
- - - ! '>='
122
+ - - ">="
140
123
  - !ruby/object:Gem::Version
141
124
  version: '0'
142
125
  description: Markdown + oEmbed + Sanitize + CodeRay = the ultimate user input rendering
@@ -147,46 +130,46 @@ executables: []
147
130
  extensions: []
148
131
  extra_rdoc_files: []
149
132
  files:
150
- - .gitignore
151
- - .rspec
152
- - .travis.yml
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
153
136
  - Gemfile
154
137
  - LICENSE
155
138
  - README.md
156
139
  - Rakefile
157
140
  - lib/kramdown/converter/slodown_html.rb
158
141
  - lib/slodown.rb
159
- - lib/slodown/embed_transformer.rb
160
142
  - lib/slodown/formatter.rb
161
143
  - lib/slodown/version.rb
162
144
  - slodown.gemspec
163
145
  - spec/basic_formatting_spec.rb
146
+ - spec/metadata_extraction_spec.rb
164
147
  - spec/spec_helper.rb
165
- homepage: http://github.com/hmans/slodown
148
+ homepage: https://github.com/hmans/slodown
166
149
  licenses: []
150
+ metadata: {}
167
151
  post_install_message:
168
152
  rdoc_options: []
169
153
  require_paths:
170
154
  - lib
171
155
  required_ruby_version: !ruby/object:Gem::Requirement
172
- none: false
173
156
  requirements:
174
- - - ! '>='
157
+ - - ">="
175
158
  - !ruby/object:Gem::Version
176
159
  version: '0'
177
160
  required_rubygems_version: !ruby/object:Gem::Requirement
178
- none: false
179
161
  requirements:
180
- - - ! '>='
162
+ - - ">="
181
163
  - !ruby/object:Gem::Version
182
164
  version: '0'
183
165
  requirements: []
184
166
  rubyforge_project:
185
- rubygems_version: 1.8.23
167
+ rubygems_version: 2.5.1
186
168
  signing_key:
187
- specification_version: 3
169
+ specification_version: 4
188
170
  summary: Markdown + oEmbed + Sanitize + CodeRay = the ultimate user input rendering
189
171
  pipeline.
190
172
  test_files:
191
173
  - spec/basic_formatting_spec.rb
174
+ - spec/metadata_extraction_spec.rb
192
175
  - spec/spec_helper.rb
@@ -1,26 +0,0 @@
1
- module Slodown
2
- class EmbedTransformer
3
- ALLOWED_DOMAINS = %w[youtube.com soundcloud.com vimeo.com]
4
-
5
- def self.call(env)
6
- node = env[:node]
7
- node_name = env[:node_name]
8
-
9
- return if env[:is_whitelisted] || !env[:node].element?
10
- return unless %w[iframe embed].include? env[:node_name]
11
-
12
- uri = URI(env[:node]['src'])
13
- domains = ALLOWED_DOMAINS.map { |d| Regexp.escape(d) }.join("|")
14
- return unless uri.host =~ /^(.+\.)?(#{domains})/
15
-
16
- Sanitize.clean_node!(node, {
17
- elements: %w[iframe embed],
18
- attributes: {
19
- all: %w[allowfullscreen frameborder height src width]
20
- }
21
- })
22
-
23
- { node_whitelist: [node] }
24
- end
25
- end
26
- end