pax_global_header 0000666 0000000 0000000 00000000064 14045314334 0014513 g ustar 00root root 0000000 0000000 52 comment=29e51132ce0ef649035d9b5cbd5dedf66f8dffd3
slack-notifier-2.4.0/ 0000775 0000000 0000000 00000000000 14045314334 0014430 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/.editorconfig 0000664 0000000 0000000 00000000354 14045314334 0017107 0 ustar 00root root 0000000 0000000 ; EditorConfig is awesome: http://EditorConfig.org
root = true ; top-most EditorConfig file
; Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
slack-notifier-2.4.0/.env-example 0000664 0000000 0000000 00000000050 14045314334 0016645 0 ustar 00root root 0000000 0000000 SLACK_WEBHOOK_URL: "https://example.com" slack-notifier-2.4.0/.gitignore 0000664 0000000 0000000 00000000137 14045314334 0016421 0 ustar 00root root 0000000 0000000 .DS_Store
Gemfile.lock
.rvmrc
.ruby-version
.ruby-gemset
.env
*.gem
coverage
spec/examples.txt
slack-notifier-2.4.0/.hound.yml 0000664 0000000 0000000 00000000042 14045314334 0016342 0 ustar 00root root 0000000 0000000 ruby:
config_file: .rubocop.yml
slack-notifier-2.4.0/.rspec 0000664 0000000 0000000 00000000040 14045314334 0015537 0 ustar 00root root 0000000 0000000 --color
--require 'spec_helper'
slack-notifier-2.4.0/.rubocop.yml 0000664 0000000 0000000 00000002533 14045314334 0016705 0 ustar 00root root 0000000 0000000 AllCops:
DisplayCopNames: true
DisplayStyleGuide: true
Include:
- 'Rakefile'
- 'bin/test'
# Naming ------------------------------------
Naming/FileName:
Exclude:
- lib/slack-notifier.rb
- spec/lib/slack-notifier_spec.rb
- slack-notifier.gemspec
- Gemfile
# Style ------------------------------------
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/MethodDefParentheses:
EnforcedStyle: require_no_parentheses_except_multiline
Style/Documentation:
Enabled: false
Style/RegexpLiteral:
Enabled: false
Style/SignalException:
Enabled: false
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%w': '[]'
# Layout ----------------------------------
Layout/MultilineOperationIndentation:
EnforcedStyle: aligned
Layout/MultilineMethodCallIndentation:
EnforcedStyle: aligned
Layout/MultilineOperationIndentation:
Enabled: false
Layout/SpaceAroundEqualsInParameterDefault:
EnforcedStyle: no_space
Layout/IndentationConsistency:
EnforcedStyle: rails
Layout/DotPosition:
EnforcedStyle: leading
# Metrics ----------------------------------
Metrics/LineLength:
Max: 128
Metrics/AbcSize:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/BlockLength:
Exclude:
- spec/**/*
# Lint -------------------------------------
Lint/EndAlignment:
EnforcedStyleAlignWith: variable
slack-notifier-2.4.0/.travis.yml 0000664 0000000 0000000 00000000772 14045314334 0016547 0 ustar 00root root 0000000 0000000 language: ruby
sudo: false
cache: bundler
bundler_args: "--without development"
before_install:
- "gem update --system"
- "gem install bundler"
rvm:
- 3.0
- 2.7
- 2.6
- ruby-head
- jruby-9.1.9.0
- jruby-head
matrix:
allow_failures:
- rvm: ruby-head
- rvm: jruby-head
notifications:
slack:
secure: Ld0tGBmwLG/ADOlLjO6ILq98+u/iq5qkuxAwN1E0SBOooALZZEJSc74jEMpgpnb22tk8QimUmLiCTE+8tWKaGiXTrvK6uvvfP6iiL9850NezHCxA3YMuWPnQQtJpTJ4135MMO8gJXu9vcswb9vW9N3v/A7VJdHbVZyT0vIMGas0=
slack-notifier-2.4.0/Gemfile 0000664 0000000 0000000 00000000621 14045314334 0015722 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
source "https://rubygems.org"
gemspec
group :development do
if RUBY_VERSION >= "2.0.0"
gem "pry-byebug"
else
gem "pry-debugger"
end
gem "benchmark-ips"
end
group :test do
gem "rake", "~> 12.0"
gem "rspec", "~> 3.5.0"
gem "rubocop", "~> 0.51", require: false if RUBY_VERSION >= "2.1"
gem "string-scrub" if RUBY_VERSION <= "1.9.3"
end
slack-notifier-2.4.0/LICENSE 0000664 0000000 0000000 00000002067 14045314334 0015442 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2014 Steven Sloan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
slack-notifier-2.4.0/Rakefile 0000664 0000000 0000000 00000000462 14045314334 0016077 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
begin
require "rubocop/rake_task"
rubocop = RuboCop::RakeTask.new
rubocop.fail_on_error = false
rescue LoadError
task :rubocop do
puts "Rubocop not loaded"
end
end
task default: %i[spec rubocop]
slack-notifier-2.4.0/bin/ 0000775 0000000 0000000 00000000000 14045314334 0015200 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/bin/test 0000775 0000000 0000000 00000000764 14045314334 0016114 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
require 'yaml'
YAML.load_file('.env').each do |key, var|
ENV[key] = var
end
rubies = ['2.0.0', '2.1', '2.2', '2.3.3', '2.4.0']
rubies.each do |ruby|
# cleanup gemfile.locks
Dir['spec*/**/*.lock'].each do |lockfile|
puts "removing #{lockfile}"
system "rm #{lockfile}"
end
pid = Process.fork do
exec "rvm #{ruby} do ruby spec/integration/ping_integration_test.rb"
end
trap 'INT' do
puts 'exiting'
pid.send(:exit)
end
Process.wait(pid)
end
slack-notifier-2.4.0/changelog.md 0000664 0000000 0000000 00000010116 14045314334 0016700 0 ustar 00root root 0000000 0000000 # 2.4.0
- Make keyword argument usage compatible for ruby 3.x [@walski #123, @yuuu #119]
# 2.3.2
- Improve compatability with CommonMark spec for markdown link formatting [@revolter #91]
Still not 100% compliant, but it is now much closer.
# 2.3.1
- use `map` to return the array of responses instead of payload in `ping` & `post` [@yhatt #88]
# 2.3.0
- feat: add `channels` middleware to split payloads to ping multiple channels [#40]
- feat: support any middleware splittin payload into an array to allow multiple payloads from a single process.
# 2.2.2
- fix wrapping of attachments passed as a hash
- fix error in `LinkFormatter` if a text payload was nil [#81]
# 2.2.1
- fix loading error caused by uninitialized constant [@pocke #78]
# 2.2.0
- raise exception when API responds with an error [@siegy22]
# 2.1.0
- addition of :at middleware to simplify notifying users & rooms [@kazuooooo #66]
# 2.0.0
[BREAKING] This is a fairly large change to how defaults are set and how messages are processed.
**Setting Defaults**
Setter methods are no longer available for setting defaults on a notifier instance, you'll now set defaults with a block on initialization.
```ruby
# previously in 1.x
notifier = Slack::Notifier.new WEBHOOK_URL, http_client: CustomClient
notifier.channel = "sup"
# in 2.x
notifier = Slack::Notifier.new WEBHOOK_URL do
http_client CustomClient
defaults channel: "sup"
end
```
Read more about [setting defaults in the readme](readme.md#setting-defaults)
**Message Processing**
Message are now processed through a configurable middleware stack. By default it acts exactly the same as the 1.x versions. [More information is available in the readme](readme.md#middleware)
# 1.5.1
- allow using a single attachment w/o putting it in an array [@Elektron1c97 #47]
# 1.5.0
- allow sending with attachments only [#48]
# 1.4.0
- Format attachment messages with the LinkFormatter [@bhuga #37]
- Add support for mailto links in markdown formatted links [@keithpitty #43]
# 1.3.1
- Fix bug with link formatter for markdown links wrapped in square braces [@bhuga #36]
# 1.3.0
- Add `#escape` to allow clients to escape special characters [@monkbroc #35]
# 1.2.1
- use `#scrub` to (more selectively) strip invalid characters from strings before attempting to format. This allows valid japanese (and more) characters to be used. Thanks to @fukayatsu for reporting.
This checks for the presence of the `scrub` method on string, so if on ruby < 2.1 you'll need to include & require the `string-scrub` gem to handle invalid characters.
# 1.2.0
- Strip invalid UTF-8 characters from message before attempting to format links. They are replaced with the unicode replacement character '[�](http://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character)'. [@ushu #26]
# 1.1.0
- add ability to pass `:http_options` to the initializer or `#ping`. this allows you to set options like `read_timeout` or `open_timeout`. See [issue #17](https://github.com/stevenosloan/slack-notifier/issues/17) for more information.
# 1.0.0
- [BREAKING!] To follow changes with slack, client is now initialized with a webhook url instead of team & token. For help upgrading read the [upgrade from 0.6.1 guide](docs/upgrade-from-0.6.1.md)
# 0.6.1
- fix bug in link_formatter to allow multiple links in a message
# 0.6.0
- add ability to pass in your own http client
- [BREAKING!] hook name moves to options array
# 0.5.0
- allow defaults to be set on initialization
- remove channel formatting [#8]
# 0.4.1
- allow default channel's to start with a "@" or "#" [#7]
# 0.4.0
- try and correct for a channel name being set without a leading "#" [@dlackty]
# 0.3.2
- add Net::HTTP wrapper to include support for ruby 1.9.3
# 0.3.1
- remove requirement for channel, no longer required by slack [@dlackty]
# 0.3.0
- add custom hook endpoint parameter [@razielgn]
# 0.2.0
- remove HTTParty dependency
# 0.1.1
- loosen httparty dependency
- refactor codebase & add specs
# 0.1.0
- now formats html or markdown links in your message to match slack's format
# 0.0.2
- fix a fat finger if a default channel is set
# 0.0.1
- initial release
slack-notifier-2.4.0/docs/ 0000775 0000000 0000000 00000000000 14045314334 0015360 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/docs/upgrade-from-0.6.1.md 0000664 0000000 0000000 00000001476 14045314334 0020742 0 ustar 00root root 0000000 0000000 Recently slack changed the way incoming webhooks are handled. Instead of taking a team name and token, they now provide a unique (obfuscated) webhook url.
To upgrade the slack-notifier gem, you'll need to find your webhook url. In slack:
- go to you're configured integrations (https://team-name.slack.com/services)
- select **Incoming Webhooks**
- select the webhook that uses the slack-notifier gem
- find the webhook url under the heading **Integration Settings**
You'll then change the way you initialize your notifier
From:
```ruby
notifier = Slack::Notifier.new 'team', 'token'
```
To:
```ruby
notifier = Slack::Notifier.new 'WEBHOOK_URL'
```
Defaults & attachemnts will continue to work like they have
```ruby
notifier = Slack::Notifier.new 'WEBHOOK_URL', icon_emoji: ":ghost:"
notifier.ping "I'm feeling spooky"
```
slack-notifier-2.4.0/lib/ 0000775 0000000 0000000 00000000000 14045314334 0015176 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/lib/slack-notifier.rb 0000664 0000000 0000000 00000002566 14045314334 0020446 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require "uri"
require "json"
require_relative "slack-notifier/util/http_client"
require_relative "slack-notifier/util/link_formatter"
require_relative "slack-notifier/util/escape"
require_relative "slack-notifier/payload_middleware"
require_relative "slack-notifier/config"
module Slack
class Notifier
attr_reader :endpoint
def initialize webhook_url, options={}, &block
@endpoint = URI.parse webhook_url
config.http_client(options.delete(:http_client)) if options.key?(:http_client)
config.defaults options
config.instance_exec(&block) if block_given?
middleware.set config.middleware
end
def config
@_config ||= Config.new
end
def ping message, options={}
if message.is_a?(Hash)
options = message
else
options[:text] = message
end
post options
end
def post payload={}
params = {}
client = payload.delete(:http_client) || config.http_client
payload = config.defaults.merge(payload)
params[:http_options] = payload.delete(:http_options) if payload.key?(:http_options)
middleware.call(payload).map do |pld|
params[:payload] = pld.to_json
client.post endpoint, params
end
end
private
def middleware
@middleware ||= PayloadMiddleware::Stack.new(self)
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/ 0000775 0000000 0000000 00000000000 14045314334 0020110 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/lib/slack-notifier/config.rb 0000664 0000000 0000000 00000001762 14045314334 0021710 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class Config
def initialize
@http_client = Util::HTTPClient
@defaults = {}
@middleware = %i[
format_message
format_attachments
at
channels
]
end
def http_client client=nil
return @http_client if client.nil?
raise ArgumentError, "the http client must respond to ::post" unless client.respond_to?(:post)
@http_client = client
end
def defaults new_defaults=nil
return @defaults if new_defaults.nil?
raise ArgumentError, "the defaults must be a Hash" unless new_defaults.is_a?(Hash)
@defaults = new_defaults
end
def middleware *args
return @middleware if args.empty?
@middleware =
if args.length == 1 && args.first.is_a?(Array) || args.first.is_a?(Hash)
args.first
else
args
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware.rb 0000664 0000000 0000000 00000001074 14045314334 0024265 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class << self
def registry
@registry ||= {}
end
def register middleware, name
registry[name] = middleware
end
end
end
end
end
require_relative "payload_middleware/stack"
require_relative "payload_middleware/base"
require_relative "payload_middleware/format_message"
require_relative "payload_middleware/format_attachments"
require_relative "payload_middleware/at"
require_relative "payload_middleware/channels"
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/ 0000775 0000000 0000000 00000000000 14045314334 0023736 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/at.rb 0000664 0000000 0000000 00000001320 14045314334 0024663 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class At < Base
middleware_name :at
options at: []
def call payload={}
return payload unless payload[:at]
payload[:text] = "#{format_ats(payload.delete(:at))}#{payload[:text]}"
payload
end
private
def format_ats ats
Array(ats).map { |at| "<#{at_cmd_char(at)}#{at}> " }
.join("")
end
def at_cmd_char at
case at
when :here, :channel, :everyone, :group
"!"
else
"@"
end
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/base.rb 0000664 0000000 0000000 00000001354 14045314334 0025200 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class Base
class << self
def middleware_name name
PayloadMiddleware.register self, name.to_sym
end
def options default_opts
@default_opts = default_opts
end
def default_opts
@default_opts ||= {}
end
end
attr_reader :notifier, :options
def initialize notifier, opts={}
@notifier = notifier
@options = self.class.default_opts.merge opts
end
def call _payload={}
raise NoMethodError, "method `call` not defined for class #{self.class}"
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/channels.rb 0000664 0000000 0000000 00000000670 14045314334 0026061 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class Channels < Base
middleware_name :channels
def call payload={}
return payload unless payload[:channel].respond_to?(:to_ary)
payload[:channel].to_ary.map do |channel|
pld = payload.dup
pld[:channel] = channel
pld
end
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/format_attachments.rb 0000664 0000000 0000000 00000002075 14045314334 0030152 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class FormatAttachments < Base
middleware_name :format_attachments
options formats: %i[html markdown]
def call payload={}
payload = payload.dup
attachments = payload.delete(:attachments)
attachments ||= payload.delete("attachments")
attachments = wrap_array(attachments).map do |attachment|
["text", :text].each do |key|
if attachment.key?(key)
attachment[key] = Util::LinkFormatter.format(attachment[key], options)
end
end
attachment
end
payload[:attachments] = attachments if attachments && !attachments.empty?
payload
end
private
def wrap_array object
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/format_message.rb 0000664 0000000 0000000 00000000644 14045314334 0027263 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class FormatMessage < Base
middleware_name :format_message
options formats: %i[html markdown]
def call payload={}
return payload unless payload[:text]
payload[:text] = Util::LinkFormatter.format(payload[:text], options)
payload
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/payload_middleware/stack.rb 0000664 0000000 0000000 00000002105 14045314334 0025366 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
class PayloadMiddleware
class Stack
attr_reader :notifier,
:stack
def initialize notifier
@notifier = notifier
@stack = []
end
def set *middlewares
middlewares =
if middlewares.length == 1 && middlewares.first.is_a?(Hash)
middlewares.first
else
middlewares.flatten
end
@stack = middlewares.map do |key, opts|
PayloadMiddleware.registry.fetch(key).new(*[notifier, opts].compact)
end
end
def call payload={}
result = stack.inject payload do |pld, middleware|
as_array(pld).flat_map do |p|
middleware.call(p)
end
end
as_array(result)
end
private
def as_array args
if args.respond_to?(:to_ary)
args.to_ary
else
[args]
end
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/util/ 0000775 0000000 0000000 00000000000 14045314334 0021065 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/lib/slack-notifier/util/escape.rb 0000664 0000000 0000000 00000000506 14045314334 0022653 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
module Util
module Escape
HTML_REGEXP = /[&><]/
HTML_REPLACE = { "&" => "&", ">" => ">", "<" => "<" }.freeze
def self.html string
string.gsub(HTML_REGEXP, HTML_REPLACE)
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/util/http_client.rb 0000664 0000000 0000000 00000003133 14045314334 0023727 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require "net/http"
module Slack
class Notifier
class APIError < StandardError; end
module Util
class HTTPClient
class << self
def post uri, params
HTTPClient.new(uri, params).call
end
end
attr_reader :uri, :params, :http_options
def initialize uri, params
@uri = uri
@http_options = params.delete(:http_options) || {}
@params = params
end
# rubocop:disable Layout/IndentHeredoc
def call
http_obj.request(request_obj).tap do |response|
unless response.is_a?(Net::HTTPSuccess)
raise Slack::Notifier::APIError, <<-MSG
The slack API returned an error: #{response.body} (HTTP Code #{response.code})
Check the "Handling Errors" section on https://api.slack.com/incoming-webhooks for more information
MSG
end
end
end
# rubocop:enable Layout/IndentHeredoc
private
def request_obj
req = Net::HTTP::Post.new uri.request_uri
req.set_form_data params
req
end
def http_obj
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = (uri.scheme == "https")
http_options.each do |opt, val|
if http.respond_to? "#{opt}="
http.send "#{opt}=", val
else
warn "Net::HTTP doesn't respond to `#{opt}=`, ignoring that option"
end
end
http
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/util/link_formatter.rb 0000664 0000000 0000000 00000004277 14045314334 0024444 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
module Util
class LinkFormatter
# http://rubular.com/r/19cNXW5qbH
HTML_PATTERN = %r{
(.+?)
}x
# the path portion of a url can contain these characters
VALID_PATH_CHARS = '\w\-\.\~\/\?\#\='
# Attempt at only matching pairs of parens per
# the markdown spec http://spec.commonmark.org/0.27/#links
#
# http://rubular.com/r/y107aevxqT
MARKDOWN_PATTERN = %r{
\[ ([^\[\]]*?) \]
\( ((https?://.*?) | (mailto:.*?)) \)
(?! [#{VALID_PATH_CHARS}]* \) )
}x
class << self
def format string, opts={}
LinkFormatter.new(string, **opts).formatted
end
end
attr_reader :formats
def initialize string, formats: %i[html markdown]
@formats = formats
@orig = string.respond_to?(:scrub) ? string.scrub : string
end
# rubocop:disable Lint/RescueWithoutErrorClass
def formatted
return @orig unless @orig.respond_to?(:gsub)
sub_markdown_links(sub_html_links(@orig))
rescue => e
raise e unless RUBY_VERSION < "2.1" && e.message.include?("invalid byte sequence")
raise e, "#{e.message}. Consider including the 'string-scrub' gem to strip invalid characters"
end
# rubocop:enable Lint/RescueWithoutErrorClass
private
def sub_html_links string
return string unless formats.include?(:html)
string.gsub(HTML_PATTERN) do
slack_link Regexp.last_match[1], Regexp.last_match[2]
end
end
def sub_markdown_links string
return string unless formats.include?(:markdown)
string.gsub(MARKDOWN_PATTERN) do
slack_link Regexp.last_match[2], Regexp.last_match[1]
end
end
def slack_link link, text=nil
"<#{link}" \
"#{text && !text.empty? ? "|#{text}" : ''}" \
">"
end
end
end
end
end
slack-notifier-2.4.0/lib/slack-notifier/version.rb 0000664 0000000 0000000 00000000214 14045314334 0022117 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
module Slack
class Notifier
VERSION = "2.4.0".freeze # rubocop:disable Style/RedundantFreeze
end
end
slack-notifier-2.4.0/readme.md 0000664 0000000 0000000 00000032057 14045314334 0016216 0 ustar 00root root 0000000 0000000 A simple wrapper to send notifications to [Slack](https://slack.com/) webhooks.
[](https://travis-ci.org/stevenosloan/slack-notifier)
[](https://codeclimate.com/github/stevenosloan/slack-notifier)
[](https://rubygems.org/gems/slack-notifier)
[](https://dependabot.com/compatibility-score.html?dependency-name=slack-notifier&package-manager=bundler&version-scheme=semver)
## Example
```ruby
require 'slack-notifier'
notifier = Slack::Notifier.new "WEBHOOK_URL"
notifier.ping "Hello World"
# => if your webhook is setup, will message "Hello World"
# => to the default channel you set in slack
```
#### Installation
Install the latest stable release:
```
$ gem install slack-notifier
```
Or with [Bundler](http://bundler.io/), add it to your Gemfile:
```ruby
gem "slack-notifier"
```
#### Setting Defaults
On initialization you can set default payloads by calling `defaults` in an initialization block:
```ruby
notifier = Slack::Notifier.new "WEBHOOK_URL" do
defaults channel: "#default",
username: "notifier"
end
notifier.ping "Hello default"
# => will message "Hello default"
# => to the "#default" channel as 'notifier'
```
To get the WEBHOOK_URL you need:
1. go to https://slack.com/apps/A0F7XDUAZ-incoming-webhooks
2. choose your team, press configure
3. in configurations press add configuration
4. choose channel, press "Add Incoming WebHooks integration"
You can also set defaults through an options hash:
```ruby
notifier = Slack::Notifier.new "WEBHOOK_URL", channel: "#default",
username: "notifier"
```
These defaults are over-ridable for any individual ping.
```ruby
notifier.ping "Hello random", channel: "#random"
# => will ping the "#random" channel
```
## Links
Slack requires links to be formatted a certain way, so the default middlware stack of slack-notifier will look through your message and attempt to convert any html or markdown links to slack's format before posting.
Here's what it's doing under the covers:
```ruby
message = "Hello world, [check](http://example.com) it out"
Slack::Notifier::Util::LinkFormatter.format(message)
# => "Hello world, it "
```
## Formatting
Slack supports various different formatting options. For example, if you want to alert an entire channel you include `` in your message
```ruby
message = " hey check this out"
notifier.ping message
#ends up posting "@channel hey check this out" in your Slack channel
```
You can see [Slack's message documentation here](https://api.slack.com/docs/formatting)
## Escaping
Since sequences starting with < have special meaning in Slack, you should use `Slack::Notifier::Util::Escape.html` if your messages may contain &, < or >.
```ruby
link_text = Slack::Notifier::Util::Escape.html("User ")
message = "Write to [#{link_text}](mailto:user@example.com)"
notifier.ping message
```
## Blocks
This plugin supports the [Slack blocks format](https://app.slack.com/block-kit-builder/) and [block kit builder](https://app.slack.com/block-kit-builder/). This is useful for displaying buttons, dropdowns, and images.
```ruby
blocks = [
{
"type": "image",
"title": {
"type": "plain_text",
"text": "image1",
"emoji": true
},
"image_url": "https://api.slack.com/img/blocks/bkb_template_images/onboardingComplex.jpg",
"alt_text": "image1"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hey there 👋 I'm TaskBot. I'm here to help you create and manage tasks in Slack.\nThere are two ways to quickly create tasks:"
}
}
]
notifier.post(blocks: blocks)
```
## Additional parameters
Any key passed to the `post` method is posted to the webhook endpoint. Check out the [Slack webhook documentation](https://api.slack.com/incoming-webhooks) for the available parameters.
Setting an icon:
```ruby
notifier.post text: "feeling spooky", icon_emoji: ":ghost:"
# or
notifier.post text: "feeling chimpy", icon_url: "http://static.mailchimp.com/web/favicon.png"
```
Adding attachments:
```ruby
a_ok_note = {
fallback: "Everything looks peachy",
text: "Everything looks peachy",
color: "good"
}
notifier.post text: "with an attachment", attachments: [a_ok_note]
```
## HTTP options
With the default HTTP client, you can send along options to customize its behavior as `:http_options` params when you post or initialize the notifier.
```ruby
notifier = Slack::Notifier.new 'WEBHOOK_URL', http_options: { open_timeout: 5 }
notifier.post text: "hello", http_options: { open_timeout: 10 }
```
**Note**: you should only send along options that [`Net::HTTP`](http://ruby-doc.org/stdlib-2.2.0/libdoc/net/http/rdoc/Net/HTTP.html) has as setters, otherwise the option will be ignored and show a warning.
### Proxies
`:http_options` can be used if you need to connect to Slack via an HTTP proxy.
For example, to connect through a local squid proxy the following options would be used.
```ruby
notifier = Slack::Notifier.new 'WEBHOOK_URL', http_options: {
proxy_address: 'localhost',
proxy_port: 3128,
proxy_from_env: false
}
```
## Custom HTTP Client
There is a packaged default client wrapping Net::HTTP, but your HTTP needs might be a little different. In that case, you can pass in your own wrapper to handle sending the notifications. It just needs to respond to `::post` with the arguments of the endpoint URI, and the payload [pretty much the same as Net:HTTP.post_form](http://ruby-doc.org/stdlib-2.1.2/libdoc/net/http/rdoc/Net/HTTP.html#method-c-post_form).
A simple example:
```ruby
module Client
def self.post uri, params={}
Net::HTTP.post_form uri, params
end
end
notifier = Slack::Notifier.new 'WEBHOOK_URL' do
http_client Client
end
```
It's also encouraged for any custom HTTP implementations to accept the `:http_options` key in params.
**Setting client per post**
You can also set the http_client per-post if you need to special case certain pings.
```ruby
notifier.post text: "hello", http_client: CustomClient
```
**Setting a No-Op client**
In development (or testing), you may want to watch the behavior of the notifier without posting to slack. This can be handled with a no-op client.
```ruby
class NoOpHTTPClient
def self.post uri, params={}
# bonus, you could log or observe posted params here
end
end
notifier = Slack::Notifier.new 'WEBHOOK_URL' do
http_client NoOpHTTPClient
end
```
## Middleware
By default slack-notifier ships with middleware to format links in the message & text field of attachments. You can configure the middleware a notifier will use on initialization:
```ruby
notifier = Slack::Notifier.new "WEBHOOK_URL" do
middleware format_message: { formats: [:html] }
end
# this example will *only* use the format_message middleware and only format :html links
notifier.post text: "Hello world! [visit this](http://example.com)"
# => will post "Hello ! [visit this](http://example.com)"
```
The middleware can be set with a their name, or by name and options. They will be triggered in order.
```ruby
notifier = Slack::Notifier.new "WEBHOOK_URL" do
middleware :format_message, :format_attachments
end
# will run format_message then format_attachments with default options
notifier = Slack::Notifier.new "WEBHOOK_URL" do
middleware format_message: { formats: [:html] },
format_attachments: { formats: [:markdown] }
end
# will run format_message w/ formats [:html] then format_attachments with formats [:markdown]
```
Available middleware:
**`format_message`**
This middleware takes the `:text` key of the payload and runs it through the [`Linkformatter`](#links). You can configure which link formats to look for with a `:formats` option. You can set `[:html]` (only html links), `[:markdown]` (only markdown links) or `[:html, :markdown]` (the default, will format both).
**`format_attachments`**
This middleware takes the `:text` key of any attachment and runs it through the [`Linkformatter`](#links). You can configure which link formats to look for with a `:formats` option. You can set `[:html]` (only html links), `[:markdown]` (only markdown links) or `[:html, :markdown]` (the default, will format both).
**`at`**
This simplifies the process of notifying users and rooms to messages. By adding an `:at` key to the payload w/ an array of symbols the appropriately formatted commands will be prepended to the message. It will accept a single name, or an array.
For example:
```ruby
notifier.post text: "hello", at: :casper
# => "<@casper> hello"
notifier.post text: "hello", at: [:here, :waldo]
# => " <@waldo> hello"
```
**`channels`**
If the `channel` argument of a payload is an array this splits the payload to be posted to each channel.
For example:
```ruby
notifier.post text: "hello", channel: ["default", "all_the_things"]
# => will post "hello" to the default and all_the_things channel
```
To send a message directly to a user, their username [no longer works](https://github.com/stevenosloan/slack-notifier/issues/51#issuecomment-414138622). Instead you'll need to get the user's ID and set that as the channel.
At the time of writing, one way to get a user's ID is to:
- go to their profile
- click **...** ("More actions")
- click **Copy Member ID**
### Writing your own Middleware
Middleware is fairly straightforward, it is any class that inherits from `Slack::Notifier::PayloadMiddleware::Base` and responds to `#call`. It will always be given the payload as a hash and should return the modified payload as a hash.
For example, lets say we want to replace words in every message, we could write a middleware like this:
```ruby
class SwapWords < Slack::Notifier::PayloadMiddleware::Base
middleware_name :swap_words # this is the key we use when setting
# the middleware stack for a notifier
options pairs: ["hipchat" => "slack"] # the options takes a hash that will
# serve as the default if not given any
# when initialized
def call payload={}
return payload unless payload[:text] # noope if there is no message to work on
# not efficient, but it's an example :)
options[:pairs].each do |from, to|
payload[:text] = payload[:text].gsub from, to
end
payload # always return the payload from your middleware
end
end
notifier = Slack::Notifier.new "WEBHOOK_URL" do
middleware :swap_words # setting our stack w/ just defaults
end
notifier.ping "hipchat is awesome!"
# => pings slack with "slack is awesome!"
notifier = Slack::Notifier.new "WEBHOOK_URL" do
# here we set new options for the middleware
middleware swap_words: { pairs: ["hipchat" => "slack",
"awesome" => "really awesome"]}
end
notifier.ping "hipchat is awesome!"
# => pings slack with "slack is really awesome!"
```
If your middleware returns an array, that will split the message into multiple pings. An example for pinging multiple channels:
```ruby
class MultiChannel < Slack::Notifier::PayloadMiddleware::Base
middleware_name :channels
def call payload={}
return payload unless payload[:channel].respond_to?(:to_ary)
payload[:channel].to_ary.map do |channel|
pld = payload.dup
pld[:channel] = channel
pld
end
end
end
```
Versioning
----------
Since version `1.0` has been released, the aim is to follow [Semantic Versioning](http://semver.org/) as much as possible. However, it is encouraged to check the [changelog](changelog.md) when updating to see what changes have been made.
To summarize the reasoning for versioning:
```
Given a version number MAJOR.MINOR.PATCH, increment:
- MAJOR version when incompatible API changes are made
- MINOR version for adding functionality in a backwards-compatible manner or bug fixes that *may* change behavior
- PATCH version for make backwards-compatible bug fixes
```
Testing
-------
```bash
$ rspec
```
There is also an integration test setup to just double check pinging across the supported rubies. To run:
1. Copy the `.env-example` file to `.env` and replace with your details.
2. Make sure `bin/test` is executable
3. then run and watch for the pings in your slack room
```bash
$ bin/test
```
Contributing
------------
If there is any thing you'd like to contribute or fix, please:
- Fork the repo
- Add tests for any new functionality
- Make your changes
- Verify all new & existing tests pass
- Make a pull request
License
-------
The slack-notifier gem is distributed under the MIT License.
slack-notifier-2.4.0/slack-notifier.gemspec 0000664 0000000 0000000 00000001261 14045314334 0020707 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require File.expand_path("../lib/slack-notifier/version", __FILE__)
Gem::Specification.new do |s|
s.name = "slack-notifier"
s.version = Slack::Notifier::VERSION
s.platform = Gem::Platform::RUBY
s.summary = "A slim ruby wrapper for posting to slack webhooks"
s.description = " A slim ruby wrapper for posting to slack webhooks "
s.authors = ["Steven Sloan"]
s.email = ["stevenosloan@gmail.com"]
s.homepage = "http://github.com/stevenosloan/slack-notifier"
s.license = "MIT"
s.files = Dir["{lib}/**/*.rb"]
s.test_files = Dir["spec/**/*.rb"]
s.require_path = "lib"
end
slack-notifier-2.4.0/spec/ 0000775 0000000 0000000 00000000000 14045314334 0015362 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/end_to_end_spec.rb 0000664 0000000 0000000 00000007516 14045314334 0021030 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# encoding: utf-8
require "spec_helper"
RSpec.describe Slack::Notifier do
{
{ text: "hello" } =>
{ payload: { text: "hello" } },
{ text: "[hello](http://example.com/world)" } =>
{ payload: { text: "" } },
{ text: 'example' } =>
{ payload: { text: "" } },
{ text: "hello/こんにちは from notifier test" } =>
{ payload: { text: "hello/こんにちは from notifier test" } },
{ text: "Hello World, enjoy [](http://example.com)." } =>
{ payload: { text: "Hello World, enjoy ." } },
{ text: "Hello World, enjoy [this](http://example.com)[this2](http://example2.com)" } =>
{ payload: { text: "Hello World, enjoy " } },
{ text: "[John](mailto:john@example.com)" } =>
{ payload: { text: "" } },
{ text: 'John' } =>
{ payload: { text: "" } },
{ text: "hello", channel: "hodor" } =>
{ payload: { text: "hello", channel: "hodor" } },
{ text: nil, attachments: [{ text: "attachment message" }] } =>
{ payload: { text: nil, attachments: [{ text: "attachment message" }] } },
{ text: "the message", channel: "foo", attachments: [{ color: "#000",
text: "attachment message",
fallback: "fallback message" }] } =>
{ payload: { text: "the message",
channel: "foo",
attachments: [{ color: "#000",
text: "attachment message",
fallback: "fallback message" }] } },
{ attachments: [{ color: "#000",
text: "attachment message",
fallback: "fallback message" }] } =>
{ payload: { attachments: [{ color: "#000",
text: "attachment message",
fallback: "fallback message" }] } },
{ attachments: { color: "#000",
text: "attachment message [hodor](http://winterfell.com)",
fallback: "fallback message" } } =>
{ payload: { attachments: [{ color: "#000",
text: "attachment message ",
fallback: "fallback message" }] } },
{ attachments: { color: "#000",
text: nil,
fallback: "fallback message" } } =>
{ payload: { attachments: [{ color: "#000",
text: nil,
fallback: "fallback message" }] } },
{ text: "hello", http_options: { timeout: 5 } } =>
{ http_options: { timeout: 5 }, payload: { text: "hello" } }
}.each do |args, payload|
it "sends correct payload for #post(#{args})" do
http_client = class_double("Slack::Notifier::Util::HTTPClient", post: nil)
notifier = Slack::Notifier.new "http://example.com", http_client: http_client
payload[:payload] = payload[:payload].to_json
expect(http_client).to receive(:post)
.with(URI.parse("http://example.com"), payload)
notifier.post(args)
end
end
it "applies options given to middleware" do
client = class_double("Slack::Notifier::Util::HTTPClient", post: nil)
notifier = Slack::Notifier.new "http://example.com" do
http_client client
middleware format_message: { formats: [] }
end
expect(client).to receive(:post)
.with(URI.parse("http://example.com"), payload: { text: "Hello [world](http://example.com)!" }.to_json)
notifier.post text: "Hello [world](http://example.com)!"
end
end
slack-notifier-2.4.0/spec/integration/ 0000775 0000000 0000000 00000000000 14045314334 0017705 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/integration/ping_integration_test.rb 0000664 0000000 0000000 00000001013 14045314334 0024624 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# encoding: utf-8
require_relative "../../lib/slack-notifier"
ruby = if defined?(JRUBY_VERSION)
"jruby #{JRUBY_VERSION}"
else
"ruby #{RUBY_VERSION}"
end
puts "testing with #{ruby}"
notifier = Slack::Notifier.new ENV["SLACK_WEBHOOK_URL"], username: "notifier"
notifier.ping "hello", channel: ["#general", "#random"]
notifier.ping "hello/こんにちは from notifier test script on #{ruby}\225"
notifier.ping attachments: [{ color: "#1BF5AF", fallback: "fallback", text: "attachment" }]
slack-notifier-2.4.0/spec/lib/ 0000775 0000000 0000000 00000000000 14045314334 0016130 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/lib/slack-notifier/ 0000775 0000000 0000000 00000000000 14045314334 0021042 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/lib/slack-notifier/config_spec.rb 0000664 0000000 0000000 00000004067 14045314334 0023655 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::Config do
describe "#http_client" do
it "is Util::HTTPClient if not set" do
subject = described_class.new
expect(subject.http_client).to eq Slack::Notifier::Util::HTTPClient
end
it "sets a new client class if given one" do
new_client = class_double("Slack::Notifier::Util::HTTPClient", post: nil)
subject = described_class.new
subject.http_client new_client
expect(subject.http_client).to eq new_client
end
it "raises an ArgumentError if given class does not respond to ::post" do
subject = described_class.new
expect do
subject.http_client :nope
end.to raise_error ArgumentError
end
end
describe "#defaults" do
it "is an empty hash by default" do
subject = described_class.new
expect(subject.defaults).to eq({})
end
it "sets a hash to default if given" do
subject = described_class.new
subject.defaults foo: :bar
expect(subject.defaults).to eq foo: :bar
end
it "raises ArgumentError if not given a hash" do
subject = described_class.new
expect do
subject.defaults :nope
end.to raise_error ArgumentError
end
end
describe "#middleware" do
it "is [:format_message, :format_attachments, :at] if not set" do
subject = described_class.new
expect(subject.middleware).to eq %i[format_message format_attachments at channels]
end
it "takes an array or a splat of args" do
subject = described_class.new
subject.middleware :layer, :two
expect(subject.middleware).to eq %i[layer two]
subject.middleware %i[one layer]
expect(subject.middleware).to eq %i[one layer]
end
it "allows passing options to middleware stack" do
subject = described_class.new
subject.middleware one: { opts: :for_one },
two: { opts: :for_two }
expect(subject.middleware).to eq one: { opts: :for_one },
two: { opts: :for_two }
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/ 0000775 0000000 0000000 00000000000 14045314334 0024670 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/at_spec.rb 0000664 0000000 0000000 00000001472 14045314334 0026637 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::At do
it "can handle array at" do
subject = described_class.new(:notifier)
payload = { text: "hello", at: %i[john ken here] }
expect(subject.call(payload)).to eq text: "<@john> <@ken> hello"
end
it "can handle single at option" do
subject = described_class.new(:notifier)
payload = { text: "hello", at: :alice }
expect(subject.call(payload)).to eq text: "<@alice> hello"
end
it "generates :text in payload if given :at & no :text" do
subject = described_class.new(:notifier)
input_payload = { at: [:here], attachments: [{ text: "hello" }] }
output_payload = { text: " ", attachments: [{ text: "hello" }] }
expect(subject.call(input_payload)).to eq output_payload
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/base_spec.rb 0000664 0000000 0000000 00000004207 14045314334 0027144 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::Base do
before(:each) do
@registry_backup = Slack::Notifier::PayloadMiddleware.registry.dup
Slack::Notifier::PayloadMiddleware.send(:remove_instance_variable, :@registry)
end
after(:each) do
# cleanup middleware registry
Slack::Notifier::PayloadMiddleware.registry
Slack::Notifier::PayloadMiddleware.send(:remove_instance_variable, :@registry)
# cleanup object constants
Object.send(:remove_const, :Subject) if Object.constants.include?(:Subject)
Slack::Notifier::PayloadMiddleware.send(:instance_variable_set, :@registry, @registry_backup)
end
describe "::middleware_name" do
it "registers class w/ given name" do
class Subject < Slack::Notifier::PayloadMiddleware::Base
end
expect(Slack::Notifier::PayloadMiddleware)
.to receive(:register).with(Subject, :subject)
class Subject
middleware_name :subject
end
end
it "uses symbolized name to register" do
class Subject < Slack::Notifier::PayloadMiddleware::Base
end
expect(Slack::Notifier::PayloadMiddleware)
.to receive(:register).with(Subject, :subject)
class Subject
middleware_name "subject"
end
end
end
describe "::options" do
it "allows setting default options for a middleware" do
class Subject < Slack::Notifier::PayloadMiddleware::Base
options foo: :bar
end
subject = Subject.new(:notifier)
expect(subject.options).to eq foo: :bar
subject = Subject.new(:notifier, foo: :baz)
expect(subject.options).to eq foo: :baz
end
end
describe "#initialize" do
it "sets given notifier as notifier" do
expect(described_class.new(:notifier).notifier).to eq :notifier
end
it "sets given options as opts" do
expect(described_class.new(:notifier, opts: :options).options).to eq opts: :options
end
end
describe "#call" do
it "raises NoMethodError (expects subclass to define)" do
expect do
described_class.new(:notifier).call
end.to raise_exception NoMethodError
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/channels_spec.rb 0000664 0000000 0000000 00000001154 14045314334 0030023 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::Channels do
it "leaves string channels alone" do
subject = described_class.new(:notifier)
payload = { text: "hello", channel: "hodor" }
expect(subject.call(payload)).to eq text: "hello", channel: "hodor"
end
it "splits payload into multiple if given an array of channels" do
subject = described_class.new(:notifier)
payload = { text: "hello", channel: %w[foo hodor] }
expect(subject.call(payload)).to eq [
{ text: "hello", channel: "foo" },
{ text: "hello", channel: "hodor" }
]
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/format_attachments_spec.rb 0000664 0000000 0000000 00000003163 14045314334 0032115 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::FormatAttachments do
it "passes the text of attachments through linkformatter with options[:formats]" do
subject = described_class.new(:notifier, formats: [:html])
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: [:html])
subject.call(attachments: [{ text: "hello" }])
end
it "searches through string or symbol keys" do
subject = described_class.new(:notifier)
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: %i[html markdown])
subject.call("attachments" => [{ "text" => "hello" }])
subject = described_class.new(:notifier)
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: %i[html markdown])
subject.call(attachments: [{ text: "hello" }])
end
it "can handle a single attachment" do
subject = described_class.new(:notifier)
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: %i[html markdown])
subject.call(attachments: { text: "hello" })
end
it "wraps attachment into array if given as a single hash" do
params = {
attachments: { text: "hello" }
}
payload = {
attachments: [{ text: "hello" }]
}
subject = described_class.new(:notifier)
expect(subject.call(params)).to eq payload
end
it "returns the payload unmodified if not :attachments key" do
payload = { foo: :bar }
subject = described_class.new(:notifier)
expect(subject.call(payload)).to eq payload
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/format_message_spec.rb 0000664 0000000 0000000 00000001722 14045314334 0031225 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::FormatMessage do
it "passes the text through linkformatter with options[:formats]" do
subject = described_class.new(:notifier, formats: [:html])
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: [:html])
subject.call(text: "hello")
subject = described_class.new(:notifier)
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: %i[html markdown])
subject.call(text: "hello")
subject = described_class.new(:notifier, formats: [:markdown])
expect(Slack::Notifier::Util::LinkFormatter).to receive(:format)
.with("hello", formats: [:markdown])
subject.call(text: "hello")
end
it "returns the payload unmodified if not :text key" do
payload = { foo: :bar }
subject = described_class.new(:notifier)
expect(subject.call(payload)).to eq payload
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware/stack_spec.rb 0000664 0000000 0000000 00000007527 14045314334 0027347 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware::Stack do
let(:return_one) do
double(call: 1)
end
let(:return_one_twice) do
double(call: [1, 1])
end
let(:return_two) do
double(call: 2)
end
let(:return_three) do
double(call: 3)
end
before(:each) do
# setup our middleware in the registry
@registry_backup = Slack::Notifier::PayloadMiddleware.registry.dup
Slack::Notifier::PayloadMiddleware.send(:remove_instance_variable, :@registry)
Slack::Notifier::PayloadMiddleware.register return_one, :return_one
Slack::Notifier::PayloadMiddleware.register return_one_twice, :return_one_twice
Slack::Notifier::PayloadMiddleware.register return_two, :return_two
Slack::Notifier::PayloadMiddleware.register return_three, :return_three
end
after(:each) do
# cleanup middleware registry
Slack::Notifier::PayloadMiddleware.send(:remove_instance_variable, :@registry)
Slack::Notifier::PayloadMiddleware.send(:instance_variable_set, :@registry, @registry_backup)
end
describe "::initialize" do
it "sets notifier to given notifier" do
expect(described_class.new(:notifier).notifier).to eq :notifier
end
it "has empty stack" do
expect(described_class.new(:notifier).stack).to match_array []
end
end
describe "#set" do
it "initializes each middleware w/ the notifier instance" do
expect(return_one).to receive(:new).with(:notifier)
expect(return_two).to receive(:new).with(:notifier)
described_class.new(:notifier).set(:return_one, :return_two)
end
it "creates the stack in an array" do
allow(return_one).to receive(:new).and_return(return_one)
allow(return_two).to receive(:new).and_return(return_two)
subject = described_class.new(:notifier)
subject.set(:return_one, :return_two)
expect(subject.stack).to be_a Array
expect(subject.stack.first.call).to eq 1
expect(subject.stack.last.call).to eq 2
end
it "creates a stack from hashes passing them as opts" do
expect(return_one).to receive(:new).with(:notifier, opts: :for_one)
expect(return_two).to receive(:new).with(:notifier, opts: :for_two)
subject = described_class.new(:notifier)
subject.set return_one: { opts: :for_one },
return_two: { opts: :for_two }
end
it "raises if a middleware is missing" do
expect do
described_class.new(:notifier).set(:missing)
end.to raise_exception KeyError
end
end
describe "#call" do
it "calls the middleware in order, passing return of each to the next" do
allow(return_one).to receive(:new).and_return(return_one)
allow(return_two).to receive(:new).and_return(return_two)
allow(return_three).to receive(:new).and_return(return_three)
subject = described_class.new(:notifier)
subject.set(:return_one, :return_three, :return_two)
expect(return_one).to receive(:call).with(5)
expect(return_three).to receive(:call).with(1)
expect(return_two).to receive(:call).with(3)
expect(subject.call(5)).to eq [2]
end
it "allows any middleware to return an array but other's don't need special behavior" do
allow(return_one_twice).to receive(:new).and_return(return_one_twice)
allow(return_two).to receive(:new).and_return(return_two)
subject = described_class.new(:notifier)
subject.set(:return_one_twice, :return_two)
expect(subject.call(5)).to eq [2, 2]
end
it "handles multiple middleware splitting payload" do
allow(return_one_twice).to receive(:new).and_return(return_one_twice)
allow(return_two).to receive(:new).and_return(return_two)
subject = described_class.new(:notifier)
subject.set(:return_one_twice, :return_one_twice, :return_two)
expect(subject.call(5)).to eq [2, 2, 2, 2]
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/payload_middleware_spec.rb 0000664 0000000 0000000 00000001712 14045314334 0026230 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::PayloadMiddleware do
before(:each) do
@registry_backup = described_class.registry.dup
Slack::Notifier::PayloadMiddleware.send(:remove_instance_variable, :@registry)
end
after(:each) do
described_class.send(:remove_instance_variable, :@registry)
described_class.send(:instance_variable_set, :@registry, @registry_backup)
end
describe "::registry" do
it "returns a hash if nothing set" do
expect(described_class.registry).to eq({})
end
it "returns memoized version if already set" do
described_class.instance_variable_set(:@registry, "hodor")
expect(described_class.registry).to eq "hodor"
end
end
describe "::register" do
it "adds given class to key in registry" do
MyClass = Struct.new(:myclass)
described_class.register MyClass, :my_class
expect(described_class.registry[:my_class]).to eq MyClass
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/util/ 0000775 0000000 0000000 00000000000 14045314334 0022017 5 ustar 00root root 0000000 0000000 slack-notifier-2.4.0/spec/lib/slack-notifier/util/http_client_spec.rb 0000664 0000000 0000000 00000003716 14045314334 0025702 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier::Util::HTTPClient do
describe "::post" do
it "initializes Util::HTTPClient with the given uri and params then calls" do
http_post_double = instance_double("Slack::Notifier::Util::HTTPClient")
expect(described_class)
.to receive(:new).with("uri", "params")
.and_return(http_post_double)
expect(http_post_double).to receive(:call)
described_class.post "uri", "params"
end
# http_post is really tested in the integration spec,
# where the internals are run through
end
describe "#initialize" do
it "allows setting of options for Net::HTTP" do
net_http_double = instance_double("Net::HTTP")
http_client = described_class.new URI.parse("http://example.com"),
http_options: { open_timeout: 5 }
allow(Net::HTTP).to receive(:new).and_return(net_http_double)
allow(net_http_double).to receive(:use_ssl=)
allow(net_http_double).to receive(:request).with(anything) do
Net::HTTPOK.new("GET", "200", "OK")
end
expect(net_http_double).to receive(:open_timeout=).with(5)
http_client.call
end
end
describe "#call" do
it "raises an error when the response is unsuccessful" do
net_http_double = instance_double("Net::HTTP")
http_client = described_class.new URI.parse("http://example.com"), {}
bad_request = Net::HTTPBadRequest.new("GET", "400", "Bad Request")
allow(bad_request).to receive(:body).and_return("something_bad")
allow(Net::HTTP).to receive(:new).and_return(net_http_double)
allow(net_http_double).to receive(:use_ssl=)
allow(net_http_double).to receive(:request).with(anything) do
bad_request
end
expect { http_client.call }.to raise_error(Slack::Notifier::APIError,
/something_bad \(HTTP Code 400\)/)
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier/util/link_formatter_spec.rb 0000664 0000000 0000000 00000017071 14045314334 0026404 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# encoding: utf-8
# rubocop:disable Metrics/LineLength
RSpec.describe Slack::Notifier::Util::LinkFormatter do
describe "initialize & formatted" do
it "can be initialized without format args" do
subject = described_class.new("Hello World")
expect(subject.formatted()).to eq("Hello World")
end
it "can be initialized with format args" do
subject = described_class.new("Hello World", formats: [:html])
expect(subject.formatted()).to eq("Hello World")
end
end
describe "::format" do
it "formats html links" do
formatted = described_class.format("Hello World, enjoy this.")
expect(formatted).to include("")
end
it "formats markdown links" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com).")
expect(formatted).to include("")
end
it "formats markdown links in brackets" do
formatted = described_class.format("Hello World, enjoy [[this](http://example.com) in brackets].")
expect(formatted).to eq("Hello World, enjoy [ in brackets].")
end
it "formats markdown links with no title" do
formatted = described_class.format("Hello World, enjoy [](http://example.com).")
expect(formatted).to include("")
end
it "handles multiple html links" do
formatted = described_class.format("Hello World, enjoy thisthis2.")
expect(formatted).to include("")
expect(formatted).to include("")
end
it "handles multiple markdown links" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com)[this2](http://example2.com).")
expect(formatted).to include("")
expect(formatted).to include("")
end
it "handles mixed html & markdown links" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com)this2.")
expect(formatted).to include("")
expect(formatted).to include("")
end
if "".respond_to? :scrub
context "when on ruby 2.1+ or have string-scrub installed" do
it "handles invalid unicode sequences" do
expect do
described_class.format("This sequence is invalid: \255")
end.not_to raise_error
end
it "replaces invalid unicode sequences with the unicode replacement character" do
formatted = described_class.format("\255")
expect(formatted).to eq "\uFFFD"
end
end
end
it "doesn't replace valid Japanese" do
formatted = described_class.format("こんにちは")
expect(formatted).to eq "こんにちは"
end
it "handles mailto links in markdown" do
formatted = described_class.format("[John](mailto:john@example.com)")
expect(formatted).to eq ""
end
it "handles mailto links in html" do
formatted = described_class.format("John")
expect(formatted).to eq ""
end
it "handles links with trailing parentheses" do
formatted = described_class.format("Hello World, enjoy [foo(bar)](http://example.com/foo(bar))bar(foo)")
expect(formatted).to include("http://example.com/foo(bar)|foo(bar)")
expect(formatted).to include("http://example.com/bar(foo)|bar(foo)")
end
it "formats a number of differently formatted links" do
input_output = {
"Hello World, enjoy [this](http://example.com)." =>
"Hello World, enjoy .",
"Hello World, enjoy [[this](http://example.com) in brackets]." =>
"Hello World, enjoy [ in brackets].",
"Hello World, enjoy ([this](http://example.com) in parens)." =>
"Hello World, enjoy ( in parens).",
"Hello World, enjoy [](http://example.com)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with query](http://example.com?foo=bar)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with fragment](http://example.com/#foo-bar)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with parens](http://example.com/foo(bar)/baz)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with query](http://example.com/(parens)?foo=bar)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with parens](http://example.com/baz?bang=foo(bar))." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with fragment](http://example.com/(parens)#foo-bar)." =>
"Hello World, enjoy .",
"Hello World, enjoy [link with fragment](http://example.com/#foo-bar=(baz))." =>
"Hello World, enjoy .",
"Hello World, enjoy [this](http://example.com?foo=bar)[this2](http://example2.com)." =>
"Hello World, enjoy .",
"Hello World, enjoy [this](http://example.com?foo=bar) [this2](http://example2.com/#fragment)." =>
"Hello World, enjoy .",
"Hello World, enjoy [this](http://example.com)this2." =>
"Hello World, enjoy .",
"Hello world, [John](mailto:john@example.com)." =>
"Hello world, .",
"Hello World, enjoy [foo(bar)](http://example.com/foo(bar))bar(foo)" =>
"Hello World, enjoy "
}
input_output.each do |input, output|
expect(described_class.format(input)).to eq output
end
end
context "with a configured stack" do
it "only formats html if html is the only item in formats" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com)this2.", formats: [:html])
expect(formatted).to eq "Hello World, enjoy [this](http://example.com)."
end
it "only formats markdown if markdown is the only item in formats" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com)this2.", formats: [:markdown])
expect(formatted).to eq "Hello World, enjoy this2."
end
it "doesn't format if formats is empty" do
formatted = described_class.format("Hello World, enjoy [this](http://example.com)this2.", formats: [])
expect(formatted).to eq "Hello World, enjoy [this](http://example.com)this2."
end
end
end
end
slack-notifier-2.4.0/spec/lib/slack-notifier_spec.rb 0000664 0000000 0000000 00000005571 14045314334 0022411 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
RSpec.describe Slack::Notifier do
let(:mock_http) do
class_double("Slack::Notifier::Util::HTTPClient", post: :posted)
end
subject { described_class.new "http://example.com", http_client: mock_http }
describe "#initialize" do
it "sets the given hook_url to the endpoint URI" do
expect(subject.endpoint).to eq URI.parse("http://example.com")
end
it "sets the default_payload options" do
subject = described_class.new "http://example.com", channel: "foo"
expect(subject.config.defaults[:channel]).to eq "foo"
end
it "sets a custom http client" do
subject = described_class.new "http://example.com", http_client: mock_http
expect(subject.config.http_client).to eq mock_http
end
describe "when given a block" do
it "yields the config object" do
test_double = double("Slack::Notifier::Config", defaults: {}, middleware: [])
allow_any_instance_of(Slack::Notifier).to receive(:config).and_return(test_double)
expect(test_double).to receive(:test_init_method).with("foo")
described_class.new "http://example.com" do
test_init_method "foo"
end
end
end
end
describe "#ping" do
it "calls #post with the message as the text key in #post" do
subject = described_class.new "http://example.com"
expect(subject).to receive(:post).with text: "message"
subject.ping "message"
end
end
describe "#post" do
def notifier_with_defaults
mock_client = mock_http
described_class.new "http://example.com" do
defaults channel: "default",
user: "rocket"
http_client mock_client
end
end
it "uses the defaults set on initialization" do
subject = notifier_with_defaults
expect(mock_http).to receive(:post).with(
URI.parse("http://example.com"),
payload: '{"channel":"default","user":"rocket","text":"hello"}'
)
subject.post text: "hello"
end
it "allows overriding the set defaults" do
subject = notifier_with_defaults
expect(mock_http).to receive(:post).with(
URI.parse("http://example.com"),
payload: '{"channel":"new","user":"ship","text":"hello"}'
)
subject.post text: "hello", channel: "new", user: "ship"
end
it "calls the middleware stack with the payload" do
subject = notifier_with_defaults
stack = instance_double("Slack::Notifier::PayloadMiddleware::Stack")
subject.instance_variable_set(:@middleware, stack)
expect(stack).to receive(:call)
.with(channel: "default", user: "rocket")
.and_return([test: "stack"])
expect(mock_http).to receive(:post).with(
URI.parse("http://example.com"),
payload: '{"test":"stack"}'
)
responses = subject.post
expect(responses).to eq([:posted])
end
end
end
slack-notifier-2.4.0/spec/spec_helper.rb 0000664 0000000 0000000 00000001353 14045314334 0020202 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require "rspec"
require "slack-notifier"
require "pry" if ENV["DEBUG"]
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
mocks.verify_partial_doubles = true
end
config.filter_run :focus
config.run_all_when_everything_filtered = true
config.disable_monkey_patching!
config.example_status_persistence_file_path = "spec/examples.txt"
config.warnings = ENV["DEBUG"] ? false : true
config.default_formatter = "doc" if config.files_to_run.one?
config.order = :random
Kernel.srand config.seed
end