Skip to content

Commit 311c524

Browse files
authored
Merge pull request #38 from github/copilot/fix-37
Comprehensive codebase refinement: DRY principles, YARD documentation, and code quality improvements
2 parents 77e9bd0 + 10c2dec commit 311c524

File tree

16 files changed

+359
-141
lines changed

16 files changed

+359
-141
lines changed

lib/hooks.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require_relative "hooks/core/logger_factory"
88
require_relative "hooks/core/plugin_loader"
99
require_relative "hooks/core/global_components"
10+
require_relative "hooks/core/component_access"
1011
require_relative "hooks/core/log"
1112
require_relative "hooks/core/failbot"
1213
require_relative "hooks/core/stats"

lib/hooks/core/component_access.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# frozen_string_literal: true
2+
3+
module Hooks
4+
module Core
5+
# Shared module providing access to global components (logger, stats, failbot)
6+
#
7+
# This module provides a consistent interface for accessing global components
8+
# across all plugin types, eliminating code duplication and ensuring consistent
9+
# behavior throughout the application.
10+
#
11+
# @example Usage in a class that needs instance methods
12+
# class MyHandler
13+
# include Hooks::Core::ComponentAccess
14+
#
15+
# def process
16+
# log.info("Processing request")
17+
# stats.increment("requests.processed")
18+
# failbot.report("Error occurred") if error?
19+
# end
20+
# end
21+
#
22+
# @example Usage in a class that needs class methods
23+
# class MyValidator
24+
# extend Hooks::Core::ComponentAccess
25+
#
26+
# def self.validate
27+
# log.info("Validating request")
28+
# stats.increment("requests.validated")
29+
# end
30+
# end
31+
module ComponentAccess
32+
# Short logger accessor
33+
# @return [Hooks::Log] Logger instance for logging messages
34+
#
35+
# Provides a convenient way to log messages without needing
36+
# to reference the full Hooks::Log namespace.
37+
#
38+
# @example Logging an error
39+
# log.error("Something went wrong")
40+
def log
41+
Hooks::Log.instance
42+
end
43+
44+
# Global stats component accessor
45+
# @return [Hooks::Plugins::Instruments::Stats] Stats instance for metrics reporting
46+
#
47+
# Provides access to the global stats component for reporting metrics
48+
# to services like DataDog, New Relic, etc.
49+
#
50+
# @example Recording a metric
51+
# stats.increment("webhook.processed", { handler: "MyHandler" })
52+
def stats
53+
Hooks::Core::GlobalComponents.stats
54+
end
55+
56+
# Global failbot component accessor
57+
# @return [Hooks::Plugins::Instruments::Failbot] Failbot instance for error reporting
58+
#
59+
# Provides access to the global failbot component for reporting errors
60+
# to services like Sentry, Rollbar, etc.
61+
#
62+
# @example Reporting an error
63+
# failbot.report("Something went wrong", { context: "additional info" })
64+
def failbot
65+
Hooks::Core::GlobalComponents.failbot
66+
end
67+
end
68+
end
69+
end

lib/hooks/core/log.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
# frozen_string_literal: true
22

33
module Hooks
4+
# Global logger accessor module
5+
#
6+
# Provides a singleton-like access pattern for the application logger.
7+
# The logger instance is set during application initialization and can
8+
# be accessed throughout the application lifecycle.
9+
#
10+
# @example Setting the logger instance
11+
# Hooks::Log.instance = Logger.new(STDOUT)
12+
#
13+
# @example Accessing the logger
14+
# Hooks::Log.instance.info("Application started")
415
module Log
516
class << self
17+
# Get or set the global logger instance
18+
# @return [Logger] The global logger instance
19+
# @attr_reader instance [Logger] Current logger instance
20+
# @attr_writer instance [Logger] Set the logger instance
621
attr_accessor :instance
722
end
823
end

lib/hooks/plugins/auth/base.rb

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "rack/utils"
44
require_relative "../../core/log"
55
require_relative "../../core/global_components"
6+
require_relative "../../core/component_access"
67

78
module Hooks
89
module Plugins
@@ -11,6 +12,8 @@ module Auth
1112
#
1213
# All custom Auth plugins must inherit from this class
1314
class Base
15+
extend Hooks::Core::ComponentAccess
16+
1417
# Validate request
1518
#
1619
# @param payload [String] Raw request body
@@ -22,42 +25,6 @@ def self.valid?(payload:, headers:, config:)
2225
raise NotImplementedError, "Validator must implement .valid? class method"
2326
end
2427

