noch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b54210e4e2f225f0932fba93629675c8a41750e4acb4eb310bac762903121f32
4
+ data.tar.gz: 45aac03c31bf11d7809ba716cf3e5f7aaf87ba9e57c3e272eb663cbd8c87952b
5
+ SHA512:
6
+ metadata.gz: 415fb99e352a6b0bb6358b0dbffe83adbe3fa8303672ee16e27003dca7d04edfeca63021ddd8443312b968f724d51e53140e6f3100683b1c689ede4e28873fcc
7
+ data.tar.gz: a92daf2edc25e3ce26f72ede45875beca949c66bd76d5ae949e9d8089c61a6b698c98765cbd04c618ee45bb1dad5010cc2ab8eb1197b45ef334057df8ca4f610
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .byebug_history
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in NOCH.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'cucumber'
8
+ gem 'http'
9
+ gem 'mock_redis'
10
+ end
@@ -0,0 +1,131 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ noch (0.1.0)
5
+ redis
6
+ slack-ruby-client
7
+ telegram-bot
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (5.2.3)
13
+ actionview (= 5.2.3)
14
+ activesupport (= 5.2.3)
15
+ rack (~> 2.0)
16
+ rack-test (>= 0.6.3)
17
+ rails-dom-testing (~> 2.0)
18
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
19
+ actionview (5.2.3)
20
+ activesupport (= 5.2.3)
21
+ builder (~> 3.1)
22
+ erubi (~> 1.4)
23
+ rails-dom-testing (~> 2.0)
24
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
25
+ activesupport (5.2.3)
26
+ concurrent-ruby (~> 1.0, >= 1.0.2)
27
+ i18n (>= 0.7, < 2)
28
+ minitest (~> 5.1)
29
+ tzinfo (~> 1.1)
30
+ addressable (2.6.0)
31
+ public_suffix (>= 2.0.2, < 4.0)
32
+ backports (3.15.0)
33
+ builder (3.2.3)
34
+ concurrent-ruby (1.1.5)
35
+ crass (1.0.4)
36
+ cucumber (3.1.2)
37
+ builder (>= 2.1.2)
38
+ cucumber-core (~> 3.2.0)
39
+ cucumber-expressions (~> 6.0.1)
40
+ cucumber-wire (~> 0.0.1)
41
+ diff-lcs (~> 1.3)
42
+ gherkin (~> 5.1.0)
43
+ multi_json (>= 1.7.5, < 2.0)
44
+ multi_test (>= 0.1.2)
45
+ cucumber-core (3.2.1)
46
+ backports (>= 3.8.0)
47
+ cucumber-tag_expressions (~> 1.1.0)
48
+ gherkin (~> 5.0)
49
+ cucumber-expressions (6.0.1)
50
+ cucumber-tag_expressions (1.1.1)
51
+ cucumber-wire (0.0.1)
52
+ diff-lcs (1.3)
53
+ domain_name (0.5.20190701)
54
+ unf (>= 0.0.5, < 1.0.0)
55
+ erubi (1.8.0)
56
+ faraday (0.15.4)
57
+ multipart-post (>= 1.2, < 3)
58
+ faraday_middleware (0.13.1)
59
+ faraday (>= 0.7.4, < 1.0)
60
+ gherkin (5.1.0)
61
+ gli (2.18.1)
62
+ hashie (3.6.0)
63
+ http (4.1.1)
64
+ addressable (~> 2.3)
65
+ http-cookie (~> 1.0)
66
+ http-form_data (~> 2.0)
67
+ http_parser.rb (~> 0.6.0)
68
+ http-cookie (1.0.3)
69
+ domain_name (~> 0.5)
70
+ http-form_data (2.1.1)
71
+ http_parser.rb (0.6.0)
72
+ httpclient (2.8.3)
73
+ i18n (1.6.0)
74
+ concurrent-ruby (~> 1.0)
75
+ loofah (2.2.3)
76
+ crass (~> 1.0.2)
77
+ nokogiri (>= 1.5.9)
78
+ mini_portile2 (2.4.0)
79
+ minitest (5.11.3)
80
+ mock_redis (0.21.0)
81
+ multi_json (1.13.1)
82
+ multi_test (0.1.2)
83
+ multipart-post (2.1.1)
84
+ nokogiri (1.10.3)
85
+ mini_portile2 (~> 2.4.0)
86
+ public_suffix (3.1.1)
87
+ rack (2.0.7)
88
+ rack-test (1.1.0)
89
+ rack (>= 1.0, < 3)
90
+ rails-dom-testing (2.0.3)
91
+ activesupport (>= 4.2.0)
92
+ nokogiri (>= 1.6)
93
+ rails-html-sanitizer (1.0.4)
94
+ loofah (~> 2.2, >= 2.2.2)
95
+ rake (10.5.0)
96
+ redis (4.1.2)
97
+ slack-ruby-client (0.14.2)
98
+ activesupport
99
+ faraday (>= 0.9)
100
+ faraday_middleware
101
+ gli
102
+ hashie
103
+ websocket-driver
104
+ telegram-bot (0.14.2)
105
+ actionpack (>= 4.0, < 6.0)
106
+ activesupport (>= 4.0, < 6.0)
107
+ httpclient (~> 2.7)
108
+ thread_safe (0.3.6)
109
+ tzinfo (1.2.5)
110
+ thread_safe (~> 0.1)
111
+ unf (0.1.4)
112
+ unf_ext
113
+ unf_ext (0.0.7.6)
114
+ websocket-driver (0.7.1)
115
+ websocket-extensions (>= 0.1.0)
116
+ websocket-extensions (0.1.4)
117
+
118
+ PLATFORMS
119
+ ruby
120
+
121
+ DEPENDENCIES
122
+ bundler (~> 2.0)
123
+ cucumber
124
+ http
125
+ minitest (~> 5.0)
126
+ mock_redis
127
+ noch!
128
+ rake (~> 10.0)
129
+
130
+ BUNDLED WITH
131
+ 2.0.1
@@ -0,0 +1,79 @@
1
+ # NOCH
2
+
3
+ NOCH stands for Notify On CHange.
4
+ Suppose you have a job running scheduled to check something. Every execution will have a result and you'll want to notify when something go wrong.
5
+ Ok, if several times the result is the same...Do you want to notify every time the job run for the same result?
6
+ If you do not, NOCH can help you with this kind of situations.
7
+ NOCH will notify when status change its value by using three simple methods
8
+
9
+ `ok!`
10
+
11
+ `warning!`
12
+
13
+ `critical!`
14
+
15
+ `skip!`
16
+
17
+ Notifications are sent to Slack channel and or Telegram group
18
+
19
+
20
+
21
+
22
+ ## Requirements
23
+
24
+ This uses [Redis](https://redis.io/) to save states. If your Redis runs out of the job run, you have to pass `REDIS_HOST` and `REDIS_PORT`
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'noch'
32
+ ```
33
+
34
+ And then execute:
35
+
36
+ $ bundle
37
+
38
+ Or install it yourself as:
39
+
40
+ $ gem install noch
41
+
42
+ ## Usage
43
+
44
+ Create a file with a name you want.
45
+
46
+ ```
47
+ # my_alert_script.rb
48
+
49
+ # SLACK 00000000000000000
50
+ # TELEGRAM 111111111111
51
+
52
+ include NOCH
53
+
54
+ if everything_is_ok?
55
+ ok! "We are rocking!"
56
+ else
57
+ critical! message: 'mmlpqtp', data: {foo: 'bar'}
58
+ end
59
+
60
+ ```
61
+
62
+ Run it!
63
+
64
+ TELEGRAM_TOKEN=<your_telegram_bot_token> TELEGRAM_USER=<your-telegram-user> SLACK_API_TOKEN=<your_slack_api_token> REDIS_HOST=<your-redis-host> REDIS_PORT=<your-redis-port> ruby my_alert_script.rb
65
+
66
+ ## Test
67
+
68
+ Just run `rake test` to run the tests and ensure all pass.
69
+
70
+ To install this gem onto your local machine, run `bundle exec rake install`.
71
+
72
+ ## Contributing
73
+
74
+ Bug reports and pull requests are welcome on GitHub at https://github.com/romanrod/noch.
75
+
76
+
77
+ ## TODO
78
+
79
+ I want to add Telegram for notification messages
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => :test
5
+
6
+ task :test do
7
+ system 'cucumber'
8
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "NOCH"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ module NOCH
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,263 @@
1
+ require "NOCH/version"
2
+ require 'slack-ruby-client'
3
+ require 'redis'
4
+ require 'telegram/bot'
5
+
6
+ module NOCH
7
+
8
+ class Error < StandardError; end
9
+
10
+ class << self
11
+ # Sets default status. Used the first time execution with passed status
12
+ # @param [String] to: the status to be saved
13
+ # @return [Hash]
14
+ def default to, data = nil
15
+ {
16
+ "status" => to,
17
+ "data" => data
18
+ }
19
+ end
20
+
21
+ def included(base)
22
+ @@alert_file = caller.select{|l| l.include? "`include'"}.first.split(".").first
23
+ @@alert_name = @@alert_file.split('/').last.split('_').map{|w| w.capitalize}.join(" ")
24
+ @@redis_host = ENV['REDIS_HOST'] || 'localhost'
25
+ @@redis_port = ENV['REDIS_PORT'] || '6379'
26
+ @@redis ||= ::Redis.new(host: @@redis_host, port: @@redis_port)
27
+ @@slack_client = if ENV['SLACK_API_TOKEN']
28
+ ::Slack.configure do |config|
29
+ config.token = ENV['SLACK_API_TOKEN']
30
+ end
31
+ ::Slack::Web::Client.new
32
+ end
33
+ @@telegram_client = if ENV['TELEGRAM_TOKEN'] and ENV['TELEGRAM_USER']
34
+ ::Telegram::Bot::Client.new(ENV['TELEGRAM_TOKEN'], ENV['TELEGRAM_USER'])
35
+ end
36
+ end
37
+
38
+ # Returns the name of the alert
39
+ def alert_name
40
+ @@alert_name
41
+ end
42
+
43
+ # Returns the current status if the passed status is differente. If there is no current status save passed status with default data
44
+ # @param [String] to: the status to be evaluated
45
+ # @return [String] status: if passed value is different
46
+ # @return [Hash] Data: data to be saved
47
+ def changed to, data
48
+ if (current = self.get_status)
49
+ current_status = ::JSON.parse(current)['status']
50
+ return current_status if current_status != to
51
+ to
52
+ else
53
+ NOCH.create_object(to, data)
54
+ end
55
+ end
56
+
57
+ # Returns the current status of the alert
58
+ # @return [Hash] {"status"=>"ok", "last_change"=>"2019-07-17T22:51:03.081-03:00", "message"=>nil, "data"=>nil}
59
+ def get_status
60
+ @@redis.get(@@alert_name)
61
+ end
62
+
63
+ # Sends notification if status change. If not returns nil
64
+ # @param [String] what: current status
65
+ # @param [optional] opts: The arguments. Could be String for only message text or a Hash to pass :message and :data
66
+ # @return [Boolean] true if notification is sent, false if nothing changed
67
+ # def evaluate_for what, message = nil, data = nil
68
+ def evaluate_for what, opts = nil
69
+ if opts.is_a? Hash
70
+ message = opts.delete(:message)
71
+ data = opts.delete(:data)
72
+ else
73
+ message = opts
74
+ end
75
+ raise "Unknown status #{what}. Allowed: ['ok','warning','critical','skip']" unless ['ok','warning','critical','skip'].include? what
76
+ from = self.changed(what, data)
77
+ !!(if (from != what)
78
+ self.set what, message, data
79
+ self.notify(from, what, message, data) unless what == 'skip'
80
+ end)
81
+ end
82
+
83
+ # Saves into Redis the alert with `to` as initial status
84
+ # @param [String] to: the value of the status to be saved
85
+ # @param [Hash] data: the data to be saved
86
+ def create_object(to, data)
87
+ return to if @@redis.set(@@alert_name, self.default(to, data).to_json) == "OK"
88
+ raise StandardError.new("Status `#{to}` could not be saved")
89
+ end
90
+
91
+ # Saves status, message and data into Redis
92
+ # @param [String] to: status to save. It could be ['ok','warning','skip','critical']
93
+ # @param [String] message (optional): The message to be sent on notification
94
+ # @param [Hash] data(optional): data to be saved for future evaluation if needed
95
+ def set to, message = nil, data = nil
96
+ data = {
97
+ 'status' => to,
98
+ 'last_change' => Time.now,
99
+ 'message' => message,
100
+ 'data' => data
101
+ }
102
+ @@redis.set @@alert_name, data.to_json
103
+ end
104
+
105
+ # Prints out notification information and sends it
106
+ # @param [String] from: the previous status
107
+ # @param [Strig] to: the current status
108
+ # @param [Hash] data (optional): the data to be saved
109
+ # @return [Boolean] true if notification sent. false if not
110
+ def notify from, to, message, data = nil
111
+ string = "Alert: #{@@alert_name}
112
+
113
+ [ #{from} ] => [ #{to} ]
114
+
115
+ #{message}
116
+
117
+ "
118
+ string += "
119
+
120
+ Data:
121
+ #{data}
122
+ " if data
123
+ print string
124
+ self.send_slack(string)
125
+ self.send_telegram(string)
126
+ end
127
+
128
+ # Sends message notification through Slack to the given slack channel
129
+ # @param [String] message: the message string to be sent
130
+ # @return [Boolean] true if notification sent. false if not
131
+ def send_slack message = nil
132
+ channel = self.slack
133
+ if channel and !!@@slack_client
134
+ @@slack_client.auth_test
135
+ if message
136
+ begin
137
+ puts "Enviando: #{message}
138
+ to: #{channel}"
139
+ @@slack_client.chat_postMessage(channel: channel, text: message, as_user: true)
140
+ true
141
+ rescue
142
+ puts "Error sending message to slack channel #{channel}"
143
+ false
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ # Returns value for slack token
150
+ def slack
151
+ self.matched_line("SLACK")&.strip
152
+ end
153
+
154
+ # Sends message notification through Slack to the given slack channel
155
+ # @param [String] message: the message string to be sent
156
+ # @return [Boolean] true if notification sent. false if not
157
+ def send_telegram message = nil
158
+ chat_id = self.telegram
159
+ if chat_id and !!@@telegram_client
160
+ if message
161
+ begin
162
+ puts "Enviando: #{message}
163
+ to: #{chat_id}"
164
+ @@telegram_client.send_message(chat_id: chat_id, text: message)
165
+ true
166
+ rescue
167
+ puts "Error sending message to telegram group #{group_id}"
168
+ false
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ # Returns value for telegram group id
175
+ def telegram
176
+ self.matched_line("TELEGRAM")&.strip
177
+ end
178
+
179
+ # To get config token and/or values, e.g for slack, returns values from line containing arg
180
+ # @return [String] value for line staring with `# <something> `
181
+ # @param [String] what = configuration name. E.g.
182
+ #
183
+ # # SLACK 1234567890987654321
184
+ #
185
+ # will return 1234567890987654321 when param what is SLACK
186
+ #
187
+ def matched_line what
188
+ line = self.content.select do |line|
189
+ line.start_with? "# #{what} "
190
+ end.first
191
+ line.split("# #{what.upcase} ").last if line
192
+ end
193
+
194
+ def content
195
+ @@content ||= IO.read(@@alert_file + ".rb").split("\n")
196
+ end
197
+ end
198
+
199
+ # Alert methods
200
+
201
+ # use to set status to ok
202
+ # @param [Hash] opts
203
+ #
204
+ # :message
205
+ # :data
206
+ #
207
+ def ok! opts = nil
208
+ NOCH.evaluate_for "ok", opts
209
+ end
210
+
211
+ # use to set status to warning
212
+ # @param [Hash] opts
213
+ #
214
+ # :message
215
+ # :data
216
+ #
217
+ def warning! opts = nil
218
+ NOCH.evaluate_for "warning", opts
219
+ end
220
+
221
+ # use to set status to critical
222
+ # @param [Hash] opts
223
+ #
224
+ # :message
225
+ # :data
226
+ #
227
+ def critical! opts = nil
228
+ NOCH.evaluate_for "critical", opts
229
+ end
230
+
231
+ # @param [Hash] opts
232
+ #
233
+ # :message
234
+ # :data
235
+ #
236
+ def skip! opts = nil
237
+ NOCH.evaluate_for "skip", opts
238
+ end
239
+
240
+ # Returns the current status. Useful when you save aditional data into status
241
+ # @return [Hash] {"status"=>"ok", "last_change"=>"2019-07-17T22:51:03.081-03:00", "message"=>nil, "data"=>nil}
242
+ def get_current
243
+ JSON.parse(NOCH.get_status)
244
+ end
245
+
246
+ # Returns the last data saved
247
+ # @return [String] last data saved
248
+ def last_data
249
+ get_current['data']
250
+ end
251
+
252
+ def alert_name
253
+ @@alert_name
254
+ end
255
+
256
+ def inspect
257
+ {
258
+ "version" => VERSION,
259
+ "alert_name" => @@alert_name
260
+ }
261
+ end
262
+
263
+ end
@@ -0,0 +1,31 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "NOCH/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "noch"
8
+ spec.version = NOCH::VERSION
9
+ spec.authors = ["Roman"]
10
+ spec.email = ["roman.g.rodriguez@gmail.com"]
11
+
12
+ spec.summary = %q{Alerts on status change}
13
+ spec.description = %q{Notify alerts when status change}
14
+ spec.homepage = "https://github.com/romanrod/noch"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+ spec.add_dependency "redis"
25
+ spec.add_dependency "slack-ruby-client"
26
+ spec.add_dependency "telegram-bot"
27
+
28
+ spec.add_development_dependency "bundler", "~> 2.0"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "minitest", "~> 5.0"
31
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: noch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roman
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slack-ruby-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: telegram-bot
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ description: Notify alerts when status change
98
+ email:
99
+ - roman.g.rodriguez@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - Gemfile.lock
107
+ - README.md
108
+ - Rakefile
109
+ - bin/console
110
+ - bin/setup
111
+ - lib/NOCH/version.rb
112
+ - lib/noch.rb
113
+ - noch.gemspec
114
+ homepage: https://github.com/romanrod/noch
115
+ licenses: []
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.0.1
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Alerts on status change
136
+ test_files: []