Class: Railsmith::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/railsmith/context.rb

Overview

Immutable value object for propagating request context through service calls. Accepts domain: plus any number of extra keyword args (actor_id, request_id, etc.) stored at the top level — no nested :meta hash needed.

Example:

ctx = Railsmith::Context.new(domain: :billing, actor_id: 42)
BillingService.call(action: :create, params: params, context: ctx.to_h)

Accessing extras:

ctx[:actor_id]   # => 42
ctx.to_h         # => { current_domain: :billing, actor_id: 42 }

Direct Known Subclasses

DomainContext

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(domain: nil, current_domain: nil, **extras) ⇒ Context

Returns a new instance of Context.

Parameters:

  • domain (Symbol, String, nil) (defaults to: nil)

    bounded-context key (preferred kwarg)

  • current_domain (Symbol, String, nil) (defaults to: nil)

    deprecated alias for domain:

  • extras (Hash)

    arbitrary extra keys stored at the top level



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/railsmith/context.rb', line 95

def initialize(domain: nil, current_domain: nil, **extras)
  if !current_domain.nil? && domain.nil?
    warn "[DEPRECATION] Railsmith::Context: `current_domain:` is deprecated; use `domain:` instead."
    domain = current_domain
  end

  @domain = self.class.normalize_current_domain(domain)
  extras[:request_id] ||= SecureRandom.uuid
  @extras = extras.freeze
  freeze
end

Instance Attribute Details

#domainObject (readonly)

Returns the value of attribute domain.



107
108
109
# File 'lib/railsmith/context.rb', line 107

def domain
  @domain
end

Class Method Details

.build(context) ⇒ Object

Coerces any context-like value into a Context instance.

  • Context → returned as-is

  • nil or {}Context.new with auto-generated request_id

  • Hash → deep-duped and wrapped in Context.new; :current_domain is remapped to :domain so the deprecated alias path is never triggered



63
64
65
66
67
68
69
70
71
72
# File 'lib/railsmith/context.rb', line 63

def self.build(context)
  case context
  when Context
    context
  when Hash
    build_from_hash(context)
  else
    new
  end
end

.currentObject

Returns the thread-local Context set by .with, or nil.



22
23
24
# File 'lib/railsmith/context.rb', line 22

def self.current
  Thread.current[THREAD_KEY]
end

.current=(ctx) ⇒ Object

Sets the thread-local Context directly. Prefer .with for scoped use.



27
28
29
# File 'lib/railsmith/context.rb', line 27

def self.current=(ctx)
  Thread.current[THREAD_KEY] = ctx
end

.normalize_current_domain(value) ⇒ Object

Normalizes a domain value to a Symbol (or nil for blank/nil input).



84
85
86
87
88
89
90
# File 'lib/railsmith/context.rb', line 84

def self.normalize_current_domain(value)
  return nil if value.nil?
  return nil if value.is_a?(String) && value.strip.empty?
  return value if value.is_a?(Symbol)

  value.respond_to?(:to_sym) ? value.to_sym : value
end

.with(context = nil, **kwargs) ⇒ Object

Runs block with a thread-local context built from the given kwargs (or an existing Context instance). Restores the previous value afterwards, even if the block raises.

Railsmith::Context.with(domain: :web, actor_id: 42) do
  UserService.call(action: :create, params: { ... })
end


38
39
40
41
42
43
44
45
46
47
# File 'lib/railsmith/context.rb', line 38

def self.with(context = nil, **kwargs)
  ctx = thread_context_from(context, **kwargs)
  previous = current
  self.current = ctx
  begin
    yield
  ensure
    self.current = previous
  end
end

Instance Method Details

#[](key) ⇒ Object

Accesses extra keys by symbol.



120
121
122
123
124
125
# File 'lib/railsmith/context.rb', line 120

def [](key)
  sym = key.to_sym
  return @domain if %i[current_domain domain].include?(sym)

  @extras[sym]
end

#blank_domain?Boolean

Returns true when no domain has been set.

Returns:

  • (Boolean)


128
129
130
# File 'lib/railsmith/context.rb', line 128

def blank_domain?
  @domain.nil?
end

#current_domainObject

Backward-compatible reader — returns the same value as domain.



115
116
117
# File 'lib/railsmith/context.rb', line 115

def current_domain
  @domain
end

#request_idObject

Returns the request ID (auto-generated UUID if not supplied at construction).



110
111
112
# File 'lib/railsmith/context.rb', line 110

def request_id
  @extras[:request_id]
end

#to_hObject

Serializes to a plain hash. Uses :current_domain as the domain key for backward compatibility with services that read context[:current_domain] directly.



135
136
137
# File 'lib/railsmith/context.rb', line 135

def to_h
  { current_domain: @domain }.merge(@extras)
end