class GraphQL::Streaming::ActionCableSubscriber

A subscriber for a channel-query combo.

@example Subscribe to a query in an ActionCable action, re-evaluating it when things change

# Initialize `context` ahead of time so it can be closed over in the subscription block
context = {}

context[:subscriber] = GraphQLSubscriber.new(self, query_id) do
  # Tell the schema how to re-fetch results:
  context[:current_user].reload
  MySchema.execute(query_string, context: context, variables: variables)
end

# Run the query
MySchema.execute(query_string, context: context, variables: variables)

# detect whether any subscriptions were added
context[:subscriber].subscribed?

Constants

CHANNEL_PREFIX

Public Class Methods

new(channel, query_id, &query_exec) click to toggle source

@param [ActionCable::Channel::Base] The channel to push updates to @param [Object] The query who the updates belong to (probably provided by the client) @yield Reruns the query

# File lib/graphql/streaming/action_cable_subscriber.rb, line 27
def initialize(channel, query_id, &query_exec)
  @channel = channel
  @query_id = query_id
  @query_exec = query_exec
  @subscribed = false
  @own_streams = []
end
trigger(subscription_handle, trigger_options = {}) click to toggle source

Trigger an event with arguments

@example Trigger post_changed

# First, subscribe with "{ post_changed(id: 1) { title } }"
# Then, trigger the event:
GraphQL::Streaming::ActionCableSubscriber.trigger(:post_changed, {id: 1})

@param [Symbol] The subscription name to trigger @param [Hash] Arguments to send with the subscription

# File lib/graphql/streaming/action_cable_subscriber.rb, line 44
def self.trigger(subscription_handle, trigger_options = {})
  ActionCable.server.broadcast("#{CHANNEL_PREFIX}#{subscription_handle}", trigger_options)
end

Public Instance Methods

close() click to toggle source

Tell this subscriber to stop sending patches @return [void]

# File lib/graphql/streaming/action_cable_subscriber.rb, line 72
def close
  @channel.stop_specific_streams(@own_streams)
  @own_streams.clear
  @subscribed = false
  nil
end
register(subscription_handle, arguments) click to toggle source

Subscribe to event named `subscription_handle`, but only when called with arguments `arguments` @param [String] the event name to subscribe to @param [Hash] the arguments to subscribe to

# File lib/graphql/streaming/action_cable_subscriber.rb, line 52
def register(subscription_handle, arguments)
  @subscribed = true
  handle = "#{CHANNEL_PREFIX}#{subscription_handle}"
  original_args = stringify_hash(arguments)

  @own_streams << @channel.stream_from(handle) do |trigger_json|
    trigger_args = JSON.parse(trigger_json)
    if original_args == trigger_args
      reevaluate_query
    end
  end
end
subscribed?() click to toggle source

@return [Boolean] True if this subscriber registered any subscriptions

# File lib/graphql/streaming/action_cable_subscriber.rb, line 66
def subscribed?
  @subscribed
end

Private Instance Methods

reevaluate_query() click to toggle source

Re-evaluate the given block and send the result as a patch to the root of the query result

# File lib/graphql/streaming/action_cable_subscriber.rb, line 84
def reevaluate_query
  payload = {
    patch: {
      path: [],
      value:  @query_exec.call,
    },
    query_id: @query_id,
  }
  @channel.send_graphql_payload(payload)
end
stringify_hash(value) click to toggle source
# File lib/graphql/streaming/action_cable_subscriber.rb, line 95
def stringify_hash(value)
  case value
  when Hash
    value.inject({}) { |memo, (k, v)| memo[k.to_s] = stringify_hash(v); memo }
  when Array
    value.map { |v| stringify_hash(v) }
  else
    value
  end
end