25-
# Short logger accessor for all subclasses
26-
# @return [Hooks::Log] Logger instance for request validation
27-
#
28-
# Provides a convenient way for validators to log messages without needing
29-
# to reference the full Hooks::Log namespace.
30-
#
31-
# @example Logging an error in an inherited class
32-
# log.error("oh no an error occured")
33-
def self.log
34-
Hooks::Log.instance
35-
end
36-
37-
# Global stats component accessor
38-
# @return [Hooks::Core::Stats] Stats instance for metrics reporting
39-
#
40-
# Provides access to the global stats component for reporting metrics
41-
# to services like DataDog, New Relic, etc.
42-
#
43-
# @example Recording a metric in an inherited class
44-
# stats.increment("auth.validation", { plugin: "hmac" })
45-
def self.stats
46-
Hooks::Core::GlobalComponents.stats
47-
end
48-
49-
# Global failbot component accessor
50-
# @return [Hooks::Core::Failbot] Failbot instance for error reporting
51-
#
52-
# Provides access to the global failbot component for reporting errors
53-
# to services like Sentry, Rollbar, etc.
54-
#
55-
# @example Reporting an error in an inherited class
56-
# failbot.report("Auth validation failed", { plugin: "hmac" })
57-
def self.failbot
58-
Hooks::Core::GlobalComponents.failbot
59-
end
60-
6128
# Retrieve the secret from the environment variable based on the key set in the configuration
6229
#
6330
# Note: This method is intended to be used by subclasses

lib/hooks/plugins/auth/hmac.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class HMAC < Base
4040
DEFAULT_CONFIG = {
4141
algorithm: "sha256",
4242
format: "algorithm=signature", # Format: algorithm=hash
43+
header: "X-Signature", # Default header containing the signature
4344
timestamp_tolerance: 300, # 5 minutes tolerance for timestamp validation
4445
version_prefix: "v0" # Default version prefix for versioned signatures
4546
}.freeze
@@ -157,7 +158,7 @@ def self.build_config(config)
157158
tolerance = validator_config[:timestamp_tolerance] || DEFAULT_CONFIG[:timestamp_tolerance]
158159

