Class: LiveQuery

Inherits:
Object show all
Defined in:
app/volt/tasks/live_query/live_query.rb

Overview

Tracks a channel and a query on a collection. Alerts the listener when the data in the query changes.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pool, data_store, collection, query) ⇒ LiveQuery

Returns a new instance of LiveQuery.



8
9
10
11
12
13
14
15
16
17
18
19
# File 'app/volt/tasks/live_query/live_query.rb', line 8

def initialize(pool, data_store, collection, query)
  @pool = pool
  @collection = collection
  @query = query

  @channels = []
  @data_store = data_store

  @query_tracker = QueryTracker.new(self, @data_store)

  run
end

Instance Attribute Details

#collectionObject (readonly)

Returns the value of attribute collection.



6
7
8
# File 'app/volt/tasks/live_query/live_query.rb', line 6

def collection
  @collection
end

#current_idsObject (readonly)

Returns the value of attribute current_ids.



6
7
8
# File 'app/volt/tasks/live_query/live_query.rb', line 6

def current_ids
  @current_ids
end

#queryObject (readonly)

Returns the value of attribute query.



6
7
8
# File 'app/volt/tasks/live_query/live_query.rb', line 6

def query
  @query
end

Instance Method Details

#add_channel(channel) ⇒ Object



89
90
91
# File 'app/volt/tasks/live_query/live_query.rb', line 89

def add_channel(channel)
  @channels << channel
end

#initial_dataObject

return the query results the first time a channel connects



75
76
77
78
79
80
81
# File 'app/volt/tasks/live_query/live_query.rb', line 75

def initial_data
  @query_tracker.results.map.with_index do |data, index|
    data = data.dup

    [index, data]
  end
end

#inspectObject



137
138
139
# File 'app/volt/tasks/live_query/live_query.rb', line 137

def inspect
  "<#{self.class} #{@collection}: #{@query.inspect}>"
end

#model_classObject

Lookup the model class



84
85
86
87
# File 'app/volt/tasks/live_query/live_query.rb', line 84

def model_class
  # recreate the "path" from the collection
  Volt::Model.class_at_path([collection, :[]])
end

#model_for_filter(data) ⇒ Object

Takes in data to be sent to the client and sets up a model to test field permissions against



122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/volt/tasks/live_query/live_query.rb', line 122

def model_for_filter(data)
  klass = Volt::Model.class_at_path([@collection])
  model = nil

  # Skip read validations when loading the model, no need to check read when checking
  # permissions.
  # TODO: We should probably document the possibility of data leak here, though really you
  # shouldn't be storing anything inside of the permissions block.
  Volt::Model.no_validate do
    model = klass.new(data, {}, :loaded)
  end

  model
end

#notify!(skip_channel = nil, only_channel = nil) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'app/volt/tasks/live_query/live_query.rb', line 106

def notify!(skip_channel = nil, only_channel = nil)
  if only_channel
    channels = [only_channel]
  else
    channels = @channels
  end

  channels = channels.reject { |c| c == skip_channel }

  channels.each do |channel|
    yield(channel)
  end
end

#notify_added(index, data, skip_channel) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'app/volt/tasks/live_query/live_query.rb', line 32

def notify_added(index, data, skip_channel)
  # Make model for testing permissions against
  model = nil

  notify! do |channel|
    # Only load the model for filtering if we are sending to a channel
    # (skip if we are the only one listening)
    model ||= model_for_filter(data)

    filtered_data = nil
    Volt.as_user(channel.user_id) do
      filtered_data = model.filtered_attributes.sync
    end

    channel.send_message('added', nil, @collection, @query, index, filtered_data)
  end
end

#notify_changed(id, data, skip_channel) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/volt/tasks/live_query/live_query.rb', line 57

def notify_changed(id, data, skip_channel)
  model = nil

  notify!(skip_channel) do |channel|
    # Only load the model for filtering if we are sending to a channel
    # (skip if we are the only one listening)
    model ||= model_for_filter(data)

    filtered_data = nil
    Volt.as_user(channel.user_id) do
      filtered_data = model.filtered_attributes.sync
    end
    # puts "Changed: #{id}, #{data} to #{channel.inspect}"
    channel.send_message('changed', nil, @collection, @query, id, filtered_data)
  end
end

#notify_moved(id, new_position, skip_channel) ⇒ Object



50
51
52
53
54
55
# File 'app/volt/tasks/live_query/live_query.rb', line 50

def notify_moved(id, new_position, skip_channel)
  # puts "Moved: #{id}, #{new_position}"
  notify! do |channel|
    channel.send_message('moved', nil, @collection, @query, id, new_position)
  end
end

#notify_removed(ids, skip_channel) ⇒ Object



25
26
27
28
29
30
# File 'app/volt/tasks/live_query/live_query.rb', line 25

def notify_removed(ids, skip_channel)
  # puts "Removed: #{ids.inspect}"
  notify! do |channel|
    channel.send_message('removed', nil, @collection, @query, ids)
  end
end

#remove_channel(channel) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/volt/tasks/live_query/live_query.rb', line 93

def remove_channel(channel)
  deleted = @channels.delete(channel)

  # remove this query, no one is listening anymore
  if @channels.empty?
    begin
      @pool.remove(@collection, @query)
    rescue Volt::GenericPoolDeleteException => e
      # ignore
    end
  end
end

#run(skip_channel = nil) ⇒ Object



21
22
23
# File 'app/volt/tasks/live_query/live_query.rb', line 21

def run(skip_channel = nil)
  @query_tracker.run(skip_channel)
end