newshound 0.1.0 → 0.2.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: decd51fd02b481edd644a56bd700575010ae0b3d747fc2880803c68c32e50bfa
4
- data.tar.gz: f44304b8a0f6621296ff13900a2e59ea702b62c08ed6491ad0a60b4839b76b6d
3
+ metadata.gz: ed2c9eae65ac8681fc1c72890cbd421ca9dd73130c37fa86c04643cb74c03856
4
+ data.tar.gz: 7ccb875f08607e1478245384376a621243fdd44e40443fb191064210cff94ba6
5
5
  SHA512:
6
- metadata.gz: 813c1c7d310854f34722819c8321d24438cafd543bf9c4dcf164e90bcf716423953ae0583656fe97476d454342fb8c4dabdfa48b66d886d6adb21abc6466521b
7
- data.tar.gz: 6c60ed849cd31e6e08066dc9893c2dcd3d3a82450923d97383717f2c5e21f2ea4943880b73ccce9926cbed83ead51b9cea0b2c08c91ba1fb8b63a6a8fb66e983
6
+ metadata.gz: a78bd31f883c89116c83b7c77b680faba172b41e0bc3f375e4a81a075503d33860ebd4778e71f11d868a794fe04effd815e240ebd3d5c6135b0ed80599c87469
7
+ data.tar.gz: 4985d3d128688284f4734391b12e7f6e34d1c69857d713d3995d2078cc4df210b64ca5f84d889841e52b1b3a9086bcf0f19e7d35bef646b60e08020379527ad6
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.4.3
data/Gemfile CHANGED
@@ -4,6 +4,12 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem "rake", "~> 13.0"
8
- gem "rspec", "~> 3.0"
9
- gem "rubocop", "~> 1.0"
7
+ group :development, :test do
8
+ gem "bundler", "~> 2.0"
9
+ gem "rake", "~> 13.0"
10
+ gem "rspec", "~> 3.0"
11
+ gem "rubocop", "~> 1.0"
12
+ gem "pg"
13
+ gem "pry"
14
+ gem "simplecov", require: false
15
+ end
data/README.md CHANGED
@@ -1,115 +1,192 @@
1
1
  # Newshound 🐕
2
2
 
3
- A Ruby gem that sniffs out exceptions and job statuses in your Rails app and reports them daily to Slack.
3
+ A Ruby gem that displays real-time exceptions and job statuses in a collapsible banner for authorized users in your Rails application.
4
4
 
5
5
  ## Features
6
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
7
+ - 🎯 **Real-time Web UI Banner** - Shows exceptions and job statuses at the top of every page
8
+ - 🔐 **Role-Based Access** - Only visible to authorized users (developers, admins, etc.)
9
+ - 📊 **Que Job Monitoring** - Real-time queue health and job status
10
+ - 🚨 **Exception Tracking** - Recent exceptions from exception-track
11
+ - 🎨 **Collapsible UI** - Clean, non-intrusive banner that expands on click
12
+ - ⚡ **Zero Configuration** - Automatically injects into HTML responses
13
+ - 🔧 **Highly Customizable** - Configure roles and authorization logic
12
14
 
13
15
  ## Installation
14
16
 
15
17
  Add to your Gemfile:
16
18
 
17
19
  ```ruby
18
- gem 'newshound', path: 'path/to/newshound' # or from git/rubygems when published
20
+ gem 'newshound'
19
21
  ```
20
22
 
21
23
  Then:
22
24
 
23
25
  ```bash
24
26
  bundle install
27
+ rails generate newshound:install
25
28
  ```
26
29
 
30
+ The generator will create `config/initializers/newshound.rb` with default configuration.
31
+
27
32
  ## Configuration
28
33
 
29
- Create an initializer `config/initializers/newshound.rb`:
34
+ ### Basic Configuration
30
35
 
