mbuzz 0.7.0 → 0.7.2

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: 0fa7c025330829c88ab9a75a22ca286d14adc1df7872295c5519df4c89536db9
4
- data.tar.gz: 62d6675e5ffe9504e2262f84551c286366acc2ad42b6ca6d224367902d9ec3fb
3
+ metadata.gz: 0d8514076473bbc4fa543510e84789f8cb91ccc99e916b3cef4f4928aaf6baf5
4
+ data.tar.gz: 159074f0dbac29eb6b721bab4288ce976e4e4361ba503caffc36f37e79cf315e
5
5
  SHA512:
6
- metadata.gz: eb90968b0d4c17bdc7c5019fa0058c257233bc00e1921f0b70a3dbf885fd8aecd1f9d80ab125d31ab535be5e55a44ecc2e0d46e9cb9c51fef4b9305634241b2f
7
- data.tar.gz: cef7cbbc7817a6d7c4eca635594ac9e3b9661d81b33cc7d956376cb9fd763a6d216651e6ec499146e31dca042f024216f3c85c08c6d83f4f31dfb1fff64e8b56
6
+ metadata.gz: 3d515f639e577a68d9daa8afe13de92dec0a5556cfad2e335ea482f7db0240ff996dd1fb6bbf3388e7f632cb8300e439028091cf1c551627c9be1c22e73ca666
7
+ data.tar.gz: 9fbe4999f2ce0855d83847d61b4684f30217c01c9074df4cced51e4083167cf3ca24e0e5223f2e57fd859aab6d88f60a68a0adc3074bd0a7f4356c0f9d3e5116
data/README.md CHANGED
@@ -121,6 +121,63 @@ Mbuzz.user_id # Current user ID (from session["user_id"])
121
121
  Mbuzz.session_id # Current session ID (from cookie)
