newshound 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: decd51fd02b481edd644a56bd700575010ae0b3d747fc2880803c68c32e50bfa
4
+ data.tar.gz: f44304b8a0f6621296ff13900a2e59ea702b62c08ed6491ad0a60b4839b76b6d
5
+ SHA512:
6
+ metadata.gz: 813c1c7d310854f34722819c8321d24438cafd543bf9c4dcf164e90bcf716423953ae0583656fe97476d454342fb8c4dabdfa48b66d886d6adb21abc6466521b
7
+ data.tar.gz: 6c60ed849cd31e6e08066dc9893c2dcd3d3a82450923d97383717f2c5e21f2ea4943880b73ccce9926cbed83ead51b9cea0b2c08c91ba1fb8b63a6a8fb66e983
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
8
+ gem "rspec", "~> 3.0"
9
+ gem "rubocop", "~> 1.0"
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # Newshound 🐕
2
+
3
+ A Ruby gem that sniffs out exceptions and job statuses in your Rails app and reports them daily to Slack.
4
+
5
+ ## Features
6
+
7
+ - 📊 Daily Que job status reports (counts by job type, queue health)
8
+ - 🚨 Last 4 exceptions from exception-track
9
+ - 💬 Slack integration via webhook or Web API
10
+ - ⏰ Automatic daily scheduling with que-scheduler
11
+ - 🔧 Configurable report times and limits
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'newshound', path: 'path/to/newshound' # or from git/rubygems when published
19
+ ```
20
+
21
+ Then:
22
+
23
+ ```bash
24
+ bundle install
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Create an initializer `config/initializers/newshound.rb`:
30
+
31
+ ```ruby
32
+ Newshound.configure do |config|
33
+ # Slack configuration (choose one method)
34
+ config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL'] # Option 1: Webhook
35
+ # OR set ENV['SLACK_API_TOKEN'] for Web API # Option 2: Web API
36
+
37
+ config.slack_channel = "#ops-alerts" # Default: "#general"
38
+ config.report_time = "09:00" # Default: "09:00" (24-hour format)
39
+ config.exception_limit = 4 # Default: 4 (last N exceptions)
40
+ config.time_zone = "America/New_York" # Default: "America/New_York"
41
+ config.enabled = true # Default: true
42
+ end
43
+ ```
44
+
45
+ ### Slack Setup
46
+
47
+ #### Option 1: Webhook URL (Simpler)
48
+ 1. Go to https://api.slack.com/apps
49
+ 2. Create a new app or select existing
50
+ 3. Enable "Incoming Webhooks"
51
+ 4. Add a new webhook for your channel
52
+ 5. Copy the webhook URL to your config
53
+
54
+ #### Option 2: Web API Token (More features)
55
+ 1. Create a Slack app at https://api.slack.com/apps
56
+ 2. Add OAuth scopes: `chat:write`, `chat:write.public`
57
+ 3. Install to workspace
58
+ 4. Copy the Bot User OAuth Token
59
+ 5. Set as `ENV['SLACK_API_TOKEN']`
60
+
61
+ ## Usage
62
+
63
+ ### Automatic Daily Reports
64
+
65
+ If you have `que-scheduler` configured, Newshound will automatically schedule daily reports at your configured time.
66
+
67
+ ### Manual Reports
68
+
69
+ ```ruby
70
+ # Send report immediately
71
+ Newshound.report!
72
+
73
+ # Or via rake task
74
+ rake newshound:report_now
75
+
76
+ # Enqueue for background processing
77
+ rake newshound:schedule
78
+
79
+ # Check configuration
80
+ rake newshound:config
81
+ ```
82
+
83
+ ### Integration with que-scheduler
84
+
85
+ Add to your `config/que_schedule.yml`:
86
+
87
+ ```yaml
88
+ newshound_daily_report:
89
+ class: "Newshound::DailyReportJob"
90
+ cron: "0 9 * * *" # 9:00 AM daily
91
+ queue: default
92
+ ```
93
+
94
+ Or let Newshound auto-configure based on your settings.
95
+
96
+ ## Report Format
97
+
98
+ The daily report includes:
99
+
100
+ ### Exception Section
101
+ - Last 4 exceptions (configurable)
102
+ - Exception class, message, controller/action
103
+ - Count of same exception type in last 24 hours
104
+ - Timestamp
105
+
106
+ ### Que Jobs Section
107
+ - Job counts by type (success/failed/total)
108
+ - Queue health status
109
+ - Ready to run count
110
+ - Scheduled jobs count
111
+ - Failed jobs in retry queue
112
+ - Jobs completed today
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Run tests
118
+ bundle exec rspec
119
+
120
+ # Run linter
121
+ bundle exec rubocop
122
+
123
+ # Console
124
+ bin/console
125
+ ```
126
+
127
+ ## Testing in Your App
128
+
129
+ ```ruby
130
+ # In rails console
131
+ Newshound.configuration.slack_webhook_url = "your-webhook"
132
+ Newshound.report! # Should post to Slack immediately
133
+ ```
134
+
135
+ ## Troubleshooting
136
+
137
+ - **No reports sent**: Check `rake newshound:config` to verify configuration
138
+ - **No exceptions showing**: Ensure `exception-track` gem is installed and logging
139
+ - **No job data**: Verify Que is configured and `que_jobs` table exists
140
+ - **Slack not receiving**: Verify webhook URL or API token is correct
141
+
142
+ ## License
143
+
144
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "rubocop/rake_task"
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[spec rubocop]
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ class Configuration
5
+ attr_accessor :slack_webhook_url, :slack_channel, :report_time,
6
+ :exception_limit, :time_zone, :enabled
7
+
8
+ def initialize
9
+ @slack_webhook_url = nil
10
+ @slack_channel = "#general"
11
+ @report_time = "09:00"
12
+ @exception_limit = 4
13
+ @time_zone = "America/New_York"
14
+ @enabled = true
15
+ end
16
+
17
+ def valid?
18
+ return false unless enabled
19
+ return false if slack_webhook_url.nil? || slack_webhook_url.empty?
20
+
21
+ true
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ class DailyReportJob < ::Que::Job
5
+ def run
6
+ return unless Newshound.configuration.valid?
7
+
8
+ Newshound.report!
9
+
10
+ destroy
11
+ rescue StandardError => e
12
+ Rails.logger.error "Newshound::DailyReportJob failed: #{e.message}"
13
+ Rails.logger.error e.backtrace.join("\n")
14
+
15
+ # Re-raise to let Que handle retry logic
16
+ raise
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ class ExceptionReporter
5
+ def generate_report
6
+ return no_exceptions_block if recent_exceptions.empty?
7
+
8
+ [
9
+ {
10
+ type: "section",
11
+ text: {
12
+ type: "mrkdwn",
13
+ text: "*🚨 Recent Exceptions (Last 24 Hours)*"
14
+ }
15
+ },
16
+ *format_exceptions
17
+ ]
18
+ end
19
+
20
+ private
21
+
22
+ def recent_exceptions
23
+ @recent_exceptions ||= fetch_recent_exceptions
24
+ end
25
+
26
+ def fetch_recent_exceptions
27
+ return [] unless defined?(ExceptionTrack::Log)
28
+
29
+ ExceptionTrack::Log
30
+ .where("created_at >= ?", 24.hours.ago)
31
+ .order(created_at: :desc)
32
+ .limit(Newshound.configuration.exception_limit)
33
+ end
34
+
35
+ def format_exceptions
36
+ recent_exceptions.map.with_index do |exception, index|
37
+ {
38
+ type: "section",
39
+ text: {
40
+ type: "mrkdwn",
41
+ text: format_exception_text(exception, index + 1)
42
+ }
43
+ }
44
+ end
45
+ end
46
+
47
+ def format_exception_text(exception, number)
48
+ <<~TEXT
49
+ *#{number}. #{exception.title || exception.exception_class}*
50
+ • *Time:* #{exception.created_at.strftime('%I:%M %p')}
51
+ • *Controller:* #{exception.controller_name}##{exception.action_name}
52
+ • *Count:* #{exception_count(exception)}
53
+ #{format_message(exception)}
54
+ TEXT
55
+ end
56
+
57
+ def format_message(exception)
58
+ return "" unless exception.message.present?
59
+
60
+ message = exception.message.truncate(100)
61
+ "• *Message:* `#{message}`"
62
+ end
63
+
64
+ def exception_count(exception)
65
+ ExceptionTrack::Log
66
+ .where(exception_class: exception.exception_class)
67
+ .where("created_at >= ?", 24.hours.ago)
68
+ .count
69
+ end
70
+
71
+ def no_exceptions_block
72
+ [
73
+ {
74
+ type: "section",
75
+ text: {
76
+ type: "mrkdwn",
77
+ text: "*✅ No Exceptions in the Last 24 Hours*"
78
+ }
79
+ }
80
+ ]
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ class QueReporter
5
+ def generate_report
6
+ [
7
+ {
8
+ type: "section",
9
+ text: {
10
+ type: "mrkdwn",
11
+ text: "*📊 Que Jobs Status*"
12
+ }
13
+ },
14
+ job_counts_section,
15
+ queue_health_section
16
+ ].compact
17
+ end
18
+
19
+ private
20
+
21
+ def job_counts_section
22
+ counts = job_counts_by_type
23
+
24
+ return no_jobs_section if counts.empty?
25
+
26
+ {
27
+ type: "section",
28
+ text: {
29
+ type: "mrkdwn",
30
+ text: format_job_counts(counts)
31
+ }
32
+ }
33
+ end
34
+
35
+ def job_counts_by_type
36
+ return {} unless defined?(::Que::Job)
37
+
38
+ ::Que::Job
39
+ .group(:job_class)
40
+ .group(:error_count)
41
+ .count
42
+ .each_with_object({}) do |((job_class, error_count), count), hash|
43
+ hash[job_class] ||= { success: 0, failed: 0, total: 0 }
44
+
45
+ if error_count.zero?
46
+ hash[job_class][:success] += count
47
+ else
48
+ hash[job_class][:failed] += count
49
+ end
50
+
51
+ hash[job_class][:total] += count
52
+ end
53
+ end
54
+
55
+ def format_job_counts(counts)
56
+ lines = ["*Job Counts by Type:*"]
57
+
58
+ counts.each do |job_class, stats|
59
+ status_emoji = stats[:failed] > 0 ? "⚠️" : "✅"
60
+ lines << "• #{status_emoji} *#{job_class}*: #{stats[:total]} total (#{stats[:success]} success, #{stats[:failed]} failed)"
61
+ end
62
+
63
+ lines.join("\n")
64
+ end
65
+
66
+ def queue_health_section
67
+ stats = queue_statistics
68
+
69
+ {
70
+ type: "section",
71
+ text: {
72
+ type: "mrkdwn",
73
+ text: format_queue_health(stats)
74
+ }
75
+ }
76
+ end
77
+
78
+ def queue_statistics
79
+ return default_stats unless defined?(::Que::Job)
80
+
81
+ {
82
+ ready: ::Que::Job.where(finished_at: nil, expired_at: nil).where("run_at <= ?", Time.current).count,
83
+ scheduled: ::Que::Job.where(finished_at: nil, expired_at: nil).where("run_at > ?", Time.current).count,
84
+ failed: ::Que::Job.where.not(error_count: 0).where(finished_at: nil).count,
85
+ finished_today: ::Que::Job.where("finished_at >= ?", Date.current.beginning_of_day).count
86
+ }
87
+ rescue StandardError => e
88
+ Rails.logger.error "Failed to fetch Que statistics: #{e.message}"
89
+ default_stats
90
+ end
91
+
92
+ def default_stats
93
+ { ready: 0, scheduled: 0, failed: 0, finished_today: 0 }
94
+ end
95
+
96
+ def format_queue_health(stats)
97
+ health_emoji = stats[:failed] > 10 ? "🔴" : stats[:failed] > 5 ? "🟡" : "🟢"
98
+
99
+ <<~TEXT
100
+ *Queue Health #{health_emoji}*
101
+ • *Ready to Run:* #{stats[:ready]}
102
+ • *Scheduled:* #{stats[:scheduled]}
103
+ • *Failed (Retry Queue):* #{stats[:failed]}
104
+ • *Completed Today:* #{stats[:finished_today]}
105
+ TEXT
106
+ end
107
+
108
+ def no_jobs_section
109
+ {
110
+ type: "section",
111
+ text: {
112
+ type: "mrkdwn",
113
+ text: "*No jobs found in the queue*"
114
+ }
115
+ }
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+
5
+ module Newshound
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ namespace :newshound do
9
+ desc "Send daily report immediately"
10
+ task report_now: :environment do
11
+ puts "Sending Newshound daily report..."
12
+ Newshound.report!
13
+ puts "Report sent successfully!"
14
+ rescue StandardError => e
15
+ puts "Failed to send report: #{e.message}"
16
+ end
17
+
18
+ desc "Schedule daily report job"
19
+ task schedule: :environment do
20
+ puts "Scheduling Newshound daily report..."
21
+ Newshound::Scheduler.run_now!
22
+ puts "Job enqueued successfully!"
23
+ end
24
+
25
+ desc "Show current configuration"
26
+ task config: :environment do
27
+ config = Newshound.configuration
28
+ puts "Newshound Configuration:"
29
+ puts " Enabled: #{config.enabled}"
30
+ puts " Slack Webhook: #{config.slack_webhook_url.present? ? '[CONFIGURED]' : '[NOT SET]'}"
31
+ puts " Slack Channel: #{config.slack_channel}"
32
+ puts " Report Time: #{config.report_time}"
33
+ puts " Exception Limit: #{config.exception_limit}"
34
+ puts " Time Zone: #{config.time_zone}"
35
+ puts " Valid: #{config.valid?}"
36
+ end
37
+ end
38
+ end
39
+
40
+ initializer "newshound.configure_que_scheduler" do
41
+ ActiveSupport.on_load(:active_record) do
42
+ if defined?(::Que::Scheduler)
43
+ require_relative "scheduler"
44
+
45
+ # Add our job to the que-scheduler configuration
46
+ schedule = Newshound::Scheduler.schedule_daily_report
47
+
48
+ Rails.logger.info "Newshound: Scheduled daily report at #{Newshound.configuration.report_time}"
49
+ else
50
+ Rails.logger.warn "Newshound: que-scheduler not found. Daily reports will need to be triggered manually."
51
+ end
52
+ end
53
+ end
54
+
55
+ config.after_initialize do
56
+ if Newshound.configuration.valid?
57
+ Rails.logger.info "Newshound initialized and ready to report!"
58
+ else
59
+ Rails.logger.warn "Newshound is not properly configured. Please check your configuration."
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ class Scheduler
5
+ def self.schedule_daily_report
6
+ return unless defined?(::Que::Scheduler)
7
+
8
+ config = Newshound.configuration
9
+ return unless config.valid?
10
+
11
+ # Schedule the job using que-scheduler
12
+ # This will be picked up by que-scheduler's configuration
13
+ schedule_config = {
14
+ "newshound_daily_report" => {
15
+ "class" => "Newshound::DailyReportJob",
16
+ "cron" => build_cron_expression(config.report_time),
17
+ "queue" => "default",
18
+ "args" => []
19
+ }
20
+ }
21
+
22
+ # Merge with existing schedule if any
23
+ if defined?(::Que::Scheduler.configuration)
24
+ ::Que::Scheduler.configuration.merge!(schedule_config)
25
+ end
26
+
27
+ schedule_config
28
+ end
29
+
30
+ def self.build_cron_expression(time_string)
31
+ # Convert "09:00" format to cron expression
32
+ # Format: minute hour * * *
33
+ hour, minute = time_string.split(":").map(&:to_i)
34
+ "#{minute} #{hour} * * *"
35
+ end
36
+
37
+ def self.run_now!
38
+ Newshound::DailyReportJob.enqueue
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slack-ruby-client"
4
+
5
+ module Newshound
6
+ class SlackNotifier
7
+ def initialize
8
+ configure_slack_client
9
+ end
10
+
11
+ def post(message)
12
+ return unless valid_configuration?
13
+
14
+ if webhook_configured?
15
+ post_via_webhook(message)
16
+ elsif web_api_configured?
17
+ post_via_web_api(message)
18
+ else
19
+ Rails.logger.error "Newshound: No valid Slack configuration found"
20
+ end
21
+ rescue StandardError => e
22
+ Rails.logger.error "Newshound: Failed to send Slack notification: #{e.message}"
23
+ end
24
+
25
+ private
26
+
27
+ def configure_slack_client
28
+ Slack.configure do |config|
29
+ config.token = ENV["SLACK_API_TOKEN"] if ENV["SLACK_API_TOKEN"]
30
+ end
31
+ end
32
+
33
+ def valid_configuration?
34
+ return false unless Newshound.configuration.valid?
35
+
36
+ webhook_configured? || web_api_configured?
37
+ end
38
+
39
+ def webhook_configured?
40
+ Newshound.configuration.slack_webhook_url.present?
41
+ end
42
+
43
+ def web_api_configured?
44
+ ENV["SLACK_API_TOKEN"].present?
45
+ end
46
+
47
+ def post_via_webhook(message)
48
+ client = Slack::Incoming::Webhook.new(Newshound.configuration.slack_webhook_url)
49
+ client.post(message)
50
+ end
51
+
52
+ def post_via_web_api(message)
53
+ client = Slack::Web::Client.new
54
+ client.chat_postMessage(
55
+ channel: Newshound.configuration.slack_channel,
56
+ blocks: message[:blocks],
57
+ text: "Daily Newshound Report"
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ VERSION = "0.1.0"
5
+ end
data/lib/newshound.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "newshound/version"
4
+ require_relative "newshound/configuration"
5
+ require_relative "newshound/exception_reporter"
6
+ require_relative "newshound/que_reporter"
7
+ require_relative "newshound/slack_notifier"
8
+ require_relative "newshound/daily_report_job"
9
+ require_relative "newshound/scheduler"
10
+ require_relative "newshound/railtie" if defined?(Rails)
11
+
12
+ module Newshound
13
+ class Error < StandardError; end
14
+
15
+ class << self
16
+ def configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def configure
21
+ yield(configuration)
22
+ end
23
+
24
+ def report!
25
+ slack_notifier = SlackNotifier.new
26
+
27
+ exception_report = ExceptionReporter.new.generate_report
28
+ que_report = QueReporter.new.generate_report
29
+
30
+ message = format_daily_report(exception_report, que_report)
31
+ slack_notifier.post(message)
32
+ end
33
+
34
+ private
35
+
36
+ def format_daily_report(exception_report, que_report)
37
+ {
38
+ blocks: [
39
+ {
40
+ type: "header",
41
+ text: {
42
+ type: "plain_text",
43
+ text: "🐕 Daily Newshound Report",
44
+ emoji: true
45
+ }
46
+ },
47
+ {
48
+ type: "section",
49
+ text: {
50
+ type: "mrkdwn",
51
+ text: "*Date:* #{Date.current.strftime('%B %d, %Y')}"
52
+ }
53
+ },
54
+ {
55
+ type: "divider"
56
+ },
57
+ *exception_report,
58
+ {
59
+ type: "divider"
60
+ },
61
+ *que_report
62
+ ]
63
+ }
64
+ end
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: newshound
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - salbanez
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: slack-ruby-client
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: que
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: que-scheduler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '4.0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '4.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: exception-track
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: bundler
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rake
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '13.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: rubocop
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: pg
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ description: Newshound sniffs out exceptions and job statuses in your Rails app and
153
+ reports them daily to Slack
154
+ email:
155
+ - salbanez@example.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - Gemfile
161
+ - README.md
162
+ - Rakefile
163
+ - lib/newshound.rb
164
+ - lib/newshound/configuration.rb
165
+ - lib/newshound/daily_report_job.rb
166
+ - lib/newshound/exception_reporter.rb
167
+ - lib/newshound/que_reporter.rb
168
+ - lib/newshound/railtie.rb
169
+ - lib/newshound/scheduler.rb
170
+ - lib/newshound/slack_notifier.rb
171
+ - lib/newshound/version.rb
172
+ homepage: https://github.com/salbanez/newshound
173
+ licenses:
174
+ - MIT
175
+ metadata:
176
+ homepage_uri: https://github.com/salbanez/newshound
177
+ source_code_uri: https://github.com/salbanez/newshound
178
+ changelog_uri: https://github.com/salbanez/newshound/blob/main/CHANGELOG.md
179
+ rdoc_options: []
180
+ require_paths:
181
+ - lib
182
+ required_ruby_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: 2.7.0
187
+ required_rubygems_version: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ requirements: []
193
+ rubygems_version: 3.6.9
194
+ specification_version: 4
195
+ summary: Daily Slack reporter for Que jobs status and exception tracking
196
+ test_files: []