live_record 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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