0.0
No release in over 3 years
There's a lot of open issues
Rails gem to push updates to models into the frontend through ActionCable
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

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:

$ bundle

Or install it yourself as:

$ gem install model_updates

Include it in your JavaScript:

//= require model_updates

Include the helper in your models:

class ApplicationRecord < ActiveRecord::Base
  include ModelUpdates::ModelExtensions
end

Add 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
end

In 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
end

Choose which attributes should be broadcasted automatically:

class Model < ApplicationRecord
  model_updates_broadcast_attributes attributes: [:updated_at]
end

If you also want creates broadcasted:

class Model < ApplicationRecord
  model_updates_broadcast_attributes attributes: [:updated_at]
  model_updates_broadcast_created
end

Or destroys:

class Model < ApplicationRecord
  model_updates_broadcast_attributes attributes: [:updated_at]
  model_updates_broadcast_destroyed
end

Usage

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_at

Or 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 = true

Contributing

Contribution directions go here.

License

The gem is available as open source under the terms of the MIT License.