live_record 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4cb6c6112e8dc95504592f04bbcc692184625232
4
- data.tar.gz: 8fc5c877c5bc1b14f84c7e4d835880cf057c62e8
3
+ metadata.gz: cd7810b1566eda128b77c0d451fedf5056c006b1
4
+ data.tar.gz: ac200f664f84fb987e563a6dbbed7b186fdcf5f1
5
5
  SHA512:
6
- metadata.gz: f4f90c7c3dcd0be1ab798a484d87af59d0fda7383c22f1568c5cf9ec0bae8c6cef13d915fa3884ed72d13e30c85128bcb978e4f1c57cd423551087a042a8f944
7
- data.tar.gz: 6fbb76854c2369cb1432b4f5dd1989e4880d16cc41707233be861999c5fbf1838cc4c23cf2d3791aca74cfe05424d773056aeafbb2e7fccb82f5e864055294a6
6
+ metadata.gz: 181ef9c74581d40fb9e4cd9f9992f25dc798370467fc93c67b6934e305c02a463390fc505210b04dc68c1a2efd2a92f32e5586d828978af897ae1eaf958ac0f5
7
+ data.tar.gz: d6c549d59bad9e84abc80d734abc39c093dd62fd673886abe8c98953ade0c5d66d327c8dc75aecd9b371573bb3f81ae0d1321232d86176f14912678d8e5fbc32
data/README.md CHANGED
@@ -32,10 +32,13 @@
32
32
 
33
33
  ### Subscribing to Record Creation
34
34
  ```js
35
- // subscribe, and auto-receive newly created Book records from the Rails server
35
+ // subscribe and auto-receive newly created Book records from the Rails server
36
36
  LiveRecord.Model.all.Book.subscribe()
37
37
 
38
- // ...or only those which are enabled
38
+ // ... or also load all Book records as well (not just the new ones)
39
+ // LiveRecord.Model.all.Book.subscribe({reload: true})
40
+
41
+ // ...or only those which are enabled (you can also combine this with `reload: true`)
39
42
  // LiveRecord.Model.all.Book.subscribe({where: {is_enabled_eq: true}})
40
43
 
41
44
  // now, we can just simply add a "create" callback, to apply our own logic whenever a new Book record is streamed from the backend
@@ -114,7 +117,7 @@
114
117
  1. Add the following to your `Gemfile`:
115
118
 
116
119
  ```ruby
117
- gem 'live_record', '~> 0.2.3'
120
+ gem 'live_record', '~> 0.2.4'
118
121
  ```
119
122
 
120
123
  2. Run:
@@ -345,16 +348,22 @@
345
348
  })
346
349
  ```
347
350
 
348
- 9. To automatically receive new Book records, you may subscribe:
351
+ 9. To automatically receive new Book records, and/or also load the old ones, you may subscribe:
349
352
 