31
36
  ```ruby
37
+ # config/initializers/newshound.rb
32
38
  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
39
+ # Enable or disable the banner
40
+ config.enabled = true
41
+
42
+ # Maximum number of exceptions to show in banner
43
+ config.exception_limit = 10
44
+
45
+ # User roles that can view the banner
46
+ config.authorized_roles = [:developer, :super_user]
47
+
48
+ # Method to call to get current user (most apps use :current_user)
49
+ config.current_user_method = :current_user
50
+ end
51
+ ```
52
+
53
+ ### Advanced: Custom Authorization
54
+
55
+ If the default role-based authorization doesn't fit your needs, you can provide custom logic:
56
+
57
+ ```ruby
58
+ # config/initializers/newshound.rb
59
+ Newshound.authorize_with do |controller|
60
+ # Your custom authorization logic
61
+ # Return true to show banner, false to hide
62
+ user = controller.current_user
63
+ user&.admin? || user&.developer?
64
+ end
65
+ ```
66
+
67
+ ### Example Authorization Scenarios
68
+
69
+ ```ruby
70
+ # Only show in development
71
+ Newshound.authorize_with do |controller|
72
+ Rails.env.development?
73
+ end
74
+
75
+ # Check multiple conditions
76
+ Newshound.authorize_with do |controller|
77
+ user = controller.current_user
78
+ user.present? &&
79
+ (user.has_role?(:admin) || user.email.ends_with?('@yourcompany.com'))
80
+ end
81
+
82
+ # Use your existing authorization system
83
+ Newshound.authorize_with do |controller|
84
+ controller.current_user&.can?(:view_newshound)
42
85
  end
43
86
  ```
44
87
 
45
- ### Slack Setup
88
+ ## How It Works
89
+
90
+ Newshound uses Rails middleware to automatically inject a banner into HTML responses for authorized users. The banner:
91
+
92
+ 1. ✅ Appears automatically on all HTML pages
93
+ 2. 🔒 Only visible to users with authorized roles
94
+ 3. 📊 Shows real-time data from your exception and job queues
95
+ 4. 🎨 Collapses to save space, expands on click
96
+ 5. 🚀 No JavaScript dependencies, pure CSS animations
46
97
 
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
98
+ ## Banner Content
53
99
 
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']`
100
+ The banner displays:
60
101
 
61
- ## Usage
102
+ ### Exception Section
103
+ - Recent exceptions from exception-track
104
+ - Exception class and message
105
+ - Controller/action where it occurred
106
+ - Timestamp
107
+ - Visual indicators (🟢 all clear / 🔴 errors)
62
108
 
63
- ### Automatic Daily Reports
109
+ ### Job Queue Section
110
+ - **Ready to Run**: Jobs waiting to execute
111
+ - **Scheduled**: Jobs scheduled for future execution
112
+ - **Failed**: Jobs in retry queue
113
+ - **Completed Today**: Successfully finished jobs
114
+ - Color-coded health status
64
115
 
65
- If you have `que-scheduler` configured, Newshound will automatically schedule daily reports at your configured time.
116
+ ## User Requirements
66
117
 
67
- ### Manual Reports
118
+ Your User model should have a `role` attribute that matches one of the configured `authorized_roles`. Common patterns:
68
119
 
69
120
  ```ruby
70
- # Send report immediately
71
- Newshound.report!
121
+ # String enum
122
+ class User < ApplicationRecord
123
+ enum role: { user: 'user', developer: 'developer', admin: 'admin' }
124
+ end
72
125
 
73
- # Or via rake task
74
- rake newshound:report_now
126
+ # Symbol enum
127
+ class User < ApplicationRecord
128
+ enum role: { user: 0, developer: 1, super_user: 2 }
129
+ end
130
+
131
+ # String column
132
+ class User < ApplicationRecord
133
+ def role
134
+ @role ||= read_attribute(:role)&.to_sym
135
+ end
136
+ end
137
+ ```
138
+
139
+ If your User model uses different attribute names, you can customize the authorization logic using `Newshound.authorize_with`.
140
+
141
+ ## Testing
75
142
 
76
- # Enqueue for background processing
77
- rake newshound:schedule
143
+ ### Test Reporters
78
144
 
79
- # Check configuration
145
+ ```bash
146
+ # Test exception reporter
147
+ rake newshound:test_exceptions
148
+
149
+ # Test job queue reporter
150
+ rake newshound:test_jobs
151
+
152
+ # Show current configuration
80
153
  rake newshound:config
81
154
  ```
82
155
 
