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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/Gemfile +7 -2
- data/README.md +112 -87
- data/Rakefile +6 -1
- data/docs/action_cable_quickstart.md +20 -16
- data/docs/activerecord_api.md +23 -17
- data/docs/authorization-policies.md +45 -35
- data/docs/client_side_scoping.md +5 -5
- data/docs/configuration_details.md +6 -46
- data/docs/pusher_faker_quickstart.md +7 -68
- data/docs/pusher_quickstart.md +7 -68
- data/docs/simple_poller_quickstart.md +6 -67
- data/docs/todo-example.md +2 -2
- data/docs/word_game.md +3 -1
- data/docs/words-example.md +2 -3
- data/examples/action-cable/Gemfile +2 -1
- data/examples/action-cable/Gemfile.lock +73 -54
- data/examples/action-cable/config/initializers/{hyper_mesh.rb → hyperloop.rb} +1 -1
- data/examples/action-cable/config/routes.rb +1 -1
- data/hyper-mesh.gemspec +10 -4
- data/lib/active_record_base.rb +3 -3
- data/{examples/action-cable-production-mode/log/.keep → lib/acts_as_string.rb} +0 -0
- data/lib/hyper-mesh.rb +11 -19
- data/lib/hyper_mesh/version.rb +3 -0
- data/lib/hypermesh/version.rb +1 -1
- data/lib/reactive_record/active_record/class_methods.rb +10 -3
- data/lib/reactive_record/active_record/instance_methods.rb +8 -0
- data/lib/reactive_record/active_record/public_columns_hash.rb +8 -2
- data/lib/reactive_record/active_record/reactive_record/collection.rb +0 -1
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +2 -1
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +55 -63
- data/lib/reactive_record/active_record/reactive_record/operations.rb +51 -0
- data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +3 -3
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +93 -84
- data/lib/reactive_record/broadcast.rb +183 -0
- data/lib/reactive_record/permissions.rb +2 -2
- data/reactive_record_test_app/Gemfile +6 -2
- data/reactive_record_test_app/Gemfile.lock +120 -60
- data/reactive_record_test_app/app/assets/javascripts/application.rb +3 -5
- data/reactive_record_test_app/app/assets/javascripts/bigdecimal.rb +1 -0
- data/reactive_record_test_app/app/assets/javascripts/reactive_record_config.js +2 -2
- data/reactive_record_test_app/app/controllers/application_controller.rb +3 -3
- data/reactive_record_test_app/app/controllers/home_controller.rb +1 -1
- data/reactive_record_test_app/app/models/models.rb.erb +6 -0
- data/reactive_record_test_app/config/application.rb +2 -0
- data/reactive_record_test_app/config/environments/development.rb +1 -1
- data/reactive_record_test_app/config/routes.rb +1 -2
- data/reactive_record_test_app/db/seeds.rb +6 -0
- data/reactive_record_test_app/script/rails +0 -0
- data/reactive_record_test_app/spec-opal/active-record/rendering_spec.rb +11 -2
- data/reactive_record_test_app/spec-opal/active-record/save_spec.rb +3 -4
- data/reactive_record_test_app/spec-opal/spec_helper.js.rb +1 -1
- data/reactive_record_test_app/spec-opal/test_spec.rb +7 -0
- data/reactive_record_test_app/spec_dont_run/README.md +7 -0
- data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/permissions_spec.rb +0 -0
- data/reactive_record_test_app/{spec-opal/active-record → spec_dont_run/active_record_broken}/prerendering_spec.rb +1 -0
- data/spec/{synchromesh/aaa-unit_tests/connection_spec.rb → batch1/aaa-unit_tests/connection_movedspec.rb} +0 -0
- data/spec/{synchromesh → batch1}/aaa-unit_tests/dummy_value_spec.rb +2 -2
- data/spec/{synchromesh → batch1}/column_types/column_type_spec.rb +2 -2
- data/spec/{synchromesh → batch1}/crud_access_regulation/broadcast_controls_access_spec.rb +1 -1
- data/spec/{synchromesh → batch1}/crud_access_regulation/model_policies_spec.rb +6 -6
- data/spec/batch1/misc/access_like_hash_spec.rb +43 -0
- data/spec/batch1/misc/while_loading_spec.rb +196 -0
- data/spec/{synchromesh → batch1}/policies/regulate_all_broadcasts_spec.rb +12 -12
- data/spec/{synchromesh → batch1}/policies/regulate_broadcast_spec.rb +25 -25
- data/spec/{synchromesh/integration → batch2}/authorization_spec.rb +8 -7
- data/spec/{synchromesh/integration → batch2}/default_scope_spec.rb +2 -2
- data/spec/{synchromesh/integration → batch2}/has_many_through_spec.rb +2 -2
- data/spec/{synchromesh/integration → batch2}/relationships_spec.rb +3 -3
- data/spec/{reactive_record → batch3}/auto_load_itself_spec.rb +1 -1
- data/spec/{reactive_record → batch3}/edge_cases_spec.rb +1 -1
- data/spec/{reactive_record → batch3}/finder_method_spec.rb +1 -1
- data/spec/{reactive_record → batch3}/many_to_many_spec.rb +2 -2
- data/spec/{reactive_record → batch3}/pry_rescue_xspec.rb +0 -0
- data/{examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css → spec/batch3/readme.txt} +0 -0
- data/spec/{reactive_record → batch3}/revert_spec.rb +2 -2
- data/spec/{reactive_record → batch3}/save_while_loading_spec.rb +1 -1
- data/spec/{reactive_record → batch3}/update_associations_spec.rb +2 -2
- data/spec/{reactive_record → batch3}/update_scopes_spec.rb +2 -2
- data/spec/{synchromesh/integration → batch4}/saving_during_commit_spec.rb +2 -2
- data/spec/{synchromesh/integration → batch4}/scope_spec.rb +30 -2
- data/spec/{synchromesh/examples → batch4}/scoped_todos_spec.rb +3 -3
- data/spec/{synchromesh/integration → batch4}/synchromesh_spec.rb +2 -2
- data/spec/{synchromesh/examples → examples}/dictionary.rb +2 -2
- data/spec/{synchromesh/examples → examples}/dictionary_with_client_scopes.rb +2 -2
- data/spec/{synchromesh/examples → examples}/random_examples.rb +1 -1
- data/spec/{reactive_record/play.rb → play_ground.rb} +0 -0
- data/spec/{reactive_record/factory.rb → reactive_record_factory.rb} +0 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/test_app/Gemfile +8 -3
- data/spec/test_app/Gemfile.lock +114 -64
- data/spec/test_app/app/views/components.rb +1 -2
- data/spec/test_app/config/application.rb +2 -0
- data/spec/test_app/config/routes.rb +1 -1
- data/spec/{synchromesh/integration/test_components.rb → test_components.rb} +0 -0
- metadata +144 -137
- data/app/controllers/reactive_record/application_controller.rb +0 -4
- data/app/controllers/reactive_record/reactive_record_controller.rb +0 -49
- data/config/routes.rb +0 -7
- data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js +0 -50
- data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js.gz +0 -0
- data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz +0 -0
- data/examples/action-cable-production-mode/tmp/.keep +0 -0
- data/examples/action-cable/log/.keep +0 -0
- data/examples/action-cable/tmp/.keep +0 -0
- data/examples/pusher-fake/log/.keep +0 -0
- data/examples/pusher-fake/tmp/.keep +0 -0
- data/examples/pusher/log/.keep +0 -0
- data/examples/pusher/tmp/.keep +0 -0
- data/examples/simple-poller/log/.keep +0 -0
- data/examples/simple-poller/tmp/.keep +0 -0
- data/examples/word-game/log/.keep +0 -0
- data/examples/word-game/tmp/.keep +0 -0
- data/examples/words/log/.keep +0 -0
- data/examples/words/tmp/.keep +0 -0
- data/lib/reactive_record/version.rb +0 -3
- data/lib/sources/hyper-mesh/pusher.js +0 -98
- data/lib/synchromesh/action_cable.rb +0 -39
- data/lib/synchromesh/client_drivers.rb +0 -357
- data/lib/synchromesh/configuration.rb +0 -40
- data/lib/synchromesh/connection.rb +0 -170
- data/lib/synchromesh/policy.rb +0 -504
- data/lib/synchromesh/synchromesh.rb +0 -159
- data/lib/synchromesh/synchromesh_controller.rb +0 -162
- data/reactive_record_test_app/README.rdoc +0 -261
- data/reactive_record_test_app/app/assets/javascripts/components/another_component.rb +0 -24
- data/reactive_record_test_app/app/assets/javascripts/components/empty_component.rb +0 -6
- data/reactive_record_test_app/app/assets/javascripts/components/todo_item_component.js.rb +0 -16
- data/reactive_record_test_app/app/assets/javascripts/components/todos_component.js.rb +0 -42
- data/reactive_record_test_app/app/assets/javascripts/components/todos_main_component.rb +0 -49
- data/reactive_record_test_app/app/assets/javascripts/react_js_test_only.js +0 -21618
- data/reactive_record_test_app/app/assets/javascripts/spec/reactive_record_xspec.js.rb +0 -42
- data/reactive_record_test_app/app/controllers/test_controller.rb +0 -7
- data/reactive_record_test_app/app/mailers/.gitkeep +0 -0
- data/reactive_record_test_app/app/models/models.rb +0 -1
- data/reactive_record_test_app/app/policies/application_policy.rb +0 -5
- data/reactive_record_test_app/app/views/components.rb +0 -4
- data/reactive_record_test_app/app/views/components/test.rb +0 -18
- data/reactive_record_test_app/app/views/home/index.html.erb +0 -1
- data/reactive_record_test_app/app/views/layouts/application.html.erb +0 -17
- data/reactive_record_test_app/config/environments/production.rb +0 -70
- data/reactive_record_test_app/config/environments/test.rb +0 -41
- data/spec/synchromesh/integration/transports_spec.rb +0 -308
- data/spec/synchromesh/policies/auto_connect_spec.rb +0 -60
- data/spec/synchromesh/policies/auto_loader_spec.rb +0 -34
- data/spec/synchromesh/policies/policy_methods_spec.rb +0 -85
- data/spec/synchromesh/policies/regulate_class_connection_spec.rb +0 -50
- data/spec/synchromesh/policies/regulate_instance_connection_spec.rb +0 -66
- 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
|
data/lib/synchromesh/policy.rb
DELETED
|
@@ -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, ®ulation)
|
|
16
|
-
regulate(ClassConnectionRegulation, :class_connection, args, ®ulation)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def always_allow_connection(*args)
|
|
20
|
-
regulate(ClassConnectionRegulation, nil, args) { true }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def regulate_instance_connections(*args, ®ulation)
|
|
24
|
-
regulate(InstanceConnectionRegulation, :instance_connections, args, ®ulation)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def regulate_all_broadcasts(*args, ®ulation)
|
|
28
|
-
regulate(ChannelBroadcastRegulation, :all_broadcasts, args, ®ulation)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def regulate_broadcast(*args, ®ulation)
|
|
32
|
-
regulate(InstanceBroadcastRegulation, :broadcast, args, ®ulation)
|
|
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, ®ulation|
|
|
40
|
-
process_args(policy, [], args, regulation) do |model|
|
|
41
|
-
get_ar_model(model).class_eval { define_method("#{method}_permitted?", ®ulation) }
|
|
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, ®ulation)
|
|
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?", ®ulation) }
|
|
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, ®ulation)
|
|
68
|
-
process_args(policy, regulation_klass.allowed_opts, args, regulation) do |regulated_klass, opts|
|
|
69
|
-
regulation_klass.add_regulation regulated_klass, opts, ®ulation
|
|
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={}, ®ulation)
|
|
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(®ulation)
|
|
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={}, ®ulation)
|
|
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), ®ulation
|
|
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
|