ModelUpdates
Rails gem to push updates through models into the frontend through ActionCable easily like this:
JavaScript:
ModelUpdates.connectModel("User", userId, "changed-name", function(args) {
console.log("User changed his name to: " + args.new_name)
})Ruby:
user = User.find(user_id)
user.model_updates_call("changed-name", new_name: "test")Installation
Add this line to your application's Gemfile:
gem 'model_updates'And then execute:
$ bundleOr install it yourself as:
$ gem install model_updatesInclude it in your JavaScript:
//= require model_updatesInclude the helper in your models:
class ApplicationRecord < ActiveRecord::Base
include ModelUpdates::ModelExtensions
endAdd required CanCan access methods to your ApplicationCable::Channel:
class ApplicationCable::Channel < ActionCable::Channel::Base
private
# Used to authorize which resources the user can read from (security)
def current_ability
@_current_ability ||= CanCanAbility.new(user: current_user)
end
# Get user from Devise
def current_user
@_current_user ||= env["warden"].user
end
endIn order to get the current user with Devise, you also have to add the following config/initializers/warden_hooks.rb:
Warden::Manager.after_set_user do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
auth.cookies.signed["#{scope}.expires_at"] = 70.minutes.from_now
endChoose which attributes should be broadcasted automatically:
class Model < ApplicationRecord
model_updates_broadcast_attributes attributes: [:updated_at]
endIf you also want creates broadcasted:
class Model < ApplicationRecord
model_updates_broadcast_attributes attributes: [:updated_at]
model_updates_broadcast_created
endOr destroys:
class Model < ApplicationRecord
model_updates_broadcast_attributes attributes: [:updated_at]
model_updates_broadcast_destroyed
endUsage
Update content on page live automatically
Do like this in your views if you are using HAML to receive automatic updates:
.model-updates{data: {model_updates: model.model_updates_data_attrs(:updated_at)}}
= model.updated_atOr like this in ERB:
<div class="model-updates" data-model-updates-model="Model" data-model-updates-id="1" data-model-updates-key="updated_at">
<%= model.updated_at %>
</div>Now that element should update automatically when the model is changed
Callbacks
Updates
You can also do a callback, once the value is changed.
<div class="model-updates" data-model-updates-model="Model" data-model-updates-id="1" data-model-updates-key="updated_at" data-model-updates-callback="myCallback">
<%= model.updated_at %>
</div>function myCallback(data) {
if (data.value == "something") {
data.element.innerText = "Test: " + data.value
} else {
data.element.innerText = data.value
}
}The data element is formatted like this:
data = {
changes: "A hash of all the registered changes (multiple attributes might by updated than just the one subscribed to in the same update call)",
element: "Your original element with the class 'model-updates'",
id: "The ID of the model",
key: "The key (attribute name) which was updated",
value: "The new value of the attribute"
}
You can also do this with pure JavaScript instead of tags like this:
ModelUpdates.connectChanged("Task", taskId, function(data) {
$(".task-element").text(data.changes.name)
})
ModelUpdates.update()Creates
You can receive create callbacks like this:
ModelUpdates.Create.connect({model: "MyModel", onCreated: function(data) {
console.log("New MyModel was created with ID: " + data.id)
})Destroys
If you want an element automatically removed on destroy:
<div class="model-updates" data-model-updates-model="<%= model.class.name %>" data-model-updates-id="<%= model.id %>" data-model-updates-remove-on-destroy="true">
<%= model.updated_at %>
</div>You can also manually listen when a model gets destroyed:
ModelUpdates.Destroy.connect({
"id": data.id,
"model": "BuildCommandExecution",
"onDestroyed": function(data) {
console.log("Model destroyed: " + data.id)
}
})Live updating new elements added dynamically after page load
You can refresh elements with a simple call like this:
ModelUpdates.update()Events
Call an event on a specific model:
user = User.find(user_id)
user.model_updates_call("changed-name", new_name: "test")Connect to a specific model:
ModelUpdates.connectModel("User", userId, "changed-name", function(args) {
console.log("User changed his name to: " + args.new_name)
})Call an event on a model class:
User.model_updates_call("changed-name", new_name: "test")Connect to events called on a models class:
ModelUpdates.connectModelClass("User", "changed-name", function(args) {
console.log("Someone his name to: " + args.new_name)
})Call this at the end of your JavaScript, which will actually connect to all the defined events in a batched way:
ModelUpdates.connectEvents()Debugging
In case you want to enable debug output from ModelUpdates from JavaScript:
ModelUpdates.configuration.debug = trueContributing
Contribution directions go here.
License
The gem is available as open source under the terms of the MIT License.