Class: Parse::LiveQuery::Subscription
- Inherits:
-
Object
- Object
- Parse::LiveQuery::Subscription
- Defined in:
- lib/parse/live_query/subscription.rb
Overview
Represents an active subscription to a LiveQuery. Manages event callbacks and subscription lifecycle.
Instance Attribute Summary collapse
-
#class_name ⇒ String
readonly
Parse class name being subscribed to.
-
#client ⇒ Parse::LiveQuery::Client
readonly
The LiveQuery client.
-
#fields ⇒ Array<String>
(also: #keys)
readonly
Field projection for returned events (nil = all fields).
-
#query ⇒ Hash
readonly
The query constraints (where clause).
-
#request_id ⇒ Integer
readonly
Unique request ID for this subscription.
-
#session_token ⇒ String?
readonly
Session token for ACL-aware subscriptions.
-
#watch ⇒ Array<String>?
readonly
Field names that trigger update events when changed (PS 7.0+
watchoption).
Instance Method Summary collapse
-
#confirm! ⇒ Object
private
Mark subscription as confirmed by server.
-
#error? ⇒ Boolean
True if in error state.
-
#fail!(error) ⇒ Object
private
Mark subscription as failed with error.
-
#handle_event(event) ⇒ Object
private
Handle an incoming event from the server.
-
#initialize(client:, class_name:, query: {}, fields: nil, keys: nil, session_token: nil, use_master_key: false, watch: nil) ⇒ Subscription
constructor
Create a new subscription.
-
#inspect ⇒ String
Redacting inspect — the default
inspectwould expose@session_token(and, via@client, the client's master/REST keys) in any log line, backtrace, error page, or error reporter that renders the subscription. -
#on(event_type) {|object, original| ... } ⇒ self
Register a callback for a specific event type.
-
#on_create {|Parse::Object| ... } ⇒ self
Register callback for create events.
-
#on_delete {|Parse::Object| ... } ⇒ self
Register callback for delete events.
-
#on_enter {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for enter events (object now matches query).
-
#on_error {|Exception| ... } ⇒ self
Register callback for errors.
-
#on_leave {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for leave events (object no longer matches query).
-
#on_subscribe { ... } ⇒ self
Register callback for successful subscription.
-
#on_unsubscribe { ... } ⇒ self
Register callback for unsubscription.
-
#on_update {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for update events.
-
#pending? ⇒ Boolean
True if pending subscription confirmation.
-
#state ⇒ Symbol
Current subscription state.
-
#subscribed? ⇒ Boolean
True if currently subscribed.
-
#to_h ⇒ Hash
Subscription info as hash.
-
#to_subscribe_message ⇒ Hash
Build the subscription message to send to the server.
-
#to_unsubscribe_message ⇒ Hash
Build the unsubscribe message.
-
#unsubscribe ⇒ Boolean
Unsubscribe from this subscription.
-
#unsubscribed? ⇒ Boolean
True if unsubscribed.
-
#use_master_key? ⇒ Boolean
Whether this subscription opted into per-subscription master-key auth via
use_master_key: true.
Constructor Details
#initialize(client:, class_name:, query: {}, fields: nil, keys: nil, session_token: nil, use_master_key: false, watch: nil) ⇒ Subscription
Create a new subscription
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/parse/live_query/subscription.rb', line 98 def initialize(client:, class_name:, query: {}, fields: nil, keys: nil, session_token: nil, use_master_key: false, watch: nil) @monitor = Monitor.new @client = client @class_name = class_name @query = query # `keys` is the post-7.0 name; accept either and prefer the explicit # `keys:` when both are supplied. @fields = keys.nil? ? fields : keys @watch = watch @session_token = session_token @use_master_key = use_master_key == true @request_id = generate_request_id @state = :pending @callbacks = Hash.new { |h, k| h[k] = [] } Logging.debug("Subscription created", request_id: @request_id, class_name: @class_name, query_keys: @query.keys) end |
Instance Attribute Details
#class_name ⇒ String (readonly)
Returns Parse class name being subscribed to.
57 58 59 |
# File 'lib/parse/live_query/subscription.rb', line 57 def class_name @class_name end |
#client ⇒ Parse::LiveQuery::Client (readonly)
Returns the LiveQuery client.
63 64 65 |
# File 'lib/parse/live_query/subscription.rb', line 63 def client @client end |
#fields ⇒ Array<String> (readonly) Also known as: keys
Returns field projection for returned events
(nil = all fields). Parse Server 7.0 renamed this subscription
option from fields to keys; #keys is the canonical alias.
68 69 70 |
# File 'lib/parse/live_query/subscription.rb', line 68 def fields @fields end |
#query ⇒ Hash (readonly)
Returns the query constraints (where clause).
60 61 62 |
# File 'lib/parse/live_query/subscription.rb', line 60 def query @query end |
#request_id ⇒ Integer (readonly)
Returns unique request ID for this subscription.
54 55 56 |
# File 'lib/parse/live_query/subscription.rb', line 54 def request_id @request_id end |
#session_token ⇒ String? (readonly)
Returns session token for ACL-aware subscriptions.
74 75 76 |
# File 'lib/parse/live_query/subscription.rb', line 74 def session_token @session_token end |
Instance Method Details
#confirm! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Mark subscription as confirmed by server
312 313 314 315 316 317 318 |
# File 'lib/parse/live_query/subscription.rb', line 312 def confirm! @monitor.synchronize { @state = :subscribed } Logging.info("Subscription confirmed", request_id: @request_id, class_name: @class_name) emit(:subscribe) end |
#error? ⇒ Boolean
Returns true if in error state.
239 240 241 |
# File 'lib/parse/live_query/subscription.rb', line 239 def error? state == :error end |
#fail!(error) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Mark subscription as failed with error
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/parse/live_query/subscription.rb', line 323 def fail!(error) @monitor.synchronize { @state = :error } # Promote String errors (which come back from the LiveQuery # server with messages like "Permission denied (code: 101)") # to typed SubscriptionError instances carrying the request_id # and class_name as structured context. The resulting # `e.message` reads `request_id=<n> class=<X> <server message>`, # so a single-line log captures the operational context the # raw server string lacks. if error.is_a?(String) error = SubscriptionError.new(error, request_id: @request_id, class_name: @class_name) end Logging.error("Subscription failed", request_id: @request_id, class_name: @class_name, error: error) emit(:error, error) end |
#handle_event(event) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Handle an incoming event from the server
303 304 305 306 307 308 |
# File 'lib/parse/live_query/subscription.rb', line 303 def handle_event(event) Logging.debug("Handling event", request_id: @request_id, event_type: event.type) emit(event.type, event.object, event.original) end |
#inspect ⇒ String
Redacting inspect — the default inspect would expose
@session_token (and, via @client, the client's master/REST
keys) in any log line, backtrace, error page, or error reporter
that renders the subscription. Reads @state directly rather
than through the monitor so a diagnostic inspect never blocks on
the lock.
133 134 135 136 137 138 |
# File 'lib/parse/live_query/subscription.rb', line 133 def inspect token = @session_token.nil? || @session_token.empty? ? "nil" : "[REDACTED]" "#<#{self.class.name} request_id=#{@request_id.inspect} " \ "class_name=#{@class_name.inspect} state=#{@state.inspect} " \ "use_master_key=#{@use_master_key} session_token=#{token}>" end |
#on(event_type) {|object, original| ... } ⇒ self
Register a callback for a specific event type
144 145 146 147 148 149 150 151 |
# File 'lib/parse/live_query/subscription.rb', line 144 def on(event_type, &block) return self unless block_given? @monitor.synchronize do @callbacks[event_type.to_sym] << block end self end |
#on_create {|Parse::Object| ... } ⇒ self
Register callback for create events
156 157 158 |
# File 'lib/parse/live_query/subscription.rb', line 156 def on_create(&block) on(:create, &block) end |
#on_delete {|Parse::Object| ... } ⇒ self
Register callback for delete events
170 171 172 |
# File 'lib/parse/live_query/subscription.rb', line 170 def on_delete(&block) on(:delete, &block) end |
#on_enter {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for enter events (object now matches query)
177 178 179 |
# File 'lib/parse/live_query/subscription.rb', line 177 def on_enter(&block) on(:enter, &block) end |
#on_error {|Exception| ... } ⇒ self
Register callback for errors
191 192 193 |
# File 'lib/parse/live_query/subscription.rb', line 191 def on_error(&block) on(:error, &block) end |
#on_leave {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for leave events (object no longer matches query)
184 185 186 |
# File 'lib/parse/live_query/subscription.rb', line 184 def on_leave(&block) on(:leave, &block) end |
#on_subscribe { ... } ⇒ self
Register callback for successful subscription
198 199 200 |
# File 'lib/parse/live_query/subscription.rb', line 198 def on_subscribe(&block) on(:subscribe, &block) end |
#on_unsubscribe { ... } ⇒ self
Register callback for unsubscription
205 206 207 |
# File 'lib/parse/live_query/subscription.rb', line 205 def on_unsubscribe(&block) on(:unsubscribe, &block) end |
#on_update {|Parse::Object, Parse::Object| ... } ⇒ self
Register callback for update events
163 164 165 |
# File 'lib/parse/live_query/subscription.rb', line 163 def on_update(&block) on(:update, &block) end |
#pending? ⇒ Boolean
Returns true if pending subscription confirmation.
229 230 231 |
# File 'lib/parse/live_query/subscription.rb', line 229 def pending? state == :pending end |
#state ⇒ Symbol
Current subscription state
122 123 124 |
# File 'lib/parse/live_query/subscription.rb', line 122 def state @monitor.synchronize { @state } end |
#subscribed? ⇒ Boolean
Returns true if currently subscribed.
224 225 226 |
# File 'lib/parse/live_query/subscription.rb', line 224 def subscribed? state == :subscribed end |
#to_h ⇒ Hash
Returns subscription info as hash.
343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/parse/live_query/subscription.rb', line 343 def to_h @monitor.synchronize do { request_id: request_id, class_name: class_name, query: query, state: @state, fields: fields, } end end |
#to_subscribe_message ⇒ Hash
Build the subscription message to send to the server
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/parse/live_query/subscription.rb', line 245 def msg = { op: "subscribe", requestId: request_id, query: { className: class_name, where: query, }, } if fields&.any? # Parse Server 7.0 (DEPPS9 / #8852) renamed the subscription field- # projection option from `fields` to `keys`. PS 7+ reads `keys` and # ignores `fields`; PS < 7 reads `fields`. Emit BOTH so projection is # honored on every supported server — sending an extra key the server # ignores is harmless, while sending only `fields` silently disables # projection (events return all columns) on PS 7+. msg[:query][:keys] = fields msg[:query][:fields] = fields end # PS 7.0 (#8028) `watch`: fire update events only when the named fields # change. Distinct from field projection (`keys`/`fields`): `watch` # controls which field mutations generate an update event; `keys` controls # which fields are returned in the event payload. msg[:query][:watch] = watch if watch&.any? msg[:sessionToken] = session_token if session_token # The subscribe frame deliberately NEVER carries `masterKey`. # Parse Server's `_handleSubscribe` does not read it — master-key # (ACL-bypass) authorization is resolved once, per connection, in # `_handleConnect` (`client.hasMasterKey`). Emitting it here put a # privileged credential on the wire for ZERO server-side effect. # `use_master_key: true` at the subscription level is an intent # assertion validated by the client (which warns when it cannot # be honored on a non-admin connection); the actual elevation is # the admin connection's connect frame. See # {Parse::LiveQuery::Client#use_master_key}. msg end |
#to_unsubscribe_message ⇒ Hash
Build the unsubscribe message
293 294 295 296 297 298 |
# File 'lib/parse/live_query/subscription.rb', line 293 def { op: "unsubscribe", requestId: request_id, } end |
#unsubscribe ⇒ Boolean
Unsubscribe from this subscription
211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/parse/live_query/subscription.rb', line 211 def unsubscribe @monitor.synchronize do return false if @state == :unsubscribed @state = :unsubscribed end Logging.debug("Unsubscribing", request_id: @request_id) client.unsubscribe(self) emit(:unsubscribe) true end |
#unsubscribed? ⇒ Boolean
Returns true if unsubscribed.
234 235 236 |
# File 'lib/parse/live_query/subscription.rb', line 234 def unsubscribed? state == :unsubscribed end |
#use_master_key? ⇒ Boolean
Returns whether this subscription opted into
per-subscription master-key auth via use_master_key: true.
287 288 289 |
# File 'lib/parse/live_query/subscription.rb', line 287 def use_master_key? @use_master_key == true end |