Class: ReactiveRecord::Broadcast

Inherits:
Object
  • Object
show all
Defined in:
lib/reactive_record/broadcast.rb

Defined Under Namespace

Classes: SendPacket

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id) ⇒ Broadcast

Returns a new instance of Broadcast.



127
128
129
130
131
132
# File 'lib/reactive_record/broadcast.rb', line 127

def initialize(id)
  @id = id
  @received = Set.new
  @record = {}
  @previous_changes = {}
end

Instance Attribute Details

#recordObject (readonly)

private



117
118
119
# File 'lib/reactive_record/broadcast.rb', line 117

def record
  @record
end

Class Method Details

.after_commit(operation, model) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/reactive_record/broadcast.rb', line 4

def self.after_commit(operation, model)
  Hyperloop::InternalPolicy.regulate_broadcast(model) do |data|
    if !Hyperloop.on_server? && Hyperloop::Connection.root_path
      send_to_server(operation, data)
    else
      SendPacket.run(data, operation: operation)
    end
  end
rescue ActiveRecord::StatementInvalid => e
  raise e unless e.message == "Could not find table 'hyperloop_connections'"
end

.in_transitObject



123
124
125
# File 'lib/reactive_record/broadcast.rb', line 123

def self.in_transit
  @in_transit ||= Hash.new { |h, k| h[k] = new(k) }
end

.open_channelsObject



119
120
121
# File 'lib/reactive_record/broadcast.rb', line 119

def self.open_channels
  @open_channels ||= Set.new
end

.send_to_server(operation, data) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/reactive_record/broadcast.rb', line 16

def self.send_to_server(operation, data)
  salt = SecureRandom.hex
  authorization = Hyperloop.authorization(salt, data[:channel], data[:broadcast_id])
  raise 'no server running' unless Hyperloop::Connection.root_path
  SendPacket.remote(
    Hyperloop::Connection.root_path,
    data,
    operation: operation,
    salt: salt,
    authorization: authorization
  )
end

.to_self(record, data = {}) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/reactive_record/broadcast.rb', line 64

def self.to_self(record, data = {})
  # simulate incoming packet after a local save
  operation = if record.new?
                :create
              elsif record.destroyed?
                :destroy
              else
                :change
              end
  dummy_broadcast = new.local(operation, record, data)
  record.backing_record.sync! data unless operation == :destroy
  ReactiveRecord::Collection.sync_scopes dummy_broadcast
end

Instance Method Details

#complete!Object



164
165
166
# File 'lib/reactive_record/broadcast.rb', line 164

def complete!
  self.class.in_transit.delete @id
end

#destroyed?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/reactive_record/broadcast.rb', line 103

def destroyed?
  @destroyed
end

#klassObject



107
108
109
# File 'lib/reactive_record/broadcast.rb', line 107

def klass
  Object.const_get(@klass)
end

#local(operation, record, data) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/reactive_record/broadcast.rb', line 134

def local(operation, record, data)
  @destroyed = operation == :destroy
  @is_new = operation == :create
  @klass = record.class.name
  @record = data
  record.backing_record.destroyed = false
  @record.merge!(id: record.id) if record.id
  record.backing_record.destroyed = @destroyed
  @backing_record = record.backing_record
  attributes = record.backing_record.attributes
  data.each do |k, v|
    next if klass.reflect_on_association(k) || attributes[k] == v
    @previous_changes[k] = [attributes[k], v]
  end
  self
end

#merge_current_values(br) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/reactive_record/broadcast.rb', line 168

def merge_current_values(br)
  current_values = Hash[*@previous_changes.collect do |attr, values|
    value = attr == :id ? record[:id] : values.first
    if br.attributes.key?(attr) &&
       br.attributes[attr] != br.convert(attr, value) &&
       br.attributes[attr] != br.convert(attr, values.last)
      puts "warning #{attr} has changed locally - will force a reload.\n"\
           "local value: #{br.attributes[attr]} remote value: #{br.convert(attr, value)}->#{br.convert(attr, values.last)}"
      return nil
    end
    [attr, value]
  end.compact.flatten].merge(br.attributes)
  klass._react_param_conversion(current_values)
end

#new?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/reactive_record/broadcast.rb', line 99

def new?
  @is_new
end

#receive(params) {|complete! if | ... } ⇒ Object

Yields:

  • (complete! if )


151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/reactive_record/broadcast.rb', line 151

def receive(params)
  @destroyed = params.operation == :destroy
  @is_new = params.operation == :create
  @channels ||= Hyperloop::IncomingBroadcast.open_channels.intersection params.channels
  #raise 'synchromesh security violation' unless @channels.include? params.channels
  @received << params.channel
  @klass ||= params.klass
  @record.merge! params.record
  @previous_changes.merge! params.previous_changes
  @backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
  yield complete! if @channels == @received
end

#record_with_current_valuesObject



78
79
80
81
82
83
84
85
86
87
# File 'lib/reactive_record/broadcast.rb', line 78

def record_with_current_values
  ReactiveRecord::Base.load_data do
    backing_record = @backing_record || klass.find(record[:id]).backing_record
    if destroyed?
      backing_record.ar_instance
    else
      merge_current_values(backing_record)
    end
  end
end

#record_with_new_valuesObject



89
90
91
92
93
94
95
96
97
# File 'lib/reactive_record/broadcast.rb', line 89

def record_with_new_values
  klass._react_param_conversion(record).tap do |ar_instance|
    if destroyed?
      ar_instance.backing_record.destroy_associations
    elsif new?
      ar_instance.backing_record.initialize_collections
    end
  end
end

#to_sObject



111
112
113
# File 'lib/reactive_record/broadcast.rb', line 111

def to_s
  "klass: #{klass} record: #{record} new?: #{new?} destroyed?: #{destroyed?}"
end