render_sync 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +153 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +521 -0
- data/Rakefile +9 -0
- data/app/assets/javascripts/sync.coffee +355 -0
- data/app/controllers/sync/refetches_controller.rb +56 -0
- data/app/helpers/render_sync/config_helper.rb +15 -0
- data/config/routes.rb +3 -0
- data/config/sync.yml +21 -0
- data/lib/generators/render_sync/install_generator.rb +14 -0
- data/lib/generators/render_sync/templates/sync.ru +14 -0
- data/lib/generators/render_sync/templates/sync.yml +34 -0
- data/lib/render_sync.rb +174 -0
- data/lib/render_sync/action.rb +39 -0
- data/lib/render_sync/actions.rb +114 -0
- data/lib/render_sync/channel.rb +23 -0
- data/lib/render_sync/clients/dummy.rb +22 -0
- data/lib/render_sync/clients/faye.rb +104 -0
- data/lib/render_sync/clients/pusher.rb +77 -0
- data/lib/render_sync/controller_helpers.rb +33 -0
- data/lib/render_sync/engine.rb +24 -0
- data/lib/render_sync/erb_tracker.rb +49 -0
- data/lib/render_sync/faye_extension.rb +45 -0
- data/lib/render_sync/model.rb +174 -0
- data/lib/render_sync/model_actions.rb +60 -0
- data/lib/render_sync/model_change_tracking.rb +97 -0
- data/lib/render_sync/model_syncing.rb +65 -0
- data/lib/render_sync/model_touching.rb +35 -0
- data/lib/render_sync/partial.rb +112 -0
- data/lib/render_sync/partial_creator.rb +47 -0
- data/lib/render_sync/reactor.rb +48 -0
- data/lib/render_sync/refetch_model.rb +21 -0
- data/lib/render_sync/refetch_partial.rb +43 -0
- data/lib/render_sync/refetch_partial_creator.rb +21 -0
- data/lib/render_sync/renderer.rb +19 -0
- data/lib/render_sync/resource.rb +115 -0
- data/lib/render_sync/scope.rb +113 -0
- data/lib/render_sync/scope_definition.rb +30 -0
- data/lib/render_sync/view_helpers.rb +106 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/sync/users/_show.html.erb +1 -0
- data/test/dummy/app/views/sync/users/refetch/_show.html.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +8 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/log/test.log +626 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/em_minitest_spec.rb +100 -0
- data/test/fixtures/sync_auth_token_missing.yml +6 -0
- data/test/fixtures/sync_erb.yml +7 -0
- data/test/fixtures/sync_faye.yml +7 -0
- data/test/fixtures/sync_pusher.yml +8 -0
- data/test/models/group.rb +3 -0
- data/test/models/project.rb +2 -0
- data/test/models/todo.rb +8 -0
- data/test/models/user.rb +82 -0
- data/test/sync/abstract_controller.rb +3 -0
- data/test/sync/action_test.rb +82 -0
- data/test/sync/channel_test.rb +15 -0
- data/test/sync/config_test.rb +25 -0
- data/test/sync/erb_tracker_test.rb +72 -0
- data/test/sync/faye_extension_test.rb +87 -0
- data/test/sync/message_test.rb +159 -0
- data/test/sync/model_test.rb +315 -0
- data/test/sync/partial_creator_test.rb +35 -0
- data/test/sync/partial_test.rb +107 -0
- data/test/sync/protected_attributes_test.rb +39 -0
- data/test/sync/reactor_test.rb +18 -0
- data/test/sync/refetch_model_test.rb +26 -0
- data/test/sync/refetch_partial_creator_test.rb +16 -0
- data/test/sync/refetch_partial_test.rb +74 -0
- data/test/sync/renderer_test.rb +19 -0
- data/test/sync/resource_test.rb +181 -0
- data/test/sync/scope_definition_test.rb +39 -0
- data/test/sync/scope_test.rb +113 -0
- data/test/test_helper.rb +66 -0
- data/test/travis/sync.ru +14 -0
- data/test/travis/sync.yml +21 -0
- metadata +317 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
module RenderSync
|
2
|
+
module ModelChangeTracking
|
3
|
+
private
|
4
|
+
# Set up callback to store record and sync scope states prior
|
5
|
+
# the update action
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
before_update :store_state_before_update, if: -> { RenderSync::Model.enabled? }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Stores the current state of the record with its attributes
|
13
|
+
# and all sync relations in an instance variable BEFORE the update
|
14
|
+
# command to later be able to check if the record has been
|
15
|
+
# added/removed from sync scopes.
|
16
|
+
#
|
17
|
+
# Uses ActiveModel::Dirty to track attribute changes
|
18
|
+
# (triggered by AR Callback before_update)
|
19
|
+
#
|
20
|
+
def store_state_before_update
|
21
|
+
record = self.dup
|
22
|
+
changed_attributes.each do |key, value|
|
23
|
+
record.send("#{key}=", value)
|
24
|
+
end
|
25
|
+
record.send("#{self.class.primary_key}=", self.send(self.class.primary_key))
|
26
|
+
|
27
|
+
@record_before_update = record
|
28
|
+
|
29
|
+
@scopes_before_update = {}
|
30
|
+
sync_scope_definitions.each do |definition|
|
31
|
+
scope = RenderSync::Scope.new_from_model(definition, record)
|
32
|
+
@scopes_before_update[definition.name] = {
|
33
|
+
scope: scope,
|
34
|
+
contains_record: scope.contains?(record)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Checks if this record has left the old scope defined by the passed scope
|
40
|
+
# definition throughout the update process
|
41
|
+
#
|
42
|
+
def left_old_scope?(definition)
|
43
|
+
scope_before_update(definition).valid? \
|
44
|
+
&& old_record_in_old_scope?(definition) \
|
45
|
+
&& !new_record_in_old_scope?(definition)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks if this record has entered the new (possibly changed) scope
|
49
|
+
# defined by the passed scope definition throughout the update process
|
50
|
+
#
|
51
|
+
def entered_new_scope?(definition)
|
52
|
+
scope_after_update(definition).valid? \
|
53
|
+
&& new_record_in_new_scope?(definition) \
|
54
|
+
&& !remained_in_old_scope?(definition)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the instance (state) of this record from before the update
|
58
|
+
# (which was previously stored by #store_state_before_update)
|
59
|
+
#
|
60
|
+
def record_before_update
|
61
|
+
@record_before_update
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_after_update
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def remained_in_old_scope?(definition)
|
69
|
+
old_record_in_old_scope?(definition) && new_record_in_old_scope?(definition)
|
70
|
+
end
|
71
|
+
|
72
|
+
def scope_before_update(definition)
|
73
|
+
@scopes_before_update[definition.name][:scope]
|
74
|
+
end
|
75
|
+
|
76
|
+
def scope_after_update(definition)
|
77
|
+
RenderSync::Scope.new_from_model(definition, record_after_update)
|
78
|
+
end
|
79
|
+
|
80
|
+
def old_record_in_old_scope?(definition)
|
81
|
+
@scopes_before_update[definition.name][:contains_record]
|
82
|
+
end
|
83
|
+
|
84
|
+
def old_record_in_new_scope?(definition)
|
85
|
+
scope_after_update(definition).contains?(record_before_update)
|
86
|
+
end
|
87
|
+
|
88
|
+
def new_record_in_new_scope?(definition)
|
89
|
+
scope_after_update(definition).contains?(record_after_update)
|
90
|
+
end
|
91
|
+
|
92
|
+
def new_record_in_old_scope?(defintion)
|
93
|
+
scope_before_update(defintion).contains?(record_after_update)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RenderSync
|
2
|
+
module ModelRenderSyncing
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def prepare_sync_create
|
7
|
+
add_sync_action(:new, self, default_scope: sync_default_scope)
|
8
|
+
|
9
|
+
sync_scope_definitions.each do |definition|
|
10
|
+
scope = RenderSync::Scope.new_from_model(definition, self)
|
11
|
+
if scope.contains?(self)
|
12
|
+
add_sync_action :new, self, scope: scope, default_scope: sync_default_scope
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_sync_update
|
18
|
+
add_sync_action :update, self
|
19
|
+
|
20
|
+
sync_scope_definitions.each do |definition|
|
21
|
+
prepare_sync_update_scope(definition)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepare_sync_destroy
|
26
|
+
add_sync_action :destroy, self, default_scope: sync_default_scope
|
27
|
+
|
28
|
+
sync_scope_definitions.each do |definition|
|
29
|
+
scope = RenderSync::Scope.new_from_model(definition, self)
|
30
|
+
if scope.valid?
|
31
|
+
add_sync_action :destroy, self,
|
32
|
+
scope: scope,
|
33
|
+
default_scope: sync_default_scope
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates update actions for subscribers on the sync scope defined by
|
39
|
+
# the passed sync scope definition.
|
40
|
+
#
|
41
|
+
# It compares the state of the record in context of the sync scope before
|
42
|
+
# and after the update. If the record has been added to a scope, it
|
43
|
+
# publishes a new partial to the subscribers of that scope. It also sends
|
44
|
+
# a destroy action to subscribers of the scope, if the record has been
|
45
|
+
# removed from it.
|
46
|
+
#
|
47
|
+
def prepare_sync_update_scope(scope_definition)
|
48
|
+
# Add destroy action for the old scope (scope_before_update)
|
49
|
+
# if this record has left it
|
50
|
+
if left_old_scope?(scope_definition)
|
51
|
+
add_sync_action :destroy, record_before_update,
|
52
|
+
scope: scope_before_update(scope_definition),
|
53
|
+
default_scope: sync_default_scope
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add new action for the new scope (scope_after_update) if this record has entered it
|
57
|
+
if entered_new_scope?(scope_definition)
|
58
|
+
add_sync_action :new, record_after_update,
|
59
|
+
scope: scope_after_update(scope_definition),
|
60
|
+
default_scope: sync_default_scope
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RenderSync
|
2
|
+
module ModelTouching
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def prepare_sync_touches
|
7
|
+
sync_touches.each do |touch_association|
|
8
|
+
add_sync_action :update, touch_association
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Return the associations to be touched after a record change
|
13
|
+
# Takes into account that an association itself may have changed during
|
14
|
+
# an update call (e.g. project_id has changed). To accomplish this, it
|
15
|
+
# uses the stored record from before the update (@record_before_update)
|
16
|
+
# and touches that as well as the current association
|
17
|
+
#
|
18
|
+
def sync_touches
|
19
|
+
sync_associations = []
|
20
|
+
|
21
|
+
self.class.sync_touches.each do |touch|
|
22
|
+
current = send(touch)
|
23
|
+
sync_associations.push(current.reload) if current.present?
|
24
|
+
|
25
|
+
if @record_before_update.present?
|
26
|
+
previous = @record_before_update.send(touch)
|
27
|
+
sync_associations.push(previous.reload) if previous.present?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
sync_associations.uniq.compact
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class Partial
|
3
|
+
attr_accessor :name, :resource, :context
|
4
|
+
|
5
|
+
def self.all(model, context, scope = nil)
|
6
|
+
resource = Resource.new(model, scope)
|
7
|
+
|
8
|
+
Dir["#{RenderSync.views_root}/#{resource.plural_name}/_*.*"].map do |partial|
|
9
|
+
partial_name = File.basename(partial)
|
10
|
+
Partial.new(partial_name[1...partial_name.index('.')], resource.model, scope, context)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find(model, partial_name, context)
|
15
|
+
resource = Resource.new(model)
|
16
|
+
plural_name = resource.plural_name
|
17
|
+
partial = Dir["app/views/sync/#{plural_name}/_#{partial_name}.*"].first
|
18
|
+
return unless partial
|
19
|
+
Partial.new(partial_name, resource.model, nil, context)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(name, resource, scope, context)
|
23
|
+
self.name = name
|
24
|
+
self.resource = Resource.new(resource, scope)
|
25
|
+
self.context = context
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_to_string
|
29
|
+
context.render_to_string(partial: path, locals: locals, formats: [:html])
|
30
|
+
end
|
31
|
+
|
32
|
+
def render
|
33
|
+
context.render(partial: path, locals: locals, formats: [:html])
|
34
|
+
end
|
35
|
+
|
36
|
+
def sync(action)
|
37
|
+
message(action).publish
|
38
|
+
end
|
39
|
+
|
40
|
+
def message(action)
|
41
|
+
RenderSync.client.build_message channel_for_action(action),
|
42
|
+
html: (render_to_string unless action.to_s == "destroy")
|
43
|
+
end
|
44
|
+
|
45
|
+
def authorized?(auth_token)
|
46
|
+
self.auth_token == auth_token
|
47
|
+
end
|
48
|
+
|
49
|
+
def auth_token
|
50
|
+
@auth_token ||= Channel.new("#{polymorphic_path}-_#{name}").to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
# For the refetch feature we need an auth_token that wasn't created
|
54
|
+
# with scopes, because the scope information is not available on the
|
55
|
+
# refetch-request. So we create a refetch_auth_token which is based
|
56
|
+
# only on model_name and id plus the name of this partial
|
57
|
+
#
|
58
|
+
def refetch_auth_token
|
59
|
+
@refetch_auth_token ||= Channel.new("#{model_path}-_#{name}").to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def channel_prefix
|
63
|
+
@channel_prefix ||= auth_token
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_channel_prefix
|
67
|
+
@update_channel_prefix ||= refetch_auth_token
|
68
|
+
end
|
69
|
+
|
70
|
+
def channel_for_action(action)
|
71
|
+
case action
|
72
|
+
when :update
|
73
|
+
"#{update_channel_prefix}-#{action}"
|
74
|
+
else
|
75
|
+
"#{channel_prefix}-#{action}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def selector_start
|
80
|
+
"#{channel_prefix}-start"
|
81
|
+
end
|
82
|
+
|
83
|
+
def selector_end
|
84
|
+
"#{channel_prefix}-end"
|
85
|
+
end
|
86
|
+
|
87
|
+
def creator_for_scope(scope)
|
88
|
+
PartialCreator.new(name, resource.model, scope, context)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def path
|
95
|
+
"sync/#{resource.plural_name}/#{name}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def locals
|
99
|
+
locals_hash = {}
|
100
|
+
locals_hash[resource.base_name.to_sym] = resource.model
|
101
|
+
locals_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def model_path
|
105
|
+
resource.model_path
|
106
|
+
end
|
107
|
+
|
108
|
+
def polymorphic_path
|
109
|
+
resource.polymorphic_path
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class PartialCreator
|
3
|
+
attr_accessor :name, :resource, :context, :partial
|
4
|
+
|
5
|
+
def initialize(name, resource, scopes, context)
|
6
|
+
self.name = name
|
7
|
+
self.resource = Resource.new(resource, scopes)
|
8
|
+
self.context = context
|
9
|
+
self.partial = Partial.new(name, self.resource.model, scopes, context)
|
10
|
+
end
|
11
|
+
|
12
|
+
def auth_token
|
13
|
+
@auth_token ||= Channel.new("#{polymorphic_path}-_#{name}").to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def channel
|
17
|
+
@channel ||= auth_token
|
18
|
+
end
|
19
|
+
|
20
|
+
def selector
|
21
|
+
"#{channel}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def sync_new
|
25
|
+
message.publish
|
26
|
+
end
|
27
|
+
|
28
|
+
def message
|
29
|
+
RenderSync.client.build_message(channel,
|
30
|
+
html: partial.render_to_string,
|
31
|
+
resourceId: resource.id,
|
32
|
+
authToken: partial.auth_token,
|
33
|
+
channelUpdate: partial.channel_for_action(:update),
|
34
|
+
channelDestroy: partial.channel_for_action(:destroy),
|
35
|
+
selectorStart: partial.selector_start,
|
36
|
+
selectorEnd: partial.selector_end
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def polymorphic_path
|
44
|
+
resource.polymorphic_new_path
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class Reactor
|
3
|
+
include MonitorMixin
|
4
|
+
|
5
|
+
# Execute EventMachine bound code block, waiting for reactor to start if
|
6
|
+
# not yet started or reactor thread has gone away
|
7
|
+
def perform
|
8
|
+
return EM.next_tick{ yield } if running?
|
9
|
+
cleanly_shutdown_reactor
|
10
|
+
condition = new_cond
|
11
|
+
Thread.new do
|
12
|
+
EM.run do
|
13
|
+
EM.next_tick do
|
14
|
+
synchronize do
|
15
|
+
condition.signal
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
synchronize do
|
21
|
+
condition.wait_until { EM.reactor_running? }
|
22
|
+
EM.next_tick { yield }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
EM.stop if running?
|
28
|
+
end
|
29
|
+
|
30
|
+
def running?
|
31
|
+
EM.reactor_running? && EM.reactor_thread.alive?
|
32
|
+
end
|
33
|
+
|
34
|
+
# If the reactor's thread died, EM still thinks it's running but it isn't.
|
35
|
+
# This will happen if we forked from a process that had the reator running.
|
36
|
+
# Tell EM it's dead. Stolen from the EM internals
|
37
|
+
#
|
38
|
+
# https://groups.google.com/forum/#!msg/ruby-amqp/zchM4QzbZRE/I43wIjbgIv4J
|
39
|
+
#
|
40
|
+
def cleanly_shutdown_reactor
|
41
|
+
if EM.reactor_running?
|
42
|
+
EM.stop_event_loop
|
43
|
+
EM.release_machine
|
44
|
+
EM.instance_variable_set '@reactor_running', false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class RefetchModel
|
3
|
+
|
4
|
+
def self.find_by_class_name_and_id(resource_name, id)
|
5
|
+
class_name = resource_name.to_s.classify
|
6
|
+
class_name.safe_constantize.find(id) if supported_classes.include?(class_name)
|
7
|
+
rescue
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.supported_classes
|
12
|
+
Thread.current["sync_refetch_classes"] = nil if Rails.env.development?
|
13
|
+
|
14
|
+
Thread.current["sync_refetch_classes"] ||= begin
|
15
|
+
Dir["app/views/sync/*/refetch"].collect{|path|
|
16
|
+
File.basename(path.gsub(/\/refetch$/, '')).classify
|
17
|
+
}.reject{|clazz| clazz.nil? }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class RefetchPartial < Partial
|
3
|
+
|
4
|
+
def self.all(model, context, scope = nil)
|
5
|
+
resource = Resource.new(model)
|
6
|
+
|
7
|
+
Dir["#{RenderSync.views_root}/#{resource.plural_name}/refetch/_*.*"].map do |partial|
|
8
|
+
partial_name = File.basename(partial)
|
9
|
+
RefetchPartial.new(partial_name[1...partial_name.index('.')], resource.model, scope, context)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find(model, partial_name, context)
|
14
|
+
resource = Resource.new(model)
|
15
|
+
plural_name = resource.plural_name
|
16
|
+
partial = Dir["#{RenderSync.views_root}/#{plural_name}/refetch/_#{partial_name}.*"].first
|
17
|
+
return unless partial
|
18
|
+
RefetchPartial.new(partial_name, resource.model, nil, context)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.find_by_authorized_resource(model, partial_name, context, auth_token)
|
22
|
+
partial = find(model, partial_name, context)
|
23
|
+
return unless partial && partial.authorized?(auth_token)
|
24
|
+
|
25
|
+
partial
|
26
|
+
end
|
27
|
+
|
28
|
+
def message(action)
|
29
|
+
RenderSync.client.build_message channel_for_action(action), refetch: true
|
30
|
+
end
|
31
|
+
|
32
|
+
def creator_for_scope(scope)
|
33
|
+
RefetchPartialCreator.new(name, resource.model, scope, context)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def path
|
40
|
+
"sync/#{resource.plural_name}/refetch/#{name}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|