HornsAndHooves-slackiq 1.2.1 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d33511bc1da33236ea8a6b3c03b4d1e2a26305fbc55d9d8db48cd71644c713c
4
- data.tar.gz: da45ca5736a952f4f75bced5cfc5c1f3f4858ad7d1a0edb383558785458308c2
3
+ metadata.gz: 5cf82d00d5319230c303822b593bc4350c99f18cc5bd7539247c2622cc35b021
4
+ data.tar.gz: 40f830f297f3469d0b32bd104c6a3eeaa1cb71c0da5e8b31a8813af7bcfd5294
5
5
  SHA512:
6
- metadata.gz: ebe7cbb7a607f79063c21f549ce0a2680ab8c3d96f0da08b00c65110a37aa3eb17228ac21ffc133933d53bb037e3f94b3afd718122e43462e5bd5251b01fdede
7
- data.tar.gz: a1907bad4728a9f07d581901a2e3e57b60e8ddd1079a9d631cc84c5e503db42d90dbfde4936b937261ee878062274d276b4ed6b9e5105cea3cc34502238f7df4
6
+ metadata.gz: a07bc1b7e428c2ceac87a093118307b549dd2cae23ce12872a80f4347459951dabef1af998853f8ac54a7abdafe109805dc20a0a7703b4b4883029dc030a554d
7
+ data.tar.gz: 5281494bd6f24caf781621d41960f9db72655a676684b9212ed51dc721695bd8f704236f8e6c8f090febda3a7a78795198af537fa869a3daf159d6bff3d2798d
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "slackiq/version"
6
6
 
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
20
20
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  s.require_paths = ["lib"]
22
22
 
23
- s.add_dependency "httparty"
24
23
  s.add_development_dependency "bundler"
25
24
  s.add_development_dependency "rake"
26
25
  s.add_development_dependency "rspec"
@@ -1,156 +1,195 @@
1
1
  require "slackiq/version"
2
2
 
3
3
  require "net/http"
4
+ require "uri"
4
5
  require "json"
5
- require "httparty"
6
+ require "date"
6
7
 
7
- require "slackiq/time_helper"
8
+ class Slackiq
8
9
 
9
- require "active_support" # For Hash#except
10
+ attr_reader :options
10
11
 
11
- module Slackiq
12
-
13
- class << self
14
-
15
- # Configure all of the webhook URLs you're going to use
16
- # @author Jason Lew
17
- def configure(webhook_urls={})
18
- raise "Argument must be a Hash" unless webhook_urls.class == Hash
19
- @@webhook_urls = webhook_urls
20
- end
21
-
22
- # Send a notification to Slack with Sidekiq info about the batch
23
- # @author Jason Lew
24
- def notify(options={})
25
- url = @@webhook_urls[options[:webhook_name]]
26
- title = options[:title]
27
- # description = options[:description]
28
- status = options[:status]
29
-
30
- if (bid = options[:bid]) && status.nil?
31
- raise <<~EOT.chomp unless defined?(Sidekiq::Batch::Status)
32
- Sidekiq::Batch::Status is not defined. \
33
- Are you sure Sidekiq Pro is set up correctly?
34
- EOT
35
- status = Sidekiq::Batch::Status.new(bid)
36
- end
37
-
38
- color = options[:color] || color_for(status)
39
-
40
- extra_fields = options.except(:webhook_name, :title, :description, :status)
12
+ # @param options [Hash]
13
+ def self.notify(options)
14
+ raise "Need to run Slackiq.configure first" if @webhook_urls.nil?
15
+ new(options.merge(webhook_urls: @webhook_urls)).execute
16
+ end
41
17
 
42
- fields = []
18
+ # @param options [Hash]
19
+ def self.configure(webhook_urls={})
20
+ @webhook_urls = webhook_urls
21
+ end
43
22
 
44
- if status
45
- created_at = status.created_at
23
+ # @param options [Hash]
24
+ def initialize(options)
25
+ @options = options
26
+ end
46
27
 
47
- if created_at
48
- time_now = Time.now
49
- duration = Slackiq::TimeHelper.elapsed_time_humanized(created_at, time_now)
50
- time_now_title = (status.complete? ? "Completed" : "Now")
51
- end
28
+ # Send a notification to Slack with Sidekiq info about the batch
29
+ def execute
30
+ time_now = Time.now
52
31
 
