render_sync 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +153 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +22 -0
  5. data/README.md +521 -0
  6. data/Rakefile +9 -0
  7. data/app/assets/javascripts/sync.coffee +355 -0
  8. data/app/controllers/sync/refetches_controller.rb +56 -0
  9. data/app/helpers/render_sync/config_helper.rb +15 -0
  10. data/config/routes.rb +3 -0
  11. data/config/sync.yml +21 -0
  12. data/lib/generators/render_sync/install_generator.rb +14 -0
  13. data/lib/generators/render_sync/templates/sync.ru +14 -0
  14. data/lib/generators/render_sync/templates/sync.yml +34 -0
  15. data/lib/render_sync.rb +174 -0
  16. data/lib/render_sync/action.rb +39 -0
  17. data/lib/render_sync/actions.rb +114 -0
  18. data/lib/render_sync/channel.rb +23 -0
  19. data/lib/render_sync/clients/dummy.rb +22 -0
  20. data/lib/render_sync/clients/faye.rb +104 -0
  21. data/lib/render_sync/clients/pusher.rb +77 -0
  22. data/lib/render_sync/controller_helpers.rb +33 -0
  23. data/lib/render_sync/engine.rb +24 -0
  24. data/lib/render_sync/erb_tracker.rb +49 -0
  25. data/lib/render_sync/faye_extension.rb +45 -0
  26. data/lib/render_sync/model.rb +174 -0
  27. data/lib/render_sync/model_actions.rb +60 -0
  28. data/lib/render_sync/model_change_tracking.rb +97 -0
  29. data/lib/render_sync/model_syncing.rb +65 -0
  30. data/lib/render_sync/model_touching.rb +35 -0
  31. data/lib/render_sync/partial.rb +112 -0
  32. data/lib/render_sync/partial_creator.rb +47 -0
  33. data/lib/render_sync/reactor.rb +48 -0
  34. data/lib/render_sync/refetch_model.rb +21 -0
  35. data/lib/render_sync/refetch_partial.rb +43 -0
  36. data/lib/render_sync/refetch_partial_creator.rb +21 -0
  37. data/lib/render_sync/renderer.rb +19 -0
  38. data/lib/render_sync/resource.rb +115 -0
  39. data/lib/render_sync/scope.rb +113 -0
  40. data/lib/render_sync/scope_definition.rb +30 -0
  41. data/lib/render_sync/view_helpers.rb +106 -0
  42. data/test/dummy/README.rdoc +28 -0
  43. data/test/dummy/Rakefile +6 -0
  44. data/test/dummy/app/assets/javascripts/application.js +13 -0
  45. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  46. data/test/dummy/app/controllers/application_controller.rb +5 -0
  47. data/test/dummy/app/helpers/application_helper.rb +2 -0
  48. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  49. data/test/dummy/app/views/sync/users/_show.html.erb +1 -0
  50. data/test/dummy/app/views/sync/users/refetch/_show.html.erb +1 -0
  51. data/test/dummy/bin/bundle +3 -0
  52. data/test/dummy/bin/rails +4 -0
  53. data/test/dummy/bin/rake +4 -0
  54. data/test/dummy/config.ru +4 -0
  55. data/test/dummy/config/application.rb +22 -0
  56. data/test/dummy/config/boot.rb +5 -0
  57. data/test/dummy/config/database.yml +8 -0
  58. data/test/dummy/config/environment.rb +5 -0
  59. data/test/dummy/config/environments/development.rb +29 -0
  60. data/test/dummy/config/environments/production.rb +80 -0
  61. data/test/dummy/config/environments/test.rb +36 -0
  62. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  63. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  64. data/test/dummy/config/initializers/inflections.rb +16 -0
  65. data/test/dummy/config/initializers/mime_types.rb +5 -0
  66. data/test/dummy/config/initializers/secret_token.rb +12 -0
  67. data/test/dummy/config/initializers/session_store.rb +3 -0
  68. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  69. data/test/dummy/config/locales/en.yml +23 -0
  70. data/test/dummy/config/routes.rb +56 -0
  71. data/test/dummy/log/test.log +626 -0
  72. data/test/dummy/public/404.html +58 -0
  73. data/test/dummy/public/422.html +58 -0
  74. data/test/dummy/public/500.html +57 -0
  75. data/test/dummy/public/favicon.ico +0 -0
  76. data/test/em_minitest_spec.rb +100 -0
  77. data/test/fixtures/sync_auth_token_missing.yml +6 -0
  78. data/test/fixtures/sync_erb.yml +7 -0
  79. data/test/fixtures/sync_faye.yml +7 -0
  80. data/test/fixtures/sync_pusher.yml +8 -0
  81. data/test/models/group.rb +3 -0
  82. data/test/models/project.rb +2 -0
  83. data/test/models/todo.rb +8 -0
  84. data/test/models/user.rb +82 -0
  85. data/test/sync/abstract_controller.rb +3 -0
  86. data/test/sync/action_test.rb +82 -0
  87. data/test/sync/channel_test.rb +15 -0
  88. data/test/sync/config_test.rb +25 -0
  89. data/test/sync/erb_tracker_test.rb +72 -0
  90. data/test/sync/faye_extension_test.rb +87 -0
  91. data/test/sync/message_test.rb +159 -0
  92. data/test/sync/model_test.rb +315 -0
  93. data/test/sync/partial_creator_test.rb +35 -0
  94. data/test/sync/partial_test.rb +107 -0
  95. data/test/sync/protected_attributes_test.rb +39 -0
  96. data/test/sync/reactor_test.rb +18 -0
  97. data/test/sync/refetch_model_test.rb +26 -0
  98. data/test/sync/refetch_partial_creator_test.rb +16 -0
  99. data/test/sync/refetch_partial_test.rb +74 -0
  100. data/test/sync/renderer_test.rb +19 -0
  101. data/test/sync/resource_test.rb +181 -0
  102. data/test/sync/scope_definition_test.rb +39 -0
  103. data/test/sync/scope_test.rb +113 -0
  104. data/test/test_helper.rb +66 -0
  105. data/test/travis/sync.ru +14 -0
  106. data/test/travis/sync.yml +21 -0
  107. metadata +317 -0
