render_sync 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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