53
- total_jobs = status.total
54
- failures = status.failures
55
- jobs_run = total_jobs - status.pending
56
-
57
- completion_percentage = (jobs_run/total_jobs.to_f)*100
58
- failure_percentage = (failures/total_jobs.to_f)*100 if total_jobs && failures
59
-
60
- # Round to two decimal places
61
- decimal_places = 2
62
- completion_percentage = completion_percentage.round(decimal_places)
63
- failure_percentage = failure_percentage.round(decimal_places)
64
-
65
- description = status.description
66
-
67
- fields += [
68
- {
69
- title: "Created",
70
- value: Slackiq::TimeHelper.format(created_at),
71
- short: true
72
- },
73
- {
74
- title: time_now_title,
75
- value: Slackiq::TimeHelper.format(time_now),
76
- short: true
77
- },
78
- {
79
- title: "Duration",
80
- value: duration,
81
- short: true
82
- },
83
- {
84
- title: "Total Jobs",
85
- value: total_jobs,
86
- short: true
87
- },
88
- {
89
- title: "Jobs Run",
90
- value: jobs_run,
91
- short: true
92
- },
93
- {
94
- title: "Completion %",
95
- value: "#{completion_percentage}%",
96
- short: true
97
- },
98
- {
99
- title: "Failures",
100
- value: status.failures,
101
- short: true
102
- },
103
- {
104
- title: "Failure %",
105
- value: "#{failure_percentage}%",
106
- short: true
107
- },
108
- ]
109
- end
32
+ title = options[:title]
33
+ status = options[:status]
110
34
 
111
- # Add extra fields
112
- fields += extra_fields.map do |title, value|
113
- {
114
- title: title,
115
- value: value,
116
- short: false
117
- }
118
- end
35
+ if (bid = options[:bid]) && status.nil?
36
+ raise <<~EOT.chomp unless defined?(Sidekiq::Batch::Status)
37
+ Sidekiq::Batch::Status is not defined. \
38
+ Are you sure Sidekiq Pro is set up correctly?
39
+ EOT
40
+ status = Sidekiq::Batch::Status.new(bid)
41
+ end
119
42
 
