slacken 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +60 -0
  7. data/Rakefile +36 -0
  8. data/lib/slacken.rb +21 -0
  9. data/lib/slacken/document_component.rb +73 -0
  10. data/lib/slacken/document_component/elim_blanks.rb +36 -0
  11. data/lib/slacken/document_component/elim_invalid_links.rb +26 -0
  12. data/lib/slacken/document_component/elim_line_breaks.rb +27 -0
  13. data/lib/slacken/document_component/extract_img_alt.rb +12 -0
  14. data/lib/slacken/document_component/group_indent.rb +27 -0
  15. data/lib/slacken/document_component/group_inlines.rb +26 -0
  16. data/lib/slacken/document_component/sanitize_link_containers.rb +40 -0
  17. data/lib/slacken/document_component/sanitize_special_tag_containers.rb +102 -0
  18. data/lib/slacken/document_component/stringfy_checkbox.rb +20 -0
  19. data/lib/slacken/document_component/stringfy_emoji.rb +20 -0
  20. data/lib/slacken/dom_container.rb +45 -0
  21. data/lib/slacken/node_type.rb +53 -0
  22. data/lib/slacken/render_element.rb +91 -0
  23. data/lib/slacken/rendering.rb +102 -0
  24. data/lib/slacken/slack_url.rb +7 -0
  25. data/lib/slacken/table_element.rb +23 -0
  26. data/lib/slacken/version.rb +3 -0
  27. data/sample/out.txt +38 -0
  28. data/sample/source.html +55 -0
  29. data/scripts/update_markup_fixture.rb +8 -0
  30. data/slacken.gemspec +28 -0
  31. data/spec/fixtures/example.html +114 -0
  32. data/spec/fixtures/markup_text.txt +73 -0
  33. data/spec/helpers/document_component_dsl.rb +11 -0
  34. data/spec/slacken/document_component/elim_blanks_spec.rb +34 -0
  35. data/spec/slacken/document_component/elim_invalid_links_spec.rb +49 -0
  36. data/spec/slacken/document_component/elim_line_breaks_spec.rb +41 -0
  37. data/spec/slacken/document_component/group_indent_spec.rb +37 -0
  38. data/spec/slacken/document_component/group_inlines_spec.rb +33 -0
  39. data/spec/slacken/document_component/sanitize_special_tag_containers_spec.rb +64 -0
  40. data/spec/slacken/document_component_spec.rb +19 -0
  41. data/spec/slacken_spec.rb +12 -0
  42. data/spec/spec_helper.rb +13 -0
  43. metadata +194 -0