83
- ### Integration with que-scheduler
156
+ ### Test in Rails Console
84
157
 
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
158
+ ```ruby
159
+ # Check if banner would show for a specific user
160
+ user = User.find(123)
161
+ controller = ApplicationController.new
162
+ controller.instance_variable_set(:@current_user, user)
163
+ Newshound::Authorization.authorized?(controller)
164
+ # => true or false
92
165
  ```
93
166
 
94
- Or let Newshound auto-configure based on your settings.
167
+ ## Troubleshooting
95
168
 
96
- ## Report Format
169
+ ### Banner not appearing
97
170
 
98
- The daily report includes:
171
+ 1. **Check if enabled**: `rake newshound:config`
172
+ 2. **Verify user role**: Make sure your user has an authorized role
173
+ 3. **Check current_user method**: Ensure your app provides the configured method
174
+ 4. **Restart server**: Changes to initializers require a restart
99
175
 
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
176
+ ### No exceptions showing
177
+
178
+ - Ensure `exception-track` gem is installed and logging exceptions
179
+ - Check that exceptions exist: `rake newshound:test_exceptions`
180
+
181
+ ### No job data
182
+
183
+ - Verify Que is configured and `que_jobs` table exists
184
+ - Check job data: `rake newshound:test_jobs`
185
+
186
+ ### Banner appears for wrong users
105
187
 
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
188
+ - Review `authorized_roles` configuration
189
+ - Consider using custom authorization with `Newshound.authorize_with`
113
190
 
114
191
  ## Development
115
192
 
@@ -124,20 +201,22 @@ bundle exec rubocop
124
201
  bin/console
