live_record 0.2.8 → 0.3.0

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: d08ed348f467c4dad7084528d36552775a03f365
4
- data.tar.gz: 3698f07abf6f4e6edc1430870fb8eb7ad6862bad
3
+ metadata.gz: 913e2af230c7be4690d16fc2015c5cc493d720e8
4
+ data.tar.gz: db0f1ae77acc7db8e2c4449659e3fe49a9e7e7c3
5
5
  SHA512:
6
- metadata.gz: 482a0751d39af81591ac4137fc2a8f12206fa4e687aeb5b1508c1ce60fb85fe1d92465ff853affad114017f1f97e19a859a2d39553f3d92d387d05ccc5840630
7
- data.tar.gz: '081b8691a3f37984961031d230afa749603c9c61e402347d63a914ed854170cf6c2df561c68d8be8514e0bfc04cfc139cfbe1878573ff72fc5057a94bb0401b8'
6
+ metadata.gz: 4a22a9f57b70907b6920481a4f54285e13e9f2e5d29e48b957ab2e0a229d7f571ccfde784ac92c7c4a1b46f57142d85626fde0349affef7bd6468b5147e065ab
7
+ data.tar.gz: 90a3db0faee89cc23cd179d7b6846c621c268e4fd6b933dc537b08a8cff1a18083f5fcd94e2e6da020eb27173e1790a674ad17674d168e05c15eee1880ce6804
data/README.md CHANGED
@@ -30,7 +30,7 @@
30
30
  * `is_enabled:boolean`
31
31
  * on the JS client-side:
32
32
 
33
- ### Subscribing to Record Creation
33
+ ### Subscribing to Records Creation
34
34
  ```js
35
35
  // subscribe and auto-receive newly created Book records from the Rails server
36
36
  LiveRecord.Model.all.Book.subscribe()
@@ -49,6 +49,28 @@
49
49
  })
50
50
  ```
51
51
 
52
+ ### Subscribing to Records Creation/Updates
53
+
54
+ ```js
55
+ // auto-load newly created Book records OR newly updated Book records
56
+ LiveRecord.Model.all.Book.autoload()
57
+
58
+ // ... or also load all Book records as well, and then subscribes for new ones that will be created / updated
59
+ // LiveRecord.Model.all.Book.autoload({reload: true})
60
+
61
+ // ...or only those which are enabled (you can also combine this with `reload: true`)
62
+ // LiveRecord.Model.all.Book.autoload({where: {is_enabled_eq: true}})
63
+
64
+ // now, we can just simply add a "create_or_update" callback, to apply our own logic whenever a new Book record is streamed from the backend
65
+ LiveRecord.Model.all.Book.addCallback('after:createOrUpdate', function() {
66
+ // let's say you have a code here that adds this new Book on the page
67
+ // `this` refers to the Book record that has been created / updated
68
+ console.log(this);
69
+ })
70
+ ```
71
+
72
+ > Now you may be wondering what the differences between `autoload()` and `subscribe()` are. Simply put, `subscribe()` only receives NEWLY CREATED records, while `autoload()` receives both CREATED and UPDATED records. Let's say your JS-client has `subscribe({where: {is_enabled_eq: true}})`. You only then receive NEW records that are "enabled", however you won't receive OLD records that were "disabled" upon creation, but then got updated to be "enabled". Now, this is where you use `autoload({where: {is_enabled_eq: true}})` instead.
73
+
52
74
  ### Subscribing to Record Updates/Destroy
53
75
 
54
76
  ```js
@@ -127,7 +149,7 @@
127
149
  1. Add the following to your `Gemfile`:
128
150
 
129
151
  ```ruby
130
- gem 'live_record', '~> 0.2.8'
152
+ gem 'live_record', '~> 0.2.9'
131
153
  ```
132
154
 
133
155
  2. Run:
@@ -371,9 +393,9 @@
371
393
  })
372
394
  ```
373
395
 
374
- ### Example 3 - Using `Subscribe({reload: true})`
396
+ ### Example 3 - Using `subscribe({reload: true})` or `autoload({reload: true})`
375
397
 
376
- > You may also load records from the backend by using `subscribe({reload: true})`. `subscribe()` just auto-loads NEW records that will be created, while `subscribe({reload: true})` first loads ALL records (subject to its {where: ...} condition), and then also auto-loads new records that will be created
398
+ > You may also load records from the backend by using `subscribe({reload: true})`. `subscribe()` just auto-loads NEW records that will be created, while `subscribe({reload: true})` first loads ALL records (subject to its {where: ...} condition), and then also auto-fetch new records that will be created. `autoload({reload: true})` can also be used instead of `subscribe({reload: true})`.
377
399
 