@@ -0,0 +1,8 @@
1
+ require 'slacken'
2
+
3
+ fixture_dir = File.expand_path('../spec/fixtures', __dir__)
4
+
5
+ File.write(
6
+ File.expand_path('markup_text.txt', fixture_dir),
7
+ Slacken.translate(File.read(File.expand_path('example.html', fixture_dir)))
8
+ )
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/slacken/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "slacken"
7
+ gem.version = Slacken::VERSION
8
+ gem.summary = %q{Translate HTML sources to markup texts for slack}
9
+ gem.description = %q{Translate HTML sources to markup texts for slack}
10
+ gem.license = "MIT"
11
+ gem.authors = ["Tomoya Chiba"]
12
+ gem.email = "tomo.asleep@gmail.com"
13
+ gem.homepage = "https://rubygems.org/gems/slacken"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_development_dependency 'bundler', '~> 1.0'
21
+ gem.add_development_dependency 'rake', '~> 10.0'
22
+ gem.add_development_dependency 'rdoc', '~> 3.0'
23
+ gem.add_development_dependency 'rspec', '~> 3.0'
24
+ gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
25
+
26
+ gem.add_runtime_dependency 'nokogiri', '~> 1.6'
27
+ gem.add_runtime_dependency 'kosi', '~> 1.0'
28
+ end
@@ -0,0 +1,114 @@
1
+
2
+ <h1>
3
+ <span id="見出し-bold" class="fragment"></span><a href="#%E8%A6%8B%E5%87%BA%E3%81%97-bold"><i class="fa fa-link"></i></a>見出し <strong>bold</strong>
4
+ </h1>
5
+
6
+ <p>テキスト<em>イタリック</em> <strong>太字</strong><br>
7
+ stringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstring</p>
8
+
9
+ <p>hogehogehogehogehogehogehoge <img class="emoji" title=":eyes:" alt=":eyes:" src="https://cdn.qiita.com/emoji/unicode/1f440.png" height="20" width="20" align="absmiddle"> hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge <img class="emoji" title=":bowtie:" alt=":bowtie:" src="https://cdn.qiita.com/emoji/bowtie.png" height="20" width="20" align="absmiddle"> </p>
10
+
11
+ <h2>
12
+ <span id="中見出し-italic" class="fragment"></span><a href="#%E4%B8%AD%E8%A6%8B%E5%87%BA%E3%81%97-italic"><i class="fa fa-link"></i></a>中見出し <em>italic</em>
13
+ </h2>
14
+
15
+ <p>内容をここに書く</p>
16
+
17
+ <ul>
18
+ <li>リスト
19
+
20
+ <ul>
21
+ <li>リスト1</li>
22
+ <li>リスト2</li>
23
+ </ul>
24
+ </li>
25
+ </ul>
26
+
27
+ <ol>
28
+ <li>Item 1</li>
29
+ <li>Item 2</li>
30
+ <li>Item 3
31
+
32
+ <ul>
33
+ <li>Item 3a</li>
34
+ <li>Item 3b</li>
35
+ </ul>
36
+ </li>
37
+ </ol>
38
+
39
+ <ul>
40
+ <li class="task-list-item">
41
+ <input type="checkbox" class="task-list-item-checkbox" checked disabled><a href="/qiitan" class="user-mention" title="qiitan">@qiitan</a>, <a href="/tomoasleep_seed" class="user-mention" title="tomoasleep_seed">@tomoasleep_seed</a> </li>
42
+ <li class="task-list-item">
43
+ <input type="checkbox" class="task-list-item-checkbox" checked disabled>List items can have <strong>bold</strong> and <em>italic text</em>
44
+ </li>
45
+ <li class="task-list-item">
46
+ <input type="checkbox" class="task-list-item-checkbox" checked disabled>Checkbox</li>
47
+ <li class="task-list-item">
48
+ <input type="checkbox" class="task-list-item-checkbox" checked disabled>Checked item</li>
49
+ <li class="task-list-item">
50
+ <input type="checkbox" class="task-list-item-checkbox" disabled>Unchecked item</li>
51
+ </ul>
52
+
53
+ <dl>
54
+ <dt>定義語リストとは?</dt>
55
+ <dd>こんなかんじで単語の意味を説明していくリストです。</dd>
56
+ <dt>他の単語</dt>
57
+ <dd>ここに説明を書く</dd>
58
+ </dl>
59
+
60
+ <p>引用してみるよ</p>
61
+
62
+ <blockquote>
63
+ <p>この文字列は引用されたものです<br>
64
+ インデントされているはず<br>
65
+ これはもインデントされている</p>
66
+ </blockquote>
67
+
68
+ <p>これはインデントされない</p>
69
+
70
+ <p>まずは <strong>「自己紹介」</strong>してみましょう。<br>
71
+ こんにちは <code>Hello, world!</code> 你好。
72
+ </p>
73
+
74
+ <div class="code-frame" data-lang="rb"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Klass</span>
75
+ <span class="k">def</span> <span class="nf">method</span>
76
+ <span class="nb">puts</span> <span class="s1">'method called!'</span>
77
+ <span class="k">end</span>
78
+ <span class="k">end</span>
79
+ </pre></div></div>
80
+
81
+ <table>
82
+ <thead>
83
+ <tr>
84
+ <th>ヘッダー</th>
85
+ <th>Header</th>
86
+ </tr>
87
+ </thead>
88
+ <tbody>
89
+ <tr>
90
+ <td>Cell1</td>
91
+ <td>セル1</td>
92
+ </tr>
93
+ <tr>
94
+ <td>試しに長い文字列を入力してみるYo</td>
95
+ <td>looooooooooooooooooong</td>
96
+ </tr>
97
+ </tbody>
98
+ </table>
99
+
100
+ <p><img src="http://cdn.qiita.com/assets/siteid-reverse-1949e989f9d8b2f6fad65a57292b2b01.png" alt="Qiita logo"></p>
101
+ <p><a href="http://qiita.com"><img src="http://cdn.qiita.com/assets/siteid-reverse-1949e989f9d8b2f6fad65a57292b2b01.png" alt="Qiita logo"></a></p>
102
+
103
+ <p><a href="http://qiita.com">http://qiita.com</a> - リンク<br>
104
+ <a href="http://qiita.com">Qiita</a></p>
105
+
106
+ <p>↓水平線</p>
107
+
108
+ <hr>
109
+
110
+ <p>↑水平線</p>
111
+
112
+ <p><del>ignore it</del></p>
113
+
114
+ <p><img class="emoji" title=":trollface:" alt=":trollface:" src="https://cdn.qiita.com/emoji/trollface.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":trollface:" alt=":trollface:" src="https://cdn.qiita.com/emoji/trollface.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":trollface:" alt=":trollface:" src="https://cdn.qiita.com/emoji/trollface.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":trollface:" alt=":trollface:" src="https://cdn.qiita.com/emoji/trollface.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":joy:" alt=":joy:" src="https://cdn.qiita.com/emoji/unicode/1f602.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":joy:" alt=":joy:" src="https://cdn.qiita.com/emoji/unicode/1f602.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":sob:" alt=":sob:" src="https://cdn.qiita.com/emoji/unicode/1f62d.png" height="20" width="20" align="absmiddle"> <img class="emoji" title=":sob:" alt=":sob:" src="https://cdn.qiita.com/emoji/unicode/1f62d.png" height="20" width="20" align="absmiddle"> </p>
@@ -0,0 +1,73 @@
1
+ *見出し bold*
2
+
3
+ テキスト _イタリック_ *太字*
4
+ stringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstringstring
5
+
6
+ hogehogehogehogehogehogehoge :eyes: hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge :bowtie:
7
+
8
+ *中見出し _italic_*
9
+
10
+ 内容をここに書く
11
+
12
+ • リスト
13
+ • リスト1
14
+ • リスト2
15
+
16
+ 1. Item 1
17
+ 2. Item 2
18
+ 3. Item 3
19
+ • Item 3a
20
+ • Item 3b
21
+
22
+ • [x]@qiitan, @tomoasleep_seed
23
+ • [x] List items can have *bold* and _italic text_
24
+ • [x] Checkbox
25
+ • [x] Checked item
26
+ • [ ] Unchecked item
27
+
28
+ • 定義語リストとは?
29
+ • こんなかんじで単語の意味を説明していくリストです。
30
+ • 他の単語
31
+ • ここに説明を書く
32
+
33
+ 引用してみるよ
34
+
35
+ > この文字列は引用されたものです
36
+ > インデントされているはず
37
+ > これはもインデントされている
38
+
39
+ これはインデントされない
40
+
41
+ まずは *「自己紹介」* してみましょう。
42
+ こんにちは `Hello, world!` 你好。
43
+
44
+ ```class Klass
45
+ def method
46
+ puts 'method called!'
47
+ end
48
+ end
49
+ ```
50
+
51
+ +--------------------------------+----------------------+
52
+ |ヘッダー |Header |
53
+ +--------------------------------+----------------------+
54
+ |Cell1 |セル1 |
55
+ |試しに長い文字列を入力してみるYo|looooooooooooooooooong|
56
+ +--------------------------------+----------------------+
57
+
58
+ <http://cdn.qiita.com/assets/siteid-reverse-1949e989f9d8b2f6fad65a57292b2b01.png|Qiita logo>
59
+
60
+ <http://qiita.com|Qiita logo>
61
+
62
+ <http://qiita.com|http://qiita.com> - リンク
63
+ <http://qiita.com|Qiita>
64
+
65
+ ↓水平線
66
+
67
+ -----------
68
+
69
+ ↑水平線
70
+
71
+ ignore it
72
+
73
+ :trollface::trollface::trollface::trollface::joy::joy::sob::sob:
@@ -0,0 +1,11 @@
1
+ # Public: Provide DSL methods to build a DocumentComponent like S-exp.
2
+ module DocumentComponentDsl
3
+ def c(type_name, *children)
4
+ options = children.first.is_a?(Hash) ? children.shift : {}
5
+ Slacken::DocumentComponent.new(type_name, children, options)
6
+ end
7
+
8
+ def text(content)
9
+ Slacken::DocumentComponent.new(:text, [], content: content)
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe ElimBlanks, dsl: true do
5
+ describe '#has_no_blanks?' do
6
+ subject { component.has_no_blanks? }
7
+
8
+ context 'when a component obviously having no blanks is given' do
9
+ let(:component) do
10
+ c(:div, text('hello world!'))
11
+ end
12
+
13
+ it { is_expected.to be_truthy }
14
+ end
15
+
16
+ context 'when a component with blank children is given' do
17
+ let(:component) do
18
+ c(:div, text(''), c(:div), c(:span))
19
+ end
20
+
21
+ it { is_expected.to be_falsey }
22
+ end
23
+
24
+ context 'when a component with a img is given' do
25
+ let(:component) do
26
+ c(:div, c(:img))
27
+ end
28
+
29
+ it { is_expected.to be_truthy }
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe ElimInvalidLinks, dsl: true do
5
+ describe '#has_no_invalid_links?' do
6
+ subject { component.has_no_invalid_links? }
7
+
8
+ context 'when a http link is given' do
9
+ let(:component) do
10
+ c(:a, { href: 'http://example.com' })
11
+ end
12
+
13
+ it { is_expected.to be_truthy }
14
+ end
15
+
16
+ context 'when a https link is given' do
17
+ let(:component) do
18
+ c(:a, { href: 'https://example.com' })
19
+ end
20
+
21
+ it { is_expected.to be_truthy }
22
+ end
23
+
24
+ context "when a disallowed link containing 'http://' is given" do
25
+ let(:component) do
26
+ c(:a, { href: '#hogefugahttp://' })
27
+ end
28
+
29
+ it { is_expected.to be_falsey }
30
+ end
31
+
32
+ context 'when a disallowed link is given' do
33
+ let(:component) do
34
+ c(:a, { href: '#hogehoge' })
35
+ end
36
+
37
+ it { is_expected.to be_falsey }
38
+ end
39
+
40
+ context 'when a disallowed link occurs as a component\'s children' do
41
+ let(:component) do
42
+ c(:div, c(:span, c(:a, { href: '#hogehoge' })))
43
+ end
44
+
45
+ it { is_expected.to be_falsey }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe ElimLineBreaks, dsl: true do
5
+ describe '#has_no_line_breaks?' do
6
+ subject { component.has_no_line_breaks? }
7
+
8
+ context 'when no linebreaks occur' do
9
+ let(:component) do
10
+ c(:div, c(:h1, text('yo!')), c(:wrapper, text('hello world!'), text('another')))
11
+ end
12
+
13
+ it { is_expected.to be_truthy }
14
+ end
15
+
16
+ context 'when a linebreak occurs in a inline component' do
17
+ let(:component) do
18
+ c(:div, c(:h1, text('yo!')), c(:wrapper, text('hello world!'), text("another\n")))
19
+ end
20
+
21
+ it { is_expected.to be_falsey }
22
+ end
23
+
24
+ context 'when a linebreak occurs in a block component' do
25
+ let(:component) do
26
+ c(:div, c(:h1, text("yo\n!")), c(:wrapper, text('hello world!'), text("another")))
27
+ end
28
+
29
+ it { is_expected.to be_falsey }
30
+ end
31
+
32
+ context 'when a linebreak occurs in a pre tag' do
33
+ let(:component) do
34
+ c(:div, c(:pre, text("yo\n!")), c(:wrapper, text('hello world!'), text("another")))
35
+ end
36
+
37
+ it { is_expected.to be_truthy }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe GroupIndent, dsl: true do
5
+ describe '#indent_grouped?' do
6
+ subject { component.indent_grouped? }
7
+
8
+ context 'when a indented list component is given' do
9
+ let(:component) do
10
+ c(:ul,
11
+ c(:li,
12
+ text('List Heading'),
13
+ c(:indent,
14
+ text('List Content1'),
15
+ c(:span, text('List Content2'), text('List Content3'))
16
+ )))
17
+ end
18
+
19
+ it { is_expected.to be_truthy }
20
+ end
21
+
22
+ context 'when a indented list where child component is not indented is given' do
23
+ let(:component) do
24
+ c(:ul,
25
+ c(:li,
26
+ text('List Heading'),
27
+ c(:span,
28
+ text('List Content1'),
29
+ c(:span, text('List Content2'), text('List Content3'))
30
+ )))
31
+ end
32
+
33
+ it { is_expected.to be_falsey }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe GroupInlines, dsl: true do
5
+ describe '#inlines_grouped?' do
6
+ subject { component.inlines_grouped? }
7
+
8
+ context 'when a grouped component is given' do
9
+ let(:component) do
10
+ c(:div, c(:wrapper, text('hello world!'), text('another')))
11
+ end
12
+
13
+ it { is_expected.to be_truthy }
14
+ end
15
+
16
+ context 'when a component whose inline components are exposed is given' do
17
+ let(:component) do
18
+ c(:div, text(''), c(:div), c(:span))
19
+ end
20
+
21
+ it { is_expected.to be_falsey }
22
+ end
23
+
24
+ context 'when a component with only block components is given' do
25
+ let(:component) do
26
+ c(:div, c(:div), c(:img), c(:p, c(:h1)))
27
+ end
28
+
29
+ it { is_expected.to be_truthy }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ class Slacken::DocumentComponent
4
+ describe SanitizeSpecialTagContainers, dsl: true do
5
+ describe '#sanitized?' do
6
+ subject { component.sanitized? }
7
+
8
+ context 'when a header occurs in a list' do
9
+ let(:component) do
10
+ c(:ul,
11
+ c(:li,
12
+ text('header'),
13
+ c(:indent,
14
+ c(:h1, text('hoge')))))
15
+ end
16
+
17
+ it { is_expected.to be_falsey }
18
+ end
19
+
20
+ context 'when a list occurs in another list' do
21
+ let(:component) do
22
+ c(:ul,
23
+ c(:li,
24
+ text('header'),
25
+ c(:indent,
26
+ c(:dl,
27
+ c(:li,
28
+ text('header2'),
29
+ c(:indent,
30
+ text('fuga')))))))
31
+ end
32
+
33
+ it { is_expected.to be_truthy }
34
+ end
35
+
36
+ context 'when a code occurs in a header' do
37
+ let(:component) do
38
+ c(:h3,
39
+ text('A Ruby Code is Given: '),
40
+ c(:span,
41
+ c(:code, text("puts('hello, world!')"))))
42
+ end
43
+
44
+ it { is_expected.to be_falsey }
45
+ end
46
+
47
+ context 'when a pre tag occurs in a table' do
48
+ let(:component) do
49
+ c(:table,
50
+ c(:thead,
51
+ c(:tr,
52
+ c(:th, text('Header1')),
53
+ c(:th, text('Header2')))),
54
+ c(:tbody,
55
+ c(:tr,
56
+ c(:td, text('Content1')),
57
+ c(:td, c(:pre, (text('Content2')))))))
58
+ end
59
+
60
+ it { is_expected.to be_falsey }
61
+ end
62
+ end
63
+ end
64
+ end