slacken 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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