350
353
  ```js
351
- // subscribe
354
+ // subscribe and auto-fetches newly created Book records from the backend
352
355
  var subscription = LiveRecord.Model.all.Book.subscribe();
353
356
 
357
+ // ...or also load all Book records (not just the new ones).
358
+ // useful for populating records at the start, and therefore you may skip using `LiveRecord.helpers.loadRecords()` already
359
+ // subscription = LiveRecord.Model.all.Book.subscribe({reload: true});
360
+
354
361
  // ...or subscribe only to certain conditions (i.e. when `is_enabled` attribute value is `true`)
355
362
  // For the list of supported operators (like `..._eq`), see JS API `MODEL.subscribe(CONFIG)` below
356
363
  // subscription = LiveRecord.Model.all.Book.subscribe({where: {is_enabled_eq: true}});
357
364
 
365
+ // you may choose to combine both `where` and `reload` arguments described above
366
+
358
367
  // now, we can just simply add a "create" callback, to apply our own logic whenever a new Book record is streamed from the backend
359
368
  LiveRecord.Model.all.Book.addCallback('after:create', function() {
360
369
  // let's say you have a code here that adds this new Book on the page
@@ -490,6 +499,7 @@
490
499
 
491
500
  ### `MODEL.subscribe(CONFIG)`
492
501
  * `CONFIG` (Object, Optional)
502
+ * `reload`: (Boolean, Default: false)
493
503
  * `where`: (Object)
494
504
  * `ATTRIBUTENAME_OPERATOR`: (Any Type)
495
505
  * `callbacks`: (Object)
@@ -498,6 +508,7 @@
498
508
  * `before:create`: (function Object)
499
509
  * `after:create`: (function Object)
500
510
  * subscribes to the `LiveRecord::PublicationsChannel`, which then automatically receives new records from the backend.
511
+ * when `reload: true`, all records (subject to `where` condition above) are immediately loaded, and not just the new ones.
501
512
  * you can also pass in `callbacks` (see above). These callbacks are only applicable to this subscription, and is independent of the Model and Instance callbacks.
502
513
  * `ATTRIBUTENAME_OPERATOR` means something like (for example): `is_enabled_eq`, where `is_enabled` is the `ATTRIBUTENAME` and `eq` is the `OPERATOR`.
503
514
  * you can have as many `ATTRIBUTENAME_OPERATOR` as you like, but keep in mind that the logic applied to them is "AND", and not "OR". For "OR" conditions, use `ransack`
@@ -536,8 +547,11 @@
536
547
  * if association is "belongs to", then returns the record (if exists in current store)
537
548
  * (i.e. `bookInstance.user()`, `bookInstance.reviews()`)
538
549
 
539
- ### `MODELINSTANCE.subscribe()`
550
+ ### `MODELINSTANCE.subscribe(config)`
551
+ * `CONFIG` (Object, Optional)
552
+ * `reload`: (Boolean, Default: false)
540
553
  * subscribes to the `LiveRecord::ChangesChannel`. This instance should already be subscribed by default after being stored, unless there is a `on:response_error` or manually `unsubscribed()` which then you should manually call this `subscribe()` function after correctly handling the response error, or whenever desired.
554
+ * when `reload: true`, the record is forced reloaded to make sure all attributes are in-sync
541
555
  * returns the `subscription` object (the ActionCable subscription object itself)
542
556
 
543
557
  ### `MODELINSTANCE.unsubscribe()`
@@ -550,7 +564,7 @@
550
564
  * the `subscription` object (the ActionCable subscription object itself)
551
565
 
552
566
  ### `MODELINSTANCE.create()`
553
- * stores the instance to the store, and then `subscribe()` to the `LiveRecord::ChangesChannel` for syncing
567
+ * stores the instance to the store, and then `subscribe({reload: true})` to the `LiveRecord::ChangesChannel` for syncing
554
568
  * returns the instance
555
569
 
556
570
  ### `MODELINSTANCE.update(ATTRIBUTES)`
@@ -596,6 +610,10 @@
596
610
  * MIT
597
611
 
598
612
  ## Changelog
613
+ * 0.2.4
614
+ * you can now pass in `{reload: true}` to `subscribe()` like the folowing:
615
+ * `MODEL.subscribe({reload: true})` to immediately load all records from backend, and not just the new ones
616
+ * `MODELINSTANCE.subscribe({reload: true})` to immediately reload the record and make sure it's in-sync
599
617
  * 0.2.3
600
618
  * IMPORTANT! renamed callback from `on:response_error` to `on:responseError` for conformity. So please update your code accordingly.
601
619
  * added [associations](#example-2---model--callbacks--associations):
@@ -5,12 +5,12 @@ LiveRecord.Model.create = (config) ->
5
5
 
6
6
  # NEW
7
7
  Model = (attributes) ->
8
- this.attributes = attributes
8
+ @attributes = attributes
9
9
 
10
- Object.keys(this.attributes).forEach (attribute_key) ->
10
+ Object.keys(@attributes).forEach (attribute_key) ->
11
11
  if Model.prototype[attribute_key] == undefined
12
12
  Model.prototype[attribute_key] = ->
13
- this.attributes[attribute_key]
13
+ @attributes[attribute_key]
14
14
  this
15
15
 
16
16
  Model.modelName = config.modelName
@@ -49,9 +49,9 @@ LiveRecord.Model.create = (config) ->
49
49
 
50
50
  Model.subscriptions = []
51
51
 
52
- Model.subscribe = (config) ->
53
- config ||= {}
52
+ Model.subscribe = (config = {}) ->
54
53
  config.callbacks ||= {}
54
+ config.reload ||= false
55
55
 
56
56
  subscription = App.cable.subscriptions.create(
57
57
  {
@@ -61,13 +61,18 @@ LiveRecord.Model.create = (config) ->
61
61
  },
62
62
 
63
63
  connected: ->
64
- if this.liveRecord._staleSince != undefined
64
+ # if forced reload of all records after subscribing, reload only once at the very start of connection, and no longer when reconnecting
65
+ if config.reload
66
+ config.reload = false
67
+ @syncRecords()
68
+
69
+ if @liveRecord._staleSince != undefined
65
70
  @syncRecords()
66
71
 
67
72
  config.callbacks['on:connect'].call(this) if config.callbacks['on:connect']
68
73
 
69
74
  disconnected: ->
70
- this.liveRecord._staleSince = (new Date()).toISOString() unless this.liveRecord._staleSince
75
+ @liveRecord._staleSince = (new Date()).toISOString() unless @liveRecord._staleSince
71
76
  config.callbacks['on:disconnect'].call(this) if config.callbacks['on:disconnect']
72
77
 
73
78
  received: (data) ->
@@ -85,9 +90,9 @@ LiveRecord.Model.create = (config) ->
85
90
  'sync_records',
86
91
  model_name: Model.modelName,
87
92
  where: config.where,
88
- stale_since: this.liveRecord._staleSince
93
+ stale_since: @liveRecord._staleSince
89
94
  )
90
- this.liveRecord._staleSince = undefined
95
+ @liveRecord._staleSince = undefined
91
96
  )
92
97
 
93
98
  subscription.liveRecord = {}
@@ -95,36 +100,43 @@ LiveRecord.Model.create = (config) ->
95
100
  subscription.liveRecord.where = config.where
96
101
  subscription.liveRecord.callbacks = config.callbacks
97
102
 
98
- this.subscriptions.push(subscription)
103
+ @subscriptions.push(subscription)
99
104
  subscription
100
105
 
101
106
  Model.unsubscribe = (subscription) ->
102
- index = this.subscriptions.indexOf(subscription)
103
- throw new Error('`subscription` argument does not exist in ' + this.modelName + ' subscriptions list') if index == -1
107
+ index = @subscriptions.indexOf(subscription)
108
+ throw new Error('`subscription` argument does not exist in ' + @modelName + ' subscriptions list') if index == -1
104
109
 
105
110
  App.cable.subscriptions.remove(subscription)
106
111
 
107
- this.subscriptions.splice(index, 1)
112
+ @subscriptions.splice(index, 1)
108
113
  subscription
109
114
 
110
- Model.prototype.subscribe = ->
111
- return this.subscription if this.subscription != undefined
115
+ Model.prototype.subscribe = (config = {}) ->
116
+ return @subscription if @subscription != undefined
117
+
118
+ config.reload ||= false
112
119
 
113
120
  # listen for record changes (update / destroy)
114
- subscription = App['live_record_' + this.modelName() + '_' + this.id()] = App.cable.subscriptions.create(
121
+ subscription = App['live_record_' + @modelName() + '_' + @id()] = App.cable.subscriptions.create(
115
122
  {
116
123
  channel: 'LiveRecord::ChangesChannel'
117
- model_name: this.modelName()
118
- record_id: this.id()
124
+ model_name: @modelName()
125
+ record_id: @id()
119
126
  },
120
127
 
121
128
  record: ->
122
129
  return @_record if @_record
123
- identifier = JSON.parse(this.identifier)
130
+ identifier = JSON.parse(@identifier)
124
131
  @_record = Model.all[identifier.record_id]
125
132
 
126
133
  # on: connect
127
134
  connected: ->
135
+ # if forced reload of this record after subscribing, reload only once at the very start of connection, and no longer when reconnecting
136
+ if config.reload
137
+ config.reload = false
138
+ @syncRecord(@record())
139
+
128
140
  if @record()._staleSince != undefined
129
141
  @syncRecord(@record())
130
142
 
@@ -173,7 +185,7 @@ LiveRecord.Model.create = (config) ->
173
185
  @record()._staleSince = undefined
174
186
  )
175
187
 
176
- this.subscription = subscription
188
+ @subscription = subscription
177
189
 
178
190
  Model.prototype.model = ->
179
191
  Model
@@ -182,45 +194,44 @@ LiveRecord.Model.create = (config) ->
182
194
  Model.modelName
183
195
 
184
196
  Model.prototype.unsubscribe = ->
185
- return if this.subscription == undefined
186
- App.cable.subscriptions.remove(this.subscription)
197
+ return if @subscription == undefined
198
+ App.cable.subscriptions.remove(@subscription)
187
199
  delete this['subscription']
188
200
 
189
201
  Model.prototype.isSubscribed = ->
190
- this.subscription != undefined
202
+ @subscription != undefined
191
203
 
192
204
  # CREATE
193
205
  Model.prototype.create = (options) ->
194
- throw new Error(Model.modelName+'('+this.id()+') is already in the store') if Model.all[this.attributes.id]
195
- this._callCallbacks('before:create', undefined)
206
+ throw new Error(Model.modelName+'('+@id()+') is already in the store') if Model.all[@attributes.id]
207
+ @_callCallbacks('before:create', undefined)
196
208
 
197
- Model.all[this.attributes.id] = this
198
- # because we do not know if this newly created object is statle, then we just set it to very long time ago, before we subscribe()
199
- this._staleSince = (new Date(1900, 0, 1)).toISOString()
200
- this.subscribe()
209
+ Model.all[@attributes.id] = this
210
+ # because we do not know if this newly created object is stale upon creation, then we force reload it
211
+ @subscribe({reload: true})
201
212
 
202
- this._callCallbacks('after:create', undefined)
213
+ @_callCallbacks('after:create', undefined)
203
214
  this
204
215
 
205
216
  # UPDATE
206
217
  Model.prototype.update = (attributes) ->
207
- this._callCallbacks('before:update', undefined)
218
+ @_callCallbacks('before:update', undefined)
208
219
 
209
220
  self = this
210
221
  Object.keys(attributes).forEach (attribute_key) ->
211
222
  self.attributes[attribute_key] = attributes[attribute_key]
212
223
 
213
- this._callCallbacks('after:update', undefined)
224
+ @_callCallbacks('after:update', undefined)
214
225
  true
215
226
 
216
227
  # DESTROY
217
228
  Model.prototype.destroy = ->
218
- this._callCallbacks('before:destroy', undefined)
229
+ @_callCallbacks('before:destroy', undefined)
219
230
 
220
- this.unsubscribe()
221
- delete Model.all[this.attributes.id]
231
+ @unsubscribe()
232
+ delete Model.all[@attributes.id]
222
233
 
223
- this._callCallbacks('after:destroy', undefined)
234
+ @_callCallbacks('after:destroy', undefined)
224
235
  this
225
236
 
226
237
  # CALLBACKS
@@ -251,16 +262,16 @@ LiveRecord.Model.create = (config) ->
251
262
 
252
263
  # adding new callbackd to the list
253
264
  Model.prototype.addCallback = Model.addCallback = (callbackKey, callbackFunction) ->
254
- index = this._callbacks[callbackKey].indexOf(callbackFunction)
265
+ index = @_callbacks[callbackKey].indexOf(callbackFunction)
255
266
  if index == -1
256
- this._callbacks[callbackKey].push(callbackFunction)
267
+ @_callbacks[callbackKey].push(callbackFunction)
257
268
  return callbackFunction
258
269
 
259
270
  # removing a callback from the list
260
271
  Model.prototype.removeCallback = Model.removeCallback = (callbackKey, callbackFunction) ->
261
- index = this._callbacks[callbackKey].indexOf(callbackFunction)
272
+ index = @_callbacks[callbackKey].indexOf(callbackFunction)
262
273
  if index != -1
263
- this._callbacks[callbackKey].splice(index, 1)
274
+ @_callbacks[callbackKey].splice(index, 1)
264
275
  return callbackFunction
265
276
 
266
277
  Model.prototype._callCallbacks = (callbackKey, args) ->
@@ -269,15 +280,15 @@ LiveRecord.Model.create = (config) ->
269
280
  callback.apply(this, args)
270
281
 
271
282
  # call instance callbacks
272
- for callback in this._callbacks[callbackKey]
283
+ for callback in @_callbacks[callbackKey]
273
284
  callback.apply(this, args)
274
285
 
275
286
  Model.prototype._setChangesFrom = (attributes) ->
276
- this.changes = {}
287
+ @changes = {}
277
288
 
278
289
  for attributeName, attributeValue of attributes
279
- unless this.attributes[attributeName] && this.attributes[attributeName] == attributeValue
280
- this.changes[attributeName] = [this.attributes[attributeName], attributeValue]
290
+ unless @attributes[attributeName] && @attributes[attributeName] == attributeValue
291
+ @changes[attributeName] = [@attributes[attributeName], attributeValue]
281
292
 
282
293
  Model.prototype._unsetChanges = () ->
283
294
  delete this['changes']
@@ -13,7 +13,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
13
13
  record.reload
14
14
  rescue ActiveRecord::RecordNotFound
15
15
  end
16
-
16
+
17
17
  authorised_attributes = authorised_attributes(record, current_user)
18
18
 
19
19
  # if not just :id
@@ -33,19 +33,27 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
33
33
  end
34
34
 
35
35
  def sync_record(data)
36
- find_record_from_params(data.symbolize_keys) do |record|
36
+ params = data.symbolize_keys
37
+
38
+ find_record_from_params(params) do |record|
37
39
  authorised_attributes = authorised_attributes(record, current_user)
38
40
 
39
41
  # if not just :id
40
42
  if authorised_attributes.size > 1
41
- live_record_update = LiveRecordUpdate.where(
42
- recordable_type: record.class.name,
43
- recordable_id: record.id
44
- ).where(
45
- 'created_at >= ?', DateTime.parse(data['stale_since']) - LiveRecord.configuration.sync_record_buffer_time
46
- ).order(id: :asc)
47
-
48
- if live_record_update.exists?
43
+ live_record_updates = nil
44
+
45
+ if params[:stale_since].present?
46
+ live_record_updates = LiveRecordUpdate.where(
47
+ recordable_type: record.class.name,
48
+ recordable_id: record.id
49
+ ).where(
50
+ 'created_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
51
+ )
52
+ end
53
+
54
+ # if stale_since is unknown, or there is a live_record_update that has happened while disconnected,
55
+ # then we update the record in the client-side
56
+ if params[:stale_since].blank? || live_record_updates.exists?
49
57
  message = { 'action' => 'update', 'attributes' => record.attributes }
50
58
  response = filtered_message(message, authorised_attributes)
51
59
  transmit response if response.present?
@@ -56,4 +64,4 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
56
64
  end
57
65
  end
58
66
  end
59
- end
67
+ end
@@ -18,7 +18,7 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
18
18
  newly_created_record = model_class.find(message['attributes']['id'])
19
19
 
20
20
  @authorised_attributes ||= authorised_attributes(newly_created_record, current_user)
21
-
21
+
22
22
  active_record_relation = SearchAdapters.mapped_active_record_relation(
23
23
  model_class: model_class,
24
24
  conditions_hash: params[:where].to_h,
@@ -42,10 +42,13 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
42
42
  model_class = params[:model_name].safe_constantize
43
43
 
44
44
  if model_class && model_class < ApplicationRecord
45
+ records = model_class.all
45
46
 
46
- records = model_class.where(
47
- 'created_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
48
- )
47
+ if params[:stale_since].present?
48
+ records = records.where(
49
+ 'created_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
50
+ )
51
+ end
49
52
 
50
53
  # we `transmmit` a message back to client for each matching record
51
54
  records.find_each do |record|
@@ -131,4 +134,4 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
131
134
  end
132
135
  end
133
136
  end
134
- end
137
+ end
@@ -1,3 +1,3 @@
1
1
  module LiveRecord
2
- VERSION = '0.2.3'.freeze
2
+ VERSION = '0.2.4'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: live_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jules Roman B. Polidario