slackert 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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +146 -0
- data/Rakefile +4 -0
- data/bin/bundle +114 -0
- data/bin/console +15 -0
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/lib/slackert.rb +30 -0
- data/lib/slackert/alerter.rb +65 -0
- data/lib/slackert/blocks.rb +129 -0
- data/lib/slackert/builder.rb +106 -0
- data/lib/slackert/level.rb +10 -0
- data/lib/slackert/templates.rb +149 -0
- data/lib/slackert/version.rb +5 -0
- data/screenshots/job-error.png +0 -0
- data/screenshots/job-executed.png +0 -0
- data/slackert.gemspec +22 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 69f9315335d1406edccde04e2ae402ef7f27b20a153593546415f9632c9b236a
|
|
4
|
+
data.tar.gz: abda8738fba7ea3a95b8b91742853893b9efde5e23394e105bec6ce02f8bff3e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: fdd1c3ef7813a16917768a2997bae3171eec7854cac41f14997a76022b511caf0efdf9ba233800f77292a8ca2ef66f0f90a19efc64a04689ee42a0dc2202a4fb
|
|
7
|
+
data.tar.gz: 3ea15b1ba76fac9a88c6e94656e03a266d27e9c651d2b8dfdcb8d95a9dfb481ceaa7f03ce772947369e432a1b9fd5bf56f75811ecfc62082c80c558237107569
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Braze
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Slackert
|
|
2
|
+
|
|
3
|
+
A simple way to create and send Slack messages through a webhook.
|
|
4
|
+
|
|
5
|
+
Especially useful for logging and alerting, supports three logging levels `Error, Info, Debug` and templates to simplify sending the same layout message with different content.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add Slacker to your Gemfile
|
|
10
|
+
|
|
11
|
+
gem 'slackert', git: git@github.com:braze-inc/braze-growth-shares-slackert.git
|
|
12
|
+
|
|
13
|
+
Then
|
|
14
|
+
|
|
15
|
+
$ bundle install
|
|
16
|
+
|
|
17
|
+
<!-- TODO: Once posted to rubygems -->
|
|
18
|
+
<!-- Or install it yourself as:
|
|
19
|
+
|
|
20
|
+
$ gem install slackert -->
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Slackert sends messages through an Incoming Webhook. To add a webhook to your channel, follow [this guide.](https://api.slack.com/messaging/webhooks)
|
|
25
|
+
|
|
26
|
+
Initialize alert client that will handle sending out your messages:
|
|
27
|
+
|
|
28
|
+
alerts = Slackert::Alerter.new(<WebhookURL>)
|
|
29
|
+
|
|
30
|
+
Create a message with a message builder:
|
|
31
|
+
|
|
32
|
+
message_builder = Slackert::MessageBuilder.new
|
|
33
|
+
message_builder.add_header('Message Title')
|
|
34
|
+
message_builder.add_plain_text('Description of the message.)
|
|
35
|
+
message = message_builder.build
|
|
36
|
+
|
|
37
|
+
And send it out:
|
|
38
|
+
|
|
39
|
+
alerts.info(message)
|
|
40
|
+
|
|
41
|
+
For more complex layouts, use `Blocks`.
|
|
42
|
+
|
|
43
|
+
### Message Building
|
|
44
|
+
|
|
45
|
+
We can also use a block initialization to simplify the process of creating a message
|
|
46
|
+
|
|
47
|
+
message = Slackert::MessageBuilder.build do |msg|
|
|
48
|
+
msg.add_header("Header)
|
|
49
|
+
msg.add_markdown_text("This is some *bold* text while _this_ is italics. :thumbsup:")
|
|
50
|
+
msg.add_divider
|
|
51
|
+
|
|
52
|
+
msg.notifiy_users(
|
|
53
|
+
['slackMemberID1', 'slackMemberID2'],
|
|
54
|
+
msg_prefix = 'Look at my message guys, '
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
Include any emojis, styling you want. Go wild!
|
|
58
|
+
|
|
59
|
+
### Blocks
|
|
60
|
+
|
|
61
|
+
Slack messages use blocks to group and style messages. Blocks allow to include pictures, buttons,
|
|
62
|
+
interactivity and more.
|
|
63
|
+
|
|
64
|
+
The blocks currently supported by slackert: `Divider, Header, Section`
|
|
65
|
+
|
|
66
|
+
You can add a divider or a header directly from the builder:
|
|
67
|
+
|
|
68
|
+
builder.add_header('Header Text')
|
|
69
|
+
|
|
70
|
+
builder.add_divider
|
|
71
|
+
|
|
72
|
+
In order to compose a section block, use `Slackert::Blocks::Section`
|
|
73
|
+
|
|
74
|
+
stats_section = Slackert::Blocks::Section.new
|
|
75
|
+
|
|
76
|
+
# Add section text - description like text, limited to only one per section
|
|
77
|
+
stats_section.add_section_text("The script executed with the following output:")
|
|
78
|
+
|
|
79
|
+
# Add field text - it is like a cell in an invisible 2-column grid on Desktop and 1-column grid one Mobile
|
|
80
|
+
stats_section.add_field_text("*Processed*:\n12345")
|
|
81
|
+
stats_section.add_field_text("*Failed Records*:\n3")
|
|
82
|
+
|
|
83
|
+
Then simply add it to the builder:
|
|
84
|
+
|
|
85
|
+
builder.add_section(stats_section)
|
|
86
|
+
|
|
87
|
+
We can also create a section straight from a hash of values, which simplifies creating sections for key: value type of data.
|
|
88
|
+
|
|
89
|
+
values = {
|
|
90
|
+
'Rows': 123,
|
|
91
|
+
'Tables': 2
|
|
92
|
+
}
|
|
93
|
+
section = Slackert::Blocks::Section.new_from_hash(values, bold_keys: true)
|
|
94
|
+
|
|
95
|
+
### Templates
|
|
96
|
+
|
|
97
|
+
Templates optimize the process of creating and sending alerts by providing layouts and an easy way to fill their content. As an example, to send a Slack message when a job finishes processing:
|
|
98
|
+
|
|
99
|
+
message = Slackert::Templates.job_finish(
|
|
100
|
+
title: "My Job Title",
|
|
101
|
+
desc: "The job is responsible for some real heavy work.",
|
|
102
|
+
result: "OK :thumbsup:",
|
|
103
|
+
stats: {
|
|
104
|
+
"Processed Rows": 10987,
|
|
105
|
+
"Updated Rows": 89,
|
|
106
|
+
"Deleted Rows": 0,
|
|
107
|
+
"Errors": 0
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
alerts.info(message)
|
|
112
|
+
|
|
113
|
+
#### Examples
|
|
114
|
+
|
|
115
|
+
#### Job Executed
|
|
116
|
+
<br>
|
|
117
|
+
<img src="./screenshots/job-executed.png" width="600" height="309">
|
|
118
|
+
<br>
|
|
119
|
+
|
|
120
|
+
#### Job Error
|
|
121
|
+
<br>
|
|
122
|
+
<img src="./screenshots/job-error.png" width="600" height="281">
|
|
123
|
+
<br>
|
|
124
|
+
|
|
125
|
+
### Logging Level
|
|
126
|
+
|
|
127
|
+
To change the logging/reporting level, simply set `Slackert.level`.
|
|
128
|
+
For example, to only report errors:
|
|
129
|
+
|
|
130
|
+
Slackert.level = Slackert::Level::ERROR
|
|
131
|
+
|
|
132
|
+
By default, slackert starts with level `INFO`
|
|
133
|
+
|
|
134
|
+
<!-- ## Development
|
|
135
|
+
|
|
136
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
137
|
+
|
|
138
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). -->
|
|
139
|
+
|
|
140
|
+
<!-- ## Contributing
|
|
141
|
+
|
|
142
|
+
Bug reports and pull requests are welcome on GitHub at # repo link -->
|
|
143
|
+
|
|
144
|
+
# License
|
|
145
|
+
|
|
146
|
+
MIT. See [LICENSE](LICENSE)
|
data/Rakefile
ADDED
data/bin/bundle
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "rubygems"
|
|
12
|
+
|
|
13
|
+
m = Module.new do
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def invoked_as_script?
|
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def env_var_version
|
|
21
|
+
ENV["BUNDLER_VERSION"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cli_arg_version
|
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
|
27
|
+
bundler_version = nil
|
|
28
|
+
update_index = nil
|
|
29
|
+
ARGV.each_with_index do |a, i|
|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
|
31
|
+
bundler_version = a
|
|
32
|
+
end
|
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
|
34
|
+
bundler_version = $1
|
|
35
|
+
update_index = i
|
|
36
|
+
end
|
|
37
|
+
bundler_version
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def gemfile
|
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
|
43
|
+
|
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lockfile
|
|
48
|
+
lockfile =
|
|
49
|
+
case File.basename(gemfile)
|
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
|
51
|
+
else "#{gemfile}.lock"
|
|
52
|
+
end
|
|
53
|
+
File.expand_path(lockfile)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def lockfile_version
|
|
57
|
+
return unless File.file?(lockfile)
|
|
58
|
+
lockfile_contents = File.read(lockfile)
|
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
|
60
|
+
Regexp.last_match(1)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def bundler_version
|
|
64
|
+
@bundler_version ||=
|
|
65
|
+
env_var_version || cli_arg_version ||
|
|
66
|
+
lockfile_version
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bundler_requirement
|
|
70
|
+
return "#{Gem::Requirement.default}.a" unless bundler_version
|
|
71
|
+
|
|
72
|
+
bundler_gem_version = Gem::Version.new(bundler_version)
|
|
73
|
+
|
|
74
|
+
requirement = bundler_gem_version.approximate_recommendation
|
|
75
|
+
|
|
76
|
+
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
|
|
77
|
+
|
|
78
|
+
requirement += ".a" if bundler_gem_version.prerelease?
|
|
79
|
+
|
|
80
|
+
requirement
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def load_bundler!
|
|
84
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
|
85
|
+
|
|
86
|
+
activate_bundler
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def activate_bundler
|
|
90
|
+
gem_error = activation_error_handling do
|
|
91
|
+
gem "bundler", bundler_requirement
|
|
92
|
+
end
|
|
93
|
+
return if gem_error.nil?
|
|
94
|
+
require_error = activation_error_handling do
|
|
95
|
+
require "bundler/version"
|
|
96
|
+
end
|
|
97
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
|
98
|
+
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
|
99
|
+
exit 42
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def activation_error_handling
|
|
103
|
+
yield
|
|
104
|
+
nil
|
|
105
|
+
rescue StandardError, LoadError => e
|
|
106
|
+
e
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
m.load_bundler!
|
|
111
|
+
|
|
112
|
+
if m.invoked_as_script?
|
|
113
|
+
load Gem.bin_path("bundler", "bundle")
|
|
114
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "slackert"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require "irb"
|
|
15
|
+
IRB.start(__FILE__)
|
data/bin/htmldiff
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("diff-lcs", "htmldiff")
|
data/bin/ldiff
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'ldiff' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("diff-lcs", "ldiff")
|
data/bin/rake
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/setup
ADDED
data/lib/slackert.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'slackert/alerter'
|
|
2
|
+
require 'slackert/level'
|
|
3
|
+
require 'slackert/builder'
|
|
4
|
+
require 'slackert/blocks'
|
|
5
|
+
require 'slackert/templates'
|
|
6
|
+
|
|
7
|
+
# Namespace for classes and modules that handle creation and delivery of Slack messages and alerts
|
|
8
|
+
module Slackert
|
|
9
|
+
@level = Level::INFO
|
|
10
|
+
|
|
11
|
+
# Sets logging level for messages. Logging level constants are defined in {Slackert::Level}
|
|
12
|
+
#
|
|
13
|
+
# @param level [Number] logging level
|
|
14
|
+
# @raise [ArgumentError] if the logging level is out of bounds
|
|
15
|
+
#
|
|
16
|
+
def self.level=(value)
|
|
17
|
+
log_values = Level.constants.map { |const| Level.const_get(const) }
|
|
18
|
+
min, max = log_values.minmax
|
|
19
|
+
raise ArgumentError, 'Invalid logging level' if value < min || value > max
|
|
20
|
+
|
|
21
|
+
@level = value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Return current logging level
|
|
25
|
+
# @return [Number] current level
|
|
26
|
+
#
|
|
27
|
+
def self.level
|
|
28
|
+
@level
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'net/https'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Slackert
|
|
8
|
+
# Slack client responsible for sending composed Slack messages
|
|
9
|
+
# @param [String] Slack incoming webhook URL to a particular channel
|
|
10
|
+
#
|
|
11
|
+
class Alerter
|
|
12
|
+
def initialize(webhook_url)
|
|
13
|
+
@uri = URI.parse(webhook_url)
|
|
14
|
+
@https = configure_https
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Sends a debug Slack message if logging level is set at Level::DEBUG
|
|
18
|
+
# @param [Hash] Slack message
|
|
19
|
+
#
|
|
20
|
+
def debug(content)
|
|
21
|
+
return if Slackert.level < Level::DEBUG
|
|
22
|
+
|
|
23
|
+
post_to_slack(content)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Sends an info Slack message if loggin level is set at Level::INFO or lower
|
|
27
|
+
# @param [Hash] Slack message
|
|
28
|
+
#
|
|
29
|
+
def info(content)
|
|
30
|
+
return if Slackert.level < Level::INFO
|
|
31
|
+
|
|
32
|
+
post_to_slack(content)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Sends an error Slack message
|
|
36
|
+
# @param [Hash] Slack message
|
|
37
|
+
#
|
|
38
|
+
def error(content)
|
|
39
|
+
post_to_slack(content)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def configure_https
|
|
45
|
+
https = Net::HTTP.new(@uri.host, @uri.port)
|
|
46
|
+
https.use_ssl = true
|
|
47
|
+
https
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def post_to_slack(content)
|
|
51
|
+
raise 'Message content cannot be empty.' if content.empty?
|
|
52
|
+
|
|
53
|
+
req = base_post_req
|
|
54
|
+
req.body = content.to_json
|
|
55
|
+
res = @https.request(req)
|
|
56
|
+
puts "Message sending unsuccesful (Code: #{res.code}, Message: #{res.message})" if res.code != '200'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def base_post_req
|
|
60
|
+
req = Net::HTTP::Post.new(@uri)
|
|
61
|
+
req['Content-type'] = 'application/json'
|
|
62
|
+
req
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slackert
|
|
4
|
+
# Block elements that a Slack message can be composed of.
|
|
5
|
+
module Blocks
|
|
6
|
+
# Abstract BlockElement.
|
|
7
|
+
# Subclass and override +to_slack+ to add a new block element.
|
|
8
|
+
class BlockElement
|
|
9
|
+
def initialize(type)
|
|
10
|
+
@type = type
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns a hash of the element, ready to be added to the block formatted message.
|
|
14
|
+
# Abstract method that needs to be implemented in each subclassed element.
|
|
15
|
+
#
|
|
16
|
+
def to_slack
|
|
17
|
+
raise NoMethodError, 'Override this implementation'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Header block element provides a title, rendered as a larger bold text on top of the message.
|
|
22
|
+
class Header < BlockElement
|
|
23
|
+
# @param text [String] header text
|
|
24
|
+
def initialize(text)
|
|
25
|
+
super('header')
|
|
26
|
+
@text = text
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# See {BlockElement#to_slack}
|
|
30
|
+
def to_slack
|
|
31
|
+
{
|
|
32
|
+
'type': @type,
|
|
33
|
+
'text': {
|
|
34
|
+
'type': 'plain_text',
|
|
35
|
+
'text': @text
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Divider block element provides a horizontal separator, simlarly to HTML's +<hr>+
|
|
42
|
+
class Divider < BlockElement
|
|
43
|
+
def initialize
|
|
44
|
+
super('divider')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# See {BlockElement#to_slack}
|
|
48
|
+
def to_slack
|
|
49
|
+
{
|
|
50
|
+
'type': @type
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Section block element is a very flexible layout block that can serve as a simple text block but it also allows
|
|
56
|
+
# for adding block elements such as field text, buttons, images and more. Currently only section text and field
|
|
57
|
+
# texts are supported.
|
|
58
|
+
#
|
|
59
|
+
# To learn more, visit https://api.slack.com/reference/block-kit/blocks#section
|
|
60
|
+
#
|
|
61
|
+
class Section < BlockElement
|
|
62
|
+
def initialize
|
|
63
|
+
@text = {}
|
|
64
|
+
@fields = []
|
|
65
|
+
super('section')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Initialize field text objects from a hash.
|
|
69
|
+
# @param [Hash] key, value pairs that each will become a field text object
|
|
70
|
+
# @param line_break [Boolean] add a line break after each key so values render right under the key
|
|
71
|
+
# instead of next to it
|
|
72
|
+
# @param bold_keys [Boolean] apply bold text formatting to the keys
|
|
73
|
+
# @return [Section]
|
|
74
|
+
def self.new_from_hash(values, line_break: true, bold_keys: true)
|
|
75
|
+
s = new
|
|
76
|
+
|
|
77
|
+
values.each do |key, value|
|
|
78
|
+
title = bold_keys ? "*#{key}*" : key
|
|
79
|
+
title = line_break ? "#{title}\n" : title
|
|
80
|
+
s.add_field_text("#{title}#{value}")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
s
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Adds a text object on top of the section. There can only be one section text added. Adding more will replace
|
|
87
|
+
# the previously added section text. It is limited to 3000 characters.
|
|
88
|
+
# @param message [String] section text message
|
|
89
|
+
# @param type [String] can be either mrkdwn or plain_text
|
|
90
|
+
#
|
|
91
|
+
def add_section_text(message, type = 'mrkdwn')
|
|
92
|
+
@text = {
|
|
93
|
+
'type': type,
|
|
94
|
+
'text': message
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Adds a field text object to the message. Field texts render in two columns on desktop and are added left
|
|
99
|
+
# to right. They show as one column on mobile.
|
|
100
|
+
# There can only be 10 fields added in total and each text item has a limit of 2000 characters.
|
|
101
|
+
# @param message [String] field text message
|
|
102
|
+
# @param type [String] can be either mrkdwn or plain_text
|
|
103
|
+
# @raise [RuntimeError] if maximum capacity of 10 field objects has been reached
|
|
104
|
+
#
|
|
105
|
+
def add_field_text(message, type = 'mrkdwn')
|
|
106
|
+
raise 'Maximum field text objects has been reached.' if @fields.length == 10
|
|
107
|
+
|
|
108
|
+
@fields.push(
|
|
109
|
+
{
|
|
110
|
+
'type': type,
|
|
111
|
+
'text': message
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# See {BlockElement#to_slack}
|
|
117
|
+
def to_slack
|
|
118
|
+
if @text.empty? && @fields.empty?
|
|
119
|
+
raise 'Either section text or field text needs to be filled in order to compose the section.'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
section = { 'type': @type }
|
|
123
|
+
section['text'] = @text unless @text.empty?
|
|
124
|
+
section['fields'] = @fields unless @fields.empty?
|
|
125
|
+
section
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'slackert/blocks'
|
|
4
|
+
|
|
5
|
+
module Slackert
|
|
6
|
+
# Builder class that allows to build a Slack message piece by piece.
|
|
7
|
+
# After the message has been constructed, it is built with ++builder.build++ method and can be passed to Alerter
|
|
8
|
+
# class for sending.
|
|
9
|
+
#
|
|
10
|
+
# Supports blocks initialization that returns a ready alert/message
|
|
11
|
+
#
|
|
12
|
+
# msg = Slackert::MessageBuilder.build do |b|
|
|
13
|
+
# b.add_header('header')
|
|
14
|
+
# ...
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
class MessageBuilder
|
|
18
|
+
# Block initialization
|
|
19
|
+
# @return [Hash] built message
|
|
20
|
+
#
|
|
21
|
+
def self.build
|
|
22
|
+
builder = new
|
|
23
|
+
yield(builder)
|
|
24
|
+
builder.build
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@content = []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Add a header to the message. It formats as a large title on top of the message.
|
|
32
|
+
# @param text [String] header text, plain text only
|
|
33
|
+
#
|
|
34
|
+
def add_header(text)
|
|
35
|
+
return if text.empty?
|
|
36
|
+
|
|
37
|
+
@content.push(Blocks::Header.new(text).to_slack)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Adds a horizontal divider.
|
|
41
|
+
#
|
|
42
|
+
def add_divider
|
|
43
|
+
@content.push(Blocks::Divider.new.to_slack)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Adds markdown text to the message.
|
|
47
|
+
# @param text [String] markdown text
|
|
48
|
+
#
|
|
49
|
+
def add_markdown_text(text)
|
|
50
|
+
return if text.empty?
|
|
51
|
+
|
|
52
|
+
mkd_section = Blocks::Section.new
|
|
53
|
+
mkd_section.add_field_text(text)
|
|
54
|
+
add_section(mkd_section)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Adds plain text to the message.
|
|
58
|
+
# @param text [String] plain text
|
|
59
|
+
#
|
|
60
|
+
def add_plain_text(text)
|
|
61
|
+
return if text.empty?
|
|
62
|
+
|
|
63
|
+
text_section = Blocks::Section.new
|
|
64
|
+
text_section.add_section_text(text)
|
|
65
|
+
add_section(text_section)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds Slack user notifications.
|
|
69
|
+
# @param user_ids [Array<String>] Slack member IDs
|
|
70
|
+
# @param msg_prefix [String] message added inline before the notifications
|
|
71
|
+
# @param delim [String] delimiter to separate Slack users
|
|
72
|
+
#
|
|
73
|
+
def notify_users(user_ids, msg_prefix = 'Please look into this: ', delim = ' | ')
|
|
74
|
+
return if user_ids.empty?
|
|
75
|
+
|
|
76
|
+
tag_section = Blocks::Section.new
|
|
77
|
+
user_notifs = user_ids.map { |user_id| "<@#{user_id}>" }
|
|
78
|
+
notifs_msg = user_notifs.join(delim)
|
|
79
|
+
tag_section.add_section_text(msg_prefix + notifs_msg)
|
|
80
|
+
add_section(tag_section)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Adds a {Blocks::Section} block element to the message.
|
|
84
|
+
# @param section [Blocks::Section] section block
|
|
85
|
+
#
|
|
86
|
+
def add_section(section)
|
|
87
|
+
@content.push(section.to_slack)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Inserts a {Blocks::Section} block element on top of the message.
|
|
91
|
+
# @param section [Blocks::Section] section block
|
|
92
|
+
#
|
|
93
|
+
def prepend_section(section)
|
|
94
|
+
@content.unshift(section.to_slack)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Builds a message. Creates a hash object from the added fields in the builder.
|
|
98
|
+
# @return [Hash] Slack message
|
|
99
|
+
#
|
|
100
|
+
def build
|
|
101
|
+
{
|
|
102
|
+
'blocks': @content
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'slackert/blocks'
|
|
4
|
+
|
|
5
|
+
module Slackert
|
|
6
|
+
# Pre-defined Slack message templates that you can use for a quick message layout
|
|
7
|
+
# instead of building one from scratch. Most of the templates are customizable and you can
|
|
8
|
+
# include or exclude fields like title, description or extra data that will appear in the message.
|
|
9
|
+
#
|
|
10
|
+
# Example Usage
|
|
11
|
+
#
|
|
12
|
+
# message = Slackert::Templates.job_start(
|
|
13
|
+
# title: 'Job Title',
|
|
14
|
+
# desc: 'This job does this and that',
|
|
15
|
+
# overview: {
|
|
16
|
+
# 'Job Type': 'Refresh',
|
|
17
|
+
# 'Action': 'Update',
|
|
18
|
+
# 'Start Time': Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
|
19
|
+
# }
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# alerts = Slackert::Alerter.new('mywebhook')
|
|
23
|
+
# alerts.info(message)
|
|
24
|
+
#
|
|
25
|
+
module Templates
|
|
26
|
+
# Message layout for a quick notification.
|
|
27
|
+
#
|
|
28
|
+
# @param title [String] optional title
|
|
29
|
+
# @param text [String] notification text, accepts markdown
|
|
30
|
+
#
|
|
31
|
+
# @return [Hash] Slack message
|
|
32
|
+
#
|
|
33
|
+
def self.notification(text:, title: '')
|
|
34
|
+
MessageBuilder.build do |alert|
|
|
35
|
+
alert.add_header(title) unless title.empty?
|
|
36
|
+
alert.add_markdown_text(text)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Message layout to notify of a job start.
|
|
41
|
+
#
|
|
42
|
+
# @param title [String] title of the message, plain text only
|
|
43
|
+
# @param desc [String] description of the message, accepts markdown
|
|
44
|
+
# @param overview [Hash] extra identifiable key: value section fields to be included in the message,
|
|
45
|
+
# both keys and values accept markdown and keys are bold by default
|
|
46
|
+
# @return [Hash] Slack message
|
|
47
|
+
#
|
|
48
|
+
def self.job_start(
|
|
49
|
+
title: '',
|
|
50
|
+
desc: '',
|
|
51
|
+
overview: {}
|
|
52
|
+
)
|
|
53
|
+
MessageBuilder.build do |alert|
|
|
54
|
+
alert.add_header(title) unless title.empty?
|
|
55
|
+
alert.add_plain_text(desc) unless desc.empty?
|
|
56
|
+
add_section_from_hash(alert, overview) unless overview.empty?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Message layout to notify of a job finish.
|
|
61
|
+
#
|
|
62
|
+
# @param title [String] title of the message, plain text only
|
|
63
|
+
# @param desc [String] description of the message, accepts markdown
|
|
64
|
+
# @param result [String] result of the job, accepts markdown
|
|
65
|
+
# @param stats [Hash] extra execution key: value section fields to be included in the message,
|
|
66
|
+
# both keys and values accept markdown and keys are bold by default
|
|
67
|
+
# @return [Hash] Slack message
|
|
68
|
+
#
|
|
69
|
+
def self.job_finish(
|
|
70
|
+
title: '',
|
|
71
|
+
desc: '',
|
|
72
|
+
result: '',
|
|
73
|
+
stats: {}
|
|
74
|
+
)
|
|
75
|
+
MessageBuilder.build do |alert|
|
|
76
|
+
alert.add_header(title)
|
|
77
|
+
alert.add_plain_text(desc)
|
|
78
|
+
alert.add_markdown_text("*Result*: #{result}")
|
|
79
|
+
add_section_from_hash(alert, stats) unless stats.empty?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Combines both job start and job finish into a single message. Best for quick jobs where a separate start
|
|
84
|
+
# and finish alerts are unnecessary.
|
|
85
|
+
#
|
|
86
|
+
# @param title [String] title of the message, plain text only
|
|
87
|
+
# @param desc [String] description of the message, accepts markdown
|
|
88
|
+
# @param result [String] result of the job, accepts markdown
|
|
89
|
+
# @param overview [Hash] a section of key: value fields, both keys and values accept markdown
|
|
90
|
+
# and keys are bold by default
|
|
91
|
+
# @param stats [Hash] a separate section of key: value fields, both keys and values accept markdown
|
|
92
|
+
# and keys are bold by default
|
|
93
|
+
# @return [Hash] Slack message
|
|
94
|
+
#
|
|
95
|
+
def self.job_executed(
|
|
96
|
+
title: '',
|
|
97
|
+
desc: '',
|
|
98
|
+
result: '',
|
|
99
|
+
overview: {},
|
|
100
|
+
stats: {}
|
|
101
|
+
)
|
|
102
|
+
MessageBuilder.build do |alert|
|
|
103
|
+
alert.add_header(title) unless title.empty?
|
|
104
|
+
alert.add_plain_text(desc) unless desc.empty?
|
|
105
|
+
alert.add_markdown_text("*Result*: #{result}") unless result.empty?
|
|
106
|
+
add_section_from_hash(alert, overview) unless overview.empty?
|
|
107
|
+
add_section_from_hash(alert, stats) unless stats.empty?
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Message on job error that notifies specified users by tagging them in the alert.
|
|
112
|
+
#
|
|
113
|
+
# @param title [String] title of the job/alert, plain text only
|
|
114
|
+
# @param error [String] error message
|
|
115
|
+
# @param notify_user_ids Array<String> Slack member IDs that will be tagged and notified
|
|
116
|
+
# @param extra [Hash] additional section of key: value fields, both accept markdown and keys are bold by default
|
|
117
|
+
# @param add_alert_emoji [Boolean] adds rotating light emoji in the title
|
|
118
|
+
#
|
|
119
|
+
# @return [Hash] Slack message
|
|
120
|
+
#
|
|
121
|
+
def self.job_error(
|
|
122
|
+
title:,
|
|
123
|
+
error:,
|
|
124
|
+
notify_user_ids: [],
|
|
125
|
+
extra: {},
|
|
126
|
+
add_alert_emoji: true
|
|
127
|
+
)
|
|
128
|
+
MessageBuilder.build do |alert|
|
|
129
|
+
header = "Error while processing #{title}"
|
|
130
|
+
header = add_alert_emoji ? ":rotating_light: #{header}" : header
|
|
131
|
+
|
|
132
|
+
alert.add_header(header)
|
|
133
|
+
alert.notify_users(notify_user_ids) unless notify_user_ids.empty?
|
|
134
|
+
alert.add_divider
|
|
135
|
+
alert.add_markdown_text('*Result*: Fail')
|
|
136
|
+
alert.add_markdown_text("*Error Output*:\n```#{error}```")
|
|
137
|
+
add_section_from_hash(alert, extra) unless extra.empty?
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.add_section_from_hash(alert, values)
|
|
142
|
+
alert.add_divider
|
|
143
|
+
section = Blocks::Section.new_from_hash(values, bold_keys: true)
|
|
144
|
+
alert.add_section(section)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private_class_method :add_section_from_hash
|
|
148
|
+
end
|
|
149
|
+
end
|
|
Binary file
|
|
Binary file
|
data/slackert.gemspec
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/slackert/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'slackert'
|
|
7
|
+
spec.version = Slackert::VERSION
|
|
8
|
+
spec.authors = ['Maciej Olko']
|
|
9
|
+
spec.summary = 'Quick and simple way to send message through Slack webhook.'
|
|
10
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
|
11
|
+
spec.homepage = 'https://github.com/braze-inc/braze-growth-shares-slackert'
|
|
12
|
+
spec.license = 'MIT'
|
|
13
|
+
|
|
14
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
15
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
16
|
+
end
|
|
17
|
+
spec.bindir = 'exe'
|
|
18
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency 'rspec', '~> 3.10.0'
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: slackert
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Maciej Olko
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-02-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.10.0
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 3.10.0
|
|
27
|
+
description:
|
|
28
|
+
email:
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- ".gitignore"
|
|
34
|
+
- Gemfile
|
|
35
|
+
- LICENSE
|
|
36
|
+
- README.md
|
|
37
|
+
- Rakefile
|
|
38
|
+
- bin/bundle
|
|
39
|
+
- bin/console
|
|
40
|
+
- bin/htmldiff
|
|
41
|
+
- bin/ldiff
|
|
42
|
+
- bin/rake
|
|
43
|
+
- bin/rspec
|
|
44
|
+
- bin/setup
|
|
45
|
+
- lib/slackert.rb
|
|
46
|
+
- lib/slackert/alerter.rb
|
|
47
|
+
- lib/slackert/blocks.rb
|
|
48
|
+
- lib/slackert/builder.rb
|
|
49
|
+
- lib/slackert/level.rb
|
|
50
|
+
- lib/slackert/templates.rb
|
|
51
|
+
- lib/slackert/version.rb
|
|
52
|
+
- screenshots/job-error.png
|
|
53
|
+
- screenshots/job-executed.png
|
|
54
|
+
- slackert.gemspec
|
|
55
|
+
homepage: https://github.com/braze-inc/braze-growth-shares-slackert
|
|
56
|
+
licenses:
|
|
57
|
+
- MIT
|
|
58
|
+
metadata: {}
|
|
59
|
+
post_install_message:
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 2.4.0
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.0.9
|
|
75
|
+
signing_key:
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: Quick and simple way to send message through Slack webhook.
|
|
78
|
+
test_files: []
|