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,21 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class RefetchPartialCreator < PartialCreator
|
3
|
+
|
4
|
+
def initialize(name, resource, scoped_resource, context)
|
5
|
+
super
|
6
|
+
self.partial = RefetchPartial.new(name, self.resource.model, nil, context)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
RenderSync.client.build_message(channel,
|
11
|
+
refetch: true,
|
12
|
+
resourceId: resource.id,
|
13
|
+
authToken: partial.auth_token,
|
14
|
+
channelUpdate: partial.channel_for_action(:update),
|
15
|
+
channelDestroy: partial.channel_for_action(:destroy),
|
16
|
+
selectorStart: partial.selector_start,
|
17
|
+
selectorEnd: partial.selector_end
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class Renderer
|
3
|
+
|
4
|
+
attr_accessor :context
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
self.context = ApplicationController.new.view_context
|
8
|
+
self.context.instance_eval do
|
9
|
+
def url_options
|
10
|
+
ActionMailer::Base.default_url_options
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_to_string(options)
|
16
|
+
context.render(options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module RenderSync
|
4
|
+
class Resource
|
5
|
+
attr_accessor :model, :scopes
|
6
|
+
|
7
|
+
# Constructor
|
8
|
+
#
|
9
|
+
# model - The ActiveModel instace for this Resource
|
10
|
+
# scopes - The optional scopes to prefix polymorphic paths with.
|
11
|
+
# Can be a Symbol/String, a parent model or an RenderSync::Scope
|
12
|
+
# or an Array with any combination.
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# class User < ActiveRecord::Base
|
17
|
+
# sync :all
|
18
|
+
# sync_scope :cool, -> { where(cool: true) }
|
19
|
+
# sync_scope :in_group, ->(group) { where(group_id: group.id) }
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# user = User.find(1)
|
23
|
+
#
|
24
|
+
# resource = Resource.new(user)
|
25
|
+
# resource.polymorphic_path => "/users/1"
|
26
|
+
# resource.polymorphic_new_path => "/users/new"
|
27
|
+
#
|
28
|
+
# resource = Resource.new(user, :admin)
|
29
|
+
# resource.polymorphic_path => "/admin/users/1"
|
30
|
+
# resource.polymorphic_new_path => "/admin/users/new"
|
31
|
+
#
|
32
|
+
# resource = Resource.new(user, [:staff, :restricted])
|
33
|
+
# resource.polymorphic_path => "/staff/restricted/users/1"
|
34
|
+
# resource.polymorphic_new_path => "/staff/restricted/users/new"
|
35
|
+
#
|
36
|
+
# resource = Resource.new(user, project)
|
37
|
+
# resource.polymorphic_path => "/projects/2/users/1"
|
38
|
+
# resource.polymorphic_new_path => "/projects/2/users/new"
|
39
|
+
#
|
40
|
+
# resource = Resource.new(user, User.cool)
|
41
|
+
# resource.polymorphic_path => "/cool/users/2"
|
42
|
+
# resource.polymorphic_new_path => "/cool/users/new"
|
43
|
+
#
|
44
|
+
# resource = Resource.new(user, User.in_group(group))
|
45
|
+
# resource.polymorphic_path => "/in_group/group/3/users/2"
|
46
|
+
# resource.polymorphic_new_path => "/in_group/group/3/users/new"
|
47
|
+
#
|
48
|
+
# resource = Resource.new(user, [:admin, User.cool, User.in_group(group)])
|
49
|
+
# resource.polymorphic_path => "admin/cool/in_group/group/3/users/2"
|
50
|
+
# resource.polymorphic_new_path => "admin/cool/in_group/group/3/users/new"
|
51
|
+
#
|
52
|
+
# resource = Resource.new(user, [:admin, project])
|
53
|
+
# resource.polymorphic_path => "/admin/projects/2/users/1"
|
54
|
+
# resource.polymorphic_new_path => "/admin/projects/2/users/new"
|
55
|
+
#
|
56
|
+
def initialize(model, scopes = nil)
|
57
|
+
self.model = model
|
58
|
+
self.scopes = scopes
|
59
|
+
end
|
60
|
+
|
61
|
+
def scopes=(new_scopes)
|
62
|
+
new_scopes = [new_scopes] unless new_scopes.nil? or new_scopes.is_a? Array
|
63
|
+
@scopes = new_scopes
|
64
|
+
end
|
65
|
+
|
66
|
+
def id
|
67
|
+
model.id
|
68
|
+
end
|
69
|
+
|
70
|
+
def name
|
71
|
+
model.class.model_name.to_s.underscore
|
72
|
+
end
|
73
|
+
|
74
|
+
def base_name
|
75
|
+
name.split('/').last
|
76
|
+
end
|
77
|
+
|
78
|
+
def plural_name
|
79
|
+
name.pluralize
|
80
|
+
end
|
81
|
+
|
82
|
+
def scopes_path
|
83
|
+
path = Pathname.new('/')
|
84
|
+
unless scopes.nil?
|
85
|
+
paths = scopes.map do |scope|
|
86
|
+
if scope.is_a?(RenderSync::Scope)
|
87
|
+
scope.polymorphic_path.relative_path_from(path)
|
88
|
+
elsif scope.class.respond_to? :model_name
|
89
|
+
Resource.new(scope).polymorphic_path.relative_path_from(path)
|
90
|
+
else
|
91
|
+
scope.to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
path = path.join(*paths)
|
95
|
+
end
|
96
|
+
path
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns an unscoped Pathname for the model (e.g. /users/1)
|
100
|
+
def model_path
|
101
|
+
Pathname.new('/').join(plural_name, id.to_s)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the scoped Pathname for the model (e.g. /users/1/todos/2)
|
105
|
+
def polymorphic_path
|
106
|
+
scopes_path.join(plural_name, id.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the scoped Pathname for a new model (e.g. /users/1/todos/new)
|
110
|
+
def polymorphic_new_path
|
111
|
+
scopes_path.join(plural_name, "new")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class Scope
|
3
|
+
attr_accessor :scope_definition, :args, :valid
|
4
|
+
|
5
|
+
def initialize(scope_definition, args)
|
6
|
+
@scope_definition = scope_definition
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return a new sync scope by passing a scope definition (containing a lambda and parameter names)
|
11
|
+
# and a set of arguments to be handed over to the lambda
|
12
|
+
def self.new_from_args(scope_definition, args)
|
13
|
+
if args.length != scope_definition.parameters.length
|
14
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for #{scope_definition.parameters.length})"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Classes currently supported as Arguments for the sync scope lambda
|
18
|
+
supported_arg_types = [Fixnum, Integer, ActiveRecord::Base]
|
19
|
+
|
20
|
+
# Check passed args for types. Raise ArgumentError if arg class is not supported
|
21
|
+
args.each_with_index do |arg, i|
|
22
|
+
|
23
|
+
unless supported_arg_types.find { |klass| break true if arg.is_a?(klass) }
|
24
|
+
param = scope_definition.parameters[i]
|
25
|
+
raise ArgumentError, "invalid argument '#{param}' (#{arg.class.name}). Currently only #{supported_arg_types.map(&:name).join(", ")} are supported"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
new(scope_definition, args)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return a new sync scope by passing a scope definition (containing a lambda and parameter names)
|
33
|
+
# and an ActiveRecord model object. The args List will be filled with the model attributes
|
34
|
+
# corrensponding to the parameter names defined in the scope_definition
|
35
|
+
def self.new_from_model(scope_definition, model)
|
36
|
+
new(scope_definition, scope_definition.parameters.map { |p| model.send(p) })
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return the ActiveRecord Relation by calling the lamda with the given args.
|
40
|
+
#
|
41
|
+
def relation
|
42
|
+
scope_definition.lambda.call(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check if the combination of stored AR relation and args is valid by calling exists? on it.
|
46
|
+
# This may raise an exception depending on the args, so we have to rescue the block
|
47
|
+
#
|
48
|
+
def valid?
|
49
|
+
@valid ||= begin
|
50
|
+
relation.exists?
|
51
|
+
true # set valid to true, if relation.exists?(model) does not throw any exception
|
52
|
+
rescue
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def invalid?
|
58
|
+
!valid?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check if the given record falls under the narrowing by the stored ActiveRecord Relation.
|
62
|
+
# Depending on the arguments set in args this can lead to an exception (e.g. when a nil is passed)
|
63
|
+
# Also set the value of valid to avoid another DB query.
|
64
|
+
#
|
65
|
+
def contains?(record)
|
66
|
+
begin
|
67
|
+
val = relation.exists?(record.id)
|
68
|
+
@valid = true # set valid to true, if relation.exists?(model) does not throw any exception
|
69
|
+
val
|
70
|
+
rescue
|
71
|
+
@valid = false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Generates an Array of path elements based on the given lambda args and their
|
76
|
+
# name which is saved in scope_definition.parameters
|
77
|
+
#
|
78
|
+
def args_path
|
79
|
+
scope_definition.parameters.each_with_index.map do |parameter, i|
|
80
|
+
if args[i].is_a? ActiveRecord::Base
|
81
|
+
[parameter.to_s, args[i].send(args[i].class.primary_key).to_s]
|
82
|
+
else
|
83
|
+
[parameter.to_s, args[i].to_s]
|
84
|
+
end
|
85
|
+
end.flatten
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the Pathname for this scope
|
89
|
+
# Example:
|
90
|
+
# class User < ActiveRecord::Base
|
91
|
+
# sync :all
|
92
|
+
# belongs_to :group
|
93
|
+
# sync_scope :in_group, ->(group) { where(group_id: group.id) }
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# group = Group.first
|
97
|
+
# User.in_group(group).polymorphic_path.to_s
|
98
|
+
# # => "/in_group/group/1"
|
99
|
+
#
|
100
|
+
def polymorphic_path
|
101
|
+
Pathname.new('/').join(*([scope_definition.name.to_s, args_path].flatten))
|
102
|
+
end
|
103
|
+
|
104
|
+
# Delegate all undefined methods to the relation, so that
|
105
|
+
# the scope behaves like an ActiveRecord::Relation, e.g. call count
|
106
|
+
# on the relation (User.in_group(group).count)
|
107
|
+
#
|
108
|
+
def method_missing(method, *args, &block)
|
109
|
+
relation.send(method, *args, &block)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RenderSync
|
2
|
+
class ScopeDefinition
|
3
|
+
attr_accessor :klass, :name, :lambda, :parameters, :args
|
4
|
+
|
5
|
+
def initialize(klass, name, lambda)
|
6
|
+
self.class.ensure_valid_params!(klass, lambda)
|
7
|
+
|
8
|
+
@klass = klass
|
9
|
+
@name = name
|
10
|
+
@lambda = lambda
|
11
|
+
@parameters = lambda.parameters.map { |p| p[1] }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Checks the validity of the parameter names contained in the lambda definition.
|
15
|
+
# E.g. if the lambda looks like this:
|
16
|
+
#
|
17
|
+
# ->(user) { where(user_id: user.id) }
|
18
|
+
#
|
19
|
+
# The name of the passed argument (user) must be present as a column name or an
|
20
|
+
# instance method (e.g. an association) of the ActiveRecord object.
|
21
|
+
#
|
22
|
+
def self.ensure_valid_params!(klass, lambda)
|
23
|
+
unless (invalid = lambda.parameters.map { |p| p[1] } - klass.column_names.map(&:to_sym) - klass.instance_methods) == []
|
24
|
+
raise ArgumentError, "Invalid parameters #{invalid}. Parameter names of the sync_scope lambda definition may only contain ActiveRecord column names or instance methods of #{klass.name}."
|
25
|
+
end
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module RenderSync
|
2
|
+
|
3
|
+
module ViewHelpers
|
4
|
+
|
5
|
+
# Surround partial render in script tags, watching for
|
6
|
+
# sync_update and sync_destroy channels from pubsub server
|
7
|
+
#
|
8
|
+
# options - The Hash of options
|
9
|
+
# partial - The String partial filename without leading underscore
|
10
|
+
# resource - The ActiveModel resource
|
11
|
+
# collection - The Array of ActiveModel resources to use in place of
|
12
|
+
# single resource
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
# <%= sync partial: 'todo', resource: todo %>
|
16
|
+
# <%= sync partial: 'todo', collection: todos %>
|
17
|
+
#
|
18
|
+
def sync(options = {})
|
19
|
+
collection = options[:collection] || [options.fetch(:resource)]
|
20
|
+
scope = options[:channel] || options[:scope] || (collection.is_a?(RenderSync::Scope) ? collection : nil)
|
21
|
+
partial_name = options.fetch(:partial, scope)
|
22
|
+
refetch = options.fetch(:refetch, false)
|
23
|
+
|
24
|
+
results = []
|
25
|
+
collection.each do |resource|
|
26
|
+
if refetch
|
27
|
+
partial = RefetchPartial.new(partial_name, resource, scope, self)
|
28
|
+
else
|
29
|
+
partial = Partial.new(partial_name, resource, scope, self)
|
30
|
+
end
|
31
|
+
results << "
|
32
|
+
<script type='text/javascript' data-sync-id='#{partial.selector_start}'>
|
33
|
+
RenderSync.onReady(function(){
|
34
|
+
var partial = new RenderSync.Partial({
|
35
|
+
name: '#{partial.name}',
|
36
|
+
resourceName: '#{partial.resource.name}',
|
37
|
+
resourceId: '#{resource.id}',
|
38
|
+
authToken: '#{partial.refetch_auth_token}',
|
39
|
+
channelUpdate: '#{partial.channel_for_action(:update)}',
|
40
|
+
channelDestroy: '#{partial.channel_for_action(:destroy)}',
|
41
|
+
selectorStart: '#{partial.selector_start}',
|
42
|
+
selectorEnd: '#{partial.selector_end}',
|
43
|
+
refetch: #{refetch}
|
44
|
+
});
|
45
|
+
partial.subscribe();
|
46
|
+
});
|
47
|
+
</script>
|
48
|
+
".squish.html_safe
|
49
|
+
results << partial.render
|
50
|
+
results << "
|
51
|
+
<script type='text/javascript' data-sync-id='#{partial.selector_end}'>
|
52
|
+
</script>
|
53
|
+
".squish.html_safe
|
54
|
+
end
|
55
|
+
|
56
|
+
safe_join(results)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Setup listener for new resource from sync_new channel, appending
|
60
|
+
# partial in place
|
61
|
+
#
|
62
|
+
# options - The Hash of options
|
63
|
+
# partial - The String partial filename without leading underscore
|
64
|
+
# resource - The ActiveModel resource
|
65
|
+
# scope - The ActiveModel resource to scope the new channel publishes to.
|
66
|
+
# Used for restricting new resource publishes to 'owner' models.
|
67
|
+
# ie, current_user, project, group, etc. When excluded, listens
|
68
|
+
# for global resource creates.
|
69
|
+
#
|
70
|
+
# direction - The String/Symbol direction to insert rendered partials.
|
71
|
+
# One of :append, :prepend. Defaults to :append
|
72
|
+
#
|
73
|
+
# Examples
|
74
|
+
# <%= sync_new partial: 'todo', resource: Todo.new, scope: @project %>
|
75
|
+
# <%= sync_new partial: 'todo', resource: Todo.new, scope: @project, direction: :prepend %>
|
76
|
+
#
|
77
|
+
def sync_new(options = {})
|
78
|
+
partial_name = options.fetch(:partial)
|
79
|
+
scope = options[:scope]
|
80
|
+
direction = options.fetch :direction, 'append'
|
81
|
+
refetch = options.fetch(:refetch, false)
|
82
|
+
resource = scope.is_a?(RenderSync::Scope) ? scope.new : options.fetch(:resource)
|
83
|
+
|
84
|
+
if refetch
|
85
|
+
creator = RefetchPartialCreator.new(partial_name, resource, scope, self)
|
86
|
+
else
|
87
|
+
creator = PartialCreator.new(partial_name, resource, scope, self)
|
88
|
+
end
|
89
|
+
"
|
90
|
+
<script type='text/javascript' data-sync-id='#{creator.selector}'>
|
91
|
+
RenderSync.onReady(function(){
|
92
|
+
var creator = new RenderSync.PartialCreator({
|
93
|
+
name: '#{partial_name}',
|
94
|
+
resourceName: '#{creator.resource.name}',
|
95
|
+
channel: '#{creator.channel}',
|
96
|
+
selector: '#{creator.selector}',
|
97
|
+
direction: '#{direction}',
|
98
|
+
refetch: #{refetch}
|
99
|
+
});
|
100
|
+
creator.subscribe();
|
101
|
+
});
|
102
|
+
</script>
|
103
|
+
".html_safe
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/test/dummy/Rakefile
ADDED