module RenderSync::Model::ClassMethods

Attributes

sync_default_scope[RW]
sync_scope_definitions[RW]
sync_touches[RW]

Public Instance Methods

sync(*actions) click to toggle source

Set up automatic syncing of partials when a record of this class is created, updated or deleted. Be sure to wrap your model actions inside a sync_enable block for sync to do its magic.

# File lib/render_sync/model.rb, line 36
def sync(*actions)
  include ModelActions unless include?(ModelActions)
  include ModelChangeTracking unless include?(ModelChangeTracking)
  include ModelRenderSyncing
  
  if actions.last.is_a? Hash
    @sync_default_scope = actions.last.fetch :default_scope
  end
  
  actions = [:create, :update, :destroy] if actions.include? :all
  actions.flatten!

  if actions.include? :create
    after_create  :prepare_sync_create,  if: -> { RenderSync::Model.enabled? }
  end
  
  if actions.include? :update
    after_update  :prepare_sync_update,  if: -> { RenderSync::Model.enabled? }
  end
  
  if actions.include? :destroy
    after_destroy :prepare_sync_destroy, if: -> { RenderSync::Model.enabled? }
  end

end
sync_scope(name, lambda) click to toggle source

Set up a sync scope for the model defining a set of records to be updated via sync

name - The name of the scope lambda - A lambda defining the scope.

Has to return an ActiveRecord::Relation.

You can define the lambda with arguments (see examples). Note that the naming of the parameters is very important. Only use names of methods or ActiveRecord attributes defined on the model (e.g. user_id). This way sync will be able to pass changed records to the lambda and track changes to the scope.

Example:

class Todo < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
  scope :incomplete, -> { where(complete: false) }

  sync :all

  sync_scope :complete, -> { where(complete: true) }
  sync_scope :by_project, ->(project_id) { where(project_id: project_id) }
  sync_scope :my_incomplete_todos, ->(user) { incomplete.where(user_id: user.id) }
end

To subscribe to these scopes you would put these lines into your views:

<%= sync partial: "todo", collection: @todos, scope: Todo.complete %>

If the collection you want to render is exactly defined be the given scope the scope can be omitted:

<%= sync partial: "todo", collection: Todo.complete %>

For rendering my_incomplete_todos:

<%= sync partial: "todo", collection: Todo.my_incomplete_todos(current_user) %>

The render_new call has to look like this:

<%= sync_new partial: "todo", resource: Todo.new, scope: Todo.complete %>

Now when a record changes sync will use the names of the lambda parameters (project_id and user), get the corresponding attributes from the record (project_id column or user association) and pass it to the lambda. This way sync can identify if a record has been added or removed from a scope and will then publish the changes to subscribers on all scoped channels.

Beware that chaining of sync scopes in the view is currently not possible. So the following example would raise an exception:

<%= sync_new partial: "todo", Todo.new, scope: Todo.mine(current_user).incomplete %>

To work around this just create an explicit sync_scope for your problem:

sync_scope :my_incomplete_todos, ->(user) { incomplete.mine(current_user) }

And in the view:

<%= sync_new partial: "todo", Todo.new, scope: Todo.my_incomplete_todos(current_user) %>
# File lib/render_sync/model.rb, line 126
def sync_scope(name, lambda)
  if self.respond_to?(name)
    raise ArgumentError, "invalid scope name '#{name}'. Already defined on #{self.name}"
  end
  
  @sync_scope_definitions[name] = RenderSync::ScopeDefinition.new(self, name, lambda)
  
  singleton_class.send(:define_method, name) do |*args|
    RenderSync::Scope.new_from_args(@sync_scope_definitions[name], args)
  end        
end
sync_touch(*args) click to toggle source

Register one or more associations to be sync’d when this record changes.

Example:

class Todo < ActiveRecord::Base
  belongs_to :project
  belongs_to :user

  sync :all
  sync_touch :project, :user
end
# File lib/render_sync/model.rb, line 150
def sync_touch(*args)
  # Only load Modules and set up callbacks if sync_touch wasn't
  # called before
  if @sync_touches.blank?
    include ModelActions unless include?(ModelActions)
    include ModelChangeTracking unless include?(ModelChangeTracking)
    include ModelTouching
  
    @sync_touches ||= []
  
    after_create   :prepare_sync_touches, if: -> { RenderSync::Model.enabled? }
    after_update   :prepare_sync_touches, if: -> { RenderSync::Model.enabled? }
    after_destroy  :prepare_sync_touches, if: -> { RenderSync::Model.enabled? }
  end

  options = args.extract_options!
  args.each do |arg|
    @sync_touches.push(arg)
  end
end