slack_transformer 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7313982b9621e47723717517eb8a2a889c113270
4
+ data.tar.gz: 2282671a2714cd2653bfdc565f606a91d055d99c
5
+ SHA512:
6
+ metadata.gz: 9ee0ac743cea68183d15c70822bc4a556b1fa9a3570547230a78f903118e48138767e31afc9e39efa6a101f41095369a3e6e3fe97813e28dcbb47af5b90ef1a8
7
+ data.tar.gz: bce104f1a1256d4a25d0d4459b7170011ed9d79a4b3f4874dfb711795b14b2d500ffbc67a63af22ebd6dd42c5f4ba486a8ac6e966c2ec975275fee30d03262a9
@@ -0,0 +1,4 @@
1
+ require 'slack_transformer/date'
2
+ require 'slack_transformer/entities'
3
+ require 'slack_transformer/html'
4
+ require 'slack_transformer/slack'
@@ -0,0 +1,41 @@
1
+ require 'set'
2
+ require 'time'
3
+
4
+ module SlackTransformer
5
+ class Date
6
+ attr_reader :input, :format, :link, :fallback
7
+
8
+ class InvalidTokenError < StandardError; end
9
+
10
+ # See https://api.slack.com/docs/message-formatting#formatting_dates
11
+ DATE_FORMAT_TOKENS = Set.new(%w[
12
+ {date_num}
13
+ {date}
14
+ {date_short}
15
+ {date_long}
16
+ {date_pretty}
17
+ {date_short_pretty}
18
+ {date_long_pretty}
19
+ {time}
20
+ {time_secs}
21
+ ])
22
+
23
+ def initialize(input, format:, link: nil, fallback:)
24
+ @input = input
25
+ @format = format
26
+ @link = link
27
+ @fallback = fallback
28
+ end
29
+
30
+ def to_slack
31
+ tokens = format.scan(/(\{\w+\})/).first
32
+
33
+ if tokens
34
+ invalid_tokens = tokens.reject { |t| DATE_FORMAT_TOKENS.include?(t) }
35
+ raise InvalidTokenError, invalid_tokens.join(', ') unless invalid_tokens.empty?
36
+ end
37
+
38
+ "<!date^#{Time.parse(input.to_s).to_i}^#{format}#{"^#{link}" if link}|#{fallback}>"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ module SlackTransformer
2
+ class Entities
3
+ attr_reader :input
4
+
5
+ def initialize(input)
6
+ @input = input
7
+ end
8
+
9
+ # See https://api.slack.com/docs/message-formatting#how_to_escape_characters
10
+ # NB: The order matters here. If you were to replace < with &lt; first, for
11
+ # example, you'd end up with &amp;lt;
12
+ def to_slack
13
+ input.gsub('&', '&amp;')
14
+ .gsub('<', '&lt;')
15
+ .gsub('>', '&gt;')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'slack_transformer/html/bold'
2
+ require 'slack_transformer/html/code'
3
+ require 'slack_transformer/html/italics'
4
+ require 'slack_transformer/html/lists'
5
+ require 'slack_transformer/html/preformatted'
6
+ require 'slack_transformer/html/strikethrough'
7
+
8
+ module SlackTransformer
9
+ class Html
10
+ attr_reader :input
11
+
12
+ TRANSFORMERS = [
13
+ SlackTransformer::Html::Bold,
14
+ SlackTransformer::Html::Italics,
15
+ SlackTransformer::Html::Strikethrough,
16
+ SlackTransformer::Html::Code,
17
+ SlackTransformer::Html::Preformatted,
18
+ SlackTransformer::Html::Lists
19
+ ]
20
+
21
+ def initialize(input)
22
+ @input = input
23
+ end
24
+
25
+ def to_slack
26
+ TRANSFORMERS.reduce(input) do |html, transformer|
27
+ transformer.new(html).to_slack
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module SlackTransformer
2
+ class Html
3
+ class Bold
4
+ attr_reader :input
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def to_slack
11
+ input.gsub(/<\/?b>/, '*')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SlackTransformer
2
+ class Html
3
+ class Code
4
+ attr_reader :input
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def to_slack
11
+ input.gsub(/<\/?code>/, '`')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SlackTransformer
2
+ class Html
3
+ class Italics
4
+ attr_reader :input
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def to_slack
11
+ input.gsub(/<\/?i>/, '_')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ require 'nokogiri'
2
+
3
+ module SlackTransformer
4
+ class Html
5
+ class Lists
6
+ attr_reader :input
7
+
8
+ def initialize(input)
9
+ @input = input
10
+ end
11
+
12
+ def to_slack
13
+ fragment = Nokogiri::HTML.fragment(input)
14
+
15
+ fragment.children.each do |child|
16
+ case child.name
17
+ when 'ul'
18
+ list = child.children.map do |c|
19
+ "• #{c.children.to_html}"
20
+ end
21
+
22
+ child.replace(list.join("\n"))
23
+ when 'ol'
24
+ list = child.children.map.with_index do |c, i|
25
+ "#{i + 1}. #{c.children.to_html}"
26
+ end
27
+
28
+ child.replace(list.join("\n"))
29
+ end
30
+ end
31
+
32
+ fragment.to_html
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ module SlackTransformer
2
+ class Html
3
+ class Preformatted
4
+ attr_reader :input
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def to_slack
11
+ input.gsub(/<\/?pre>/, '```')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SlackTransformer
2
+ class Html
3
+ class Strikethrough
4
+ attr_reader :input
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def to_slack
11
+ input.gsub(/<\/?s>/, '~')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ require 'slack_transformer/slack/blockquote'
2
+ require 'slack_transformer/slack/bold'
3
+ require 'slack_transformer/slack/code'
4
+ require 'slack_transformer/slack/italics'
5
+ require 'slack_transformer/slack/preformatted'
6
+ require 'slack_transformer/slack/quote'
7
+ require 'slack_transformer/slack/strikethrough'
8
+
9
+ module SlackTransformer
10
+ class Slack
11
+ attr_reader :input
12
+
13
+ TRANSFORMERS = [
14
+ SlackTransformer::Slack::Bold,
15
+ SlackTransformer::Slack::Italics,
16
+ SlackTransformer::Slack::Strikethrough,
17
+ SlackTransformer::Slack::Code,
18
+ SlackTransformer::Slack::Preformatted,
19
+ SlackTransformer::Slack::Quote,
20
+ SlackTransformer::Slack::Blockquote
21
+ ]
22
+
23
+ def initialize(input)
24
+ @input = input
25
+ end
26
+
27
+ def to_html
28
+ html = TRANSFORMERS.reduce(input) do |html, transformer|
29
+ transformer.new(html).to_html
30
+ end
31
+
32
+ "<p>#{html.gsub("\n", '<br>')}</p>"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Blockquote
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ ^
8
+
9
+ # start of line
10
+
11
+ ([*_~]?)
12
+
13
+ # optionally preceded by *, _, or ~
14
+
15
+ >{3}(.+)
16
+
17
+ # one or more of anything preceded by >
18
+
19
+ \1
20
+
21
+ # followed by *, _, or ~ if preceded by it
22
+
23
+ $
24
+
25
+ # end of line
26
+ /mx
27
+
28
+ def initialize(input)
29
+ @input = input
30
+ end
31
+
32
+ def to_html
33
+ input.gsub(PATTERN) do
34
+ outer = Regexp.last_match(1)
35
+ inner = Regexp.last_match(2).gsub("\n", '<br>')
36
+
37
+ "#{outer}<blockquote>#{inner}</blockquote>#{outer}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Bold
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ (?<=^|\W)
8
+
9
+ # preceded by start of line or non-word character
10
+
11
+ (?<![*~`])
12
+
13
+ # but not *, ~, or `, which are mrkdwn delimiters
14
+ # Note: _ is also a mrkdwn delimiter, but it's included in \W because
15
+ # it's a word character.
16
+
17
+ ([_~]?)
18
+
19
+ # optionally preceded by _ or ~
20
+
21
+ \*((?:[^*]|(?<=[\w\s])\*+(?=\w))+)\*
22
+
23
+ # one or more of either not *, or one or more * preceded by word
24
+ # character or space and followed by word character, surrounded by *
25
+
26
+ \1
27
+
28
+ # followed by _ or ~ if preceded by it
29
+
30
+ (?![*`])
31
+
32
+ # not followed by * or `
33
+
34
+ (?=\W|$)
35
+
36
+ # followed by non-word character or end of line
37
+ /x
38
+
39
+ def initialize(input)
40
+ @input = input
41
+ end
42
+
43
+ def to_html
44
+ input.gsub(PATTERN, '\1<b>\2</b>\1')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Code
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ (?<=^|\W|_)
8
+
9
+ # preceded by start of line, non-word character, or _
10
+
11
+ (?<!`)
12
+
13
+ # but not `
14
+
15
+ `([^`]+?)(`+)
16
+
17
+ # one or more of not ` preceded by ` followed by one or more `
18
+ /x
19
+
20
+ def initialize(input)
21
+ @input = input
22
+ end
23
+
24
+ def to_html
25
+ input.gsub(PATTERN) do |match|
26
+ closing_backticks = Regexp.last_match(2)
27
+ closing_backticks_length = closing_backticks.length
28
+ closing_backticks_remainder = closing_backticks_length % 3
29
+
30
+ if closing_backticks_remainder == 0
31
+ match
32
+ else
33
+ inner_text = Regexp.last_match(1)
34
+ inner_trailing_backticks = '`' * (closing_backticks_length / 3 * 3)
35
+ outer_trailing_backticks = '`' * (closing_backticks_remainder - 1)
36
+
37
+ "<code>#{inner_text}#{inner_trailing_backticks}</code>#{outer_trailing_backticks}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Italics
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ (?<=^|\W)
8
+
9
+ # preceded by start of line or non-word character
10
+
11
+ (?<![*~`])
12
+
13
+ # but not *, ~, or `, which are mrkdwn delimiters
14
+ # Note: _ is also a mrkdwn delimiter, but it's included in \W because
15
+ # it's a word character.
16
+
17
+ ([*~]?)
18
+
19
+ # optionally preceded by * or ~
20
+
21
+ _((?:[^_]|(?<=[a-zA-Z0-9\s])_+(?=[a-zA-Z0-9]))+)_
22
+
23
+ # one or more of either not _, or one or more _ preceded by letter,
24
+ # number, or space and followed by letter or number, surrounded by _
25
+
26
+ \1
27
+
28
+ # followed by * or ~ if preceded by it
29
+
30
+ (?!```)
31
+
32
+ # not followed by ```
33
+
34
+ (?=\W|$)
35
+
36
+ # followed by non-word character or end of line
37
+ /x
38
+
39
+ def initialize(input)
40
+ @input = input
41
+ end
42
+
43
+ def to_html
44
+ input.gsub(PATTERN, '\1<i>\2</i>\1')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,45 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Preformatted
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ (?<=^|\W|_)
8
+
9
+ # preceded by start of line, non-word character, or _
10
+
11
+ (?<!~)
12
+
13
+ # but not ~
14
+
15
+ ([*_~]?)
16
+
17
+ # optionally preceded by *, _, or ~
18
+
19
+ ```(.+)```
20
+
21
+ # one or more of anything surrounded by ```
22
+
23
+ \1
24
+
25
+ # followed by *, _, or ~ if preceded by it
26
+
27
+ (?!~)
28
+
29
+ # not followed by ~
30
+
31
+ (?=\W|_|$)
32
+
33
+ # followed by non-word character, _, or end of line
34
+ /x
35
+
36
+ def initialize(input)
37
+ @input = input
38
+ end
39
+
40
+ def to_html
41
+ input.gsub(PATTERN, '\1<pre>\2</pre>\1')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Quote
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ ^
8
+
9
+ # start of line
10
+
11
+ ([*_~]?)
12
+
13
+ # optionally preceded by *, _, or ~
14
+
15
+ >(.+?)
16
+
17
+ # one or more of anything preceded by >
18
+
19
+ \1
20
+
21
+ # followed by *, _, or ~ if preceded by it
22
+
23
+ (?:\n|$)
24
+
25
+ # followed by newline or end of line
26
+ /x
27
+
28
+ def initialize(input)
29
+ @input = input
30
+ end
31
+
32
+ def to_html
33
+ input.gsub(PATTERN, '\1<blockquote>\2</blockquote>\1')
34
+ .gsub('</blockquote><blockquote>', '<br>')
35
+ .gsub(/(>(?:\n|$)){2,}/) { |match| match[0...-2] }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ module SlackTransformer
2
+ class Slack
3
+ class Strikethrough
4
+ attr_reader :input
5
+
6
+ PATTERN = /
7
+ (?<=^|\W)
8
+
9
+ # preceded by start of line or non-word character
10
+
11
+ (?<![*~`])
12
+
13
+ # but not *, ~, or `, which are mrkdwn delimiters
14
+ # Note: _ is also a mrkdwn delimiter, but it's included in \W because
15
+ # it's a word character.
16
+
17
+ ([*_]?)
18
+
19
+ # optionally preceded by * or _
20
+
21
+ ~((?:[^~]|(?<=[\w\s])~+(?=\w))+)~
22
+
23
+ # one or more of either not ~, or one or more ~ preceded by word
24
+ # character or space and followed by word character, surrounded by ~
25
+
26
+ \1
27
+
28
+ # followed by * or _ if preceded by it
29
+
30
+ (?![*`])
31
+
32
+ # not followed by * or `
33
+
34
+ (?=\W|$)
35
+
36
+ # followed by non-word character or end of line
37
+ /x
38
+
39
+ def initialize(input)
40
+ @input = input
41
+ end
42
+
43
+ def to_html
44
+ input.gsub(PATTERN, '\1<s>\2</s>\1')
45
+ end
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slack_transformer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric Wang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.7.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.7.0
41
+ description:
42
+ email:
43
+ - eric@geteverwise.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/slack_transformer.rb
49
+ - lib/slack_transformer/date.rb
50
+ - lib/slack_transformer/entities.rb
51
+ - lib/slack_transformer/html.rb
52
+ - lib/slack_transformer/html/bold.rb
53
+ - lib/slack_transformer/html/code.rb
54
+ - lib/slack_transformer/html/italics.rb
55
+ - lib/slack_transformer/html/lists.rb
56
+ - lib/slack_transformer/html/preformatted.rb
57
+ - lib/slack_transformer/html/strikethrough.rb
58
+ - lib/slack_transformer/slack.rb
59
+ - lib/slack_transformer/slack/blockquote.rb
60
+ - lib/slack_transformer/slack/bold.rb
61
+ - lib/slack_transformer/slack/code.rb
62
+ - lib/slack_transformer/slack/italics.rb
63
+ - lib/slack_transformer/slack/preformatted.rb
64
+ - lib/slack_transformer/slack/quote.rb
65
+ - lib/slack_transformer/slack/strikethrough.rb
66
+ homepage: https://github.com/everwise/slack_transformer
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.4.5
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Transform HTML into Slack-ready mrkdwn and back
90
+ test_files: []