Module: RenderSync::Model::ClassMethods

Defined in:
lib/render_sync/model.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#sync_default_scopeObject

Returns the value of attribute sync_default_scope.



30
31
32
# File 'lib/render_sync/model.rb', line 30

def sync_default_scope
  @sync_default_scope
end

#sync_scope_definitionsObject

Returns the value of attribute sync_scope_definitions.



30
31
32
# File 'lib/render_sync/model.rb', line 30

def sync_scope_definitions
  @sync_scope_definitions
end

#sync_touchesObject

Returns the value of attribute sync_touches.



30
31
32
# File 'lib/render_sync/model.rb', line 30

def sync_touches
  @sync_touches
end

Instance Method Details

#sync(*actions) ⇒ Object

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.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 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) ⇒ Object

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) %>


126
127
128
129
130
131
132
133
134
135
136
# 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) ⇒ Object

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


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# 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