159160
DEFAULT_CONFIG.merge({
160-
header: validator_config[:header] || "X-Signature",
161+
header: validator_config[:header] || DEFAULT_CONFIG[:header],
161162
timestamp_header: validator_config[:timestamp_header],
162163
timestamp_tolerance: tolerance,
163164
algorithm: algorithm,

lib/hooks/plugins/handlers/base.rb

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative "../../core/global_components"
4+
require_relative "../../core/component_access"
45

56
module Hooks
67
module Plugins
@@ -9,6 +10,8 @@ module Handlers
910
#
1011
# All custom handlers must inherit from this class and implement the #call method
1112
class Base
13+
include Hooks::Core::ComponentAccess
14+
1215
# Process a webhook request
1316
#
1417
# @param payload [Hash, String] Parsed request body (JSON Hash) or raw string
@@ -19,42 +22,6 @@ class Base
1922
def call(payload:, headers:, config:)
2023
raise NotImplementedError, "Handler must implement #call method"
2124
end
22-
23-
# Short logger accessor for all subclasses
24-
# @return [Hooks::Log] Logger instance
25-
#
26-
# Provides a convenient way for handlers to log messages without needing
27-
# to reference the full Hooks::Log namespace.
28-
#
29-
# @example Logging an error in an inherited class
30-
# log.error("oh no an error occured")
31-
def log
32-
Hooks::Log.instance
33-
end
34-
35-
# Global stats component accessor
36-
# @return [Hooks::Core::Stats] Stats instance for metrics reporting
37-
#
38-
# Provides access to the global stats component for reporting metrics
39-
# to services like DataDog, New Relic, etc.
40-
#
41-
# @example Recording a metric in an inherited class
42-
# stats.increment("webhook.processed", { handler: "MyHandler" })
43-
def stats
44-
Hooks::Core::GlobalComponents.stats
45-
end
46-
47-
# Global failbot component accessor
48-
# @return [Hooks::Core::Failbot] Failbot instance for error reporting
49-
#
50-
# Provides access to the global failbot component for reporting errors
51-
# to services like Sentry, Rollbar, etc.
52-
#
53-
# @example Reporting an error in an inherited class
54-
# failbot.report("Something went wrong", { handler: "MyHandler" })
55-
def failbot
56-
Hooks::Core::GlobalComponents.failbot
57-
end
5825
end
5926
end
6027
end

lib/hooks/plugins/handlers/default.rb

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
# frozen_string_literal: true
22

3-
# Default handler when no custom handler is found
4-
# This handler simply acknowledges receipt of the webhook and shows a few of the built-in features
3+
# Default webhook handler implementation
4+
#
5+
# This handler provides a basic webhook processing implementation that can be used
6+
# as a fallback when no custom handler is configured for an endpoint. It demonstrates
7+
# the standard handler interface and provides basic logging functionality.
8+
#
9+
# @example Usage in endpoint configuration
10+
# handler:
11+
# type: DefaultHandler
12+
#
13+
# @see Hooks::Plugins::Handlers::Base
514
class DefaultHandler < Hooks::Plugins::Handlers::Base
15+
# Process a webhook request with basic acknowledgment
16+
#
17+
# Provides a simple webhook processing implementation that logs the request
18+
# and returns a standard acknowledgment response. This is useful for testing
19+
# webhook endpoints or as a placeholder during development.
20+
#
21+
# @param payload [Hash, String] The webhook payload (parsed JSON or raw string)
22+
# @param headers [Hash<String, String>] HTTP headers from the webhook request
23+
# @param config [Hash] Endpoint configuration containing handler options
24+
# @return [Hash] Response indicating successful processing
25+
# @option config [Hash] :opts Additional handler-specific configuration options
26+
#
27+
# @example Basic usage
28+
# handler = DefaultHandler.new
29+
# response = handler.call(
30+
# payload: { "event" => "push" },
31+
# headers: { "Content-Type" => "application/json" },
32+
# config: { opts: {} }
33+
# )
34+
# # => { message: "webhook processed successfully", handler: "DefaultHandler", timestamp: "..." }
635
def call(payload:, headers:, config:)
736

837
log.info("🔔 Default handler invoked for webhook 🔔")

lib/hooks/plugins/instruments/failbot.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,25 @@ module Plugins
77
module Instruments
88
# Default failbot instrument implementation
99
#
10-
# This is a stub implementation that does nothing by default.
11-
# Users can replace this with their own implementation for services
12-
# like Sentry, Rollbar, etc.
10+
# This is a no-op implementation that provides the error reporting interface
11+
# without actually sending errors anywhere. It serves as a safe default when
12+
# no custom error reporting implementation is configured.
13+
#
14+
# Users should replace this with their own implementation for services
15+
# like Sentry, Rollbar, Honeybadger, etc.
16+
#
17+
# @example Replacing with a custom implementation
18+
# # In your application initialization:
19+
# custom_failbot = MySentryFailbotImplementation.new
20+
# Hooks::Core::GlobalComponents.failbot = custom_failbot
21+
#
22+
# @see Hooks::Plugins::Instruments::FailbotBase
23+
# @see Hooks::Core::GlobalComponents
1324
class Failbot < FailbotBase
14-
# Inherit from FailbotBase to provide a default implementation of the failbot instrument.
25+
# Inherit from FailbotBase to provide a default no-op implementation
26+
# of the error reporting instrument interface.
27+
#
28+
# All methods from FailbotBase are inherited and provide safe no-op behavior.
1529
end
1630
end
1731
end

lib/hooks/plugins/instruments/failbot_base.rb

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,70 @@
11
# frozen_string_literal: true
22

3+
require_relative "../../core/component_access"
4+
35
module Hooks
46
module Plugins
57
module Instruments
68
# Base class for all failbot instrument plugins
79
#
8-
# All custom failbot implementations must inherit from this class and implement
9-
# the required methods for error reporting.
10+
# This class provides the foundation for implementing custom error reporting
11+
# instruments. Subclasses should implement specific methods for their target
12+
# error reporting service (Sentry, Rollbar, Honeybadger, etc.).
13+
#
14+
# @abstract Subclass and implement service-specific error reporting methods
15+
# @example Implementing a custom failbot instrument
16+
# class MySentryFailbot < Hooks::Plugins::Instruments::FailbotBase
17+
# def report(error_or_message, context = {})
18+
# case error_or_message
19+
# when Exception
20+
# Sentry.capture_exception(error_or_message, extra: context)
21+
# else
22+
# Sentry.capture_message(error_or_message.to_s, extra: context)
23+
# end
24+
# log.debug("Reported error to Sentry")
25+
# end
26+
# end
27+
#
28+
# @see Hooks::Plugins::Instruments::Failbot
1029
class FailbotBase
11-
# Short logger accessor for all subclasses
12-
# @return [Hooks::Log] Logger instance
30+
include Hooks::Core::ComponentAccess
31+
32+
# Report an error or message to the error tracking service
33+
#
34+
# This is a no-op implementation that subclasses should override
35+
# to provide actual error reporting functionality.
36+
#
37+
# @param error_or_message [Exception, String] The error to report or message string
38+
# @param context [Hash] Additional context information about the error
39+
# @return [void]
40+
# @note Subclasses should implement this method for their specific service
41+
# @example Override in subclass
42+
# def report(error_or_message, context = {})
43+
# if error_or_message.is_a?(Exception)
44+
# ErrorService.report_exception(error_or_message, context)
45+
# else
46+
# ErrorService.report_message(error_or_message, context)
47+
# end
48+
# end
49+
def report(error_or_message, context = {})
50+
# No-op implementation for base class
51+
end
52+
53+
# Report a warning-level message
1354
#
14-
# Provides a convenient way for instruments to log messages without needing
15-
# to reference the full Hooks::Log namespace.
55+
# This is a no-op implementation that subclasses should override
56+
# to provide actual warning reporting functionality.
1657
#
17-
# @example Logging debug info in an inherited class
18-
# log.debug("Sending error to external service")
19-
def log
20-
Hooks::Log.instance
58+
# @param message [String] Warning message to report
59+
# @param context [Hash] Additional context information
60+
# @return [void]
61+
# @note Subclasses should implement this method for their specific service
62+
# @example Override in subclass
63+
# def warn(message, context = {})
64+
# ErrorService.report_warning(message, context)
65+
# end
66+
def warn(message, context = {})
67+
# No-op implementation for base class
2168
end
2269
end
2370
end

0 commit comments

Comments
 (0)