122
122
  ```
123
123
 
124
+ ## Background Jobs
125
+
126
+ When tracking from background jobs (Sidekiq, GoodJob, etc.), there's no HTTP request context. Rails 7+ handles this automatically via `CurrentAttributes`.
127
+
128
+ ### Rails 7+ (Automatic)
129
+
130
+ mbuzz uses `ActiveSupport::CurrentAttributes` which Rails automatically serializes into ActiveJob payloads:
131
+
132
+ ```ruby
133
+ # In your controller - just enqueue the job
134
+ class OrdersController < ApplicationController
135
+ def create
136
+ @order = Order.create!(order_params)
137
+ ProcessOrderJob.perform_later(@order.id)
138
+ # visitor_id is automatically captured and passed to the job
139
+ end
140
+ end
141
+
142
+ # In your job - mbuzz just works!
143
+ class ProcessOrderJob < ApplicationJob
144
+ def perform(order_id)
145
+ order = Order.find(order_id)
146
+ # Mbuzz::Current.visitor_id was restored by Rails
147
+ Mbuzz.conversion("purchase", revenue: order.total)
148
+ end
149
+ end
150
+ ```
151
+
152
+ **How it works:**
153
+ 1. Middleware captures `visitor_id` from cookie into `Mbuzz::Current`
154
+ 2. Controller enqueues job
155
+ 3. Rails serializes `Mbuzz::Current` into job payload
156
+ 4. Job runs → Rails restores `Mbuzz::Current`
157
+ 5. `Mbuzz.conversion()` reads from `Current` - works!
158
+
159
+ ### Alternative: Explicit visitor_id
160
+
161
+ For non-Rails apps or when you need more control:
162
+
163
+ ```ruby
164
+ # Store visitor_id on your model
165
+ class Order < ApplicationRecord
166
+ before_create { self.mbuzz_visitor_id = Mbuzz.visitor_id }
167
+ end
168
+
169
+ # Pass explicitly in background job
170
+ class ProcessOrderJob
171
+ def perform(order_id)
172
+ order = Order.find(order_id)
173
+ Mbuzz.conversion("purchase",
174
+ visitor_id: order.mbuzz_visitor_id, # Explicit
175
+ revenue: order.total
176
+ )
177
+ end
178
+ end
179
+ ```
180
+
124
181
  ## Rack / Sinatra Integration
125
182
 
126
183
  For non-Rails apps, add the middleware manually:
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mbuzz
4
+ class Client
5
+ class SessionRequest
6
+ # Response keys
7
+ STATUS_KEY = "status"
8
+ VISITOR_ID_KEY = "visitor_id"
9
+ SESSION_ID_KEY = "session_id"
10
+ CHANNEL_KEY = "channel"
11
+
12
+ # Response values
13
+ ACCEPTED_STATUS = "accepted"
14
+
15
+ def initialize(visitor_id:, session_id:, url:, referrer: nil, device_fingerprint: nil, started_at: nil)
16
+ @visitor_id = visitor_id
17
+ @session_id = session_id
18
+ @url = url
19
+ @referrer = referrer
20
+ @device_fingerprint = device_fingerprint
21
+ @started_at = started_at
22
+ end
23
+
24
+ def call
25
+ return false unless valid?
26
+
27
+ parse_response(response)
28
+ rescue StandardError
29
+ false
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :visitor_id, :session_id, :url, :referrer, :device_fingerprint, :started_at
35
+
36
+ def valid?
37
+ present?(visitor_id) && present?(session_id) && present?(url)
38
+ end
39
+
40
+ def response
41
+ @response ||= Api.post_with_response(SESSIONS_PATH, { session: payload })
42
+ end
43
+
44
+ def payload
45
+ {
46
+ visitor_id: visitor_id,
47
+ session_id: session_id,
48
+ url: url,
49
+ referrer: referrer,
50
+ device_fingerprint: device_fingerprint,
51
+ started_at: started_at || Time.now.utc.iso8601
52
+ }.compact
53
+ end
54
+
55
+ def parse_response(resp)
56
+ return false unless accepted?(resp)
57
+
58
+ {
59
+ success: true,
60
+ visitor_id: resp[VISITOR_ID_KEY],
61
+ session_id: resp[SESSION_ID_KEY],
62
+ channel: resp[CHANNEL_KEY]
63
+ }
64
+ end
65
+
66
+ def accepted?(resp)
67
+ resp && resp[STATUS_KEY] == ACCEPTED_STATUS
68
+ end
69
+
70
+ def present?(value)
71
+ value && !value.to_s.strip.empty?
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/mbuzz/client.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative "client/track_request"
4
4
  require_relative "client/identify_request"
5
5
  require_relative "client/conversion_request"
6
+ require_relative "client/session_request"
6
7
 
7
8
  module Mbuzz
8
9
  class Client
@@ -30,5 +31,16 @@ module Mbuzz
30
31
  identifier: identifier
31
32
  ).call
32
33
  end
34
+
35
+ def self.session(visitor_id:, session_id:, url:, referrer: nil, device_fingerprint: nil, started_at: nil)
36
+ SessionRequest.new(
37
+ visitor_id: visitor_id,
38
+ session_id: session_id,
39
+ url: url,
40
+ referrer: referrer,
41
+ device_fingerprint: device_fingerprint,
42
+ started_at: started_at
43
+ ).call
44
+ end
33
45
  end
34
46
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mbuzz
4
+ # CurrentAttributes for automatic background job context propagation.
5
+ #
6
+ # Rails automatically serializes CurrentAttributes into ActiveJob payloads
7
+ # and restores them when jobs execute. This means visitor_id captured during
8
+ # the original request is available in background jobs without any manual
9
+ # passing or database storage.
10
+ #
11
+ # How it works:
12
+ # 1. Middleware captures visitor_id from cookie
13
+ # 2. Stores in Mbuzz::Current.visitor_id
14
+ # 3. Controller enqueues background job
15
+ # 4. Rails serializes Current attributes into job payload
16
+ # 5. Job runs on different thread/process
17
+ # 6. Rails restores Current.visitor_id before job executes
18
+ # 7. Mbuzz.event/conversion reads from Current.visitor_id
19
+ #
20
+ # This is why customers don't need to store visitor_id in their database.
21
+ #
22
+ class Current < ActiveSupport::CurrentAttributes
23
+ attribute :visitor_id
24
+ attribute :session_id
25
+ attribute :user_id
26
+ attribute :ip
27
+ attribute :user_agent
28
+ end
29
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rack"
4
+ require "digest"
5
+ require "securerandom"
4
6
 
5
7
  module Mbuzz
6
8
  module Middleware
@@ -16,12 +18,20 @@ module Mbuzz
16
18
  context = build_request_context(request)
17
19
 
18
20
  env[ENV_VISITOR_ID_KEY] = context[:visitor_id]
21
+ env[ENV_SESSION_ID_KEY] = context[:session_id]
19
22
  env[ENV_USER_ID_KEY] = context[:user_id]
20
23
 
24
+ store_in_current_attributes(context, request)
25
+
26
+ create_session_async(context, request) if context[:new_session]
27
+
21
28
  RequestContext.with_context(request: request) do
22
29
  status, headers, body = @app.call(env)
23
30
  set_visitor_cookie(headers, context, request)
31
+ set_session_cookie(headers, context, request)
24
32
  [status, headers, body]
33
+ ensure
34
+ reset_current_attributes
25
35
  end
26
36
  end
27
37
 
@@ -45,10 +55,26 @@ module Mbuzz
45
55
 
46
56
  # Build all request-specific context as a frozen hash
47
57
  # This ensures thread-safety by using local variables only
58
+ # IMPORTANT: All values needed by async session creation must be captured here,
59
+ # NOT accessed from the request object in the background thread (see #create_session)
48
60
  def build_request_context(request)
61
+ existing_session_id = session_id_from_cookie(request)
62
+ new_session = existing_session_id.nil?
63
+ ip = extract_ip(request)
64
+ user_agent = request.user_agent.to_s
65
+
49
66
  {
50
67
  visitor_id: resolve_visitor_id(request),
51
- user_id: user_id_from_session(request)
68
+ session_id: existing_session_id || generate_session_id,
69
+ user_id: user_id_from_session(request),
70
+ new_session: new_session,
71
+ # Session creation data - captured here for thread-safety
72
+ # Background thread must NOT read from request object
73
+ url: request.url,
74
+ referrer: request.referer,
75
+ ip: ip,
76
+ user_agent: user_agent,
77
+ device_fingerprint: Digest::SHA256.hexdigest("#{ip}|#{user_agent}")[0, 32]
52
78
  }.freeze
53
79
  end
54
80
 
@@ -60,11 +86,47 @@ module Mbuzz
60
86
  request.cookies[VISITOR_COOKIE_NAME]
61
87
  end
62
88
 
89
+ def session_id_from_cookie(request)
90
+ request.cookies[SESSION_COOKIE_NAME]
91
+ end
92
+
93
+ def generate_session_id
94
+ SecureRandom.uuid
95
+ end
96
+
63
97
  def user_id_from_session(request)
64
98
  request.session[SESSION_USER_ID_KEY] if request.session
65
99
  end
66
100
 
67
- # Cookie setting - only visitor cookie (sessions are server-side)
101
+ # Session creation - async to not block request
102
+ # IMPORTANT: This runs in a background thread. All data must come from
103
+ # the context hash, NOT from the request object (which may be invalid)
104
+
105
+ def create_session_async(context, _request)
106
+ Thread.new do
107
+ create_session(context)
108
+ rescue StandardError => e
109
+ log_error("Session creation failed: #{e.message}") if Mbuzz.config.debug
110
+ end
111
+ end
112
+
113
+ def create_session(context)
114
+ Client.session(
115
+ visitor_id: context[:visitor_id],
116
+ session_id: context[:session_id],
117
+ url: context[:url],
118
+ referrer: context[:referrer],
119
+ device_fingerprint: context[:device_fingerprint]
120
+ )
121
+ end
122
+
123
+ def log_error(message)
124
+ return unless defined?(Rails) && Rails.logger
125
+
126
+ Rails.logger.error("[Mbuzz] #{message}")
127
+ end
128
+
129
+ # Cookie setting - visitor and session cookies
68
130
 
69
131
  def set_visitor_cookie(headers, context, request)
70
132
  Rack::Utils.set_cookie_header!(
@@ -74,6 +136,14 @@ module Mbuzz
74
136
  )
75
137
  end
76
138
 
139
+ def set_session_cookie(headers, context, request)
140
+ Rack::Utils.set_cookie_header!(
141
+ headers,
142
+ SESSION_COOKIE_NAME,
143
+ session_cookie_options(context, request)
144
+ )
145
+ end
146
+
77
147
  def visitor_cookie_options(context, request)
78
148
  base_cookie_options(request).merge(
79
149
  value: context[:visitor_id],
@@ -81,6 +151,13 @@ module Mbuzz
81
151
  )
82
152
  end
83
153
 
154
+ def session_cookie_options(context, request)
155
+ base_cookie_options(request).merge(
156
+ value: context[:session_id],
157
+ max_age: SESSION_COOKIE_MAX_AGE
158
+ )
159
+ end
160
+
84
161
  def base_cookie_options(request)
85
162
  options = {
86
163
  path: VISITOR_COOKIE_PATH,
@@ -90,6 +167,30 @@ module Mbuzz
90
167
  options[:secure] = true if request.ssl?
91
168
  options
92
169
  end
170
+
171
+ # Store context in CurrentAttributes for background job propagation
172
+ def store_in_current_attributes(context, request)
173
+ return unless defined?(Mbuzz::Current)
174
+
175
+ Mbuzz::Current.visitor_id = context[:visitor_id]
176
+ Mbuzz::Current.session_id = context[:session_id]
177
+ Mbuzz::Current.user_id = context[:user_id]
178
+ Mbuzz::Current.ip = extract_ip(request)
179
+ Mbuzz::Current.user_agent = request.user_agent
180
+ end
181
+
182
+ def reset_current_attributes
183
+ return unless defined?(Mbuzz::Current)
184
+
185
+ Mbuzz::Current.reset
186
+ end
187
+
188
+ def extract_ip(request)
189
+ forwarded = request.env["HTTP_X_FORWARDED_FOR"]
190
+ return forwarded.split(",").first.strip if forwarded
191
+
192
+ request.ip
193
+ end
93
194
  end
94
195
  end
95
196
  end
data/lib/mbuzz/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mbuzz
4
- VERSION = "0.7.0"
4
+ VERSION = "0.7.2"
5
5
  end
data/lib/mbuzz.rb CHANGED
@@ -9,6 +9,9 @@ require_relative "mbuzz/client"
9
9
  require_relative "mbuzz/middleware/tracking"
10
10
  require_relative "mbuzz/controller_helpers"
11
11
 
12
+ # CurrentAttributes for automatic background job context propagation (Rails only)
13
+ require_relative "mbuzz/current" if defined?(ActiveSupport::CurrentAttributes)
14
+
12
15
  require_relative "mbuzz/railtie" if defined?(Rails::Railtie)
13
16
 
14
17
  module Mbuzz
@@ -17,15 +20,20 @@ module Mbuzz
17
20
  EVENTS_PATH = "/events"
18
21
  IDENTIFY_PATH = "/identify"
19
22
  CONVERSIONS_PATH = "/conversions"
23
+ SESSIONS_PATH = "/sessions"
20
24
 
21
25
  VISITOR_COOKIE_NAME = "_mbuzz_vid"
22
26
  VISITOR_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 2 # 2 years
23
27
  VISITOR_COOKIE_PATH = "/"
24
28
  VISITOR_COOKIE_SAME_SITE = "Lax"
25
29
 
30
+ SESSION_COOKIE_NAME = "_mbuzz_sid"
31
+ SESSION_COOKIE_MAX_AGE = 30 * 60 # 30 minutes
32
+
26
33
  SESSION_USER_ID_KEY = "user_id"
27
34
  ENV_USER_ID_KEY = "mbuzz.user_id"
28
35
  ENV_VISITOR_ID_KEY = "mbuzz.visitor_id"
36
+ ENV_SESSION_ID_KEY = "mbuzz.session_id"
29
37
 
30
38
  # ============================================================================
31
39
  # Configuration
@@ -62,18 +70,25 @@ module Mbuzz
62
70
  # Context Accessors
63
71
  # ============================================================================
64
72
 
73
+ # Returns visitor_id from Current (background jobs) or request context
65
74
  def self.visitor_id
66
- RequestContext.current&.request&.env&.dig(ENV_VISITOR_ID_KEY) || fallback_visitor_id
75
+ current_visitor_id || RequestContext.current&.request&.env&.dig(ENV_VISITOR_ID_KEY)
67
76
  end
68
77
 
69
- def self.fallback_visitor_id
70
- @fallback_visitor_id ||= Visitor::Identifier.generate
78
+ def self.user_id
79
+ current_user_id || RequestContext.current&.request&.env&.dig(ENV_USER_ID_KEY)
71
80
  end
72
- private_class_method :fallback_visitor_id
73
81
 
74
- def self.user_id
75
- RequestContext.current&.request&.env&.dig(ENV_USER_ID_KEY)
82
+ # Check Current attributes (for background job support)
83
+ def self.current_visitor_id
84
+ defined?(Current) ? Current.visitor_id : nil
85
+ end
86
+ private_class_method :current_visitor_id
87
+
88
+ def self.current_user_id
89
+ defined?(Current) ? Current.user_id : nil
76
90
  end
91
+ private_class_method :current_user_id
77
92
 
78
93
  # ============================================================================
79
94
  # 4-Call Model API
@@ -82,20 +97,30 @@ module Mbuzz
82
97
  # Track an event (journey step)
83
98
  #
84
99
  # @param event_type [String] The name of the event
100
+ # @param visitor_id [String, nil] Explicit visitor ID (required for background jobs)
85
101
  # @param properties [Hash] Custom event properties (url, referrer auto-added)
86
102
  # @param identifier [Hash, nil] Optional identifier for cross-device identity resolution
87
103
  # @return [Hash, false] Result hash on success, false on failure
88
104
  #
89
- # @example
105
+ # @example Normal usage (within request context)
90
106
  # Mbuzz.event("add_to_cart", product_id: "SKU-123", price: 49.99)
91
107
  #
108
+ # @example Background job (must pass explicit visitor_id)
109
+ # Mbuzz.event("order_processed", visitor_id: order.mbuzz_visitor_id, order_id: order.id)
110
+ #
92
111
  # @example With identifier for cross-device tracking
93
112
  # Mbuzz.event("page_view", identifier: { email: "user@example.com" })
94
113
  #
95
- def self.event(event_type, identifier: nil, **properties)
114
+ def self.event(event_type, visitor_id: nil, identifier: nil, **properties)
115
+ resolved_visitor_id = visitor_id || self.visitor_id
116
+ resolved_user_id = user_id
117
+
118
+ # Must have at least one identifier
119
+ return false unless resolved_visitor_id || resolved_user_id
120
+
96
121
  Client.track(
97
- visitor_id: visitor_id,
98
- user_id: user_id,
122
+ visitor_id: resolved_visitor_id,
123
+ user_id: resolved_user_id,
99
124
  event_type: event_type,
100
125
  properties: enriched_properties(properties),
101
126
  ip: current_ip,
@@ -113,6 +138,7 @@ module Mbuzz
113
138
  # Track a conversion (revenue-generating outcome)
114
139
  #
115
140
  # @param conversion_type [String] The type of conversion
141
+ # @param visitor_id [String, nil] Explicit visitor ID (required for background jobs)
116
142
  # @param revenue [Numeric, nil] Revenue amount
117
143
  # @param user_id [String, nil] User ID for acquisition-linked conversions
118
144
  # @param is_acquisition [Boolean] Mark this as the acquisition conversion for this user
@@ -121,9 +147,12 @@ module Mbuzz
121
147
  # @param properties [Hash] Custom properties
122
148
  # @return [Hash, false] Result hash on success, false on failure
123
149
  #
124
- # @example Basic conversion
150
+ # @example Basic conversion (within request context)
125
151
  # Mbuzz.conversion("purchase", revenue: 99.99, order_id: "ORD-123")
126
152
  #
153
+ # @example Background job (must pass explicit visitor_id)
154
+ # Mbuzz.conversion("purchase", visitor_id: order.mbuzz_visitor_id, revenue: 99.99)
155
+ #
127
156
  # @example Acquisition conversion (marks signup as THE acquisition moment)
128
157
  # Mbuzz.conversion("signup", user_id: "user_123", is_acquisition: true)
129
158
  #
@@ -133,9 +162,14 @@ module Mbuzz
133
162
  # @example With identifier for cross-device tracking
134
163
  # Mbuzz.conversion("purchase", identifier: { email: "user@example.com" })
135
164
  #
136
- def self.conversion(conversion_type, revenue: nil, user_id: nil, is_acquisition: false, inherit_acquisition: false, identifier: nil, **properties)
165
+ def self.conversion(conversion_type, visitor_id: nil, revenue: nil, user_id: nil, is_acquisition: false, inherit_acquisition: false, identifier: nil, **properties)
166
+ resolved_visitor_id = visitor_id || self.visitor_id
167
+
168
+ # Must have at least one identifier (visitor_id or user_id)
169
+ return false unless resolved_visitor_id || user_id
170
+
137
171
  Client.conversion(
138
- visitor_id: visitor_id,
172
+ visitor_id: resolved_visitor_id,
139
173
  user_id: user_id,
140
174
  conversion_type: conversion_type,
141
175
  revenue: revenue,
@@ -181,12 +215,26 @@ module Mbuzz
181
215
  private_class_method :enriched_properties
182
216
 
183
217
  def self.current_ip
184
- RequestContext.current&.ip
218
+ current_attributes_ip || RequestContext.current&.ip
185
219
  end
186
220
  private_class_method :current_ip
187
221
 
188
222
  def self.current_user_agent
189
- RequestContext.current&.user_agent
223
+ current_attributes_user_agent || RequestContext.current&.user_agent
190
224
  end
191
225
  private_class_method :current_user_agent
226
+
227
+ def self.current_attributes_ip
228
+ return nil unless defined?(Current)
229
+
230
+ Current.ip
231
+ end
232
+ private_class_method :current_attributes_ip
233
+
234
+ def self.current_attributes_user_agent
235
+ return nil unless defined?(Current)
236
+
237
+ Current.user_agent
238
+ end
239
+ private_class_method :current_attributes_user_agent
192
240
  end
@@ -1,6 +1,13 @@
1
1
  # mbuzz SDK v0.7.0 - Deterministic Session IDs
2
2
 
3
- **Status**: Proposed
3
+ > **⚠️ SUPERSEDED**: This spec has been replaced by server-side session resolution.
4
+ > See: `multibuzz/lib/specs/1_visitor_session_tracking_spec.md`
5
+ >
6
+ > **What changed**: Instead of SDKs generating deterministic session IDs using time-buckets,
7
+ > the server now handles all session resolution using IP + User-Agent fingerprinting with
8
+ > a true 30-minute sliding window. SDKs no longer manage session cookies.
9
+
10
+ **Status**: SUPERSEDED (2026-01-09)
4
11
  **Last Updated**: 2025-12-29
5
12
  **Breaking Change**: No (backward compatible)
6
13
  **Affects**: All SDKs (Ruby, Python, PHP, Node)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbuzz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - mbuzz team
@@ -44,9 +44,11 @@ files:
44
44
  - lib/mbuzz/client.rb
45
45
  - lib/mbuzz/client/conversion_request.rb
46
46
  - lib/mbuzz/client/identify_request.rb
47
+ - lib/mbuzz/client/session_request.rb
47
48
  - lib/mbuzz/client/track_request.rb
48
49
  - lib/mbuzz/configuration.rb
49
50
  - lib/mbuzz/controller_helpers.rb
51
+ - lib/mbuzz/current.rb
50
52
  - lib/mbuzz/middleware/tracking.rb
51
53
  - lib/mbuzz/railtie.rb
52
54
  - lib/mbuzz/request_context.rb