@@ -0,0 +1,15 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe RenderSync::Channel do
4
+ include TestHelper
5
+
6
+ describe '#signature' do
7
+ before do
8
+ @channel = RenderSync::Channel.new("testing")
9
+ end
10
+
11
+ it "Creates uniqe HMAC signature" do
12
+ assert_equal 40, @channel.signature.length
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe RenderSync.config do
4
+ before do
5
+ RenderSync.load_config(
6
+ File.expand_path("../../fixtures/sync_erb.yml", __FILE__),
7
+ "test"
8
+ )
9
+ end
10
+
11
+ describe "#load_config" do
12
+ it "Evaluates ERB from the config file" do
13
+ assert_equal("erb secret", RenderSync.config[:auth_token])
14
+ end
15
+
16
+ it "raises an exception if auth_token is missing" do
17
+ assert_raises ArgumentError do
18
+ RenderSync.load_config(
19
+ File.expand_path("../../fixtures/sync_auth_token_missing.yml", __FILE__),
20
+ "test"
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ require_relative '../test_helper'
2
+ require 'rails/all'
3
+ require 'active_support/core_ext/array/access'
4
+ require 'render_sync/erb_tracker'
5
+
6
+ describe RenderSync::ERBTracker do
7
+ Template = Struct.new(:source)
8
+ it 'tracks collection partials' do
9
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
10
+ <%= sync partial: 'item', collection: something.things %>
11
+ TEMPLATE
12
+
13
+ assert_equal ['sync/things/item'], dependencies
14
+ end
15
+
16
+ it 'tracks collection instance variable partials' do
17
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
18
+ <%= sync partial: 'item', collection: @something.things %>
19
+ TEMPLATE
20
+
21
+ assert_equal ['sync/things/item'], dependencies
22
+ end
23
+
24
+ it 'tracks resource partials' do
25
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
26
+ <%= sync partial: 'item', resource: thing %>
27
+ TEMPLATE
28
+
29
+ assert_equal ['sync/things/item'], dependencies
30
+ end
31
+
32
+ it 'tracks sync_new partials' do
33
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
34
+ <%= sync_new partial: 'item', resource: thing %>
35
+ TEMPLATE
36
+
37
+ assert_equal ['sync/things/item'], dependencies
38
+ end
39
+
40
+ it 'tracks multiple sync partials' do
41
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
42
+ <%= sync partial: 'item', resource: thing %>
43
+ <%= sync partial: 'other_item', resource: rock %>
44
+ TEMPLATE
45
+
46
+ assert_equal ['sync/things/item', 'sync/rocks/other_item'], dependencies
47
+ end
48
+
49
+ it 'tracks resource instance variable partials' do
50
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
51
+ <%= sync partial: 'item', resource: @thing %>
52
+ TEMPLATE
53
+
54
+ assert_equal ['sync/things/item'], dependencies
55
+ end
56
+
57
+ it 'tracks haml resource partials' do
58
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
59
+ =sync partial: 'item', resource: thing
60
+ TEMPLATE
61
+
62
+ assert_equal ['sync/things/item'], dependencies
63
+ end
64
+
65
+ it 'tracks regular renders too' do
66
+ dependencies = RenderSync::ERBTracker.call "name", Template.new(<<-TEMPLATE)
67
+ <%= render partial: 'things/item', collection: things %>
68
+ TEMPLATE
69
+
70
+ assert_equal ['things/item'], dependencies
71
+ end
72
+ end
@@ -0,0 +1,87 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe RenderSync::FayeExtension do
4
+ include TestHelper
5
+
6
+ before do
7
+ @server = RenderSync::FayeExtension.new
8
+ @message = {
9
+ "channel" => "/channel",
10
+ "message" => "HTML",
11
+ "ext" => {"auth_token" => "secret"}
12
+ }
13
+ @unauthed_message = {
14
+ "channel" => "/channel",
15
+ "message" => "HTML",
16
+ "ext" => {"auth_token" => "WRONG"}
17
+ }
18
+ @batched_message = {
19
+ "data" => [{
20
+ "channel" => "/channel",
21
+ "message" => "HTML",
22
+ "ext" => {"auth_token" => "secret"}
23
+ },
24
+ {
25
+ "channel" => "/channel",
26
+ "message" => "HTML",
27
+ "ext" => {"auth_token" => "secret"}
28
+ }]
29
+ }
30
+ end
31
+
32
+ describe 'incoming messages' do
33
+ describe 'message authentication' do
34
+ describe 'with valid auth_token' do
35
+ it 'should be valid' do
36
+ assert @server.message_authenticated?(@message)
37
+ end
38
+ end
39
+
40
+ describe 'with invalid auth_token' do
41
+ it 'should be invalid' do
42
+ refute @server.message_authenticated?(@unauthed_message)
43
+ end
44
+
45
+ it 'should add error to message before callback' do
46
+ assert @server.incoming(@unauthed_message, Proc.new{|msg| msg["error"]})
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'outgoing messages' do
53
+ it 'should strip out message auth_token to prevent auth_token leak' do
54
+ assert_equal nil, @server.outgoing(
55
+ {"ext" => {"auth_token" => "secret"}},
56
+ Proc.new{|message| message["ext"]["auth_token"] }
57
+ )
58
+ end
59
+ end
60
+
61
+ describe 'batch_incoming' do
62
+ it 'proccesses all batched messages as single message' do
63
+ assert @server.batch_incoming(@batched_message, Proc.new{})
64
+ end
65
+ end
66
+
67
+ describe 'single_incoming(message, callback)' do
68
+ end
69
+
70
+ describe 'batch_publish?' do
71
+ describe 'with batched messages' do
72
+ it 'should be true' do
73
+ assert @server.batch_publish?({
74
+ 'channel' => '/batch_publish'
75
+ })
76
+ end
77
+ end
78
+
79
+ describe 'with single message' do
80
+ it 'should be false' do
81
+ refute @server.batch_publish?({
82
+ 'channel' => '/some-channel'
83
+ })
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,159 @@
1
+ require_relative '../test_helper'
2
+ require 'mocha/setup'
3
+
4
+
5
+ describe "Faye" do
6
+ include TestHelperFaye
7
+
8
+ before do
9
+ @message = RenderSync.client.build_message("/my-channel", html: "<p>Some Data</p>")
10
+ end
11
+
12
+ describe "normalize_channel" do
13
+ it 'converts channel to faye server friendly format with leading forward-slash' do
14
+ assert_equal "/", RenderSync.client.normalize_channel("alfjalkjfkfjaslkfj2342424")[0]
15
+ end
16
+ end
17
+
18
+ describe '#to_hash' do
19
+ before do
20
+ @message_hash = @message.to_hash
21
+ end
22
+
23
+ it "Converts message to hash for Faye publish" do
24
+ assert @message_hash.keys.include?(:channel)
25
+ assert @message_hash.keys.include?(:data)
26
+ assert @message_hash.keys.include?(:ext)
27
+ end
28
+
29
+ it "Includes auth_token for Faye password security" do
30
+ assert @message_hash[:ext][:auth_token]
31
+ end
32
+ end
33
+
34
+ describe '#to_json' do
35
+ it "Converts message to json for Faye publish" do
36
+ assert @message.to_json
37
+ end
38
+ end
39
+
40
+ describe "asynchronous publishing" do
41
+ include EM::MiniTest::Spec
42
+
43
+ before do
44
+ RenderSync.stubs(:async?).returns true
45
+ end
46
+
47
+ describe "batched message publishing" do
48
+ before do
49
+ @messages = 10.times.collect{|i| RenderSync.client.build_message("/ch#{i}", {html: ""})}
50
+ end
51
+
52
+ it 'should publish array of messages with single post to faye' do
53
+ EM.expects(:next_tick).once.returns true
54
+ assert RenderSync.client.batch_publish(@messages)
55
+ end
56
+ end
57
+
58
+ describe '#publish' do
59
+ it 'Publishes a message to Faye' do
60
+ RenderSync.reactor.expects(:perform).once
61
+ @message.publish
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "synchronous publishing" do
67
+ before do
68
+ Net::HTTP.stubs(:post_form).returns true
69
+ RenderSync.stubs(:async?).returns false
70
+ end
71
+
72
+ describe "batched message publishing" do
73
+ before do
74
+ @messages = 10.times.collect{|i| RenderSync.client.build_message("/ch#{i}", {html: ""})}
75
+ end
76
+
77
+ it 'should publish array of messages with single post to faye' do
78
+ assert RenderSync.client.batch_publish(@messages)
79
+ end
80
+ end
81
+ describe '#publish' do
82
+ it 'Publishes a message to Faye' do
83
+ assert @message.publish
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+
91
+
92
+
93
+ describe "Pusher" do
94
+ include TestHelperPusher
95
+
96
+ before do
97
+ @message = RenderSync.client.build_message("/my-channel", html: "<p>Some Data</p>")
98
+ end
99
+
100
+ describe "normalize_channel" do
101
+ it 'converts channel to pusher server friendly format without leading forward-slash' do
102
+ refute RenderSync.client.normalize_channel("alfjalkjfkfjaslkfj2342424")[0] == "/"
103
+ end
104
+ end
105
+
106
+ describe '#to_json' do
107
+ it "Converts message to json for Faye publish" do
108
+ assert @message.to_json
109
+ end
110
+ end
111
+
112
+ describe "asynchronous publishing" do
113
+ include EM::MiniTest::Spec
114
+
115
+ before do
116
+ RenderSync.stubs(:async?).returns true
117
+ end
118
+
119
+ describe "batched message publishing" do
120
+ before do
121
+ @messages = 10.times.collect{|i| RenderSync.client.build_message("/ch#{i}", {html: ""})}
122
+ end
123
+
124
+ it 'should publish array of messages with single post to faye' do
125
+ assert RenderSync.client.batch_publish(@messages)
126
+ end
127
+ end
128
+
129
+ describe '#publish' do
130
+ it 'Publishes a message to Pusher' do
131
+ RenderSync.reactor.expects(:perform).once
132
+ @message.publish
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "synchronous publishing" do
138
+ before do
139
+ Pusher.stubs(:trigger).returns(true)
140
+ RenderSync.stubs(:async?).returns false
141
+ end
142
+
143
+ describe "batched message publishing" do
144
+ before do
145
+ @messages = 10.times.collect{|i| RenderSync.client.build_message("/ch#{i}", {html: ""})}
146
+ end
147
+
148
+ it 'should publish array of messages with single post to faye' do
149
+ assert RenderSync.client.batch_publish(@messages)
150
+ end
151
+ end
152
+ describe '#publish' do
153
+ it 'Publishes a message to Pusher' do
154
+ assert @message.publish
155
+ end
156
+ end
157
+ end
158
+ end
159
+
@@ -0,0 +1,315 @@
1
+ require_relative '../test_helper'
2
+ require 'mocha/setup'
3
+ require 'rails/all'
4
+
5
+ setup_database
6
+
7
+ describe RenderSync::Model do
8
+
9
+ it 'can is disabled by default' do
10
+ refute RenderSync::Model.enabled?
11
+ end
12
+
13
+ it 'can be enabled and disabled' do
14
+ RenderSync::Model.enable!
15
+ assert RenderSync::Model.enabled?
16
+
17
+ RenderSync::Model.disable!
18
+ refute RenderSync::Model.enabled?
19
+ end
20
+
21
+ it 'can be given a block to have things enabled in' do
22
+ RenderSync::Model.enable do
23
+ assert RenderSync::Model.enabled?
24
+ end
25
+
26
+ refute RenderSync::Model.enabled?
27
+ end
28
+
29
+ describe 'syncing of model changes to all listening channels' do
30
+ it 'publishes record (create/update/destroy) to main new channel' do
31
+ RenderSync::Model.enable do
32
+ user = UserWithoutScopes.new
33
+
34
+ # Create
35
+ user.save!
36
+ assert user.persisted?
37
+ assert_equal 1, user.sync_actions.size
38
+
39
+ assert_equal :new, user.sync_actions[0].name
40
+ assert_equal "/user_without_scopes/#{user.id}", user.sync_actions[0].test_path
41
+
42
+ # Update
43
+ user.update_attributes!(name: "Foo")
44
+ assert user.persisted?
45
+ assert_equal 1, user.sync_actions.size
46
+
47
+ assert_equal :update, user.sync_actions[0].name
48
+ assert_equal "/user_without_scopes/#{user.id}", user.sync_actions[0].test_path
49
+
50
+ # Destroy
51
+ user.destroy
52
+ assert user.destroyed?
53
+ assert_equal 1, user.sync_actions.size
54
+
55
+ assert_equal :destroy, user.sync_actions[0].name
56
+ assert_equal "/user_without_scopes/#{user.id}", user.sync_actions[0].test_path
57
+ end
58
+ end
59
+
60
+ it 'publishes record with default scope to scope channel and parent channel' do
61
+ RenderSync::Model.enable do
62
+
63
+ # Create
64
+ group = Group.create!
65
+ user = UserWithDefaultScope.new(group: group)
66
+ user.save!
67
+
68
+ assert user.persisted?
69
+ assert_equal 1, user.sync_actions.size
70
+
71
+ assert_equal :new, user.sync_actions[0].name
72
+ assert_equal "/groups/#{group.id}/user_with_default_scopes/#{user.id}", user.sync_actions[0].test_path
73
+
74
+ # Update
75
+ user.update_attributes!(name: "Foo")
76
+
77
+ assert user.persisted?
78
+ assert_equal 1, user.sync_actions.size
79
+
80
+ assert_equal :update, user.sync_actions[0].name
81
+ assert_equal "/user_with_default_scopes/#{user.id}", user.sync_actions[0].test_path
82
+
83
+ # Destroy
84
+ user.destroy
85
+
86
+ assert user.destroyed?
87
+ assert_equal 1, user.sync_actions.size
88
+
89
+ assert_equal :destroy, user.sync_actions[0].name
90
+ assert_equal "/groups/#{group.id}/user_with_default_scopes/#{user.id}", user.sync_actions[0].test_path
91
+
92
+ end
93
+ end
94
+
95
+ it 'publishes record with simple named sync scope' do
96
+ RenderSync::Model.enable do
97
+
98
+ # Create user not in scope 'old' (age > 90)
99
+ user = UserWithSimpleScope.create!(age: 85)
100
+
101
+ assert_equal 1, user.sync_actions.size
102
+
103
+ assert_equal :new, user.sync_actions[0].name
104
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
105
+
106
+ # Create user in scope 'old' (age >= 90)
107
+ user = UserWithSimpleScope.new(age: 95)
108
+ user.save!
109
+ assert_equal 2, user.sync_actions.size
110
+
111
+ assert_equal :new, user.sync_actions[0].name
112
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
113
+
114
+ assert_equal :new, user.sync_actions[1].name
115
+ assert_equal "/old/user_with_simple_scopes/#{user.id}", user.sync_actions[1].test_path
116
+
117
+ # Update of independent attribute name (user still in scope 'old')
118
+ user.update_attributes!(name: "Foo")
119
+ assert !user.changed?
120
+ assert_equal 1, user.sync_actions.size
121
+
122
+ assert_equal :update, user.sync_actions[0].name
123
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
124
+
125
+ # Update of dependent attribute age, so that the user no longer falls into scope 'old'
126
+ # and has to be destroyed on the scoped channel
127
+ user.update_attributes!(age: 80)
128
+ assert !user.changed?
129
+ assert_equal 2, user.sync_actions.size
130
+
131
+ assert_equal :update, user.sync_actions[0].name
132
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
133
+
134
+ assert_equal :destroy, user.sync_actions[1].name
135
+ assert_equal "/old/user_with_simple_scopes/#{user.id}", user.sync_actions[1].test_path
136
+
137
+ # Update of dependent attribute age, so that the record will fall into scope 'old'
138
+ # and has to be published as new on that scoped channel
139
+ user.update_attributes(age: 100)
140
+
141
+ assert !user.changed?
142
+ assert_equal 2, user.sync_actions.size
143
+
144
+ assert_equal :update, user.sync_actions[0].name
145
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
146
+
147
+ assert_equal :new, user.sync_actions[1].name
148
+ assert_equal "/old/user_with_simple_scopes/#{user.id}", user.sync_actions[1].test_path
149
+
150
+ # Destroy user currently in scoped by 'old'
151
+ user.destroy
152
+
153
+ assert user.destroyed?
154
+ assert_equal 2, user.sync_actions.size
155
+
156
+ assert_equal :destroy, user.sync_actions[0].name
157
+ assert_equal "/user_with_simple_scopes/#{user.id}", user.sync_actions[0].test_path
158
+
159
+ assert_equal :destroy, user.sync_actions[1].name
160
+ assert_equal "/old/user_with_simple_scopes/#{user.id}", user.sync_actions[1].test_path
161
+
162
+ end
163
+ end
164
+
165
+ it 'publishes record with a named sync scope that takes arguments' do
166
+ RenderSync::Model.enable do
167
+
168
+ # Create user not in scope 'in_group'
169
+ group1 = Group.create
170
+ group2 = Group.create
171
+ user = UserWithAdvancedScope.create!
172
+
173
+ assert user.persisted?
174
+ assert_equal 1, user.sync_actions.size
175
+
176
+ assert_equal :new, user.sync_actions[0].name
177
+ assert_equal "/user_with_advanced_scopes/#{user.id}", user.sync_actions[0].test_path
178
+
179
+ # Create user in scope 'in_group'
180
+ user = UserWithAdvancedScope.create!(group: group1)
181
+
182
+ assert user.persisted?
183
+ assert_equal 2, user.sync_actions.size
184
+
185
+ assert_equal :new, user.sync_actions[0].name
186
+ assert_equal "/user_with_advanced_scopes/#{user.id}", user.sync_actions[0].test_path
187
+
188
+ assert_equal :new, user.sync_actions[1].name
189
+ assert_equal "/in_group/group/#{group1.id}/user_with_advanced_scopes/#{user.id}", user.sync_actions[1].test_path
190
+
191
+ # Change group
192
+ user.update_attributes(group: group2)
193
+
194
+ assert user.persisted?
195
+ assert_equal 3, user.sync_actions.size
196
+
197
+ assert_equal :update, user.sync_actions[0].name
198
+ assert_equal "/user_with_advanced_scopes/#{user.id}", user.sync_actions[0].test_path
199
+
200
+ assert_equal :destroy, user.sync_actions[1].name
201
+ assert_equal "/in_group/group/#{group1.id}/user_with_advanced_scopes/#{user.id}", user.sync_actions[1].test_path
202
+
203
+ assert_equal :new, user.sync_actions[2].name
204
+ assert_equal "/in_group/group/#{group2.id}/user_with_advanced_scopes/#{user.id}", user.sync_actions[2].test_path
205
+
206
+ end
207
+ end
208
+
209
+ end
210
+
211
+ describe "touching associated records explicitly" do
212
+ it 'unsyncd user touches single association if configured' do
213
+ RenderSync::Model.enable do
214
+ group1 = Group.create
215
+ group2 = Group.create
216
+ user = UserJustTouchingGroup.create!(group: group1)
217
+
218
+ assert user.persisted?
219
+ assert_equal 1, user.sync_actions.size
220
+
221
+ assert_equal :update, user.sync_actions[0].name
222
+ assert_equal "/groups/#{group1.id}", user.sync_actions[0].test_path
223
+
224
+ user.group = group2
225
+ user.save!
226
+
227
+ assert !user.changed?
228
+ assert_equal 2, user.sync_actions.size
229
+
230
+ assert_equal :update, user.sync_actions[0].name
231
+ assert_equal "/groups/#{group2.id}", user.sync_actions[0].test_path
232
+
233
+ assert_equal :update, user.sync_actions[1].name
234
+ assert_equal "/groups/#{group1.id}", user.sync_actions[1].test_path
235
+
236
+ user.group = nil
237
+ user.save!
238
+
239
+ assert !user.changed?
240
+ assert_equal 1, user.sync_actions.size
241
+
242
+ assert_equal :update, user.sync_actions[0].name
243
+ assert_equal "/groups/#{group2.id}", user.sync_actions[0].test_path
244
+ end
245
+ end
246
+
247
+ it 'syncd user touches single association if configured' do
248
+ RenderSync::Model.enable do
249
+ group1 = Group.create
250
+ group2 = Group.create
251
+ user = UserTouchingGroup.create!(group: group1)
252
+
253
+ assert user.persisted?
254
+ assert_equal 2, user.sync_actions.size
255
+
256
+ assert_equal :update, user.sync_actions[1].name
257
+ assert_equal "/groups/#{group1.id}", user.sync_actions[1].test_path
258
+
259
+ user.group = group2
260
+ user.save!
261
+
262
+ assert !user.changed?
263
+ assert_equal 3, user.sync_actions.size
264
+
265
+ assert_equal :update, user.sync_actions[1].name
266
+ assert_equal "/groups/#{group2.id}", user.sync_actions[1].test_path
267
+
268
+ assert_equal :update, user.sync_actions[2].name
269
+ assert_equal "/groups/#{group1.id}", user.sync_actions[2].test_path
270
+
271
+ user.group = nil
272
+ user.save!
273
+
274
+ assert !user.changed?
275
+ assert_equal 2, user.sync_actions.size
276
+
277
+ assert_equal :update, user.sync_actions[1].name
278
+ assert_equal "/groups/#{group2.id}", user.sync_actions[1].test_path
279
+ end
280
+ end
281
+
282
+ it 'touches multiple associations if configured' do
283
+ RenderSync::Model.enable do
284
+ group = Group.create
285
+ project = Project.create
286
+ user = UserTouchingGroupAndProject.create!(group: group, project: project)
287
+
288
+ assert user.persisted?
289
+ assert_equal 3, user.sync_actions.size
290
+
291
+ assert_equal :update, user.sync_actions[1].name
292
+ assert_equal "/groups/#{group.id}", user.sync_actions[1].test_path
293
+
294
+ assert_equal :update, user.sync_actions[2].name
295
+ assert_equal "/projects/#{project.id}", user.sync_actions[2].test_path
296
+
297
+ end
298
+ end
299
+ end
300
+
301
+
302
+ it 'does not have a sync default scope if it is not specified' do
303
+ user = User.new name: "Foo"
304
+ assert user.sync_default_scope.nil?
305
+ end
306
+
307
+ it 'does not sync if sync is not enabled' do
308
+ user = UserWithSimpleScope.new name: "Foo"
309
+ user.stubs(:publish_actions)
310
+
311
+ user.expects(:publish_actions).never
312
+ user.save!
313
+ end
314
+
315
+ end