Skip to content

Commit 3bab9e5

Browse files
CopilotGrantBirki
andcommitted
DRY refactor: extract shared ComponentAccess module
Co-authored-by: GrantBirki <[email protected]>
1 parent d9109d7 commit 3bab9e5

File tree

8 files changed

+142
-110
lines changed

8 files changed

+142
-110
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/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/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/lifecycle.rb

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
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
78
# Base class for global lifecycle plugins
89
#
910
# Plugins can hook into request/response/error lifecycle events
1011
class Lifecycle
12+
include Hooks::Core::ComponentAccess
13+
1114
# Called before handler execution
1215
#
1316
# @param env [Hash] Rack environment
@@ -30,42 +33,6 @@ def on_response(env, response)
3033
def on_error(exception, env)
3134
# Override in subclass for error handling logic
3235
end
33-
34-
# Short logger accessor for all subclasses
35-
# @return [Hooks::Log] Logger instance
36-
#
37-
# Provides a convenient way for lifecycle plugins to log messages without needing
38-
# to reference the full Hooks::Log namespace.
39-
#
40-
# @example Logging an error in an inherited class
41-
# log.error("oh no an error occured")
42-
def log
43-
Hooks::Log.instance
44-
end
45-
46-
# Global stats component accessor
47-
# @return [Hooks::Core::Stats] Stats instance for metrics reporting
48-
#
49-
# Provides access to the global stats component for reporting metrics
50-
# to services like DataDog, New Relic, etc.
51-
#
52-
# @example Recording a metric in an inherited class
53-
# stats.increment("lifecycle.request_processed")
54-
def stats
55-
Hooks::Core::GlobalComponents.stats
56-
end
57-
58-
# Global failbot component accessor
59-
# @return [Hooks::Core::Failbot] Failbot instance for error reporting
60-
#
61-
# Provides access to the global failbot component for reporting errors
62-
# to services like Sentry, Rollbar, etc.
63-
#
64-
# @example Reporting an error in an inherited class
65-
# failbot.report("Lifecycle hook failed")
66-
def failbot
67-
Hooks::Core::GlobalComponents.failbot
68-
end
6936
end
7037
end
7138
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
describe Hooks::Core::ComponentAccess do
4+
let(:test_class_with_include) do
5+
Class.new do
6+
include Hooks::Core::ComponentAccess
7+
end
8+
end
9+
10+
let(:test_class_with_extend) do
11+
Class.new do
12+
extend Hooks::Core::ComponentAccess
13+
end
14+
end
15+
16+
describe "when included" do
17+
let(:instance) { test_class_with_include.new }
18+
19+
describe "#log" do
20+
it "provides access to global logger" do
21+
expect(instance.log).to be(Hooks::Log.instance)
22+
end
23+
end
24+
25+
describe "#stats" do
26+
it "provides access to global stats" do
27+
expect(instance.stats).to be_a(Hooks::Plugins::Instruments::Stats)
28+
expect(instance.stats).to eq(Hooks::Core::GlobalComponents.stats)
29+
end
30+
end
31+
32+
describe "#failbot" do
33+
it "provides access to global failbot" do
34+
expect(instance.failbot).to be_a(Hooks::Plugins::Instruments::Failbot)
35+
expect(instance.failbot).to eq(Hooks::Core::GlobalComponents.failbot)
36+
end
37+
end
38+
end
39+
40+
describe "when extended" do
41+
describe ".log" do
42+
it "provides access to global logger" do
43+
expect(test_class_with_extend.log).to be(Hooks::Log.instance)
44+
end
45+
end
46+
47+
describe ".stats" do
48+
it "provides access to global stats" do
49+
expect(test_class_with_extend.stats).to be_a(Hooks::Plugins::Instruments::Stats)
50+
expect(test_class_with_extend.stats).to eq(Hooks::Core::GlobalComponents.stats)
51+
end
52+
end
53+
54+
describe ".failbot" do
55+
it "provides access to global failbot" do
56+
expect(test_class_with_extend.failbot).to be_a(Hooks::Plugins::Instruments::Failbot)
57+
expect(test_class_with_extend.failbot).to eq(Hooks::Core::GlobalComponents.failbot)
58+
end
59+
end
60+
end
61+
end

spec/unit/lib/hooks/handlers/base_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def call(payload:, headers:, config:)
145145

146146
describe "documentation compliance" do
147147
it "has the expected public interface" do
148-
expect(described_class.instance_methods(false)).to include(:call, :log, :stats, :failbot)
148+
expect(described_class.instance_methods).to include(:call, :log, :stats, :failbot)
149149
end
150150

151151
it "call method accepts the documented parameters" do

spec/unit/lib/hooks/plugins/auth/base_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def self.valid?(payload:, headers:, config:)
206206

207207
describe "documentation compliance" do
208208
it "has the expected public interface" do
209-
expect(described_class.methods(false)).to include(:valid?, :log, :stats, :failbot, :fetch_secret)
209+
expect(described_class.methods).to include(:valid?, :log, :stats, :failbot, :fetch_secret)
210210
end
211211

212212
it "valid? method accepts the documented parameters" do

0 commit comments

Comments
 (0)