Module: Upkeep::Rails

Defined in:
lib/upkeep/rails.rb,
lib/upkeep/rails/replay.rb,
lib/upkeep/rails/install.rb,
lib/upkeep/rails/railtie.rb,
lib/upkeep/rails/testing.rb,
lib/upkeep/rails/delivery_job.rb,
lib/upkeep/rails/cable/channel.rb,
lib/upkeep/rails/configuration.rb,
lib/upkeep/rails/activation_token.rb,
lib/upkeep/rails/controller_runtime.rb,
lib/upkeep/rails/action_view_capture.rb,
lib/upkeep/rails/client_subscription.rb,
lib/upkeep/rails/cable/subscriber_identity.rb

Defined Under Namespace

Modules: ActionViewCapture, ActivationToken, Cable, ClientSubscription, ControllerRuntime, Install, Replay, Testing Classes: Configuration, ConfigurationError, DeliveryJob, Railtie

Constant Summary collapse

SUBSCRIPTION_IDENTITY =
"upkeep.subscription_identity"
REQUEST_CAPTURE =
"request_capture.upkeep"
DELIVERY_ENQUEUE =
"delivery_enqueue.upkeep"
DELIVERY_ENQUEUE_ERROR =
"delivery_enqueue_error.upkeep"
INTERNAL_DELIVERY_TABLES =
%w[
  upkeep_subscriptions
  upkeep_subscription_index_entries
  upkeep_subscription_shape_index_entries
].freeze

Class Method Summary collapse

Class Method Details

.configurationObject



31
32
33
# File 'lib/upkeep/rails.rb', line 31

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:



35
36
37
# File 'lib/upkeep/rails.rb', line 35

def configure
  yield configuration
end

.deliver_changes!(changes = Runtime::ChangeLog.drain) ⇒ Upkeep::Delivery::Transport::DispatchReport

Dispatches committed application changes through the configured delivery adapter.

ControllerRuntime calls this automatically after non-GET actions. Apps usually should not call it from controllers or models.

Parameters:

  • changes (Array<#to_h>) (defaults to: Runtime::ChangeLog.drain)

    change events to deliver. Defaults to the current runtime change log.

Returns:



111
112
113
114
115
116
117
118
119
# File 'lib/upkeep/rails.rb', line 111

def deliver_changes!(changes = Runtime::ChangeLog.drain)
  changes = deliverable_changes(changes)
  return Delivery::Transport::DispatchReport.new([]) if changes.empty?

  dispatch_changes(changes)
rescue StandardError => error
  instrument_delivery_enqueue_error(changes, error)
  Delivery::Transport::DispatchReport.new([])
end

.deliver_changes_now!(changes = Runtime::ChangeLog.drain) ⇒ Upkeep::Delivery::TurboStreams::Batch, Upkeep::Delivery::Transport::DispatchReport

Delivers committed application changes immediately in the current process.

This is used by the inline delivery adapter and Active Job worker. Tests that need deterministic async delivery should use Upkeep::Rails::Testing instead of calling this directly.

Parameters:

  • changes (Array<#to_h>) (defaults to: Runtime::ChangeLog.drain)

    change events to deliver. Defaults to the current runtime change log.

Returns:



131
132
133
134
135
136
137
# File 'lib/upkeep/rails.rb', line 131

def deliver_changes_now!(changes = Runtime::ChangeLog.drain)
  changes = deliverable_changes(changes)
  return Delivery::Transport::DispatchReport.new([]) if changes.empty?

  batch = delivery_batch_for([changes])
  transport.deliver(batch)
end

.register_controller_subscription(controller, capture) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/upkeep/rails.rb', line 59

def register_controller_subscription(controller, capture)
  recorder = capture.recorder
  return unless subscription_response?(controller, capture)

  decision = Cable::SubscriberIdentity.decision_for(controller.request, recorder: recorder)
  unless recorder.reactive?
    instrument_subscription_identity(
      decision,
      registered: false,
      deopt_reason: "refused_boundary",
      refused_boundaries: recorder.refused_boundaries.map(&:reason)
    )
    return
  end

  identity = Cable::SubscriberIdentity.derive_from_request(
    controller.request,
    recorder: recorder,
    decision: decision
  )
  registration = subscription_registrar.register(
    identity: identity,
    decision: decision,
    recorder: recorder,
    signature: capture.signature,
    metadata: (decision).merge(
      path: controller.request.fullpath,
      stream_name: identity.stream_name
    )
  )
  instrument_subscription_identity(decision, registered: true, subscription: registration.subscription)

  registration
rescue Cable::UnidentifiedSubscriber => error
  instrument_subscription_identity(
    decision || Cable::SubscriberIdentity.decision_for(controller.request, recorder: recorder),
    registered: false,
    deopt_reason: "unidentified_identity",
    error: error.message
  )
  nil
end

.reset_runtime!Object



48
49
50
51
52
53
54
55
56
57
# File 'lib/upkeep/rails.rb', line 48

def reset_runtime!
  @delivery_dispatcher&.shutdown
  @delivery_dispatcher = nil
  @subscription_shape_cache&.reset
  @subscription_registrar = nil
  discard_subscription_store! if @subscriptions
  @subscriptions = build_subscription_store
  @subscriptions.reset
  @transport = Delivery::BroadcastTransport.new
end

.subscriptionsObject



39
40
41
42
# File 'lib/upkeep/rails.rb', line 39

def subscriptions
  discard_subscription_store! if @subscriptions && subscription_store_config_changed?
  @subscriptions ||= build_subscription_store
end

.transportObject



44
45
46
# File 'lib/upkeep/rails.rb', line 44

def transport
  @transport ||= Delivery::BroadcastTransport.new
end

.validate_configuration!(environment: rails_environment) ⇒ Object



139
140
141
142
143
144
# File 'lib/upkeep/rails.rb', line 139

def validate_configuration!(environment: rails_environment)
  return true unless configuration.enabled

  validate_subscription_store!(environment: environment)
  true
end