378
400
  ```js
379
401
  var subscription = LiveRecord.Model.all.Book.subscribe({
@@ -387,12 +409,12 @@
387
409
  });
388
410
  ```
389
411
 
390
- > Take note however that `subscribe()` above not only LOADS but also SUBSCRIBES! See 9. below for details
412
+ > Take note however that `subscribe()` and `autoload()` not only LOADS but also SUBSCRIBES! See 9. below for details
391
413
 
392
- 9. To automatically receive new Book records, and/or also load the old ones, you may subscribe:
414
+ 9. To automatically receive new Book records, you may subscribe:
393
415
 
394
416
  ```js
395
- // subscribe and auto-fetches newly created Book records from the backend
417
+ // subscribe and auto-fetch newly created Book records from the backend
396
418
  var subscription = LiveRecord.Model.all.Book.subscribe();
397
419
 
398
420
  // ...or also load all Book records (not just the new ones).
@@ -418,66 +440,113 @@
418
440
  LiveRecord.Model.all.Book.unsubscribe(subscription);
419
441
  ```
420
442
 
421
- ### Ransack Search Queries (Optional)
443
+ 10. To automatically receive new/updated Book records, you may autoload:
422
444
 
423
- * If you need more complex queries to pass into the `.subscribe(where: { ... })` above, [ransack](https://github.com/activerecord-hackery/ransack) gem is supported.
424
- * For example you can then do:
425
- ```js
426
- // querying upon the `belongs_to :user`
427
- subscription = LiveRecord.Model.all.Book.subscribe({where: {user_is_admin_eq: true, is_enabled_eq: true}});
445
+ ```js
446
+ // subscribe and auto-fetch newly created / updated Book records from the backend
447
+ var subscription = LiveRecord.Model.all.Book.autoload();
428
448
 
429
- // or querying "OR" conditions
430
- subscription = LiveRecord.Model.all.Book.subscribe({where: {title_eq: 'I am Batman', content_eq: 'I am Batman', m: 'or'}});
431
- ```
449
+ // ...or also load all Book records (not just the new ones).
450
+ // useful for populating records at the start, and therefore you may skip using `LiveRecord.helpers.loadRecords()` already
451
+ // subscription = LiveRecord.Model.all.Book.autoload({reload: true});
432
452
 
433
- #### Model File (w/ Ransack) Example
453
+ // ...or subscribe only to certain conditions (i.e. when `is_enabled` attribute value is `true`)
454
+ // For the list of supported operators (like `..._eq`), see JS API `MODEL.autoload(CONFIG)` below
455
+ // subscription = LiveRecord.Model.all.Book.autoload({where: {is_enabled_eq: true}});
434
456
 
435
- ```ruby
436
- # app/models/book.rb
437
- class Book < ApplicationRecord
438
- include LiveRecord::Model::Callbacks
439
- has_many :live_record_updates, as: :recordable, dependent: :destroy
457
+ // you may choose to combine both `where` and `reload` arguments described above
440
458
 
441
- def self.live_record_whitelisted_attributes(book, current_user)
442
- [:id, :title, :is_enabled]
443
- end
444
-
445
- ## this method will be invoked when `subscribe()` is called
446
- ## but, you should not use this method when using `ransack` gem!
447
- ## ransack's methods like `ransackable_attributes` below will be invoked instead
448
- # def self.live_record_queryable_attributes(book, current_user)
449
- # [:id, :title, :is_enabled]
450
- # end
451
-
452
- private
459
+ // now, we can just simply add a "createOrUpdate" callback, to apply our own logic whenever a new/updated Book record is streamed from the backend
460
+ LiveRecord.Model.all.Book.addCallback('after:createOrUpdate', function() {
461
+ // let's say you have a code here that adds this new Book on the page
462
+ // `this` refers to the Book record that has been created/updated
463
+ console.log(this);
464
+ })
453
465
 
454
- # see ransack gem for more details regarding Authorization: https://github.com/activerecord-hackery/ransack#authorization-whitelistingblacklisting
466
+ // you may also add callbacks specific to this `subscription`, as you may want to have multiple subscriptions. Then, see JS API `MODEL.autoload(CONFIG)` below for information
455
467
 
456
- # this method will be invoked when `subscribe()` is called
457
- # LiveRecord passes the `current_user` into `auth_object`, so you can access `current_user` inside below
458
- def self.ransackable_attributes(auth_object = nil)
459
- column_names + _ransackers.keys
460
- end
461
- end
468
+ // you may also want to unsubscribe as you wish
469
+ LiveRecord.Model.all.Book.unsubscribe(subscription);
462
470
  ```
463
471
 
464
- ### Reconnection Streaming For New Records (when client got disconnected)
472
+ ### Ransack Search Queries (Optional)
465
473
 
466
- * To be able to stream newly created records upon reconnection, the only requirement is that you should have a `created_at` attribute on your Models, which by default should already be there. However, to speed up queries, I highly suggest to add index on `created_at` with the following
474
+ * If you need more complex queries to pass into the `.subscribe(where: { ... })` or `.autoload({where: {...}})` above, [ransack](https://github.com/activerecord-hackery/ransack) gem is supported.
475
+ * For example you can then do:
476
+ ```js
477
+ // querying upon the `belongs_to :user`
478
+ subscription = LiveRecord.Model.all.Book.subscribe({where: {user_is_admin_eq: true, is_enabled_eq: true}});
467
479
 
468
- ```bash
469
- # this will create a file under db/migrate folder, then edit that file (see the ruby code below)
470
- rails generate migration add_created_at_index_to_MODELNAME
480
+ // or querying "OR" conditions
481
+ subscription = LiveRecord.Model.all.Book.subscribe({where: {title_eq: 'I am Batman', content_eq: 'I am Batman', m: 'or'}});
471
482
  ```
472
483
 
473
- ```ruby
474
- # db/migrate/2017**********_add_created_at_index_to_MODELNAME.rb
475
- class AddCreatedAtIndexToMODELNAME < ActiveRecord::Migration[5.0] # or 5.1, etc
476
- def change
477
- add_index :TABLENAME, :created_at
478
- end
484
+ #### Model File (w/ Ransack) Example
485
+
486
+ ```ruby
487
+ # app/models/book.rb
488
+ class Book < ApplicationRecord
489
+ include LiveRecord::Model::Callbacks
490
+ has_many :live_record_updates, as: :recordable, dependent: :destroy
491
+
492
+ def self.live_record_whitelisted_attributes(book, current_user)
493
+ [:id, :title, :is_enabled]
479
494
  end
480
- ```
495
+
496
+ ## this method will be invoked when `subscribe()` or `autoload()` is called
497
+ ## but, you should not use this method when using `ransack` gem!
498
+ ## ransack's methods like `ransackable_attributes` below will be invoked instead
499
+ # def self.live_record_queryable_attributes(book, current_user)
500
+ # [:id, :title, :is_enabled]
501
+ # end
502
+
503
+ private
504
+
505
+ # see ransack gem for more details regarding Authorization: https://github.com/activerecord-hackery/ransack#authorization-whitelistingblacklisting
506
+
507
+ # this method will be invoked when `subscribe()` or `autoload()` is called
508
+ # LiveRecord passes the `current_user` into `auth_object`, so you can access `current_user` inside below
509
+ def self.ransackable_attributes(auth_object = nil)
510
+ column_names + _ransackers.keys
511
+ end
512
+ end
513
+ ```
514
+
515
+ ### Reconnection Streaming For `subscribe()` (when client got disconnected)
516
+
517
+ * To be able to restream newly created records upon reconnection, the only requirement is that you should have a `created_at` attribute on your Models, which by default should already be there. However, to speed up queries, I highly suggest to add index on `created_at` with the following
518
+
519
+ ```bash
520
+ # this will create a file under db/migrate folder, then edit that file (see the ruby code below)
521
+ rails generate migration add_created_at_index_to_MODELNAME
522
+ ```
523
+
524
+ ```ruby
525
+ # db/migrate/2017**********_add_created_at_index_to_MODELNAME.rb
526
+ class AddCreatedAtIndexToMODELNAME < ActiveRecord::Migration[5.0] # or 5.1, etc
527
+ def change
528
+ add_index :TABLENAME, :created_at
529
+ end
530
+ end
531
+ ```
532
+
533
+ ### Reconnection Streaming For `autoload()` (when client got disconnected)
534
+
535
+ * To be able to restream newly created/updated records upon reconnection, the only requirement is that you should have a `updated_at` attribute on your Models, which by default should already be there. However, to speed up queries, I highly suggest to add index on `updated_at` with the following
536
+
537
+ ```bash
538
+ # this will create a file under db/migrate folder, then edit that file (see the ruby code below)
539
+ rails generate migration add_updated_at_index_to_MODELNAME
540
+ ```
541
+
542
+ ```ruby
543
+ # db/migrate/2017**********_add_created_at_index_to_MODELNAME.rb
544
+ class AddUpdatedAtIndexToMODELNAME < ActiveRecord::Migration[5.0] # or 5.1, etc
545
+ def change
546
+ add_index :TABLENAME, :updated_at
547
+ end
548
+ end
549
+ ```
481
550
 
482
551
  ## Plugins
483
552
 
@@ -541,9 +610,6 @@
541
610
  * `hasMany` and `belongsTo` `modelName` above should be a valid defined `LiveRecord.Model`
542
611
  * returns the newly created `MODEL`
543
612
 
544
- ### `MODEL.all`
545
- * Object of which properties are IDs of the records
546
-
547
613
  ### `MODEL.subscribe(CONFIG)`
548
614
  * `CONFIG` (Object, Optional)
549
615
  * `reload`: (Boolean, Default: false)
@@ -554,6 +620,7 @@
554
620
  * `on:disconnect`: (function Object)
555
621
  * `before:create`: (function Object; function argument = record)
556
622
  * `after:create`: (function Object; function argument = record)
623
+ * returns an ActionCable subscription object
557
624
  * subscribes to the `LiveRecord::PublicationsChannel`, which then automatically receives new records from the backend.
558
625
  * when `reload: true`, all records (subject to `where` condition above) are immediately loaded, and not just the new ones.
559
626
  * 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.
@@ -572,12 +639,35 @@
572
639
  * `gteq` greater than or equal to; i.e. `created_at_gteq: '2017-12-291T13:47:59.238Z'`
573
640
  * `in` in Array; i.e. `id_in: [2, 56, 19, 68]`
574
641
  * `not_in` in Array; i.e. `id_not_in: [2, 56, 19, 68]`
575
- * `matches` matches using SQL `LIKE`; i.e. `matches: '%Harry Potter%'`
576
- * `does_not_match` does not match using SQL `NOT LIKE`; i.e. `does_not_match: '%Harry Potter%'`
642
+ * `matches` matches using SQL `LIKE`; i.e. `title_matches: '%Harry Potter%'`
643
+ * `does_not_match` does not match using SQL `NOT LIKE`; i.e. `title_does_not_match: '%Harry Potter%'`
644
+
645
+ ### `MODEL.autoload(CONFIG)`
646
+ * `CONFIG` (Object, Optional)
647
+ * `reload`: (Boolean, Default: false)
648
+ * `where`: (Object)
649
+ * `ATTRIBUTENAME_OPERATOR`: (Any Type)
650
+ * `callbacks`: (Object)
651
+ * `on:connect`: (function Object)
652
+ * `on:disconnect`: (function Object)
653
+ * `before:createOrUpdate`: (function Object; function argument = record)
654
+ * `after:createOrUpdate`: (function Object; function argument = record)
655
+ * returns an ActionCable subscription object
656
+ * subscribes to the `LiveRecord::AutoloadsChannel`, which then automatically receives new/updated records from the backend.
657
+ * when `reload: true`, all records (subject to `where` condition above) are immediately loaded, and not just the future new/updated ones.
658
+ * 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.
659
+ * autoload **creates** a record instance to the JS-store if the record does not yet exist, while it just **updates** the record instance if already in the store.
660
+ * `ATTRIBUTENAME_OPERATOR` means something like (for example): `is_enabled_eq`, where `is_enabled` is the `ATTRIBUTENAME` and `eq` is the `OPERATOR`.
661
+ * 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`
662
+
663
+ > Supported query operators are the same as [`subscribe()` above](#list-of-default-supported-query-operators).
577
664
 
578
665
  ### `MODEL.unsubscribe(SUBSCRIPTION)`
579
666
  * unsubscribes to the `LiveRecord::PublicationsChannel`, thereby will not be receiving new records anymore.
580
667
 
668
+ ### `MODEL.all`
669
+ * Object of which properties are IDs of the records
670
+
581
671
  ### `new LiveRecord.Model.all.MODELNAME(ATTRIBUTES)`
582
672
  * `ATTRIBUTES` (Object)
583
673
  * returns a `MODELINSTANCE` of the the Model having `ATTRIBUTES` attributes
@@ -599,7 +689,7 @@
599
689
  ### `MODELINSTANCE.subscribe(config)`
600
690
  * `CONFIG` (Object, Optional)
601
691
  * `reload`: (Boolean, Default: false)
602
- * 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.
692
+ * subscribes to the `LiveRecord::ChangesChannel`. This instance should already be subscribed by default after being stored, unless there is a `on:responseError` or manually `unsubscribed()` which then you should manually call this `subscribe()` function after correctly handling the response error, or whenever desired.
603
693
  * when `reload: true`, the record is forced reloaded to make sure all attributes are in-sync
604
694
  * returns the `subscription` object (the ActionCable subscription object itself)
605
695
 
@@ -652,6 +742,8 @@
652
742
  ## TODOs
653
743
  * Change `feature` specs into `system` specs after [this rspec-rails pull request](https://github.com/rspec/rspec-rails/pull/1813) gets merged.
654
744
 
745
+ * Rename `subscribe()` and `autoload()` into something more descriptive and intuitive, as both of them actually "subscribes". Perhaps rename `subscribe()` into `subscribe({to: 'create'})` and rename `autoload()` into `subscribe({to: 'createOrUpdate'})`?
746
+
655
747
  ## Contributing
656
748
  * pull requests and forks are very much welcomed! :) Let me know if you find any bug! Thanks
657
749
 
@@ -659,6 +751,9 @@
659
751
  * MIT
660
752
 
661
753
  ## Changelog
754
+ * 0.3.0
755
+ * Ability to now auto-load **created** or **updated** records that match your specified "where" condition.
756
+ * See [Setup #10 above](#setup)
662
757
  * 0.2.8
663
758
  * You can now specify `:id` into `live_record_whitelisted_attributes` for verbosity; used to be automatically-included by default. Needed to do this otherwise there was this minor bug where `subscribe()` still receives records (having just `:id` attribute though) even when it is specified to be not-authorized.
664
759
  * fixed minor bug when `live_record_whitelisted_attributes` is not returning anything, throwing a `NoMethodError`
@@ -62,6 +62,84 @@ LiveRecord.Model.create = (config) ->
62
62
 
63
63
  Model.subscriptions = []
64
64
 
65
+ Model.autoload = (config = {}) ->
66
+ config.callbacks ||= {}
67
+ config.reload ||= false
68
+
69
+ subscription = App.cable.subscriptions.create(
70
+ {
71
+ channel: 'LiveRecord::AutoloadsChannel'
72
+ model_name: Model.modelName
73
+ where: config.where
74
+ },
75
+
76
+ connected: ->
77
+ # if forced reload of all records after subscribing, reload only once at the very start of connection, and no longer when reconnecting
78
+ if config.reload
79
+ config.reload = false
80
+ @syncRecords()
81
+
82
+ if @liveRecord._staleSince != undefined
83
+ @syncRecords()
84
+
85
+ config.callbacks['on:connect'].call(this) if config.callbacks['on:connect']
86
+
87
+ disconnected: ->
88
+ @liveRecord._staleSince = (new Date()).toISOString() unless @liveRecord._staleSince
89
+ config.callbacks['on:disconnect'].call(this) if config.callbacks['on:disconnect']
90
+
91
+ received: (data) ->
92
+ if data.error
93
+ @liveRecord._staleSince = (new Date()).toISOString() unless @liveRecord._staleSince
94
+ @onError[data.error.code].call(this, data)
95
+ else
96
+ @onAction[data.action].call(this, data)
97
+
98
+ onAction:
99
+ create_or_update: (data) ->
100
+ record = Model.all[data.attributes.id]
101
+
102
+ # if record already exists
103
+ if record
104
+ doesRecordAlreadyExist = true
105
+ # else if not
106
+ else
107
+ record = new Model(data.attributes)
108
+ doesRecordAlreadyExist = false
109
+
110
+ config.callbacks['before:create_or_update'].call(this, record) if config.callbacks['before:create_or_update']
111
+ if doesRecordAlreadyExist
112
+ record.update(data.attributes)
113
+ else
114
+ record.create()
115
+ console.log(record)
116
+ config.callbacks['after:create_or_update'].call(this, record) if config.callbacks['after:create_or_update']
117
+
118
+ # handler for received() callback above
119
+ onError:
120
+ forbidden: (data) ->
121
+ console.error('[LiveRecord Response Error]', data.error.code, ':', data.error.message, 'for', this)
122
+ bad_request: (data) ->
123
+ console.error('[LiveRecord Response Error]', data.error.code, ':', data.error.message, 'for', this)
124
+
125
+ syncRecords: ->
126
+ @perform(
127
+ 'sync_records',
128
+ model_name: Model.modelName,
129
+ where: config.where,
130
+ stale_since: @liveRecord._staleSince
131
+ )
132
+ @liveRecord._staleSince = undefined
133
+ )
134
+
135
+ subscription.liveRecord = {}
136
+ subscription.liveRecord.modelName = Model.modelName
137
+ subscription.liveRecord.where = config.where
138
+ subscription.liveRecord.callbacks = config.callbacks
139
+
140
+ @subscriptions.push(subscription)
141
+ subscription
142
+
65
143
  Model.subscribe = (config = {}) ->
66
144
  config.callbacks ||= {}
67
145
  config.reload ||= false
@@ -0,0 +1,92 @@
1
+ class LiveRecord::AutoloadsChannel < LiveRecord::BaseChannel
2
+ def subscribed
3
+ stream_from 'live_record:autoloads', coder: ActiveSupport::JSON do |message|
4
+ new_or_updated_record = message['model_name'].safe_constantize.find_by(id: message['record_id']) || break
5
+ new_or_changed_attributes = message.fetch('attributes')
6
+
7
+ is_autoload_dependent_to_new_or_changed_attributes = false
8
+
9
+ conditions_hash = params[:where].to_h
10
+
11
+ # if no conditions supplied to autoload(), then it means every attribute is dependent
12
+ if conditions_hash.empty?
13
+ is_autoload_dependent_to_new_or_changed_attributes = true
14
+ else
15
+ @autoload_dependent_attributes ||= [].tap do |autoload_dependent_attributes|
16
+ conditions_hash.each do |key, value|
17
+ operator = key.split('_').last
18
+ # to get attribute_name, we subtract the end part of the string with size of operator substring; i.e.: created_at_lteq -> created_at
19
+ attribute_name = key[0..(-1 - operator.size - 1)]
20
+
21
+ autoload_dependent_attributes << attribute_name
22
+ end
23
+ end
24
+
25
+ @autoload_dependent_attributes.each do |autoload_dependent_attribute|
26
+ if new_or_changed_attributes.include? autoload_dependent_attribute
27
+ is_autoload_dependent_to_new_or_changed_attributes = true
28
+ break
29
+ end
30
+ end
31
+ end
32
+
33
+ # now if this autoload subscription is indeed dependent on the attributes,
34
+ # then we now check if the where condition values actually match
35
+ # and then transmit data if authorised attributes match condition values
36
+ if is_autoload_dependent_to_new_or_changed_attributes
37
+ model_class = params[:model_name].safe_constantize
38
+
39
+ active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
40
+ model_class: model_class,
41
+ conditions_hash: conditions_hash,
42
+ current_user: current_user,
43
+ )
44
+
45
+ if active_record_relation.exists?(id: new_or_updated_record.id)
46
+ whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(new_or_updated_record, current_user)
47
+
48
+ if whitelisted_attributes.size > 0
49
+ message = { 'action' => 'create_or_update', 'attributes' => new_or_updated_record.attributes }
50
+ response = filtered_message(message, whitelisted_attributes)
51
+ transmit response if response.present?
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def sync_records(data)
59
+ params = data.symbolize_keys
60
+ model_class = params[:model_name].safe_constantize
61
+
62
+ if model_class && model_class < ApplicationRecord
63
+
64
+ active_record_relation = LiveRecord::BaseChannel::SearchAdapters.mapped_active_record_relation(
65
+ model_class: model_class,
66
+ conditions_hash: params[:where].to_h,
67
+ current_user: current_user,
68
+ )
69
+
70
+ if params[:stale_since].present?
71
+ active_record_relation = active_record_relation.where(
72
+ 'updated_at >= ?', DateTime.parse(params[:stale_since]) - LiveRecord.configuration.sync_record_buffer_time
73
+ )
74
+ end
75
+
76
+ # we `transmmit` a message back to client for each matching record
77
+ active_record_relation.find_each do |record|
78
+ # but first, check for the authorised attributes, if exists
79
+ whitelisted_attributes = LiveRecord::BaseChannel::Helpers.whitelisted_attributes(record, current_user)
80
+
81
+ if whitelisted_attributes.size > 0
82
+ message = { 'action' => 'create_or_update', 'attributes' => record.attributes }
83
+ response = filtered_message(message, whitelisted_attributes)
84
+ transmit response if response.present?
85
+ end
86
+ end
87
+ else
88
+ respond_with_error(:bad_request, 'Not a correct model name')
89
+ reject_subscription
90
+ end
91
+ end
92
+ end