120
- attachments = [
121
- {
122
- fallback: title,
123
- color: color,
124
- title: title,
125
- text: description,
126
- fields: fields,
127
- }
43
+ return if status.nil?
44
+
45
+ color = options[:color] || color_for(status)
46
+
47
+ duration = elapsed_time_humanized(status.created_at, time_now)
48
+ time_title = status.complete? ? "Completed" : "Now"
49
+ jobs_run = status.total - status.pending
50
+
51
+ completion_percentage = percentage(jobs_run / status.total.to_f)
52
+ failure_percentage = percentage(status.failures / status.total.to_f)
53
+
54
+ fields = [
55
+ {
56
+ title: title,
57
+ value: status.description,
58
+ short: false
59
+ },
60
+ {
61
+ title: "Batch ID",
62
+ value: status.bid,
63
+ short: false
64
+ },
65
+ {
66
+ title: "Created",
67
+ value: time_format(status.created_at),
68
+ short: true
69
+ },
70
+ {
71
+ title: time_title,
72
+ value: time_format(time_now),
73
+ short: true
74
+ },
75
+ {
76
+ title: "Duration",
77
+ value: duration,
78
+ short: true
79
+ },
80
+ {
81
+ title: "Total Jobs",
82
+ value: status.total,
83
+ short: true
84
+ },
85
+ {
86
+ title: "Jobs Run",
87
+ value: jobs_run,
88
+ short: true
89
+ },
90
+ {
91
+ title: "Completion %",
92
+ value: completion_percentage,
93
+ short: true
94
+ },
95
+ {
96
+ title: "Failures",
97
+ value: status.failures,
98
+ short: true
99
+ },
100
+ {
101
+ title: "Failure %",
102
+ value: failure_percentage,
103
+ short: true
104
+ }
105
+ ]
106
+
107
+ body = {
108
+ attachments: [
109
+ fields: fields,
110
+ color: color
128
111
  ]
112
+ }
113
+ http_post(body)
114
+ end
129
115
 
130
- body = { attachments: attachments }.to_json
116
+ # @param data [Hash]
117
+ private def http_post(data)
118
+ url = options[:webhook_urls].fetch(options[:webhook_name])
119
+ uri = URI.parse(url)
120
+ http = Net::HTTP.new(uri.host, uri.port)
121
+ http.use_ssl = true if uri.port == 443
122
+
123
+ header = {"Content-Type": "application/json"}
124
+ request = Net::HTTP::Post.new(uri.request_uri, header)
125
+ request.body = data.to_json
126
+ http.request(request)
127
+ end
131
128
 
132
- HTTParty.post(url, body: body)
133
- end
129
+ # @param number [Numeric]
130
+ # @param precision [Integer]
131
+ # @param multiply100 [Boolean]
132
+ private def percentage(number, precision: 2, multiply100: true)
133
+ number = number * 100 if multiply100
134
+ rounded = number.to_f.round(precision)
135
+ format = number == rounded.to_i ? "%.f" : "%.#{precision}f"
136
+ (format % rounded) + "%"
137
+ end
134
138
 
135
- # Send a notification without Sidekiq batch info
136
- # @author Jason Lew
137
- def message(text, options)
138
- url = @@webhook_urls[options[:webhook_name]]
139
- body = { text: text }.to_json
140
- HTTParty.post(url, body: body)
139
+ # @param status [Sidekiq::Batch::Status]
140
+ private def color_for(status)
141
+ colors = {
142
+ red: "f00000",
143
+ yellow: "ffc000",
144
+ green: "009800"
145
+ }
146
+
147
+ if status.total == 0
148
+ colors[:yellow]
149
+ elsif status.failures > 0
150
+ colors[:red]
151
+ elsif status.failures == 0
152
+ colors[:green]
153
+ else
154
+ colors[:yellow]
141
155
  end
156
+ end
157
+
158
+ # @param t0 [DateTime]
159
+ # @param t1 [DateTime]
160
+ private def elapsed_time_humanized(t0, t1, precision: 2)
161
+ time_humanize(
162
+ elapsed_seconds(t0, t1, precision: precision),
163
+ precision: precision
164
+ )
165
+ end
142
166
 
143
- private
144
- def color_for(status)
145
- if status.total == 0
146
- "#FBBD08" # yellow
147
- elsif status.failures > 0
148
- "#FF0000" # red
149
- elsif status.failures == 0
150
- "#1C9513" # green
151
- else
152
- "#FBBD08" # yellow
167
+ # @param t0 [DateTime]
168
+ # @param t1 [DateTime]
169
+ private def elapsed_seconds(t0, t1, precision: 2)
170
+ dt0 = t0.to_datetime
171
+ dt1 = t1.to_datetime
172
+ ((dt1 - dt0) * 24 * 60 * 60).to_f.round(precision)
173
+ end
174
+
175
+ # http://stackoverflow.com/questions/4136248/how-to-generate-a-human-readable-time-range-using-ruby-on-rails
176
+ # @param secs [Integer]
177
+ private def time_humanize(secs, precision: 2)
178
+ [[60, :s], [60, :m], [24, :h], [1000, :d]].map do |count, name|
179
+ if secs > 0
180
+ secs, n = secs.divmod(count)
181
+ if name == :s
182
+ num = n.to_f == n.to_i ? n.to_i : n.to_f
183
+ "%.#{precision}f#{name}" % num
184
+ else
185
+ "#{n.to_i}#{name}"
186
+ end
153
187
  end
154
- end
188
+ end.compact.reverse.join(" ")
189
+ end
190
+
191
+ # @param time [DateTime]
192
+ private def time_format(time)
193
+ time.strftime("%D @ %I:%M:%S %P")
155
194
  end
156
195
  end
@@ -1,3 +1,3 @@
1
- module Slackiq
2
- VERSION = "1.2.1"
1
+ class Slackiq
2
+ VERSION = "1.3.0".freeze
3
3
  end
metadata CHANGED
@@ -1,30 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: HornsAndHooves-slackiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - HornsAndHooves
8
8
  - Peter Maneykowski
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-04-03 00:00:00.000000000 Z
12
+ date: 2020-07-12 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: httparty
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: bundler
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -86,13 +72,12 @@ files:
86
72
  - bin/console
87
73
  - bin/setup
88
74
  - lib/slackiq.rb
89
- - lib/slackiq/time_helper.rb
90
75
  - lib/slackiq/version.rb
91
76
  homepage: https://github.com/HornsAndHooves/slackiq
92
77
  licenses:
93
78
  - MIT
94
79
  metadata: {}
95
- post_install_message:
80
+ post_install_message:
96
81
  rdoc_options: []
97
82
  require_paths:
98
83
  - lib
@@ -108,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
93
  version: '0'
109
94
  requirements: []
110
95
  rubygems_version: 3.0.6
111
- signing_key:
96
+ signing_key:
112
97
  specification_version: 4
113
98
  summary: 'HornsAndHooves: Slack and Sidekiq Pro integration'
114
99
  test_files: []
@@ -1,37 +0,0 @@
1
- require "date"
2
-
3
- module Slackiq
4
- module TimeHelper
5
-
6
- class << self
7
-
8
- def elapsed_time_humanized(t0, t1)
9
- humanize(elapsed_seconds(t0, t1))
10
- end
11
-
12
- def elapsed_seconds(t0, t1)
13
- dt0 = t0.to_datetime
14
- dt1 = t1.to_datetime
15
- ((dt1-dt0)*24*60*60).to_f.round(2)
16
- end
17
-
18
- # http://stackoverflow.com/questions/4136248/how-to-generate-a-human-readable-time-range-using-ruby-on-rails
19
- def humanize(secs)
20
- [[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
21
- if secs > 0
22
- secs, n = secs.divmod(count)
23
- if name == :s
24
- "%.2f#{name}" % [n.to_f]
25
- else
26
- "#{n.to_i}#{name}"
27
- end
28
- end
29
- }.compact.reverse.join(" ")
30
- end
31
-
32
- def format(time)
33
- time.strftime("%D @ %r").gsub("PM", "pm").gsub("AM", "am")
34
- end
35
- end
36
- end
37
- end