hyper-mesh 0.5.3 → 0.5.4

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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +7 -2
  4. data/README.md +112 -87
  5. data/Rakefile +6 -1
  6. data/docs/action_cable_quickstart.md +20 -16
  7. data/docs/activerecord_api.md +23 -17
  8. data/docs/authorization-policies.md +45 -35
  9. data/docs/client_side_scoping.md +5 -5
  10. data/docs/configuration_details.md +6 -46
  11. data/docs/pusher_faker_quickstart.md +7 -68
  12. data/docs/pusher_quickstart.md +7 -68
  13. data/docs/simple_poller_quickstart.md +6 -67
  14. data/docs/todo-example.md +2 -2
  15. data/docs/word_game.md +3 -1
  16. data/docs/words-example.md +2 -3
  17. data/examples/action-cable/Gemfile +2 -1
  18. data/examples/action-cable/Gemfile.lock +73 -54
  19. data/examples/action-cable/config/initializers/{hyper_mesh.rb → hyperloop.rb} +1 -1
  20. data/examples/action-cable/config/routes.rb +1 -1
  21. data/hyper-mesh.gemspec +10 -4
  22. data/lib/active_record_base.rb +3 -3
  23. data/{examples/action-cable-production-mode/log/.keep → lib/acts_as_string.rb} +0 -0
  24. data/lib/hyper-mesh.rb +11 -19
  25. data/lib/hyper_mesh/version.rb +3 -0
  26. data/lib/hypermesh/version.rb +1 -1
  27. data/lib/reactive_record/active_record/class_methods.rb +10 -3
  28. data/lib/reactive_record/active_record/instance_methods.rb +8 -0
  29. data/lib/reactive_record/active_record/public_columns_hash.rb +8 -2
  30. data/lib/reactive_record/active_record/reactive_record/collection.rb +0 -1
  31. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +2 -1
  32. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +55 -63
  33. data/lib/reactive_record/active_record/reactive_record/operations.rb +51 -0
  34. data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +3 -3
  35. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +93 -84
  36. data/lib/reactive_record/broadcast.rb +183 -0
  37. data/lib/reactive_record/permissions.rb +2 -2
  38. data/reactive_record_test_app/Gemfile +6 -2
  39. data/reactive_record_test_app/Gemfile.lock +120 -60
  40. data/reactive_record_test_app/app/assets/javascripts/application.rb +3 -5
  41. data/reactive_record_test_app/app/assets/javascripts/bigdecimal.rb +1 -0
  42. data/reactive_record_test_app/app/assets/javascripts/reactive_record_config.js +2 -2
  43. data/reactive_record_test_app/app/controllers/application_controller.rb +3 -3
  44. data/reactive_record_test_app/app/controllers/home_controller.rb +1 -1
  45. data/reactive_record_test_app/app/models/models.rb.erb +6 -0
  46. data/reactive_record_test_app/config/application.rb +2 -0
  47. data/reactive_record_test_app/config/environments/development.rb +1 -1
  48. data/reactive_record_test_app/config/routes.rb +1 -2
  49. data/reactive_record_test_app/db/seeds.rb +6 -0
  50. data/reactive_record_test_app/script/rails +0 -0
  51. data/reactive_record_test_app/spec-opal/active-record/rendering_spec.rb +11 -2
  52. data/reactive_record_test_app/spec-opal/active-record/save_spec.rb +3 -4
  53. data/reactive_record_test_app/spec-opal/spec_helper.js.rb +1 -1
  54. data/reactive_record_test_app/spec-opal/test_spec.rb +7 -0
  55. data/reactive_record_test_app/spec_dont_run/README.md +7 -0
  56. data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/permissions_spec.rb +0 -0
  57. data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/prerendering_spec.rb +1 -0
  58. data/spec/{synchromesh/aaa-unit_tests/connection_spec.rb → batch1/aaa-unit_tests/connection_movedspec.rb} +0 -0
  59. data/spec/{synchromesh → batch1}/aaa-unit_tests/dummy_value_spec.rb +2 -2
  60. data/spec/{synchromesh → batch1}/column_types/column_type_spec.rb +2 -2
  61. data/spec/{synchromesh → batch1}/crud_access_regulation/broadcast_controls_access_spec.rb +1 -1
  62. data/spec/{synchromesh → batch1}/crud_access_regulation/model_policies_spec.rb +6 -6
  63. data/spec/batch1/misc/access_like_hash_spec.rb +43 -0
  64. data/spec/batch1/misc/while_loading_spec.rb +196 -0
  65. data/spec/{synchromesh → batch1}/policies/regulate_all_broadcasts_spec.rb +12 -12
  66. data/spec/{synchromesh → batch1}/policies/regulate_broadcast_spec.rb +25 -25
  67. data/spec/{synchromesh/integration → batch2}/authorization_spec.rb +8 -7
  68. data/spec/{synchromesh/integration → batch2}/default_scope_spec.rb +2 -2
  69. data/spec/{synchromesh/integration → batch2}/has_many_through_spec.rb +2 -2
  70. data/spec/{synchromesh/integration → batch2}/relationships_spec.rb +3 -3
  71. data/spec/{reactive_record → batch3}/auto_load_itself_spec.rb +1 -1
  72. data/spec/{reactive_record → batch3}/edge_cases_spec.rb +1 -1
  73. data/spec/{reactive_record → batch3}/finder_method_spec.rb +1 -1
  74. data/spec/{reactive_record → batch3}/many_to_many_spec.rb +2 -2
  75. data/spec/{reactive_record → batch3}/pry_rescue_xspec.rb +0 -0
  76. data/{examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css → spec/batch3/readme.txt} +0 -0
  77. data/spec/{reactive_record → batch3}/revert_spec.rb +2 -2
  78. data/spec/{reactive_record → batch3}/save_while_loading_spec.rb +1 -1
  79. data/spec/{reactive_record → batch3}/update_associations_spec.rb +2 -2
  80. data/spec/{reactive_record → batch3}/update_scopes_spec.rb +2 -2
  81. data/spec/{synchromesh/integration → batch4}/saving_during_commit_spec.rb +2 -2
  82. data/spec/{synchromesh/integration → batch4}/scope_spec.rb +30 -2
  83. data/spec/{synchromesh/examples → batch4}/scoped_todos_spec.rb +3 -3
  84. data/spec/{synchromesh/integration → batch4}/synchromesh_spec.rb +2 -2
  85. data/spec/{synchromesh/examples → examples}/dictionary.rb +2 -2
  86. data/spec/{synchromesh/examples → examples}/dictionary_with_client_scopes.rb +2 -2
  87. data/spec/{synchromesh/examples → examples}/random_examples.rb +1 -1
  88. data/spec/{reactive_record/play.rb → play_ground.rb} +0 -0
  89. data/spec/{reactive_record/factory.rb → reactive_record_factory.rb} +0 -0
  90. data/spec/spec_helper.rb +3 -2
  91. data/spec/test_app/Gemfile +8 -3
  92. data/spec/test_app/Gemfile.lock +114 -64
  93. data/spec/test_app/app/views/components.rb +1 -2
  94. data/spec/test_app/config/application.rb +2 -0
  95. data/spec/test_app/config/routes.rb +1 -1
  96. data/spec/{synchromesh/integration/test_components.rb → test_components.rb} +0 -0
  97. metadata +144 -137
  98. data/app/controllers/reactive_record/application_controller.rb +0 -4
  99. data/app/controllers/reactive_record/reactive_record_controller.rb +0 -49
  100. data/config/routes.rb +0 -7
  101. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js +0 -50
  102. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js.gz +0 -0
  103. data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz +0 -0
  104. data/examples/action-cable-production-mode/tmp/.keep +0 -0
  105. data/examples/action-cable/log/.keep +0 -0
  106. data/examples/action-cable/tmp/.keep +0 -0
  107. data/examples/pusher-fake/log/.keep +0 -0
  108. data/examples/pusher-fake/tmp/.keep +0 -0
  109. data/examples/pusher/log/.keep +0 -0
  110. data/examples/pusher/tmp/.keep +0 -0
  111. data/examples/simple-poller/log/.keep +0 -0
  112. data/examples/simple-poller/tmp/.keep +0 -0
  113. data/examples/word-game/log/.keep +0 -0
  114. data/examples/word-game/tmp/.keep +0 -0
  115. data/examples/words/log/.keep +0 -0
  116. data/examples/words/tmp/.keep +0 -0
  117. data/lib/reactive_record/version.rb +0 -3
  118. data/lib/sources/hyper-mesh/pusher.js +0 -98
  119. data/lib/synchromesh/action_cable.rb +0 -39
  120. data/lib/synchromesh/client_drivers.rb +0 -357
  121. data/lib/synchromesh/configuration.rb +0 -40
  122. data/lib/synchromesh/connection.rb +0 -170
  123. data/lib/synchromesh/policy.rb +0 -504
  124. data/lib/synchromesh/synchromesh.rb +0 -159
  125. data/lib/synchromesh/synchromesh_controller.rb +0 -162
  126. data/reactive_record_test_app/README.rdoc +0 -261
  127. data/reactive_record_test_app/app/assets/javascripts/components/another_component.rb +0 -24
  128. data/reactive_record_test_app/app/assets/javascripts/components/empty_component.rb +0 -6
  129. data/reactive_record_test_app/app/assets/javascripts/components/todo_item_component.js.rb +0 -16
  130. data/reactive_record_test_app/app/assets/javascripts/components/todos_component.js.rb +0 -42
  131. data/reactive_record_test_app/app/assets/javascripts/components/todos_main_component.rb +0 -49
  132. data/reactive_record_test_app/app/assets/javascripts/react_js_test_only.js +0 -21618
  133. data/reactive_record_test_app/app/assets/javascripts/spec/reactive_record_xspec.js.rb +0 -42
  134. data/reactive_record_test_app/app/controllers/test_controller.rb +0 -7
  135. data/reactive_record_test_app/app/mailers/.gitkeep +0 -0
  136. data/reactive_record_test_app/app/models/models.rb +0 -1
  137. data/reactive_record_test_app/app/policies/application_policy.rb +0 -5
  138. data/reactive_record_test_app/app/views/components.rb +0 -4
  139. data/reactive_record_test_app/app/views/components/test.rb +0 -18
  140. data/reactive_record_test_app/app/views/home/index.html.erb +0 -1
  141. data/reactive_record_test_app/app/views/layouts/application.html.erb +0 -17
  142. data/reactive_record_test_app/config/environments/production.rb +0 -70
  143. data/reactive_record_test_app/config/environments/test.rb +0 -41
  144. data/spec/synchromesh/integration/transports_spec.rb +0 -308
  145. data/spec/synchromesh/policies/auto_connect_spec.rb +0 -60
  146. data/spec/synchromesh/policies/auto_loader_spec.rb +0 -34
  147. data/spec/synchromesh/policies/policy_methods_spec.rb +0 -85
  148. data/spec/synchromesh/policies/regulate_class_connection_spec.rb +0 -50
  149. data/spec/synchromesh/policies/regulate_instance_connection_spec.rb +0 -66
  150. data/spec/test_app/log/.keep +0 -0