125
202
  ```
126
203
 
127
- ## Testing in Your App
204
+ ## Dependencies
128
205
 
129
- ```ruby
130
- # In rails console
131
- Newshound.configuration.slack_webhook_url = "your-webhook"
132
- Newshound.report! # Should post to Slack immediately
133
- ```
206
+ - **Rails** >= 6.0
207
+ - **que** >= 1.0 (for job monitoring)
208
+ - **exception-track** >= 0.1 (for exception tracking)
134
209
 
135
- ## Troubleshooting
210
+ ## Upgrading from 0.1.x
211
+
212
+ If you were using the previous Slack-based version:
213
+
214
+ 1. Remove Slack/SNS configuration from your initializer
215
+ 2. Remove `que-scheduler` if only used for Newshound
216
+ 3. Update to new configuration format (see above)
217
+ 4. Restart your Rails server
136
218
 
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
219
+ The banner will now appear automatically for authorized users instead of sending Slack notifications.
141
220
 
142
221
  ## License
143
222
 
@@ -0,0 +1,117 @@
1
+ # Transport Layer Usage
2
+
3
+ The Newshound gem now supports multiple transport adapters for sending notifications. You can choose between Slack (direct) and Amazon SNS based on your environment needs.
4
+
5
+ ## Configuration
6
+
7
+ ### Using Slack Transport (Default)
8
+
9
+ ```ruby
10
+ # config/initializers/newshound.rb
11
+ Newshound.configure do |config|
12
+ config.transport_adapter = :slack # This is the default
13
+ config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
14
+ config.slack_channel = '#your-channel'
15
+ # ... other configurations
16
+ end
17
+ ```
18
+
19
+ ### Using SNS Transport
20
+
21
+ ```ruby
22
+ # config/initializers/newshound.rb
23
+ Newshound.configure do |config|
24
+ config.transport_adapter = :sns
25
+ config.sns_topic_arn = ENV['SNS_TOPIC_ARN']
26
+ config.aws_region = ENV['AWS_REGION'] || 'us-east-1'
27
+
28
+ # Optional: Provide AWS credentials explicitly
29
+ # If not provided, will use default AWS credential chain
30
+ config.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
31
+ config.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
32
+
33
+ # ... other configurations
34
+ end
35
+ ```
36
+
37
+ ### Environment-based Configuration
38
+
39
+ You can switch transport based on environment:
40
+
41
+ ```ruby
42
+ # config/initializers/newshound.rb
43
+ Newshound.configure do |config|
44
+ if Rails.env.production?
45
+ # Use SNS in production to route through AWS infrastructure
46
+ config.transport_adapter = :sns
47
+ config.sns_topic_arn = ENV['SNS_TOPIC_ARN']
48
+ config.aws_region = ENV['AWS_REGION']
49
+ else
50
+ # Use direct Slack in development/staging
51
+ config.transport_adapter = :slack
52
+ config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
53
+ config.slack_channel = '#dev-notifications'
54
+ end
55
+
56
+ # Common configurations
57
+ config.report_time = "09:00"
58
+ config.exception_limit = 4
59
+ config.time_zone = "America/New_York"
60
+ end
61
+ ```
62
+
63
+ ## Custom Transport Adapters
64
+
65
+ You can also create your own transport adapter:
66
+
67
+ ```ruby
68
+ class MyCustomTransport < Newshound::Transport::Base
69
+ def deliver(message)
70
+ # Your custom delivery logic here
71
+ # Return true on success, false on failure
72
+
73
+ # Example: Send to custom API endpoint
74
+ response = HTTParty.post(
75
+ 'https://api.example.com/notifications',
76
+ body: message.to_json,
77
+ headers: { 'Content-Type' => 'application/json' }
78
+ )
79
+
80
+ response.success?
81
+ rescue StandardError => e
82
+ logger.error "Failed to deliver: #{e.message}"
83
+ false
84
+ end
85
+ end
86
+
87
+ # Use the custom transport
88
+ Newshound.configure do |config|
89
+ config.transport_adapter = MyCustomTransport
90
+ # ... other configurations
91
+ end
92
+ ```
93
+
94
+ ## AWS SNS Setup
95
+
96
+ If using SNS transport, ensure you have:
97
+
98
+ 1. Created an SNS topic in AWS
99
+ 2. Subscribed your Slack webhook URL to the SNS topic
100
+ 3. Configured proper IAM permissions for publishing to the topic
101
+ 4. Installed the aws-sdk-sns gem (it's optional):
102
+
103
+ ```ruby
104
+ # Add to your Gemfile if using SNS
105
+ gem 'aws-sdk-sns', '~> 1.0'
106
+ ```
107
+
108
+ ## Testing
109
+
110
+ The transport layer is fully testable. You can inject mock transports in your tests:
111
+
112
+ ```ruby
113
+ # In your tests
114
+ mock_transport = double('Transport', deliver: true)
115
+ notifier = Newshound::SlackNotifier.new(transport: mock_transport)
116
+ notifier.post({ text: "Test message" })
117
+ ```
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Newshound
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Configures Newshound for your Rails application"
11
+
12
+ def create_initializer
13
+ template "newshound.rb", "config/initializers/newshound.rb"
14
+ end
15
+
16
+ def display_post_install_message
17
+ say ""
18
+ say "===============================================================================", :green
19
+ say " Newshound has been successfully installed!", :green
20
+ say ""
21
+ say " Next steps:", :yellow
22
+ say " 1. Configure authorized roles in config/initializers/newshound.rb"
23
+ say " 2. Make sure your User model has a role attribute (or customize authorization)"
24
+ say " 3. Restart your Rails server"
25
+ say ""
26
+ say " The Newshound banner will automatically appear at the top of pages for"
27
+ say " authorized users (developers and super_users by default)."
28
+ say ""
29
+ say " To test the reporters, run:", :cyan
30
+ say " rake newshound:test_exceptions"
31
+ say " rake newshound:test_jobs"
32
+ say ""
33
+ say " For more information, visit:", :blue
34
+ say " https://github.com/salbanez/newshound"
35
+ say "===============================================================================", :green
36
+ say ""
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ Newshound.configure do |config|
4
+ # Enable or disable Newshound completely
5
+ # When enabled, the banner will be shown to authorized users
6
+ # Default is true
7
+ config.enabled = true
8
+
9
+ # Maximum number of exceptions to include in banner
10
+ # Default is 10
11
+ config.exception_limit = 10
12
+
13
+ # User roles that are authorized to view the Newshound banner
14
+ # These should match the role values in your User model
15
+ # Default is [:developer, :super_user]
16
+ config.authorized_roles = [:developer, :super_user]
17
+
18
+ # Method name to call to get the current user
19
+ # Most apps use :current_user (Devise, etc.)
20
+ # Default is :current_user
21
+ config.current_user_method = :current_user
22
+ end
23
+
24
+ # Advanced: Custom authorization logic
25
+ # If the default role-based authorization doesn't fit your needs,
26
+ # you can provide a custom authorization block:
27
+ #
28
+ # Newshound.authorize_with do |controller|
29
+ # # Your custom logic here
30
+ # # Return true to show the banner, false to hide it
31
+ # controller.current_user&.admin?
32
+ # end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newshound
4
+ module Authorization
5
+ class << self
6
+ # Check if the current user/controller is authorized to view Newshound data
7
+ def authorized?(controller)
8
+ return false unless Newshound.configuration.enabled
9
+
10
+ # Use custom authorization block if provided
11
+ if Newshound.configuration.authorization_block
12
+ return Newshound.configuration.authorization_block.call(controller)
13
+ end
14
+
15
+ # Default authorization: check if current_user has an authorized role
16
+ user = current_user_from(controller)
17
+ return false unless user
18
+
19
+ user_role = user_role_from(user)
20
+ return false unless user_role
21
+
22
+ Newshound.configuration.authorized_roles.include?(user_role.to_sym)
23
+ end
24
+
25
+ private
26
+
27
+ def current_user_from(controller)
28
+ method_name = Newshound.configuration.current_user_method
29
+ return nil unless controller.respond_to?(method_name)
30
+
31
+ controller.send(method_name)
32
+ end
33
+
34
+ def user_role_from(user)
35
+ return nil unless user
36
+
37
+ # Try common role attribute names
38
+ [:role, :user_role, :type].each do |attr|
39
+ return user.send(attr) if user.respond_to?(attr)
40
+ end
41
+
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -2,23 +2,24 @@
2
2
 
3
3
  module Newshound
4
4
  class Configuration
5
- attr_accessor :slack_webhook_url, :slack_channel, :report_time,
6
- :exception_limit, :time_zone, :enabled
5
+ attr_accessor :exception_limit, :enabled, :authorized_roles,
6
+ :current_user_method, :authorization_block
7
7
 
8
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"
9
+ @exception_limit = 10
14
10
  @enabled = true
11
+ @authorized_roles = [:developer, :super_user]
12
+ @current_user_method = :current_user
13
+ @authorization_block = nil
14
+ end
15
+
16
+ # Allow custom authorization logic
17
+ def authorize_with(&block)
18
+ @authorization_block = block
15
19
  end
16
20
 
17
21
  def valid?
18
- return false unless enabled
19
- return false if slack_webhook_url.nil? || slack_webhook_url.empty?
20
-
21
- true
22
+ enabled
22
23
  end
23
24
  end
24
25
  end
@@ -2,9 +2,17 @@
2
2
 
3
3
  module Newshound
4
4
  class ExceptionReporter
5
+ attr_reader :exception_source, :configuration, :time_range
6
+
7
+ def initialize(exception_source: nil, configuration: nil, time_range: 24.hours)
8
+ @exception_source = exception_source || (defined?(ExceptionTrack::Log) ? ExceptionTrack::Log : nil)
9
+ @configuration = configuration || Newshound.configuration
10
+ @time_range = time_range
11
+ end
12
+
5
13
  def generate_report
6
14
  return no_exceptions_block if recent_exceptions.empty?
7
-
15
+
8
16
  [
9
17
  {
10
18
  type: "section",
@@ -24,12 +32,12 @@ module Newshound
24
32
  end
25
33
 
26
34
  def fetch_recent_exceptions
27
- return [] unless defined?(ExceptionTrack::Log)
28
-
29
- ExceptionTrack::Log
30
- .where("created_at >= ?", 24.hours.ago)
35
+ return [] unless exception_source
36
+
37
+ exception_source
38
+ .where("created_at >= ?", time_range.ago)
31
39
  .order(created_at: :desc)
32
- .limit(Newshound.configuration.exception_limit)
40
+ .limit(configuration.exception_limit)
33
41
  end
34
42
 
35
43
  def format_exceptions
@@ -62,9 +70,11 @@ module Newshound
62
70
  end
63
71
 
64
72
  def exception_count(exception)
65
- ExceptionTrack::Log
73
+ return 0 unless exception_source
74
+
75
+ exception_source
66
76
  .where(exception_class: exception.exception_class)
67
- .where("created_at >= ?", 24.hours.ago)
77
+ .where("created_at >= ?", time_range.ago)
68
78
  .count
69
79
  end
70
80