slack_message 1.7.1 → 2.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 +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +1 -1
- data/README.md +134 -74
- data/lib/slack_message/api.rb +42 -17
- data/lib/slack_message/configuration.rb +12 -20
- data/lib/slack_message/dsl.rb +34 -7
- data/lib/slack_message/rspec.rb +241 -0
- data/lib/slack_message.rb +6 -10
- data/slack_message.gemspec +1 -1
- data/spec/slack_message_spec.rb +89 -20
- data/spec/spec_helper.rb +3 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e92b2faa8e8b8118a0e168cef488d64b94255c841d774909647ede3d9a9faf6b
|
4
|
+
data.tar.gz: d494af801908aa9f5cf25141b012df26e23a69f8ef9389c3c8bb580c46641133
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fceee6aeb36f7fc6b855c1ce90f647b67ce480c78e62ec2fe45bbfc347e6c9efa6ca910804c51b0edb12321953d96014ac137572d2140328109444f68512ca4
|
7
|
+
data.tar.gz: a9fe4697d85ac2f91197a39dacc46d1740c251f8472dcb91e8ba2132caac5c9f029cb5f88894f39542e1436ce554562d87108661863c2574655e64c866c5115e
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.1.0] - 2021-11-01
|
6
|
+
- Change to use Slack Apps for all profiles. This should allow growth toward
|
7
|
+
updating messages, working with interactive messages etc.
|
8
|
+
- As a result, allow custom icons per profile / message.
|
9
|
+
- When sending a message, the first `text` block is used for the notification
|
10
|
+
content. Should resolve "this content cannot be displayed".
|
11
|
+
- Significant restructuring of README.
|
12
|
+
|
13
|
+
## [2.0.0] - 2021-11-01
|
14
|
+
- Yeah that was all broken.
|
15
|
+
|
16
|
+
## [1.9.0] - 2021-10-27
|
17
|
+
- Add many validations so that trying to add e.g. empty text won't succeed.
|
18
|
+
Previously that would be accepted but return `invalid_blocks` from the API.
|
19
|
+
|
20
|
+
## [1.8.1] - 2021-10-08
|
21
|
+
- Cleaned that rspec code a bit, added more matchers for real world use.
|
22
|
+
|
23
|
+
## [1.8.0] - 2021-10-07
|
24
|
+
- Added the ability to test in RSpec.
|
25
|
+
|
5
26
|
## [1.7.1] - 2021-10-06
|
6
27
|
- Fixed literally a syntax issue.
|
7
28
|
- Fixed specs.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,8 +3,7 @@ SlackMessage: a Friendly DSL for Slack
|
|
3
3
|
|
4
4
|
SlackMessage is a wrapper over the [Block Kit
|
5
5
|
API](https://app.slack.com/block-kit-builder/) to make it easy to read and
|
6
|
-
write messages to slack in your ruby application.
|
7
|
-
is built to be opinionated to keep your configuration needs low.
|
6
|
+
write messages to slack in your ruby application.
|
8
7
|
|
9
8
|
Posting a message to Slack should be this easy:
|
10
9
|
|
@@ -16,80 +15,105 @@ end
|
|
16
15
|
|
17
16
|
To install, just add `slack_message` to your bundle and you're ready to go.
|
18
17
|
|
18
|
+
Opinionated Stances
|
19
|
+
------------
|
20
|
+
|
21
|
+
Slack's API has a lot of options available to you! But this gem takes some
|
22
|
+
opinionated stances on how to make use of that API. For instance:
|
23
|
+
|
24
|
+
* No dependencies. Your lockfile is enough of a mess already.
|
25
|
+
* Webhooks are passé. Only Slack Apps are supported now.
|
26
|
+
* Unless you request otherwise, text is always rendered using `mrkdwn`. If you
|
27
|
+
want plaintext, you'll need to ask for it. Same for the `emoji` flag.
|
28
|
+
* As many API semantics as possible are hidden. For instance, if you post to
|
29
|
+
something that looks like an email address, `slack_message` is going to try to
|
30
|
+
look it up as an email address.
|
31
|
+
* A few little hacks on the block syntax, such as adding a `blank_line` (which
|
32
|
+
doesn't exist in the API), or leading spaces.
|
33
|
+
* Configuration should be as simple as possible. But as much work as possible
|
34
|
+
should be moved from callers into configuration.
|
19
35
|
|
20
36
|
Usage
|
21
37
|
------------
|
22
38
|
|
23
39
|
### Configuration
|
24
40
|
|
25
|
-
To get started, you'll need to
|
26
|
-
to
|
27
|
-
|
41
|
+
To get started, you'll need to create a Slack App with some appropriate
|
42
|
+
permissions. It used to be possible to use the Webhook API, but that's long
|
43
|
+
since been deprecated, and apps are pretty [straightforward to
|
44
|
+
create](https://api.slack.com/tutorials/tracks/getting-a-token).
|
45
|
+
|
46
|
+
Generally, make sure your token has permissions for `users:read` and `chat:write`.
|
28
47
|
|
29
48
|
```ruby
|
30
49
|
SlackMessage.configure do |config|
|
31
|
-
|
50
|
+
api_token = 'xoxb-11111111111-2222222222-33333333333333333'
|
32
51
|
|
33
|
-
config.add_profile(
|
52
|
+
config.add_profile(api_token: api_token)
|
34
53
|
end
|
35
54
|
```
|
36
55
|
|
37
|
-
You should
|
38
|
-
|
39
|
-
`config/initializers/slack_message.rb`.
|
56
|
+
You should keep your token in a safe place like `ENV`. If using this gem with
|
57
|
+
Rails, place this code in somewhere like `config/initializers/slack_message.rb`.
|
40
58
|
|
41
59
|
#### Additional Profiles
|
42
60
|
|
43
|
-
If
|
44
|
-
|
45
|
-
|
61
|
+
If your app uses slack messages for several different purposes, it's common to
|
62
|
+
want to post to different channels as different names / icons / etc. To do that
|
63
|
+
more easily and consistently, you can specify multiple profiles:
|
46
64
|
|
47
65
|
```ruby
|
48
66
|
SlackMessage.configure do |config|
|
67
|
+
api_token = 'xoxb-11111111111-2222222222-33333333333333333'
|
68
|
+
|
49
69
|
# default profile
|
50
|
-
config.add_profile(name: 'Slack Notifier'
|
70
|
+
config.add_profile(api_token: api_token, name: 'Slack Notifier')
|
51
71
|
|
52
72
|
# additional profiles (see below for usage)
|
53
|
-
config.add_profile(:prod_alert_bot,
|
54
|
-
|
73
|
+
config.add_profile(:prod_alert_bot,
|
74
|
+
name: 'Prod Alert Bot'
|
75
|
+
icon: ':mooseandsquirrel:'
|
76
|
+
)
|
77
|
+
config.add_profile(:sidekiq_bot,
|
78
|
+
api_token: ENV.fetch('SIDEKIQ_SLACK_APP_API_TOKEN'),
|
79
|
+
name: 'Sidekiq Bot',
|
80
|
+
)
|
55
81
|
end
|
56
82
|
```
|
57
83
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
84
|
+
A number of parameters are available to make it simpler to use a profile without
|
85
|
+
specifying repetitive information. Most all have corresponding options when
|
86
|
+
composing a message:
|
87
|
+
|
88
|
+
| Config | Default | Value |
|
89
|
+
|-----------------|-----------------|-----------------------------------------------------------------|
|
90
|
+
| api_token | None | Your Slack App API Key. |
|
91
|
+
| name | From Slack App | The bot name for your message. |
|
92
|
+
| icon | From Slack App | Profile icon for your message. Specify as :emoji: or image URL. |
|
93
|
+
| default_channel | None (optional) | Channel / user to post to by default. |
|
94
|
+
|
95
|
+
|
96
|
+
Setting a `default_channel` specifically will allow you to use `post_as`, which
|
97
|
+
is a convenient shortcut for bots that repeatedly post to one channel as a
|
98
|
+
consistent identity:
|
62
99
|
|
63
100
|
```ruby
|
64
101
|
SlackMessage.configure do |config|
|
65
|
-
config.add_profile(:
|
66
|
-
|
67
|
-
|
102
|
+
config.add_profile(:red_alert_bot,
|
103
|
+
api_token: ENV.fetch('SLACK_API_TOKEN'),
|
104
|
+
name: 'Red Alerts',
|
105
|
+
icon: ':klaxon:',
|
68
106
|
default_channel: '#red_alerts'
|
69
107
|
)
|
70
108
|
end
|
71
109
|
|
72
|
-
SlackMessage.post_as(:
|
110
|
+
SlackMessage.post_as(:red_alert_bot) do
|
73
111
|
text ":ambulance: weeooo weeooo something went wrong"
|
74
112
|
end
|
75
113
|
```
|
76
114
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
#### Configuring User Search
|
81
|
-
|
82
|
-
Slack's API no longer allows you to send DMs to users by username. You need to
|
83
|
-
look up a user's internal ID and send to that ID. Thankfully, there is a lookup
|
84
|
-
by email endpoint for this. If you'd like to post messages to users by their
|
85
|
-
email address, you'll need a
|
86
|
-
[separate API Token](https://api.slack.com/tutorials/tracks/getting-a-token):
|
87
|
-
|
88
|
-
```ruby
|
89
|
-
SlackMessage.configure do |config|
|
90
|
-
config.api_token = 'xoxb-11111111111-2222222222-33333333333333333'
|
91
|
-
end
|
92
|
-
```
|
115
|
+
There's no reason you can't use the same API key for several profiles. Profiles
|
116
|
+
are most useful to create consistent name / icon setups for apps with many bots.
|
93
117
|
|
94
118
|
### Posting Messages
|
95
119
|
|
@@ -104,14 +128,12 @@ end
|
|
104
128
|
That's it! SlackMessage will automatically serialize for the API like this:
|
105
129
|
|
106
130
|
```json
|
107
|
-
[{"type":"section","text":{"type":"mrkdwn","text":"We did it! :thumbsup:"}}]
|
131
|
+
[{"type":"section","text":{"type":"mrkdwn","text":"We did it @here! :thumbsup:"}}]
|
108
132
|
```
|
109
133
|
|
110
134
|
Details like remembering that Slack made a mystifying decision to force you to
|
111
135
|
request "mrkdwn", or requiring your text to be wrapped into a section are handled
|
112
|
-
for you.
|
113
|
-
|
114
|
-
Building up messages is meant to be as user-friendly as possible:
|
136
|
+
for you. Building up messages is meant to be as user-friendly as possible:
|
115
137
|
|
116
138
|
```ruby
|
117
139
|
SlackMessage.build do
|
@@ -147,20 +169,21 @@ automatically:
|
|
147
169
|
]
|
148
170
|
```
|
149
171
|
|
150
|
-
|
151
|
-
|
172
|
+
It's just as easy to send messages directly to users. SlackMessage will look for
|
173
|
+
targets that are email-addressish, and look them up for you automatically:
|
152
174
|
|
153
175
|
```ruby
|
154
|
-
|
155
|
-
|
176
|
+
user_email = 'hello@joemastey.com'
|
177
|
+
|
178
|
+
SlackMessage.post_to(user_email) do
|
179
|
+
text "You specifically did it! :thumbsup:"
|
156
180
|
end
|
157
181
|
```
|
158
182
|
|
159
183
|
SlackMessage is able to build all kinds of rich messages for you, and has been
|
160
184
|
a real joy to use for the author at least. To understand a bit more about the
|
161
|
-
possibilities of blocks,
|
162
|
-
Builder](https://app.slack.com/block-kit-builder/)
|
163
|
-
better. There are lots of options:
|
185
|
+
possibilities of blocks, you should play around with Slack's [Block Kit
|
186
|
+
Builder](https://app.slack.com/block-kit-builder/). There are lots of options:
|
164
187
|
|
165
188
|
```ruby
|
166
189
|
SlackMessage.post_to('#general') do
|
@@ -195,38 +218,75 @@ For now you'll need to read a bit of the source code to get the entire API. Sorr
|
|
195
218
|
working on it.
|
196
219
|
|
197
220
|
If you've defined multiple profiles in configuration, you can specify which to
|
198
|
-
use for your message by specifying
|
221
|
+
use for your message by specifying its name:
|
199
222
|
|
200
223
|
```ruby
|
201
224
|
SlackMessage.post_to('#general', as: :sidekiq_bot) do
|
202
225
|
text ":octagonal_sign: A job has failed permanently and needs to be rescued."
|
203
|
-
link_button "Sidekiq Dashboard",
|
226
|
+
link_button "Sidekiq Dashboard", sidekiq_dashboard_url, style: :danger
|
204
227
|
end
|
205
228
|
```
|
206
229
|
|
207
|
-
You can also
|
230
|
+
You can also override profile bot details when sending a message:
|
208
231
|
|
209
232
|
```ruby
|
210
233
|
SlackMessage.post_to('#general') do
|
211
234
|
bot_name "CoffeeBot"
|
235
|
+
bot_icon ":coffee:"
|
212
236
|
|
213
237
|
text ":coffee::clock: Time to take a break!"
|
214
238
|
end
|
215
239
|
```
|
216
240
|
|
217
|
-
|
218
|
-
------------
|
241
|
+
### Testing
|
219
242
|
|
220
|
-
|
221
|
-
|
243
|
+
You can do some basic testing against SlackMessage, at least if you use RSpec!
|
244
|
+
You'll need to require and include the testing behavior like this, in your
|
245
|
+
spec_helper file:
|
222
246
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
247
|
+
```ruby
|
248
|
+
require 'slack_message/rspec'
|
249
|
+
|
250
|
+
RSpec.configure do |config|
|
251
|
+
include SlackMessage::RSpec
|
252
|
+
|
253
|
+
# your other config
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
This will stop API calls for posting messages, and will allow you access to
|
258
|
+
some custom matchers:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
expect {
|
262
|
+
SlackMessage.post_to('#general') { text "foo" }
|
263
|
+
}.to post_slack_message_to('#general').with_content_matching(/foo/)
|
264
|
+
|
265
|
+
expect {
|
266
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
267
|
+
}.to post_slack_message_as(:schmoebot)
|
268
|
+
|
269
|
+
expect {
|
270
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
271
|
+
}.to post_slack_message_as('Schmoe Bot')
|
272
|
+
|
273
|
+
expect {
|
274
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
275
|
+
}.to post_slack_message_with_icon(':schmoebot:')
|
276
|
+
|
277
|
+
expect {
|
278
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
279
|
+
}.to post_slack_message_with_icon_matching(/gravatar/)
|
280
|
+
|
281
|
+
expect {
|
282
|
+
SlackMessage.post_to('#general') { text "foo" }
|
283
|
+
}.to post_to_slack
|
284
|
+
```
|
285
|
+
|
286
|
+
Be forewarned, I'm frankly not that great at more complicated RSpec matchers,
|
287
|
+
so I'm guessing there are some bugs. Also, because the content of a message
|
288
|
+
gets turned into a complex JSON object, matching against content isn't capable
|
289
|
+
of very complicated regexes.
|
230
290
|
|
231
291
|
What it Doesn't Do
|
232
292
|
------------
|
@@ -234,25 +294,25 @@ What it Doesn't Do
|
|
234
294
|
This gem is intended to stay fairly simple. Other gems have lots of config
|
235
295
|
options and abilities, which is wonderful, but overall complicates usage. If
|
236
296
|
you want to add a feature, open an issue on Github first to see if it's likely
|
237
|
-
to be merged.
|
238
|
-
|
239
|
-
|
240
|
-
the block API, I'd be inclined to merge features that sustainably expand the
|
241
|
-
DSL to include more of the block API itself.
|
297
|
+
to be merged. This gem was built out of an existing need that _didn't_ include
|
298
|
+
most of the block API, but I'd be inclined to merge features that sustainably
|
299
|
+
expand the DSL to include more useful features.
|
242
300
|
|
243
|
-
|
301
|
+
Some behaviors that are still planned but not yet added:
|
244
302
|
|
245
303
|
* some API documentation amirite?
|
246
304
|
* allow custom http_options in configuration
|
247
305
|
* more of BlockKit's options
|
248
306
|
* any interactive elements at all (I don't understand them yet)
|
249
|
-
*
|
250
|
-
*
|
307
|
+
* editing / updating messages
|
308
|
+
* multiple recipients
|
309
|
+
* more interesting return types for your message (probably related to the above)
|
310
|
+
* richer text formatting (for instance, `ul` is currently a hack)
|
251
311
|
|
252
312
|
Contributing
|
253
313
|
------------
|
254
314
|
|
255
|
-
Contributions are very welcome. Fork, fix, submit
|
315
|
+
Contributions are very welcome. Fork, fix, submit pull.
|
256
316
|
|
257
317
|
Contribution is expected to conform to the [Contributor Covenant](https://github.com/jmmastey/slack_message/blob/master/CODE_OF_CONDUCT.md).
|
258
318
|
|
data/lib/slack_message/api.rb
CHANGED
@@ -3,13 +3,11 @@ require 'net/https'
|
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
class SlackMessage::Api
|
6
|
-
def self.user_id_for(email)
|
7
|
-
token = SlackMessage.configuration.api_token
|
8
|
-
|
6
|
+
def self.user_id_for(email, profile)
|
9
7
|
uri = URI("https://slack.com/api/users.lookupByEmail?email=#{email}")
|
10
8
|
request = Net::HTTP::Get.new(uri).tap do |req|
|
11
|
-
req['Authorization'] = "Bearer #{
|
12
|
-
req['Content-type'] = "application/json"
|
9
|
+
req['Authorization'] = "Bearer #{profile[:api_token]}"
|
10
|
+
req['Content-type'] = "application/json; charset=utf-8"
|
13
11
|
end
|
14
12
|
|
15
13
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
@@ -38,30 +36,57 @@ class SlackMessage::Api
|
|
38
36
|
end
|
39
37
|
|
40
38
|
def self.post(payload, target, profile)
|
41
|
-
profile[:url] = profile[:url]
|
42
|
-
|
43
|
-
uri = URI.parse(profile[:url])
|
44
39
|
params = {
|
45
40
|
channel: target,
|
46
|
-
username: profile[:name],
|
47
|
-
blocks: payload
|
48
|
-
|
41
|
+
username: payload.custom_bot_name || profile[:name],
|
42
|
+
blocks: payload.render,
|
43
|
+
text: payload.notification_text,
|
44
|
+
}
|
45
|
+
|
46
|
+
icon = payload.custom_bot_icon || profile[:icon]
|
47
|
+
if icon =~ /^:\w+:$/
|
48
|
+
params[:icon_emoji] = icon
|
49
|
+
elsif icon =~ /^(https?:\/\/)?[0-9a-z]+\.[-_0-9a-z]+/ # very naive regex, I know. it'll be fine.
|
50
|
+
params[:icon_url] = icon
|
51
|
+
elsif !(icon.nil? || icon == '')
|
52
|
+
raise ArgumentError, "Couldn't figure out icon '#{icon}'. Try :emoji: or a URL."
|
53
|
+
end
|
49
54
|
|
50
|
-
response =
|
55
|
+
response = post_message(profile, params)
|
56
|
+
body = JSON.parse(response.body)
|
57
|
+
error = body.fetch("error", "")
|
51
58
|
|
52
59
|
# let's try to be helpful about error messages
|
53
|
-
if
|
54
|
-
raise "Couldn't send slack message because the
|
55
|
-
elsif
|
60
|
+
if ["token_revoked", "token_expired", "invalid_auth", "not_authed"].include?(error)
|
61
|
+
raise "Couldn't send slack message because the API key for profile '#{profile[:handle]}' is wrong."
|
62
|
+
elsif ["no_permission", "ekm_access_denied"].include?(error)
|
63
|
+
raise "Couldn't send slack message because the API key for profile '#{profile[:handle]}' isn't allowed to post messages."
|
64
|
+
elsif error == "channel_not_found"
|
56
65
|
raise "Tried to send Slack message to non-existent channel or user '#{target}'"
|
57
|
-
elsif
|
66
|
+
elsif error == "invalid_arguments"
|
58
67
|
raise "Tried to send Slack message with invalid payload."
|
59
68
|
elsif response.code == "302"
|
60
|
-
raise "Got 302 response while posting to Slack. Check your
|
69
|
+
raise "Got 302 response while posting to Slack. Check your API key for profile '#{profile[:handle]}'."
|
61
70
|
elsif response.code != "200"
|
62
71
|
raise "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
63
72
|
end
|
64
73
|
|
65
74
|
response
|
66
75
|
end
|
76
|
+
|
77
|
+
# mostly test harness
|
78
|
+
def self.post_message(profile, params)
|
79
|
+
uri = URI("https://slack.com/api/chat.postMessage")
|
80
|
+
request = Net::HTTP::Post.new(uri).tap do |req|
|
81
|
+
req['Authorization'] = "Bearer #{profile[:api_token]}"
|
82
|
+
req['Content-type'] = "application/json; charset=utf-8"
|
83
|
+
req.body = params.to_json
|
84
|
+
end
|
85
|
+
|
86
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
87
|
+
http.request(request)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private_class_method :post_message
|
67
92
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module SlackMessage::Configuration
|
2
|
-
@@api_token = nil
|
3
2
|
@@profiles = {}
|
4
3
|
|
5
4
|
def self.reset
|
6
|
-
@@api_token = nil
|
7
5
|
@@profiles = {}
|
8
6
|
end
|
9
7
|
|
@@ -13,35 +11,29 @@ module SlackMessage::Configuration
|
|
13
11
|
|
14
12
|
###
|
15
13
|
|
16
|
-
def self.
|
17
|
-
@@
|
14
|
+
def self.clear_profiles! # test harness, mainly
|
15
|
+
@@profiles = {}
|
18
16
|
end
|
19
17
|
|
20
|
-
def self.api_token
|
21
|
-
unless @@api_token.is_a? String
|
22
|
-
raise ArgumentError, "Please set an API token to use API features."
|
23
|
-
end
|
24
|
-
|
25
|
-
@@api_token
|
26
|
-
end
|
27
|
-
|
28
|
-
###
|
29
|
-
|
30
|
-
def self.add_profile(handle = :default, name:, url:, default_channel: nil)
|
18
|
+
def self.add_profile(handle = :default, api_token:, name: nil, icon: nil, default_channel: nil)
|
31
19
|
if @@profiles.include?(handle)
|
32
20
|
warn "WARNING: Overriding profile '#{handle}' in SlackMessage config"
|
33
21
|
end
|
34
22
|
|
35
|
-
@@profiles[handle] = {
|
23
|
+
@@profiles[handle] = {
|
24
|
+
handle: handle,
|
25
|
+
api_token: api_token,
|
26
|
+
name: name,
|
27
|
+
icon: icon,
|
28
|
+
default_channel: default_channel
|
29
|
+
}
|
36
30
|
end
|
37
31
|
|
38
|
-
def self.profile(handle
|
32
|
+
def self.profile(handle)
|
39
33
|
unless @@profiles.include?(handle)
|
40
34
|
raise ArgumentError, "Unknown SlackMessage profile '#{handle}'."
|
41
35
|
end
|
42
36
|
|
43
|
-
@@profiles[handle]
|
44
|
-
profile[:name] = custom_name if !custom_name.nil?
|
45
|
-
end
|
37
|
+
@@profiles[handle]
|
46
38
|
end
|
47
39
|
end
|
data/lib/slack_message/dsl.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class SlackMessage::Dsl
|
2
|
-
attr_reader :body, :default_section, :custom_bot_name
|
2
|
+
attr_reader :body, :default_section, :custom_bot_name, :custom_bot_icon
|
3
|
+
attr_accessor :notification_text
|
3
4
|
|
4
5
|
EMSPACE = " " # unicode emspace
|
5
6
|
|
@@ -9,8 +10,10 @@ class SlackMessage::Dsl
|
|
9
10
|
@caller_self = eval("self", block.binding)
|
10
11
|
|
11
12
|
@body = []
|
12
|
-
@default_section = Section.new
|
13
|
+
@default_section = Section.new(self)
|
13
14
|
@custom_bot_name = nil
|
15
|
+
@custom_bot_icon = nil
|
16
|
+
@notification_text = nil
|
14
17
|
end
|
15
18
|
|
16
19
|
# allowable top-level entities within a block
|
@@ -18,7 +21,7 @@ class SlackMessage::Dsl
|
|
18
21
|
def section(&block)
|
19
22
|
finalize_default_section
|
20
23
|
|
21
|
-
section = Section.new.tap do |s|
|
24
|
+
section = Section.new(self).tap do |s|
|
22
25
|
s.instance_eval(&block)
|
23
26
|
end
|
24
27
|
|
@@ -52,6 +55,10 @@ class SlackMessage::Dsl
|
|
52
55
|
def context(text)
|
53
56
|
finalize_default_section
|
54
57
|
|
58
|
+
if text == "" || text.nil?
|
59
|
+
raise ArgumentError, "tried to create a context block without a value"
|
60
|
+
end
|
61
|
+
|
55
62
|
@body.push({ type: "context", elements: [{
|
56
63
|
type: "mrkdwn", text: text
|
57
64
|
}]})
|
@@ -78,6 +85,10 @@ class SlackMessage::Dsl
|
|
78
85
|
@custom_bot_name = name
|
79
86
|
end
|
80
87
|
|
88
|
+
def bot_icon(icon)
|
89
|
+
@custom_bot_icon = icon
|
90
|
+
end
|
91
|
+
|
81
92
|
# end bot name
|
82
93
|
|
83
94
|
def render
|
@@ -98,18 +109,23 @@ class SlackMessage::Dsl
|
|
98
109
|
@body.push(default_section.render)
|
99
110
|
end
|
100
111
|
|
101
|
-
@default_section = Section.new
|
112
|
+
@default_section = Section.new(self)
|
102
113
|
end
|
103
114
|
|
104
115
|
class Section
|
105
116
|
attr_reader :body
|
106
117
|
|
107
|
-
def initialize
|
118
|
+
def initialize(parent)
|
119
|
+
@parent = parent
|
108
120
|
@body = { type: "section" }
|
109
121
|
@list = List.new
|
110
122
|
end
|
111
123
|
|
112
124
|
def text(msg)
|
125
|
+
if msg == "" || msg.nil?
|
126
|
+
raise ArgumentError, "tried to create text node without a value"
|
127
|
+
end
|
128
|
+
|
113
129
|
if @body.include?(:text)
|
114
130
|
@body[:text][:text] << "\n#{msg}"
|
115
131
|
|
@@ -119,14 +135,16 @@ class SlackMessage::Dsl
|
|
119
135
|
end
|
120
136
|
|
121
137
|
def ul(elements)
|
122
|
-
raise
|
138
|
+
raise ArgumentError, "please pass an array" unless elements.respond_to?(:map)
|
139
|
+
|
123
140
|
text(
|
124
141
|
elements.map { |text| "#{EMSPACE}• #{text}" }.join("\n")
|
125
142
|
)
|
126
143
|
end
|
127
144
|
|
128
145
|
def ol(elements)
|
129
|
-
raise
|
146
|
+
raise ArgumentError, "please pass an array" unless elements.respond_to?(:map)
|
147
|
+
|
130
148
|
text(
|
131
149
|
elements.map.with_index(1) { |text, idx| "#{EMSPACE}#{idx}. #{text}" }.join("\n")
|
132
150
|
)
|
@@ -186,6 +204,10 @@ class SlackMessage::Dsl
|
|
186
204
|
end
|
187
205
|
|
188
206
|
def list_item(title, value)
|
207
|
+
if value == "" || value.nil?
|
208
|
+
raise ArgumentError, "can't create a list item for '#{title}' without a value"
|
209
|
+
end
|
210
|
+
|
189
211
|
@list.add(title, value)
|
190
212
|
end
|
191
213
|
|
@@ -199,6 +221,11 @@ class SlackMessage::Dsl
|
|
199
221
|
|
200
222
|
def render
|
201
223
|
body[:fields] = @list.render if @list.any?
|
224
|
+
|
225
|
+
if body[:text] && body[:text][:text] && !@parent.notification_text
|
226
|
+
@parent.notification_text = body[:text][:text]
|
227
|
+
end
|
228
|
+
|
202
229
|
body
|
203
230
|
end
|
204
231
|
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
require 'rspec/mocks'
|
3
|
+
|
4
|
+
# Honestly, this code is what happens when you do not understand the RSpec
|
5
|
+
# custom expectation API really at all, but you really want to create your
|
6
|
+
# matcher. This code is soo baaad.
|
7
|
+
#
|
8
|
+
# We override API calls by entirely replacing the low-level API method. Then we
|
9
|
+
# use our overridden version to capture and record calls. When someone creates
|
10
|
+
# a new expectation, an object is created, so we allow that object to register
|
11
|
+
# itself to receive notification when a slack message _would have_ been posted.
|
12
|
+
#
|
13
|
+
# Then once the expectation is fulfilled, that class unregisters itself so that
|
14
|
+
# it can be cleaned up properly.
|
15
|
+
#
|
16
|
+
|
17
|
+
module SlackMessage::RSpec
|
18
|
+
extend RSpec::Matchers::DSL
|
19
|
+
|
20
|
+
@@listeners = []
|
21
|
+
|
22
|
+
def self.register_expectation_listener(expectation_instance)
|
23
|
+
@@listeners << expectation_instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.unregister_expectation_listener(expectation_instance)
|
27
|
+
@@listeners.delete(expectation_instance)
|
28
|
+
end
|
29
|
+
|
30
|
+
FauxResponse = Struct.new(:code, :body)
|
31
|
+
|
32
|
+
def self.included(_)
|
33
|
+
SlackMessage::Api.singleton_class.undef_method(:post_message)
|
34
|
+
SlackMessage::Api.define_singleton_method(:post_message) do |profile, params|
|
35
|
+
@@listeners.each do |listener|
|
36
|
+
listener.record_call(params.merge(profile: profile))
|
37
|
+
end
|
38
|
+
|
39
|
+
response = {"ok"=>true,
|
40
|
+
"channel"=>"D12345678",
|
41
|
+
"ts"=>"1635863996.002300",
|
42
|
+
"message"=>
|
43
|
+
{"type"=>"message", "subtype"=>"bot_message",
|
44
|
+
"text"=>"foo",
|
45
|
+
"ts"=>"1635863996.002300",
|
46
|
+
"username"=>"SlackMessage",
|
47
|
+
"icons"=>{"emoji"=>":successkid:"},
|
48
|
+
"bot_id"=>"B1234567890",
|
49
|
+
"blocks"=>
|
50
|
+
[{"type"=>"section",
|
51
|
+
"block_id"=>"hAh7",
|
52
|
+
"text"=>{"type"=>"mrkdwn", "text"=>"foo", "verbatim"=>false}}]}}
|
53
|
+
|
54
|
+
return FauxResponse.new('200', response.to_json)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# w/ channel
|
59
|
+
matcher :post_slack_message_to do |expected|
|
60
|
+
match do |actual|
|
61
|
+
@instance ||= PostTo.new
|
62
|
+
@instance.with_channel(expected)
|
63
|
+
|
64
|
+
actual.call
|
65
|
+
@instance.enforce_expectations
|
66
|
+
end
|
67
|
+
|
68
|
+
chain :with_content_matching do |content|
|
69
|
+
@instance ||= PostTo.new
|
70
|
+
@instance.with_content_matching(content)
|
71
|
+
end
|
72
|
+
|
73
|
+
failure_message { @instance.failure_message }
|
74
|
+
failure_message_when_negated { @instance.failure_message_when_negated }
|
75
|
+
|
76
|
+
supports_block_expectations
|
77
|
+
end
|
78
|
+
|
79
|
+
# no channel
|
80
|
+
matcher :post_to_slack do |expected|
|
81
|
+
match do |actual|
|
82
|
+
@instance ||= PostTo.new
|
83
|
+
|
84
|
+
actual.call
|
85
|
+
@instance.enforce_expectations
|
86
|
+
end
|
87
|
+
|
88
|
+
chain :with_content_matching do |content|
|
89
|
+
@instance ||= PostTo.new
|
90
|
+
@instance.with_content_matching(content)
|
91
|
+
end
|
92
|
+
|
93
|
+
failure_message { @instance.failure_message }
|
94
|
+
failure_message_when_negated { @instance.failure_message_when_negated }
|
95
|
+
|
96
|
+
supports_block_expectations
|
97
|
+
end
|
98
|
+
|
99
|
+
# name / profile matcher
|
100
|
+
matcher :post_slack_message_as do |expected|
|
101
|
+
match do |actual|
|
102
|
+
@instance ||= PostTo.new
|
103
|
+
@instance.with_profile(expected)
|
104
|
+
|
105
|
+
actual.call
|
106
|
+
@instance.enforce_expectations
|
107
|
+
end
|
108
|
+
|
109
|
+
chain :with_content_matching do |content|
|
110
|
+
@instance ||= PostTo.new
|
111
|
+
@instance.with_content_matching(content)
|
112
|
+
end
|
113
|
+
|
114
|
+
failure_message { @instance.failure_message }
|
115
|
+
failure_message_when_negated { @instance.failure_message_when_negated }
|
116
|
+
|
117
|
+
supports_block_expectations
|
118
|
+
end
|
119
|
+
|
120
|
+
# icon matcher
|
121
|
+
matcher :post_slack_message_with_icon do |expected|
|
122
|
+
match do |actual|
|
123
|
+
@instance ||= PostTo.new
|
124
|
+
@instance.with_icon(expected)
|
125
|
+
|
126
|
+
actual.call
|
127
|
+
@instance.enforce_expectations
|
128
|
+
end
|
129
|
+
|
130
|
+
chain :with_content_matching do |content|
|
131
|
+
@instance ||= PostTo.new
|
132
|
+
@instance.with_content_matching(content)
|
133
|
+
end
|
134
|
+
|
135
|
+
failure_message { @instance.failure_message }
|
136
|
+
failure_message_when_negated { @instance.failure_message_when_negated }
|
137
|
+
|
138
|
+
supports_block_expectations
|
139
|
+
end
|
140
|
+
|
141
|
+
matcher :post_slack_message_with_icon_matching do |expected|
|
142
|
+
match do |actual|
|
143
|
+
@instance ||= PostTo.new
|
144
|
+
@instance.with_icon_matching(expected)
|
145
|
+
|
146
|
+
actual.call
|
147
|
+
@instance.enforce_expectations
|
148
|
+
end
|
149
|
+
|
150
|
+
chain :with_content_matching do |content|
|
151
|
+
@instance ||= PostTo.new
|
152
|
+
@instance.with_content_matching(content)
|
153
|
+
end
|
154
|
+
|
155
|
+
failure_message { @instance.failure_message }
|
156
|
+
failure_message_when_negated { @instance.failure_message_when_negated }
|
157
|
+
|
158
|
+
supports_block_expectations
|
159
|
+
end
|
160
|
+
|
161
|
+
class PostTo
|
162
|
+
def initialize
|
163
|
+
@captured_calls = []
|
164
|
+
@content = nil
|
165
|
+
@channel = nil
|
166
|
+
@profile = nil
|
167
|
+
@icon = nil
|
168
|
+
@icon_matching = nil
|
169
|
+
|
170
|
+
SlackMessage::RSpec.register_expectation_listener(self)
|
171
|
+
end
|
172
|
+
|
173
|
+
def record_call(deets)
|
174
|
+
@captured_calls.push(deets)
|
175
|
+
end
|
176
|
+
|
177
|
+
def with_channel(channel)
|
178
|
+
@channel = channel
|
179
|
+
end
|
180
|
+
|
181
|
+
def with_icon(icon)
|
182
|
+
@icon = icon
|
183
|
+
end
|
184
|
+
|
185
|
+
def with_icon_matching(icon)
|
186
|
+
raise ArgumentError unless icon.is_a? Regexp
|
187
|
+
@icon_matching = icon
|
188
|
+
end
|
189
|
+
|
190
|
+
def with_content_matching(content)
|
191
|
+
raise ArgumentError unless content.is_a? Regexp
|
192
|
+
@content = content
|
193
|
+
end
|
194
|
+
|
195
|
+
def with_profile(profile)
|
196
|
+
@profile = profile
|
197
|
+
end
|
198
|
+
|
199
|
+
def enforce_expectations
|
200
|
+
SlackMessage::RSpec.unregister_expectation_listener(self)
|
201
|
+
|
202
|
+
@captured_calls
|
203
|
+
.filter { |call| !@channel || call[:channel] == @channel }
|
204
|
+
.filter { |call| !@profile || [call[:profile][:handle], call[:username]].include?(@profile) }
|
205
|
+
.filter { |call| !@content || call.fetch(:blocks).to_s =~ @content }
|
206
|
+
.filter { |call| !@icon || call.fetch(:icon_emoji, call.fetch(:icon_url, '')) == @icon }
|
207
|
+
.filter { |call| !@icon_matching || call.fetch(:icon_emoji, call.fetch(:icon_url, '')) =~ @icon_matching }
|
208
|
+
.any?
|
209
|
+
end
|
210
|
+
|
211
|
+
def failure_message
|
212
|
+
"expected block to #{failure_expression}"
|
213
|
+
end
|
214
|
+
|
215
|
+
def failure_message_when_negated
|
216
|
+
"expected block not to #{failure_expression}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def failure_expression
|
220
|
+
concat = []
|
221
|
+
|
222
|
+
if @channel
|
223
|
+
concat << "post a slack message to '#{@channel}'"
|
224
|
+
elsif @profile
|
225
|
+
concat << "post a slack message as '#{@profile}'"
|
226
|
+
elsif @icon
|
227
|
+
concat << "post a slack message with icon '#{@icon}'"
|
228
|
+
elsif @icon_matching
|
229
|
+
concat << "post a slack message with icon matching '#{@icon_matching.inspect}'"
|
230
|
+
else
|
231
|
+
concat << "post a slack message"
|
232
|
+
end
|
233
|
+
|
234
|
+
if @content
|
235
|
+
concat << "with content matching #{@content.inspect}"
|
236
|
+
end
|
237
|
+
|
238
|
+
concat.join " "
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
data/lib/slack_message.rb
CHANGED
@@ -11,19 +11,15 @@ module SlackMessage
|
|
11
11
|
configuration.configure(&block)
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.user_id_for(email) # spooky undocumented public method 👻
|
15
|
-
Api::user_id_for(email)
|
16
|
-
end
|
17
|
-
|
18
14
|
def self.post_to(target, as: :default, &block)
|
19
15
|
payload = Dsl.new(block).tap do |instance|
|
20
16
|
instance.instance_eval(&block)
|
21
17
|
end
|
22
18
|
|
23
|
-
profile = Configuration.profile(as
|
24
|
-
target = user_id_for(target) if target =~ /^\S{1,}@\S{2,}\.\S{2,}$/
|
19
|
+
profile = Configuration.profile(as)
|
20
|
+
target = Api::user_id_for(target, profile) if target =~ /^\S{1,}@\S{2,}\.\S{2,}$/
|
25
21
|
|
26
|
-
Api.post(payload
|
22
|
+
Api.post(payload, target, profile)
|
27
23
|
end
|
28
24
|
|
29
25
|
def self.post_as(profile_name, &block)
|
@@ -31,15 +27,15 @@ module SlackMessage
|
|
31
27
|
instance.instance_eval(&block)
|
32
28
|
end
|
33
29
|
|
34
|
-
profile = Configuration.profile(profile_name
|
30
|
+
profile = Configuration.profile(profile_name)
|
35
31
|
if profile[:default_channel].nil?
|
36
32
|
raise ArgumentError, "Sorry, you need to specify a default_channel for profile #{profile_name} to use post_as"
|
37
33
|
end
|
38
34
|
|
39
35
|
target = profile[:default_channel]
|
40
|
-
target = user_id_for(target) if target =~ /^\S{1,}@\S{2,}\.\S{2,}$/
|
36
|
+
target = Api::user_id_for(target, profile) if target =~ /^\S{1,}@\S{2,}\.\S{2,}$/
|
41
37
|
|
42
|
-
Api.post(payload
|
38
|
+
Api.post(payload, target, profile)
|
43
39
|
end
|
44
40
|
|
45
41
|
def self.build(&block)
|
data/slack_message.gemspec
CHANGED
data/spec/slack_message_spec.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe SlackMessage do
|
4
|
-
it "includes a bunch of stuff" do
|
5
|
-
expect(SlackMessage).to respond_to(:post_to)
|
6
|
-
end
|
7
|
-
|
8
4
|
describe "API convenience" do
|
5
|
+
before do
|
6
|
+
SlackMessage.configure do |config|
|
7
|
+
config.add_profile(name: 'default profile', api_token: 'abc123')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
SlackMessage.configuration.reset
|
13
|
+
end
|
14
|
+
|
9
15
|
it "can grab user IDs" do
|
10
|
-
|
16
|
+
profile = SlackMessage::Configuration.profile(:default)
|
11
17
|
allow(Net::HTTP).to receive(:start).and_return(
|
12
18
|
double(code: "200", body: '{ "user": { "id": "ABC123" }}')
|
13
19
|
)
|
14
20
|
|
15
|
-
result = SlackMessage.user_id_for("hello@joemastey.com")
|
21
|
+
result = SlackMessage::Api.user_id_for("hello@joemastey.com", profile)
|
16
22
|
expect(result).to eq("ABC123")
|
17
23
|
end
|
18
24
|
end
|
@@ -40,36 +46,99 @@ RSpec.describe SlackMessage do
|
|
40
46
|
SlackMessage.configuration.reset
|
41
47
|
end
|
42
48
|
|
43
|
-
it "
|
49
|
+
it "lets you add and fetch profiles" do
|
44
50
|
SlackMessage.configure do |config|
|
45
|
-
config.api_token
|
51
|
+
config.add_profile(name: 'default profile', api_token: 'abc123')
|
52
|
+
config.add_profile(:nonstandard, name: 'another profile', api_token: 'abc123')
|
46
53
|
end
|
47
54
|
|
48
|
-
expect(SlackMessage.configuration.
|
55
|
+
expect(SlackMessage.configuration.profile(:default)[:name]).to eq('default profile')
|
56
|
+
expect(SlackMessage.configuration.profile(:nonstandard)[:name]).to eq('another profile')
|
57
|
+
|
58
|
+
expect {
|
59
|
+
SlackMessage.configuration.profile(:missing)
|
60
|
+
}.to raise_error(ArgumentError)
|
49
61
|
end
|
62
|
+
end
|
50
63
|
|
51
|
-
|
64
|
+
describe "custom expectations" do
|
65
|
+
before do
|
52
66
|
SlackMessage.configure do |config|
|
53
|
-
|
67
|
+
config.clear_profiles!
|
68
|
+
config.add_profile(name: 'default profile', api_token: 'abc123')
|
54
69
|
end
|
70
|
+
end
|
55
71
|
|
72
|
+
it "can assert expectations against posts" do
|
56
73
|
expect {
|
57
|
-
SlackMessage.
|
58
|
-
}.
|
74
|
+
SlackMessage.post_to('#lieutenant') { text "foo" }
|
75
|
+
}.not_to post_slack_message_to('#general')
|
76
|
+
|
77
|
+
expect {
|
78
|
+
SlackMessage.post_to('#general') { text "foo" }
|
79
|
+
}.to post_slack_message_to('#general').with_content_matching(/foo/)
|
59
80
|
end
|
60
81
|
|
61
|
-
it "
|
82
|
+
it "resets state properly" do
|
83
|
+
expect {
|
84
|
+
SlackMessage.post_to('#general') { text "foo" }
|
85
|
+
}.to post_slack_message_to('#general')
|
86
|
+
|
87
|
+
expect { }.not_to post_slack_message_to('#general')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "lets you assert by profile name" do
|
62
91
|
SlackMessage.configure do |config|
|
63
|
-
config.
|
64
|
-
config.add_profile(:
|
92
|
+
config.clear_profiles!
|
93
|
+
config.add_profile(:schmoebot, name: 'Schmoe', api_token: 'abc123', icon: ':schmoebot:', default_channel: '#schmoes')
|
65
94
|
end
|
66
95
|
|
67
|
-
expect
|
68
|
-
|
96
|
+
expect {
|
97
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
98
|
+
}.to post_slack_message_to('#schmoes')
|
69
99
|
|
70
100
|
expect {
|
71
|
-
SlackMessage.
|
72
|
-
}.to
|
101
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
102
|
+
}.to post_slack_message_as(:schmoebot)
|
103
|
+
|
104
|
+
expect {
|
105
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
106
|
+
}.to post_slack_message_as('Schmoe').with_content_matching(/foo/)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "lets you assert by profile image" do
|
110
|
+
SlackMessage.configure do |config|
|
111
|
+
config.clear_profiles!
|
112
|
+
config.add_profile(:schmoebot, name: 'Schmoe', api_token: 'abc123', icon: ':schmoebot:', default_channel: '#schmoes')
|
113
|
+
end
|
114
|
+
|
115
|
+
expect {
|
116
|
+
SlackMessage.post_as(:schmoebot) { text "foo" }
|
117
|
+
}.to post_slack_message_with_icon(':schmoebot:')
|
118
|
+
|
119
|
+
expect {
|
120
|
+
SlackMessage.post_as(:schmoebot) do
|
121
|
+
bot_icon ':schmalternate:'
|
122
|
+
text "foo"
|
123
|
+
end
|
124
|
+
}.to post_slack_message_with_icon(':schmalternate:')
|
125
|
+
|
126
|
+
expect {
|
127
|
+
SlackMessage.post_as(:schmoebot) do
|
128
|
+
bot_icon 'https://thispersondoesnotexist.com/image'
|
129
|
+
text "foo"
|
130
|
+
end
|
131
|
+
}.to post_slack_message_with_icon_matching(/thisperson/)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "lets you assert notification text" do
|
135
|
+
# TODO :|
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can assert more generally too tbh" do
|
139
|
+
expect {
|
140
|
+
SlackMessage.post_to('#general') { text "foo" }
|
141
|
+
}.to post_to_slack.with_content_matching(/foo/)
|
73
142
|
end
|
74
143
|
end
|
75
144
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative '../lib/slack_message'
|
2
|
+
require_relative '../lib/slack_message/rspec'
|
2
3
|
|
3
4
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
5
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
@@ -49,4 +50,6 @@ RSpec.configure do |config|
|
|
49
50
|
# test failures related to randomization by passing the same `--seed` value
|
50
51
|
# as the one that triggered the failure.
|
51
52
|
Kernel.srand config.seed
|
53
|
+
|
54
|
+
include SlackMessage::RSpec
|
52
55
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slack_message
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Mastey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- lib/slack_message/api.rb
|
71
71
|
- lib/slack_message/configuration.rb
|
72
72
|
- lib/slack_message/dsl.rb
|
73
|
+
- lib/slack_message/rspec.rb
|
73
74
|
- slack_message.gemspec
|
74
75
|
- spec/slack_message_spec.rb
|
75
76
|
- spec/spec_helper.rb
|