@@ -1,40 +0,0 @@
1
- module HyperMesh
2
- # configuration utility
3
- module Configuration
4
-
5
- def configuration
6
- config_reset
7
- yield self
8
- config_initialized
9
- end
10
-
11
- def define_setting(name, default = nil, &block)
12
- class_variable_set("@@#{name}", default)
13
-
14
- define_class_method "#{name}=" do |value|
15
- class_variable_set("@@#{name}", value)
16
- block.call value if block
17
- value
18
- end
19
-
20
- define_class_method name do
21
- class_variable_get("@@#{name}")
22
- end
23
- end
24
-
25
- def config_reset
26
- raise "must implement"
27
- end
28
-
29
- def config_initialized
30
- end
31
-
32
- private
33
-
34
- def define_class_method(name, &block)
35
- (class << self; self; end).instance_eval do
36
- define_method name, &block
37
- end
38
- end
39
- end
40
- end
@@ -1,170 +0,0 @@
1
- module HyperMesh
2
- module AutoCreate
3
- def needs_init?
4
- return true unless connection.tables.include?(table_name)
5
- return false unless HyperMesh.on_server?
6
- return true if defined?(Rails::Server)
7
- return true unless Connection.root_path
8
- uri = URI("#{Connection.root_path}server_up")
9
- http = Net::HTTP.new(uri.host, uri.port)
10
- request = Net::HTTP::Get.new(uri.path)
11
- if uri.scheme == 'https'
12
- http.use_ssl = true
13
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
14
- end
15
- http.request(request) && return rescue true
16
- end
17
-
18
- def create_table(*args, &block)
19
- connection.create_table(table_name, *args, &block) if needs_init?
20
- end
21
- end
22
-
23
- class Connection < ActiveRecord::Base
24
- class QueuedMessage < ActiveRecord::Base
25
-
26
- extend AutoCreate
27
-
28
- self.table_name = 'synchromesh_queued_messages'
29
-
30
- do_not_synchronize
31
-
32
- serialize :data
33
-
34
- belongs_to :synchromesh_connection,
35
- class_name: 'HyperMesh::Connection',
36
- foreign_key: 'connection_id'
37
-
38
- scope :for_session,
39
- ->(session) { joins(:synchromesh_connection).where('session = ?', session) }
40
-
41
- # For simplicity we use QueuedMessage with connection_id 0
42
- # to store the current path which is used by consoles to
43
- # communicate back to the server
44
-
45
- default_scope { where('connection_id IS NULL OR connection_id != 0') }
46
-
47
- def self.root_path=(path)
48
- unscoped.find_or_create_by(connection_id: 0).update(data: path)
49
- end
50
-
51
- def self.root_path
52
- unscoped.find_or_create_by(connection_id: 0).data
53
- end
54
- end
55
-
56
- extend AutoCreate
57
-
58
- def self.build_tables
59
- create_table(force: true) do |t|
60
- t.string :channel
61
- t.string :session
62
- t.datetime :created_at
63
- t.datetime :expires_at
64
- t.datetime :refresh_at
65
- end
66
- QueuedMessage.create_table(force: true) do |t|
67
- t.text :data
68
- t.integer :connection_id
69
- end
70
- end
71
-
72
- do_not_synchronize
73
-
74
- self.table_name = 'synchromesh_connections'
75
-
76
- has_many :messages,
77
- foreign_key: 'connection_id',
78
- class_name: 'HyperMesh::Connection::QueuedMessage',
79
- dependent: :destroy
80
- scope :expired,
81
- -> { where('expires_at IS NOT NULL AND expires_at < ?', Time.zone.now) }
82
- scope :pending_for,
83
- ->(channel) { where(channel: channel).where('session IS NOT NULL') }
84
- scope :inactive,
85
- -> { where('session IS NULL AND refresh_at < ?', Time.zone.now) }
86
-
87
- def self.needs_refresh?
88
- exists?(['refresh_at IS NOT NULL AND refresh_at < ?', Time.zone.now])
89
- end
90
-
91
- def transport
92
- self.class.transport
93
- end
94
-
95
- before_create do
96
- if session
97
- self.expires_at = Time.now + transport.expire_new_connection_in
98
- elsif transport.refresh_channels_every != :never
99
- self.refresh_at = Time.now + transport.refresh_channels_every
100
- end
101
- end
102
-
103
- class << self
104
- attr_accessor :transport
105
-
106
- def active
107
- if HyperMesh.on_server?
108
- expired.delete_all
109
- refresh_connections if needs_refresh?
110
- end
111
- all.pluck(:channel).uniq
112
- end
113
-
114
- def open(channel, session = nil, root_path = nil)
115
- self.root_path = root_path
116
- find_or_create_by(channel: channel, session: session)
117
- end
118
-
119
- def send_to_channel(channel, data)
120
- pending_for(channel).each do |connection|
121
- QueuedMessage.create(data: data, synchromesh_connection: connection)
122
- end
123
- transport.send(channel, data) if exists?(channel: channel, session: nil)
124
- end
125
-
126
- def read(session, root_path)
127
- self.root_path = root_path
128
- where(session: session)
129
- .update_all(expires_at: Time.now + transport.expire_polled_connection_in)
130
- QueuedMessage.for_session(session).destroy_all.pluck(:data)
131
- end
132
-
133
- def connect_to_transport(channel, session, root_path)
134
- self.root_path = root_path
135
- if (connection = find_by(channel: channel, session: session))
136
- messages = connection.messages.pluck(:data)
137
- connection.destroy
138
- else
139
- messages = []
140
- end
141
- open(channel)
142
- messages
143
- end
144
-
145
- def disconnect(channel)
146
- find_by(channel: channel, session: nil).destroy
147
- end
148
-
149
- def root_path=(path)
150
- QueuedMessage.root_path = path if path
151
- end
152
-
153
- def root_path
154
- QueuedMessage.root_path
155
- rescue
156
- nil
157
- end
158
-
159
- def refresh_connections
160
- refresh_started_at = Time.zone.now
161
- channels = transport.refresh_channels
162
- next_refresh = refresh_started_at + transport.refresh_channels_every
163
- channels.each do |channel|
164
- find_by(channel: channel, session: nil).update(refresh_at: next_refresh)
165
- end
166
- inactive.delete_all
167
- end
168
- end
169
- end
170
- end
@@ -1,504 +0,0 @@
1
- module HyperMesh
2
-
3
- class InternalClassPolicy
4
-
5
- def initialize(regulated_klass)
6
- @regulated_klass = regulated_klass
7
- end
8
-
9
- EXPOSED_METHODS = [
10
- :regulate_class_connection, :always_allow_connection, :regulate_instance_connections,
11
- :regulate_all_broadcasts, :regulate_broadcast, :allow_change, :allow_create, :allow_read,
12
- :allow_update, :allow_destroy
13
- ]
14
-
15
- def regulate_class_connection(*args, &regulation)
16
- regulate(ClassConnectionRegulation, :class_connection, args, &regulation)
17
- end
18
-
19
- def always_allow_connection(*args)
20
- regulate(ClassConnectionRegulation, nil, args) { true }
21
- end
22
-
23
- def regulate_instance_connections(*args, &regulation)
24
- regulate(InstanceConnectionRegulation, :instance_connections, args, &regulation)
25
- end
26
-
27
- def regulate_all_broadcasts(*args, &regulation)
28
- regulate(ChannelBroadcastRegulation, :all_broadcasts, args, &regulation)
29
- end
30
-
31
- def regulate_broadcast(*args, &regulation)
32
- regulate(InstanceBroadcastRegulation, :broadcast, args, &regulation)
33
- end
34
-
35
-
36
- CHANGE_POLICIES = [:create, :update, :destroy]
37
-
38
- def self.allow_policy(policy, method)
39
- define_method "allow_#{policy}" do |*args, &regulation|
40
- process_args(policy, [], args, regulation) do |model|
41
- get_ar_model(model).class_eval { define_method("#{method}_permitted?", &regulation) }
42
- end
43
- end
44
- end
45
-
46
- CHANGE_POLICIES.each { |policy| allow_policy policy, policy }
47
-
48
- allow_policy(:read, :view)
49
-
50
- def allow_change(*args, &regulation)
51
- process_args('change', [:on], args, regulation) do |model, opts|
52
- model = get_ar_model(model)
53
- opts[:on] ||= CHANGE_POLICIES
54
- opts[:on].each do |policy|
55
- check_valid_on_option policy
56
- model.class_eval { define_method("#{policy}_permitted?", &regulation) }
57
- end
58
- end
59
- end
60
-
61
- def get_ar_model(str)
62
- str.is_a?(Class) ? str : Object.const_get(str)
63
- rescue
64
- raise "#{str} is not a class"
65
- end
66
-
67
- def regulate(regulation_klass, policy, args, &regulation)
68
- process_args(policy, regulation_klass.allowed_opts, args, regulation) do |regulated_klass, opts|
69
- regulation_klass.add_regulation regulated_klass, opts, &regulation
70
- end
71
- end
72
-
73
- def process_args(policy, allowed_opts, args, regulation)
74
- raise "you must provide a block to the regulate_#{policy} method" unless regulation
75
- *args, opts = args if args.last.is_a? Hash
76
- opts ||= {}
77
- args = process_to_opt(allowed_opts, opts, args)
78
- args.each do |regulated_klass|
79
- yield regulated_klass, opts
80
- end
81
- end
82
-
83
- def process_to_opt(allowed_opts, opts, args)
84
- opts.each do |opt, value|
85
- unless opt == :to || allowed_opts.include?(opt)
86
- raise "Unknown ':#{opt} => #{value}' option in policy definition"
87
- end
88
- end
89
- if (to = opts[:to])
90
- raise "option to: :#{to} is not recognized in allow_#{policy}" unless to == :all
91
- raise "option to: :all cannot be used with other classes" unless args.empty?
92
- [ActiveRecord::Base]
93
- elsif args.empty?
94
- [@regulated_klass]
95
- else
96
- args
97
- end
98
- end
99
-
100
- def check_valid_on_option(policy)
101
- unless CHANGE_POLICIES.include? policy
102
- valid_policies = CHANGE_POLICIES.collect { |s| ":{s}" }.to_sentence
103
- raise "only #{valid_policies} are allowed on the regulate_access :on option"
104
- end
105
- end
106
-
107
- end
108
-
109
- class Regulation
110
-
111
- class << self
112
-
113
- def add_regulation(klass, opts={}, &regulation)
114
- klass = regulations[klass]
115
- klass.opts.merge! opts
116
- klass.regulations << regulation
117
- klass
118
- end
119
-
120
- def regulations
121
- @regulations ||= Hash.new do |hash, klass|
122
- if klass.is_a? String
123
- hash[klass] = new(klass)
124
- elsif klass.is_a?(Class) && klass.name
125
- hash[klass.name]
126
- else
127
- hash[klass.class.name]
128
- end
129
- end
130
- end
131
-
132
- def wrap_policy(policy, regulation)
133
- begin
134
- policy_klass = regulation.binding.receiver
135
- rescue
136
- raise "Could not determine the class when regulating. This is probably caused by doing something like &:send_all"
137
- end
138
- wrapped_policy = policy_klass.new(nil, nil)
139
- wrapped_policy.synchromesh_internal_policy_object = policy
140
- wrapped_policy
141
- end
142
-
143
- def allowed_opts
144
- []
145
- end
146
-
147
- end
148
-
149
- attr_reader :klass
150
-
151
- def opts
152
- @opts ||= {}
153
- end
154
-
155
- def regulations
156
- @regulations ||= []
157
- end
158
-
159
- def regulate_for(acting_user)
160
- Enumerator.new do |y|
161
- regulations.each do |regulation|
162
- y << acting_user.instance_eval(&regulation)
163
- end
164
- end
165
- end
166
-
167
- def auto_connect_disabled?
168
- opts.has_key?(:auto_connect) && !opts[:auto_connect]
169
- end
170
-
171
- def initialize(klass)
172
- @klass = klass
173
- end
174
-
175
- end
176
-
177
- class ClassConnectionRegulation < Regulation
178
-
179
- def connectable?(acting_user)
180
- regulate_for(acting_user).all? unless regulations.empty? rescue nil
181
- end
182
-
183
- def self.connect(channel, acting_user)
184
- raise "connection failed" unless regulations[channel].connectable?(acting_user)
185
- end
186
-
187
- def self.connections_for(acting_user, auto_connections_only)
188
- regulations.collect do |channel, regulation|
189
- next if auto_connections_only && regulation.auto_connect_disabled?
190
- next if !regulation.connectable?(acting_user)
191
- channel
192
- end.compact
193
- end
194
-
195
- def self.allowed_opts
196
- [:auto_connect]
197
- end
198
-
199
- end
200
-
201
- class InstanceConnectionRegulation < Regulation
202
-
203
- def connectable_to(acting_user, auto_connections_only)
204
- return [] if auto_connections_only && auto_connect_disabled?
205
- regulate_for(acting_user).entries.compact.flatten(1) rescue []
206
- end
207
-
208
- def self.connect(instance, acting_user)
209
- unless regulations[instance].connectable_to(acting_user, false).include? instance
210
- raise "connection failed"
211
- end
212
- end
213
-
214
- def self.connections_for(acting_user, auto_connections_only)
215
- regulations.collect do |_channel, regulation|
216
- # the following was bizarelly passing true for auto_connections_only????
217
- regulation.connectable_to(acting_user, auto_connections_only).collect do |obj|
218
- if auto_connections_only
219
- [obj.class.name, obj.id]
220
- else
221
- InternalPolicy.channel_to_string obj
222
- end
223
- end
224
- end.flatten(1)
225
- end
226
-
227
- def self.allowed_opts
228
- [:auto_connect]
229
- end
230
- end
231
-
232
- class ChannelBroadcastRegulation < Regulation
233
- class << self
234
- def add_regulation(channel, opts={}, &regulation)
235
- regulations_to_channels[regulation] << super
236
- end
237
-
238
- def broadcast(policy)
239
- regulations_to_channels.each do |regulation, channels|
240
- wrapped_policy = wrap_policy(policy, regulation)
241
- channels.each do |channel|
242
- regulation.call wrapped_policy
243
- policy.send_unassigned_sets_to channel.klass
244
- end
245
- end
246
- end
247
-
248
- def regulations_to_channels
249
- @regulations_to_channels ||= Hash.new { |hash, key| hash[key] = [] }
250
- end
251
- end
252
- end
253
-
254
- class InstanceBroadcastRegulation < Regulation
255
- def self.broadcast(instance, policy)
256
- regulations[instance].regulations.each do |regulation|
257
- instance.instance_exec wrap_policy(policy, regulation), &regulation
258
- end
259
- if policy.has_unassigned_sets?
260
- raise "#{instance.class.name} instance broadcast policy not sent to any channel"
261
- end
262
- end
263
- end
264
-
265
- module AutoConnect
266
- def self.channels(session, acting_user)
267
- channels = ClassConnectionRegulation.connections_for(acting_user, true) +
268
- InstanceConnectionRegulation.connections_for(acting_user, true)
269
- channels.each do |channel|
270
- Connection.open(channel, session)
271
- end
272
- channels
273
- end
274
- end
275
-
276
- class InternalPolicy
277
-
278
- EXPOSED_METHODS = [:send_all, :send_all_but, :send_only, :obj]
279
-
280
- def send_all
281
- SendSet.new(self)
282
- end
283
-
284
- def send_all_but(*execeptions)
285
- SendSet.new(self, exclude: execeptions)
286
- end
287
-
288
- def send_only(*white_list)
289
- SendSet.new(self, white_list: white_list)
290
- end
291
-
292
- def obj
293
- @obj
294
- end
295
-
296
- def self.regulate_connection(acting_user, channel_string)
297
- channel = channel_string.split("-")
298
- if channel.length > 1
299
- id = channel[1..-1].join("-")
300
- object = Object.const_get(channel[0]).find(id)
301
- InstanceConnectionRegulation.connect(object, acting_user)
302
- else
303
- ClassConnectionRegulation.connect(channel[0], acting_user)
304
- end
305
- end
306
-
307
- def self.regulate_broadcast(model, &block)
308
- internal_policy = InternalPolicy.new(
309
- model, model.attribute_names, Connection.active
310
- )
311
- ChannelBroadcastRegulation.broadcast(internal_policy)
312
- InstanceBroadcastRegulation.broadcast(model, internal_policy)
313
- internal_policy.broadcast &block
314
- end
315
-
316
- def initialize(obj, attribute_names, available_channels)
317
- @obj = obj
318
- attribute_names = attribute_names.map(&:to_sym).to_set
319
- @unassigned_send_sets = []
320
- @channel_sets = Hash.new { |hash, key| hash[key] = attribute_names }
321
- @available_channels = available_channels
322
- end
323
-
324
- def channel_available?(channel)
325
- channel && @available_channels.include?(channel_to_string(channel))
326
- end
327
-
328
- def id
329
- @id ||= "#{self.object_id}-#{Time.now.to_f}"
330
- end
331
-
332
- def has_unassigned_sets?
333
- !@unassigned_send_sets.empty?
334
- end
335
-
336
- def send_unassigned_sets_to(channel)
337
- if channel_available?(channel) && has_unassigned_sets?
338
- @channel_sets[channel] = @unassigned_send_sets.inject(@channel_sets[channel]) do |set, send_set|
339
- send_set.merge(set)
340
- end
341
- end
342
- @unassigned_send_sets = []
343
- end
344
-
345
- def add_unassigned_send_set(send_set)
346
- @unassigned_send_sets << send_set
347
- end
348
-
349
- def send_set_to(send_set, channels)
350
- channels.flatten(1).each do |channel|
351
- merge_set(send_set, channel) if channel_available? channel
352
- @unassigned_send_sets.delete(send_set)
353
- end
354
- end
355
-
356
- def merge_set(send_set, channel)
357
- return unless channel
358
- channel = channel.name if channel.is_a?(Class) && channel.name
359
- @channel_sets[channel] = send_set.merge(@channel_sets[channel])
360
- end
361
-
362
- def channel_list
363
- @channel_sets.collect { |channel, _value| channel_to_string channel }
364
- end
365
-
366
- def self.channel_to_string(channel)
367
- if channel.is_a?(Class) && channel.name
368
- channel.name
369
- elsif channel.is_a? String
370
- channel
371
- else
372
- "#{channel.class.name}-#{channel.id}"
373
- end
374
- end
375
-
376
- def channel_to_string(channel)
377
- self.class.channel_to_string(channel)
378
- end
379
-
380
- def filter(h, attribute_set)
381
- r = {}
382
- h.each do |key, value|
383
- r[key.to_sym] = value if attribute_set.member?(key.to_sym) || (key.to_sym == :id)
384
- end unless attribute_set.empty?
385
- #model_id = h[:id] || h["id"]
386
- #r[:id] = model_id if model_id && !attribute_set.empty?
387
- r
388
- end
389
-
390
- def send_message(header, channel, attribute_set, &block)
391
- record = filter(@obj.react_serializer, attribute_set)
392
- previous_changes = filter(@obj.previous_changes, attribute_set)
393
- return if record.empty? && previous_changes.empty?
394
- message = header.merge(
395
- channel: channel_to_string(channel),
396
- record: record,
397
- previous_changes: previous_changes
398
- )
399
- yield message
400
- end
401
-
402
- def broadcast(&block)
403
- klass = @obj.class
404
- header = {broadcast_id: id, channels: channel_list, klass: klass.name}
405
- @channel_sets.each do |channel, attribute_set|
406
- send_message header, channel, attribute_set, &block
407
- end
408
- end
409
- end
410
-
411
- class SendSet
412
-
413
- def to(*channels)
414
- @policy.send_set_to(self, channels)
415
- end
416
-
417
- def initialize(policy, exclude: nil, white_list: nil)
418
- @policy = policy
419
- @policy.add_unassigned_send_set(self)
420
- @excluded = exclude.map &:to_sym if exclude
421
- @white_list = white_list.map &:to_sym if white_list
422
- end
423
-
424
- def merge(set)
425
- set = set.difference(@excluded) if @excluded
426
- set = set.intersection(@white_list) if @white_list
427
- set
428
- end
429
-
430
- end
431
-
432
- module ClassPolicyMethods
433
- def synchromesh_internal_policy_object
434
- @synchromesh_internal_policy_object ||= InternalClassPolicy.new(name || self)
435
- end
436
- InternalClassPolicy::EXPOSED_METHODS.each do |policy_method|
437
- define_method policy_method do |*klasses, &block|
438
- synchromesh_internal_policy_object.send policy_method, *klasses, &block
439
- end unless respond_to? policy_method
440
- end
441
- end
442
-
443
- module PolicyMethods
444
- def self.included(base)
445
- base.class_eval do
446
- extend ClassPolicyMethods
447
- end
448
- end
449
- attr_accessor :synchromesh_internal_policy_object
450
- InternalPolicy::EXPOSED_METHODS.each do |method|
451
- define_method method do |*args, &block|
452
- synchromesh_internal_policy_object.send method, *args, &block
453
- end unless respond_to? method
454
- end
455
- define_method :initialize do |*args|
456
- end unless instance_methods(false).include?(:initialize)
457
- end
458
-
459
- module PolicyAutoLoader
460
- def self.load(name, value)
461
- const_get("#{name}Policy") if name && !(name =~ /Policy$/) && value.is_a?(Class)
462
- rescue Exception => e
463
- raise e if e.is_a?(LoadError) && e.message =~ /Unable to autoload constant #{name}Policy/
464
- end
465
- end
466
- end
467
-
468
- class Module
469
- alias pre_synchromesh_const_set const_set
470
-
471
- def const_set(name, value)
472
- pre_synchromesh_const_set(name, value).tap do
473
- HyperMesh::PolicyAutoLoader.load(name, value)
474
- end
475
- end
476
- end
477
-
478
- class Class
479
-
480
- alias pre_synchromesh_inherited inherited
481
-
482
- def inherited(child_class)
483
- pre_synchromesh_inherited(child_class).tap do
484
- HyperMesh::PolicyAutoLoader.load(child_class.name, child_class)
485
- end
486
- end
487
-
488
- HyperMesh::ClassPolicyMethods.instance_methods.each do |method|
489
- define_method method do |*args, &block|
490
- if name =~ /Policy$/
491
- @synchromesh_internal_policy_object = HyperMesh::InternalClassPolicy.new(name.gsub(/Policy$/,""))
492
- include HyperMesh::PolicyMethods
493
- send method, *args, &block
494
- else
495
- class << self
496
- HyperMesh::ClassPolicyMethods.instance_methods.each do |method|
497
- undef_method method
498
- end
499
- end
500
- method_missing(method, *args, &block)
501
- end
502
- end unless respond_to? method
503
- end
504
- end