Class: LiveRecord::PublicationsChannel

Inherits:
BaseChannel
  • Object
show all
Defined in:
app/channels/live_record/publications_channel.rb

Overview

This channel streams new records to connected clients whenever the “where” condition supplied by the client matches This implementation can be quite inefficient because there’s only one pub-sub queue used for each model, but because of constraints ( see github.com/jrpolidario/live_record/issues/2 ) and because Users are authorised-validated anyway in each stream, then there’s already an overhead delay. I am prioritising development convenience (as Rails does), in order to achieve a simpler API; in this example, it would be something like in JS: ‘LiveRecord.Model.all.Book.subscribe(is_enabled: true)`

Instance Method Summary collapse

Instance Method Details

#subscribedObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'app/channels/live_record/publications_channel.rb', line 9

def subscribed
  model_class = params[:model_name].safe_constantize

  if !(model_class && model_class < ApplicationRecord)
    respond_with_error(:bad_request, 'Not a correct model name')
    reject_subscription
  end

  if !(model_class && LiveRecord::BaseChannel::Helpers.queryable_attributes(model_class, current_user).present?)
    respond_with_error(:forbidden, 'You do not have privileges to query')
    reject_subscription
  end

  stream_from "live_record:publications:#{params[:model_name].underscore}", coder: ActiveSupport::JSON do |message|
    newly_created_record = model_class.find(message['attributes']['id'])

    active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
      model_class: model_class,
      conditions_hash: params[:where].to_h,
      current_user: current_user
    )

    if active_record_relation.exists?(id: newly_created_record.id)
      whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(newly_created_record, current_user)

      if whitelisted_attributes.size > 0
        message = { 'action' => 'create', 'attributes' => message['attributes'] }
        response = filtered_message(message, whitelisted_attributes)
        transmit response if response.present?
      end
    end
  end
end

#sync_records(data) ⇒ Object

TODO: split up sync_records action because it currently both handles “syncing” and “reloading”



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
# File 'app/channels/live_record/publications_channel.rb', line 44

def sync_records(data)
  params = data.symbolize_keys
  model_class = params[:model_name].safe_constantize

  if model_class && model_class < ApplicationRecord
    is_being_reloaded = params[:stale_since].blank?
    is_being_synced = params[:stale_since].present?

    active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
      model_class: model_class,
      conditions_hash: params[:where].to_h,
      current_user: current_user,
    )

    if is_being_synced
      active_record_relation = active_record_relation.where(
        'created_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
      )
    end

    # we `transmmit` a message back to client for each matching record
    active_record_relation.find_each do |record|
      # but first, check for the authorised attributes, if exists
      whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)

      if whitelisted_attributes.size > 0
        message = { 'action' => 'create', 'attributes' => record.attributes }
        response = filtered_message(message, whitelisted_attributes)
        transmit response if response.present?
      end
    end

    # if being reloaded, we finally still transmit a "done" action indicating that reloading has just finished
    if is_being_reloaded
      response = { 'action' => 'afterReload', 'recordIds' => active_record_relation.pluck(:id) }
      transmit response
    end
  else
    respond_with_error(:bad_request, 'Not a correct model name')
    reject_subscription
  end
end