slack_transformer 1.0.0

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