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,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/404.html -->
52
+ <div class="dialog">
53
+ <h1>The page you were looking for doesn't exist.</h1>
54
+ <p>You may have mistyped the address or the page may have moved.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/422.html -->
52
+ <div class="dialog">
53
+ <h1>The change you wanted was rejected.</h1>
54
+ <p>Maybe you tried to change something you didn't have access to.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/500.html -->
52
+ <div class="dialog">
53
+ <h1>We're sorry, but something went wrong.</h1>
54
+ </div>
55
+ <p>If you are the application owner check the logs for more information.</p>
56
+ </body>
57
+ </html>
File without changes
@@ -0,0 +1,100 @@
1
+ # Copyright (c) 2011 Pete Higgins
2
+ # https://github.com/phiggins/em-minitest-spec
3
+ #
4
+ require 'eventmachine'
5
+
6
+ module EM # :nodoc:
7
+ module MiniTest # :nodoc:
8
+ module Spec # :nodoc
9
+ VERSION = '1.1.0' # :nodoc:
10
+
11
+ ##
12
+ # +wait+ indicates that the spec is not expected to be completed when
13
+ # the block is finished running. A call to +done+ is required when using
14
+ # +wait+.
15
+ #
16
+ # # setup my spec to use EM::MiniTest::Spec
17
+ # describe MyClass do
18
+ # include EM::MiniTest::Spec
19
+ #
20
+ # # The call to defer will return immediately, so the spec code
21
+ # # needs to keep running until callback is called.
22
+ # it "does some async things" do
23
+ # defer_me = lambda do
24
+ # # some async stuff
25
+ # end
26
+ #
27
+ # callback = lambda do
28
+ # done!
29
+ # end
30
+ #
31
+ # EM.defer defer_me, callback
32
+ #
33
+ # wait!
34
+ # end
35
+ def wait
36
+ @wait = true
37
+ end
38
+ alias wait! wait
39
+
40
+ ##
41
+ # Indicates that an async spec is finished. See +wait+ for example usage.
42
+ def done
43
+ EM.cancel_timer(@timeout)
44
+ EM.stop
45
+ end
46
+ alias done! done
47
+
48
+ ##
49
+ # A helper method for the use case of waiting for some operation to
50
+ # complete that is not necessarily under the control of the spec code.
51
+ #
52
+ # # These are exactly equivalent
53
+ # it "waits with the helper" do
54
+ # wait_for do
55
+ # assert true
56
+ # end
57
+ # end
58
+ #
59
+ # it "waits manually" do
60
+ # EM.next_tick do
61
+ # assert true
62
+ # done!
63
+ # end
64
+ #
65
+ # wait!
66
+ # end
67
+ def wait_for
68
+ EM.next_tick do
69
+ yield
70
+ done!
71
+ end
72
+
73
+ wait!
74
+ end
75
+
76
+ def self.included base # :nodoc:
77
+ base.extend(ClassMethods)
78
+ end
79
+
80
+ module ClassMethods # :nodoc:
81
+ def it *args, &block # :nodoc:
82
+ return super unless block_given?
83
+
84
+ super do
85
+ @wait = false
86
+
87
+ EM.run do
88
+ @timeout = EM.add_timer(0.1) do
89
+ flunk "test timed out!"
90
+ end
91
+
92
+ instance_eval(&block)
93
+ done! unless @wait
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,6 @@
1
+ # Faye
2
+ test:
3
+ server: "http://localhost:9292/faye"
4
+ adapter_javascript_url: "http://localhost:9292/faye/faye.js"
5
+ adapter: "Faye"
6
+ async: false
@@ -0,0 +1,7 @@
1
+ # Faye
2
+ test:
3
+ server: "http://localhost:9292/faye"
4
+ adapter_javascript_url: "http://localhost:9292/faye/faye.js"
5
+ auth_token: <%= "erb secret" %>
6
+ adapter: "Faye"
7
+ async: false
@@ -0,0 +1,7 @@
1
+ # Faye
2
+ test:
3
+ server: "http://localhost:9292/faye"
4
+ adapter_javascript_url: "http://localhost:9292/faye/faye.js"
5
+ auth_token: "secret"
6
+ adapter: "Faye"
7
+ async: false
@@ -0,0 +1,8 @@
1
+ # Pusher
2
+ test:
3
+ adapter_javascript_url: "http://js.pusher.com/2.0/pusher.min.js"
4
+ app_id: "YOUR_PUSHER_APP_ID"
5
+ api_key: "YOUR_PUSHER_API_KEY"
6
+ auth_token: "YOUR_PUSHER_SECRET"
7
+ adapter: "Pusher"
8
+ async: false
@@ -0,0 +1,3 @@
1
+ class Group < ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,2 @@
1
+ class Project < ActiveRecord::Base
2
+ end
@@ -0,0 +1,8 @@
1
+ class Todo < ActiveRecord::Base
2
+ belongs_to :user
3
+
4
+ sync :all
5
+ sync_scope :complete, -> { where(complete: true) }
6
+ sync_scope :by_user, ->(user) { where(user_id: user.id) }
7
+ sync_scope :with_user_id, ->(user_id) { where(user_id: user_id) }
8
+ end
@@ -0,0 +1,82 @@
1
+ class User < ActiveRecord::Base
2
+ belongs_to :group
3
+
4
+ def id
5
+ 1
6
+ end
7
+
8
+ sync :all
9
+ sync_scope :cool, -> { where(cool: true) }
10
+ sync_scope :in_group, ->(group) { where(group_id: group.id)}
11
+ sync_scope :with_group_id, ->(group_id) { where(group_id: group_id)}
12
+ sync_scope :with_min_age_in_group, ->(age, group_id) { where(group_id: group_id).where(["age >= ?", age])}
13
+ end
14
+
15
+ class UserWithoutScopes < ActiveRecord::Base
16
+ self.table_name = :users
17
+
18
+ def id
19
+ 1
20
+ end
21
+
22
+ sync :all
23
+ end
24
+
25
+ class UserWithDefaultScope < ActiveRecord::Base
26
+ self.table_name = :users
27
+ belongs_to :group
28
+
29
+ sync :all, default_scope: :group
30
+ end
31
+
32
+ class UserWithSimpleScope < ActiveRecord::Base
33
+ self.table_name = :users
34
+
35
+ sync :all
36
+ sync_scope :old, -> { where(["users.age >= ?", 90]) }
37
+ end
38
+
39
+ class UserWithAdvancedScope < ActiveRecord::Base
40
+ self.table_name = :users
41
+ belongs_to :group
42
+
43
+ sync :all
44
+ sync_scope :in_group, ->(group) { where(group_id: group.id) }
45
+ end
46
+
47
+ class UserTouchingGroup < ActiveRecord::Base
48
+ self.table_name = :users
49
+ belongs_to :group
50
+
51
+ sync :all
52
+ sync_touch :group
53
+ end
54
+
55
+ class UserJustTouchingGroup < ActiveRecord::Base
56
+ self.table_name = :users
57
+ belongs_to :group
58
+
59
+ sync_touch :group
60
+ end
61
+
62
+ class UserTouchingGroupAndProject < ActiveRecord::Base
63
+ self.table_name = :users
64
+ belongs_to :group
65
+ belongs_to :project
66
+
67
+ sync :all
68
+ sync_touch :group, :project
69
+ end
70
+
71
+ # Setup test user with protected attributes (only allow cool)
72
+ # if Rails < 4 or Rails > 4 with gem protected_attributes
73
+ class UserWithProtectedAttributes < ActiveRecord::Base
74
+ attr_accessible :cool if Rails.version < "4"
75
+ self.table_name = :users
76
+ sync :all
77
+ end
78
+
79
+ if Rails.version < "4"
80
+ UserWithProtectedAttributes.mass_assignment_sanitizer = :strict
81
+ end
82
+
@@ -0,0 +1,3 @@
1
+ class ActionController::Base
2
+ prepend_view_path 'test/sync/views'
3
+ end
@@ -0,0 +1,82 @@
1
+ require_relative '../test_helper'
2
+ require 'mocha/setup'
3
+ require 'rails/all'
4
+
5
+ describe RenderSync::Action do
6
+ include TestHelper
7
+
8
+ describe '#initialize' do
9
+ it 'sets instance variables on initialize without scopes' do
10
+ action = RenderSync::Action.new("record", :new)
11
+ assert_equal "record", action.record
12
+ assert_equal :new, action.name
13
+ assert_equal [], action.scope
14
+ end
15
+
16
+ it 'sets instance variables on initialize with scope' do
17
+ action = RenderSync::Action.new("record", :new, scope: "scope")
18
+ assert_equal ["scope"], action.scope
19
+ end
20
+
21
+ it 'sets instance variables on initialize with default_scope' do
22
+ action = RenderSync::Action.new("record", :new, default_scope: "scope")
23
+ assert_equal ["scope"], action.scope
24
+ end
25
+
26
+ it 'sets instance variables on initialize with scope and default_scope' do
27
+ action = RenderSync::Action.new("record", :new, scope: "scope", default_scope: "default")
28
+ assert_equal ["default", "scope"], action.scope
29
+ end
30
+
31
+ it 'sets instance variables on initialize with default_scope' do
32
+ user = User.create!
33
+ scope = User.cool
34
+ action = RenderSync::Action.new(user, :new, scope: scope, default_scope: :group)
35
+ assert_equal [:group, scope], action.scope
36
+ end
37
+
38
+
39
+ it 'sets instance variables on initialize with nested scopes' do
40
+ action = RenderSync::Action.new("record", :new, scope: ["nested", "scopes"], default_scope: ["my", "cool"])
41
+ assert_equal [["my", "cool"], ["nested", "scopes"]], action.scope
42
+ end
43
+ end
44
+
45
+ describe "#perform" do
46
+
47
+ it 'calls different actions without scope' do
48
+ record = User.new name: "Foo"
49
+
50
+ action = RenderSync::Action.new(record, :new)
51
+ action.expects(:sync_new).with(record, scope: [])
52
+ action.perform
53
+
54
+ action = RenderSync::Action.new(record, :update)
55
+ action.expects(:sync_update).with(record, scope: [])
56
+ action.perform
57
+
58
+ action = RenderSync::Action.new(record, :destroy)
59
+ action.expects(:sync_destroy).with(record, scope: [])
60
+ action.perform
61
+ end
62
+
63
+ it 'calls different actions with scope' do
64
+ record = User.new name: "Foo"
65
+
66
+ action = RenderSync::Action.new(record, :new, scope: "scope", default_scope: "default")
67
+ action.expects(:sync_new).with(record, scope: ["default", "scope"])
68
+ action.perform
69
+
70
+ action = RenderSync::Action.new(record, :update, scope: "scope", default_scope: "default")
71
+ action.expects(:sync_update).with(record, scope: ["default", "scope"])
72
+ action.perform
73
+
74
+ action = RenderSync::Action.new(record, :destroy, scope: "scope", default_scope: "default")
75
+ action.expects(:sync_destroy).with(record, scope: ["default", "scope"])
76
+ action.perform
77
+ end
78
+
79
+
80
+ end
81
+
82
+ end