hyper-operation 0.5.12 → 0.99.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.
- checksums.yaml +5 -5
- data/.gitignore +4 -1
- data/.travis.yml +33 -0
- data/DOCS-POLICIES.md +93 -0
- data/DOCS.md +1 -1
- data/Gemfile +5 -1
- data/Gemfile.lock +379 -0
- data/README.md +10 -9
- data/Rakefile +10 -2
- data/hyper-operation.gemspec +34 -29
- data/lib/hyper-operation.rb +5 -4
- data/lib/hyper-operation/boot.rb +1 -1
- data/lib/hyper-operation/engine.rb +1 -1
- data/lib/hyper-operation/http.rb +309 -0
- data/lib/hyper-operation/railway/params_wrapper.rb +1 -0
- data/lib/hyper-operation/server_op.rb +83 -18
- data/lib/hyper-operation/transport/client_drivers.rb +71 -28
- data/lib/hyper-operation/transport/connection.rb +22 -20
- data/lib/hyper-operation/transport/hyperloop.rb +1 -1
- data/lib/hyper-operation/transport/hyperloop_controller.rb +6 -1
- data/lib/hyper-operation/transport/policy.rb +78 -13
- data/lib/hyper-operation/version.rb +1 -1
- metadata +95 -319
- data/CODE_OF_CONDUCT.md +0 -49
- data/examples/chat-app/.gitignore +0 -21
- data/examples/chat-app/Gemfile +0 -57
- data/examples/chat-app/Gemfile.lock +0 -283
- data/examples/chat-app/README.md +0 -3
- data/examples/chat-app/Rakefile +0 -6
- data/examples/chat-app/app/assets/config/manifest.js +0 -3
- data/examples/chat-app/app/assets/images/.keep +0 -0
- data/examples/chat-app/app/assets/javascripts/application.js +0 -3
- data/examples/chat-app/app/assets/javascripts/cable.js +0 -13
- data/examples/chat-app/app/assets/javascripts/channels/.keep +0 -0
- data/examples/chat-app/app/assets/stylesheets/application.css +0 -15
- data/examples/chat-app/app/channels/application_cable/channel.rb +0 -4
- data/examples/chat-app/app/channels/application_cable/connection.rb +0 -4
- data/examples/chat-app/app/controllers/application_controller.rb +0 -3
- data/examples/chat-app/app/controllers/concerns/.keep +0 -0
- data/examples/chat-app/app/controllers/home_controller.rb +0 -5
- data/examples/chat-app/app/helpers/application_helper.rb +0 -2
- data/examples/chat-app/app/hyperloop/components/app.rb +0 -11
- data/examples/chat-app/app/hyperloop/components/formatted_div.rb +0 -13
- data/examples/chat-app/app/hyperloop/components/input_box.rb +0 -29
- data/examples/chat-app/app/hyperloop/components/message.rb +0 -29
- data/examples/chat-app/app/hyperloop/components/messages.rb +0 -9
- data/examples/chat-app/app/hyperloop/components/nav.rb +0 -30
- data/examples/chat-app/app/hyperloop/operations/operations.rb +0 -56
- data/examples/chat-app/app/hyperloop/stores/message_store.rb +0 -23
- data/examples/chat-app/app/models/application_record.rb +0 -3
- data/examples/chat-app/app/models/concerns/.keep +0 -0
- data/examples/chat-app/app/models/models.rb +0 -2
- data/examples/chat-app/app/models/public/.keep +0 -0
- data/examples/chat-app/app/models/public/announcement.rb +0 -8
- data/examples/chat-app/app/policies/application_policy.rb +0 -5
- data/examples/chat-app/app/views/layouts/application.html.erb +0 -51
- data/examples/chat-app/bin/bundle +0 -3
- data/examples/chat-app/bin/rails +0 -9
- data/examples/chat-app/bin/rake +0 -9
- data/examples/chat-app/bin/setup +0 -34
- data/examples/chat-app/bin/spring +0 -17
- data/examples/chat-app/bin/update +0 -29
- data/examples/chat-app/config.ru +0 -5
- data/examples/chat-app/config/application.rb +0 -13
- data/examples/chat-app/config/boot.rb +0 -3
- data/examples/chat-app/config/cable.yml +0 -9
- data/examples/chat-app/config/database.yml +0 -25
- data/examples/chat-app/config/environment.rb +0 -5
- data/examples/chat-app/config/environments/development.rb +0 -56
- data/examples/chat-app/config/environments/production.rb +0 -86
- data/examples/chat-app/config/environments/test.rb +0 -42
- data/examples/chat-app/config/initializers/application_controller_renderer.rb +0 -6
- data/examples/chat-app/config/initializers/assets.rb +0 -11
- data/examples/chat-app/config/initializers/backtrace_silencers.rb +0 -7
- data/examples/chat-app/config/initializers/cookies_serializer.rb +0 -5
- data/examples/chat-app/config/initializers/filter_parameter_logging.rb +0 -4
- data/examples/chat-app/config/initializers/hyperloop.rb +0 -4
- data/examples/chat-app/config/initializers/inflections.rb +0 -16
- data/examples/chat-app/config/initializers/mime_types.rb +0 -4
- data/examples/chat-app/config/initializers/new_framework_defaults.rb +0 -24
- data/examples/chat-app/config/initializers/session_store.rb +0 -3
- data/examples/chat-app/config/initializers/wrap_parameters.rb +0 -14
- data/examples/chat-app/config/locales/en.yml +0 -23
- data/examples/chat-app/config/puma.rb +0 -47
- data/examples/chat-app/config/routes.rb +0 -5
- data/examples/chat-app/config/secrets.yml +0 -22
- data/examples/chat-app/config/spring.rb +0 -6
- data/examples/chat-app/db/seeds.rb +0 -7
- data/examples/chat-app/lib/assets/.keep +0 -0
- data/examples/chat-app/lib/tasks/.keep +0 -0
- data/examples/chat-app/log/.keep +0 -0
- data/examples/chat-app/public/404.html +0 -67
- data/examples/chat-app/public/422.html +0 -67
- data/examples/chat-app/public/500.html +0 -66
- data/examples/chat-app/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/chat-app/public/apple-touch-icon.png +0 -0
- data/examples/chat-app/public/favicon.ico +0 -0
- data/examples/chat-app/public/robots.txt +0 -5
- data/examples/chat-app/test/controllers/.keep +0 -0
- data/examples/chat-app/test/fixtures/.keep +0 -0
- data/examples/chat-app/test/fixtures/files/.keep +0 -0
- data/examples/chat-app/test/helpers/.keep +0 -0
- data/examples/chat-app/test/integration/.keep +0 -0
- data/examples/chat-app/test/mailers/.keep +0 -0
- data/examples/chat-app/test/models/.keep +0 -0
- data/examples/chat-app/test/test_helper.rb +0 -10
- data/examples/chat-app/tmp/.keep +0 -0
- data/examples/chat-app/vendor/assets/javascripts/.keep +0 -0
- data/examples/chat-app/vendor/assets/stylesheets/.keep +0 -0
- data/examples/five-letter-word-game/.gitignore +0 -21
- data/examples/five-letter-word-game/Gemfile +0 -62
- data/examples/five-letter-word-game/Gemfile.lock +0 -291
- data/examples/five-letter-word-game/README.md +0 -24
- data/examples/five-letter-word-game/Rakefile +0 -6
- data/examples/five-letter-word-game/app/assets/config/manifest.js +0 -3
- data/examples/five-letter-word-game/app/assets/images/.keep +0 -0
- data/examples/five-letter-word-game/app/assets/javascripts/application.js +0 -4
- data/examples/five-letter-word-game/app/assets/javascripts/cable.js +0 -13
- data/examples/five-letter-word-game/app/assets/javascripts/channels/.keep +0 -0
- data/examples/five-letter-word-game/app/assets/stylesheets/application.css +0 -15
- data/examples/five-letter-word-game/app/channels/application_cable/channel.rb +0 -4
- data/examples/five-letter-word-game/app/channels/application_cable/connection.rb +0 -4
- data/examples/five-letter-word-game/app/controllers/application_controller.rb +0 -14
- data/examples/five-letter-word-game/app/controllers/concerns/.keep +0 -0
- data/examples/five-letter-word-game/app/controllers/home_controller.rb +0 -5
- data/examples/five-letter-word-game/app/helpers/application_helper.rb +0 -2
- data/examples/five-letter-word-game/app/hyperloop/components/app.rb +0 -65
- data/examples/five-letter-word-game/app/hyperloop/components/guesses.rb +0 -8
- data/examples/five-letter-word-game/app/hyperloop/components/input_word.rb +0 -13
- data/examples/five-letter-word-game/app/hyperloop/models/user.rb +0 -27
- data/examples/five-letter-word-game/app/hyperloop/operations/ops.rb +0 -115
- data/examples/five-letter-word-game/app/hyperloop/stores/store.rb +0 -120
- data/examples/five-letter-word-game/app/jobs/application_job.rb +0 -2
- data/examples/five-letter-word-game/app/mailers/application_mailer.rb +0 -4
- data/examples/five-letter-word-game/app/models/application_record.rb +0 -3
- data/examples/five-letter-word-game/app/models/concerns/.keep +0 -0
- data/examples/five-letter-word-game/app/policies/hyperloop/application_policy.rb +0 -3
- data/examples/five-letter-word-game/app/policies/user_policy.rb +0 -4
- data/examples/five-letter-word-game/app/views/layouts/application.html.erb +0 -14
- data/examples/five-letter-word-game/app/views/layouts/mailer.html.erb +0 -13
- data/examples/five-letter-word-game/app/views/layouts/mailer.text.erb +0 -1
- data/examples/five-letter-word-game/bin/bundle +0 -3
- data/examples/five-letter-word-game/bin/rails +0 -9
- data/examples/five-letter-word-game/bin/rake +0 -9
- data/examples/five-letter-word-game/bin/setup +0 -34
- data/examples/five-letter-word-game/bin/spring +0 -17
- data/examples/five-letter-word-game/bin/update +0 -29
- data/examples/five-letter-word-game/config.ru +0 -5
- data/examples/five-letter-word-game/config/application.rb +0 -12
- data/examples/five-letter-word-game/config/boot.rb +0 -3
- data/examples/five-letter-word-game/config/cable.yml +0 -9
- data/examples/five-letter-word-game/config/database.yml +0 -46
- data/examples/five-letter-word-game/config/environment.rb +0 -5
- data/examples/five-letter-word-game/config/environments/development.rb +0 -56
- data/examples/five-letter-word-game/config/environments/production.rb +0 -86
- data/examples/five-letter-word-game/config/environments/test.rb +0 -42
- data/examples/five-letter-word-game/config/initializers/application_controller_renderer.rb +0 -6
- data/examples/five-letter-word-game/config/initializers/assets.rb +0 -15
- data/examples/five-letter-word-game/config/initializers/backtrace_silencers.rb +0 -7
- data/examples/five-letter-word-game/config/initializers/cookies_serializer.rb +0 -5
- data/examples/five-letter-word-game/config/initializers/filter_parameter_logging.rb +0 -4
- data/examples/five-letter-word-game/config/initializers/hyperloop.rb +0 -19
- data/examples/five-letter-word-game/config/initializers/inflections.rb +0 -16
- data/examples/five-letter-word-game/config/initializers/mime_types.rb +0 -4
- data/examples/five-letter-word-game/config/initializers/new_framework_defaults.rb +0 -24
- data/examples/five-letter-word-game/config/initializers/session_store.rb +0 -3
- data/examples/five-letter-word-game/config/initializers/wrap_parameters.rb +0 -14
- data/examples/five-letter-word-game/config/locales/en.yml +0 -23
- data/examples/five-letter-word-game/config/puma.rb +0 -47
- data/examples/five-letter-word-game/config/routes.rb +0 -5
- data/examples/five-letter-word-game/config/secrets.yml +0 -22
- data/examples/five-letter-word-game/config/spring.rb +0 -6
- data/examples/five-letter-word-game/db/schema.rb +0 -28
- data/examples/five-letter-word-game/db/seeds.rb +0 -7
- data/examples/five-letter-word-game/lib/assets/.keep +0 -0
- data/examples/five-letter-word-game/lib/tasks/.keep +0 -0
- data/examples/five-letter-word-game/log/.keep +0 -0
- data/examples/five-letter-word-game/public/404.html +0 -67
- data/examples/five-letter-word-game/public/422.html +0 -67
- data/examples/five-letter-word-game/public/500.html +0 -66
- data/examples/five-letter-word-game/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/five-letter-word-game/public/apple-touch-icon.png +0 -0
- data/examples/five-letter-word-game/public/favicon.ico +0 -0
- data/examples/five-letter-word-game/public/robots.txt +0 -5
- data/examples/five-letter-word-game/test/controllers/.keep +0 -0
- data/examples/five-letter-word-game/test/fixtures/.keep +0 -0
- data/examples/five-letter-word-game/test/fixtures/files/.keep +0 -0
- data/examples/five-letter-word-game/test/helpers/.keep +0 -0
- data/examples/five-letter-word-game/test/integration/.keep +0 -0
- data/examples/five-letter-word-game/test/mailers/.keep +0 -0
- data/examples/five-letter-word-game/test/models/.keep +0 -0
- data/examples/five-letter-word-game/test/test_helper.rb +0 -10
- data/examples/five-letter-word-game/tmp/.keep +0 -0
- data/examples/five-letter-word-game/vendor/assets/javascripts/.keep +0 -0
- data/examples/five-letter-word-game/vendor/assets/stylesheets/.keep +0 -0
- data/examples/smoke_test/.gitignore +0 -21
- data/examples/smoke_test/Gemfile +0 -59
- data/examples/smoke_test/Gemfile.lock +0 -289
- data/examples/smoke_test/README.md +0 -24
- data/examples/smoke_test/Rakefile +0 -6
- data/examples/smoke_test/app/assets/config/manifest.js +0 -3
- data/examples/smoke_test/app/assets/images/.keep +0 -0
- data/examples/smoke_test/app/assets/javascripts/application.js +0 -15
- data/examples/smoke_test/app/assets/javascripts/cable.js +0 -13
- data/examples/smoke_test/app/assets/javascripts/channels/.keep +0 -0
- data/examples/smoke_test/app/assets/stylesheets/application.css +0 -15
- data/examples/smoke_test/app/channels/application_cable/channel.rb +0 -4
- data/examples/smoke_test/app/channels/application_cable/connection.rb +0 -4
- data/examples/smoke_test/app/controllers/app_controller.rb +0 -5
- data/examples/smoke_test/app/controllers/application_controller.rb +0 -3
- data/examples/smoke_test/app/helpers/application_helper.rb +0 -2
- data/examples/smoke_test/app/hyperloop/components/hello.rb +0 -25
- data/examples/smoke_test/app/hyperloop/operations/operations/nested_send_to_all.rb +0 -7
- data/examples/smoke_test/app/hyperloop/operations/send_to_all.rb +0 -6
- data/examples/smoke_test/app/hyperloop/stores/messages.rb +0 -4
- data/examples/smoke_test/app/jobs/application_job.rb +0 -2
- data/examples/smoke_test/app/mailers/application_mailer.rb +0 -4
- data/examples/smoke_test/app/models/application_record.rb +0 -3
- data/examples/smoke_test/app/models/concerns/.keep +0 -0
- data/examples/smoke_test/app/policies/application_policy.rb +0 -8
- data/examples/smoke_test/app/views/layouts/application.html.erb +0 -14
- data/examples/smoke_test/app/views/layouts/mailer.html.erb +0 -13
- data/examples/smoke_test/app/views/layouts/mailer.text.erb +0 -1
- data/examples/smoke_test/bin/bundle +0 -3
- data/examples/smoke_test/bin/rails +0 -9
- data/examples/smoke_test/bin/rake +0 -9
- data/examples/smoke_test/bin/setup +0 -34
- data/examples/smoke_test/bin/spring +0 -17
- data/examples/smoke_test/bin/update +0 -29
- data/examples/smoke_test/config.ru +0 -5
- data/examples/smoke_test/config/application.rb +0 -15
- data/examples/smoke_test/config/boot.rb +0 -3
- data/examples/smoke_test/config/cable.yml +0 -9
- data/examples/smoke_test/config/database.yml +0 -25
- data/examples/smoke_test/config/environment.rb +0 -5
- data/examples/smoke_test/config/environments/development.rb +0 -54
- data/examples/smoke_test/config/environments/production.rb +0 -86
- data/examples/smoke_test/config/environments/test.rb +0 -42
- data/examples/smoke_test/config/initializers/application_controller_renderer.rb +0 -6
- data/examples/smoke_test/config/initializers/assets.rb +0 -11
- data/examples/smoke_test/config/initializers/backtrace_silencers.rb +0 -7
- data/examples/smoke_test/config/initializers/cookies_serializer.rb +0 -5
- data/examples/smoke_test/config/initializers/filter_parameter_logging.rb +0 -4
- data/examples/smoke_test/config/initializers/hyperloop.rb +0 -32
- data/examples/smoke_test/config/initializers/inflections.rb +0 -16
- data/examples/smoke_test/config/initializers/mime_types.rb +0 -4
- data/examples/smoke_test/config/initializers/new_framework_defaults.rb +0 -24
- data/examples/smoke_test/config/initializers/session_store.rb +0 -3
- data/examples/smoke_test/config/initializers/wrap_parameters.rb +0 -14
- data/examples/smoke_test/config/locales/en.yml +0 -23
- data/examples/smoke_test/config/puma.rb +0 -47
- data/examples/smoke_test/config/routes.rb +0 -5
- data/examples/smoke_test/config/secrets.yml +0 -22
- data/examples/smoke_test/config/spring.rb +0 -6
- data/examples/smoke_test/db/seeds.rb +0 -7
- data/examples/smoke_test/lib/assets/.keep +0 -0
- data/examples/smoke_test/lib/tasks/.keep +0 -0
- data/examples/smoke_test/log/.keep +0 -0
- data/examples/smoke_test/public/404.html +0 -67
- data/examples/smoke_test/public/422.html +0 -67
- data/examples/smoke_test/public/500.html +0 -66
- data/examples/smoke_test/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/smoke_test/public/apple-touch-icon.png +0 -0
- data/examples/smoke_test/public/favicon.ico +0 -0
- data/examples/smoke_test/public/robots.txt +0 -5
- data/examples/smoke_test/test/controllers/.keep +0 -0
- data/examples/smoke_test/test/fixtures/.keep +0 -0
- data/examples/smoke_test/test/fixtures/files/.keep +0 -0
- data/examples/smoke_test/test/helpers/.keep +0 -0
- data/examples/smoke_test/test/integration/.keep +0 -0
- data/examples/smoke_test/test/mailers/.keep +0 -0
- data/examples/smoke_test/test/models/.keep +0 -0
- data/examples/smoke_test/test/test_helper.rb +0 -10
- data/examples/smoke_test/tmp/.keep +0 -0
- data/examples/smoke_test/vendor/assets/javascripts/.keep +0 -0
- data/examples/smoke_test/vendor/assets/stylesheets/.keep +0 -0
- data/lib/hyper-operation/call_by_class_name.rb +0 -60
|
@@ -1,37 +1,99 @@
|
|
|
1
|
+
require 'net/http' unless RUBY_ENGINE == 'opal'
|
|
2
|
+
|
|
1
3
|
module Hyperloop
|
|
2
4
|
class ServerOp < Operation
|
|
3
5
|
|
|
4
6
|
class << self
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
include React::IsomorphicHelpers
|
|
8
|
+
|
|
9
|
+
if RUBY_ENGINE == 'opal'
|
|
10
|
+
if on_opal_client?
|
|
11
|
+
def run(*args)
|
|
12
|
+
hash = _Railway.params_wrapper.combine_arg_array(args)
|
|
13
|
+
hash = serialize_params(hash)
|
|
14
|
+
Hyperloop::HTTP.post(
|
|
15
|
+
"#{`window.HyperloopEnginePath`}/execute_remote",
|
|
16
|
+
payload: {json: {operation: name, params: hash}.to_json},
|
|
17
|
+
headers: {'X-CSRF-Token' => Hyperloop::ClientDrivers.opts[:form_authenticity_token] }
|
|
18
|
+
)
|
|
19
|
+
.then do |response|
|
|
20
|
+
deserialize_response response.json[:response]
|
|
21
|
+
end
|
|
22
|
+
.fail do |response|
|
|
23
|
+
Exception.new response.json[:error]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
elsif on_opal_server?
|
|
27
|
+
def run(*args)
|
|
28
|
+
promise = Promise.new
|
|
29
|
+
response = internal_iso_run(name, args)
|
|
30
|
+
if response[:json][:response]
|
|
31
|
+
promise.resolve(response[:json][:response])
|
|
32
|
+
else
|
|
33
|
+
promise.reject Exception.new response[:json][:error]
|
|
34
|
+
end
|
|
35
|
+
promise
|
|
36
|
+
end
|
|
17
37
|
end
|
|
18
|
-
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
isomorphic_method(:internal_iso_run) do |f, klass_name, op_params|
|
|
41
|
+
f.send_to_server(klass_name, op_params)
|
|
42
|
+
f.when_on_server {
|
|
43
|
+
Hyperloop::ServerOp.run_from_client(:acting_user, controller, klass_name, *op_params)
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def descendants_map_cache
|
|
48
|
+
# calling descendants alone may take 10ms in a complex app, so better cache it
|
|
49
|
+
@cached_descendants ||= Hyperloop::ServerOp.descendants.map(&:to_s)
|
|
50
|
+
end
|
|
19
51
|
|
|
20
52
|
def run_from_client(security_param, controller, operation, params)
|
|
53
|
+
if Rails.env.production?
|
|
54
|
+
# in production everything is eager loaded so ServerOp.descendants is filled and can be used to guard the .constantize
|
|
55
|
+
unless Hyperloop::ServerOp.descendants_map_cache.include?(operation)
|
|
56
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:illegal_remote_op_call, "Operation: #{operation} (in production)")
|
|
57
|
+
end
|
|
58
|
+
# however ...
|
|
59
|
+
else
|
|
60
|
+
# ... in development things are autoloaded on demand, thus ServerOp.descendants can be empty or partially filled and above guard
|
|
61
|
+
# would fail legal operations. To prevent this, the class has to be loaded first, what .const_get will take care of, and then
|
|
62
|
+
# its guarded, to achieve similar behaviour as in production. Doing the const_get first, before the guard,
|
|
63
|
+
# would not be safe for production and allow for potential remote code execution!
|
|
64
|
+
begin
|
|
65
|
+
const = Object.const_get(operation)
|
|
66
|
+
rescue NameError
|
|
67
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:illegal_remote_op_call, "Operation: #{operation} (const not found)")
|
|
68
|
+
end
|
|
69
|
+
unless const < Hyperloop::ServerOp
|
|
70
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:illegal_remote_op_call, "Operation: #{operation} (not a ServerOp subclass)")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
21
73
|
operation.constantize.class_eval do
|
|
22
74
|
if _Railway.params_wrapper.method_defined?(:controller)
|
|
23
75
|
params[:controller] = controller
|
|
24
76
|
elsif !_Railway.params_wrapper.method_defined?(security_param)
|
|
25
77
|
raise AccessViolation
|
|
26
78
|
end
|
|
27
|
-
run(params)
|
|
79
|
+
run(deserialize_params(params))
|
|
28
80
|
.then { |r| return { json: { response: serialize_response(r) } } }
|
|
29
|
-
.fail { |e| return
|
|
81
|
+
.fail { |e| return handle_exception(e, operation, params) }
|
|
30
82
|
end
|
|
31
83
|
rescue Exception => e
|
|
32
|
-
|
|
84
|
+
handle_exception(e, operation, params)
|
|
33
85
|
end
|
|
34
86
|
|
|
87
|
+
def handle_exception(e, operation, params)
|
|
88
|
+
if defined? ::Rails
|
|
89
|
+
params.delete(:controller)
|
|
90
|
+
::Rails.logger.debug "\033[0;31;1mERROR: Hyperloop::ServerOp exception caught when running "\
|
|
91
|
+
"#{operation} with params \"#{params}\": #{e}\033[0;30;21m"
|
|
92
|
+
end
|
|
93
|
+
{ json: { error: e }, status: 500 }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
35
97
|
def remote(path, *args)
|
|
36
98
|
promise = Promise.new
|
|
37
99
|
uri = URI("#{path}execute_remote_api")
|
|
@@ -39,7 +101,6 @@ module Hyperloop
|
|
|
39
101
|
request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
|
40
102
|
if uri.scheme == 'https'
|
|
41
103
|
http.use_ssl = true
|
|
42
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
43
104
|
end
|
|
44
105
|
request.body = {
|
|
45
106
|
operation: name,
|
|
@@ -86,9 +147,13 @@ module Hyperloop
|
|
|
86
147
|
end
|
|
87
148
|
regulation ||= proc { args }
|
|
88
149
|
on_dispatch do |params, operation|
|
|
150
|
+
operation.instance_variable_set(:@_dispatched_channels, []) unless operation.instance_variable_get(:@_dispatched_channels)
|
|
89
151
|
serialized_params = serialize_dispatch(params.to_h)
|
|
90
152
|
[operation.instance_exec(*context, ®ulation)].flatten.compact.uniq.each do |channel|
|
|
91
|
-
|
|
153
|
+
unless operation.instance_variable_get(:@_dispatched_channels).include?(channel)
|
|
154
|
+
operation.instance_variable_set(:@_dispatched_channels, operation.instance_variable_get(:@_dispatched_channels) << channel)
|
|
155
|
+
Hyperloop.dispatch(channel: Hyperloop::InternalPolicy.channel_to_string(channel), operation: operation.class.name, params: serialized_params)
|
|
156
|
+
end
|
|
92
157
|
end
|
|
93
158
|
end
|
|
94
159
|
end if RUBY_ENGINE != 'opal'
|
|
@@ -7,8 +7,25 @@ module Hyperloop
|
|
|
7
7
|
# client interface to sync_change or sync_destroy
|
|
8
8
|
|
|
9
9
|
class Application
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
extend React::IsomorphicHelpers::ClassMethods
|
|
11
|
+
|
|
12
|
+
if on_opal_client?
|
|
13
|
+
def self.acting_user_id
|
|
14
|
+
ClientDrivers.opts[:acting_user_id]
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
def self.acting_user_id
|
|
18
|
+
ClientDrivers.client_drivers_get_acting_user_id
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.env
|
|
23
|
+
@env = ClientDrivers.env unless @env
|
|
24
|
+
@env
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.production?
|
|
28
|
+
env == 'production'
|
|
12
29
|
end
|
|
13
30
|
end
|
|
14
31
|
|
|
@@ -59,8 +76,9 @@ module Hyperloop
|
|
|
59
76
|
}
|
|
60
77
|
elsif ClientDrivers.opts[:transport] == :action_cable
|
|
61
78
|
channel = "#{ClientDrivers.opts[:channel]}-#{channel_string}"
|
|
62
|
-
HTTP.post(ClientDrivers.polling_path('action-cable-auth', channel)).then do |response|
|
|
79
|
+
Hyperloop::HTTP.post(ClientDrivers.polling_path('action-cable-auth', channel), headers: { 'X-CSRF-Token' => ClientDrivers.opts[:form_authenticity_token] }).then do |response|
|
|
63
80
|
%x{
|
|
81
|
+
var fix_opal_0110 = 'return';
|
|
64
82
|
#{Hyperloop.action_cable_consumer}.subscriptions.create(
|
|
65
83
|
{
|
|
66
84
|
channel: "Hyperloop::ActionCableChannel",
|
|
@@ -71,9 +89,11 @@ module Hyperloop
|
|
|
71
89
|
},
|
|
72
90
|
{
|
|
73
91
|
connected: function() {
|
|
74
|
-
#{ClientDrivers.
|
|
92
|
+
if (#{ClientDrivers.env == 'development'}) { console.log("ActionCable connected to: ", channel_string); }
|
|
93
|
+
#{ClientDrivers.complete_connection(channel_string)}
|
|
75
94
|
},
|
|
76
95
|
received: function(data) {
|
|
96
|
+
if (#{ClientDrivers.env == 'development'}) { console.log("ActionCable received: ", data); }
|
|
77
97
|
#{ClientDrivers.sync_dispatch(JSON.parse(`JSON.stringify(data)`)['data'])}
|
|
78
98
|
}
|
|
79
99
|
}
|
|
@@ -81,7 +101,7 @@ module Hyperloop
|
|
|
81
101
|
}
|
|
82
102
|
end
|
|
83
103
|
else
|
|
84
|
-
HTTP.get(ClientDrivers.polling_path(:subscribe, channel_string))
|
|
104
|
+
Hyperloop::HTTP.get(ClientDrivers.polling_path(:subscribe, channel_string))
|
|
85
105
|
end
|
|
86
106
|
end
|
|
87
107
|
end
|
|
@@ -105,24 +125,26 @@ module Hyperloop
|
|
|
105
125
|
# will remove the session from the list.
|
|
106
126
|
|
|
107
127
|
prerender_footer do |controller|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
unless Hyperloop.transport == :none
|
|
129
|
+
if defined?(PusherFake)
|
|
130
|
+
path = ::Rails.application.routes.routes.detect do |route|
|
|
131
|
+
route.app == Hyperloop::Engine ||
|
|
132
|
+
(route.app.respond_to?(:app) && route.app.app == Hyperloop::Engine)
|
|
133
|
+
end.path.spec
|
|
134
|
+
pusher_fake_js = PusherFake.javascript(
|
|
135
|
+
auth: { headers: { 'X-CSRF-Token' => controller.send(:form_authenticity_token) } },
|
|
136
|
+
authEndpoint: "#{path}/hyperloop-pusher-auth"
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
controller.session.delete 'hyperloop-dummy-init' unless controller.session.id
|
|
140
|
+
id = "#{SecureRandom.uuid}-#{controller.session.id}"
|
|
141
|
+
auto_connections = Hyperloop::AutoConnect.channels(id, controller.acting_user)
|
|
118
142
|
end
|
|
119
|
-
controller.session.delete 'hyperloop-dummy-init' unless controller.session.id
|
|
120
|
-
id = "#{SecureRandom.uuid}-#{controller.session.id}"
|
|
121
|
-
auto_connections = Hyperloop::AutoConnect.channels(id, controller.acting_user)
|
|
122
143
|
config_hash = {
|
|
123
144
|
transport: Hyperloop.transport,
|
|
124
145
|
id: id,
|
|
125
146
|
acting_user_id: (controller.acting_user && controller.acting_user.id),
|
|
147
|
+
env: ::Rails.env,
|
|
126
148
|
client_logging: Hyperloop.client_logging,
|
|
127
149
|
pusher_fake_js: pusher_fake_js,
|
|
128
150
|
key: Hyperloop.key,
|
|
@@ -149,9 +171,27 @@ module Hyperloop
|
|
|
149
171
|
attr_reader :opts
|
|
150
172
|
end
|
|
151
173
|
|
|
174
|
+
isomorphic_method(:client_drivers_get_acting_user_id) do |f|
|
|
175
|
+
f.send_to_server if RUBY_ENGINE == 'opal'
|
|
176
|
+
f.when_on_server { (controller.acting_user && controller.acting_user.id) }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
isomorphic_method(:env) do |f|
|
|
180
|
+
f.when_on_client { opts[:env] }
|
|
181
|
+
f.send_to_server
|
|
182
|
+
f.when_on_server { ::Rails.env }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def self.complete_connection(channel, retries = 10)
|
|
186
|
+
get_queued_data('connect-to-transport', channel).fail do
|
|
187
|
+
after(0.25) { complete_connection(channel, retries - 1) } unless retries.zero?
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
152
191
|
def self.get_queued_data(operation, channel = nil, opts = {})
|
|
153
|
-
HTTP.get(polling_path(operation, channel), opts).then do |response|
|
|
192
|
+
Hyperloop::HTTP.get(polling_path(operation, channel), opts).then do |response|
|
|
154
193
|
response.json.each do |data|
|
|
194
|
+
`console.log("simple_poller received: ", data)` if ClientDrivers.env == 'development'
|
|
155
195
|
sync_dispatch(data[1])
|
|
156
196
|
end
|
|
157
197
|
end
|
|
@@ -161,21 +201,24 @@ module Hyperloop
|
|
|
161
201
|
|
|
162
202
|
if @initialized
|
|
163
203
|
# 1) skip initialization if already initialized
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
204
|
+
if on_opal_client? && Hyperloop.action_cable_consumer
|
|
205
|
+
# 2) if running action_cable make sure connection is up after pinging the server_up
|
|
206
|
+
# action cable closes the connection if files change on the server
|
|
207
|
+
Hyperloop::HTTP.get("#{`window.HyperloopEnginePath`}/server_up") do
|
|
208
|
+
`#{Hyperloop.action_cable_consumer}.connection.open()` if `#{Hyperloop.action_cable_consumer}.connection.disconnected`
|
|
209
|
+
end
|
|
210
|
+
end
|
|
169
211
|
return
|
|
170
212
|
end
|
|
171
213
|
|
|
172
214
|
@initialized = true
|
|
215
|
+
@opts = {}
|
|
216
|
+
|
|
217
|
+
if on_opal_client?
|
|
173
218
|
|
|
174
|
-
if RUBY_ENGINE == 'opal'
|
|
175
219
|
@opts = Hash.new(`window.HyperloopOpts`)
|
|
176
|
-
end
|
|
177
220
|
|
|
178
|
-
|
|
221
|
+
|
|
179
222
|
if opts[:transport] == :pusher
|
|
180
223
|
|
|
181
224
|
opts[:dispatch] = lambda do |data|
|
|
@@ -209,7 +252,7 @@ module Hyperloop
|
|
|
209
252
|
elsif opts[:transport] == :simple_poller
|
|
210
253
|
opts[:auto_connect].each { |channel| IncomingBroadcast.add_connection(*channel) }
|
|
211
254
|
every(opts[:seconds_between_poll]) do
|
|
212
|
-
get_queued_data(:read, nil
|
|
255
|
+
get_queued_data(:read, nil)
|
|
213
256
|
end
|
|
214
257
|
end
|
|
215
258
|
end
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
module Hyperloop
|
|
2
2
|
module AutoCreate
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return true unless Connection.root_path
|
|
10
|
-
uri = URI("#{Connection.root_path}server_up")
|
|
11
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
12
|
-
request = Net::HTTP::Get.new(uri.path)
|
|
13
|
-
if uri.scheme == 'https'
|
|
14
|
-
http.use_ssl = true
|
|
15
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
3
|
+
def table_exists?
|
|
4
|
+
# works with both rails 4 and 5 without deprecation warnings
|
|
5
|
+
if connection.respond_to?(:data_sources)
|
|
6
|
+
connection.data_sources.include?(table_name)
|
|
7
|
+
else
|
|
8
|
+
connection.tables.include?(table_name)
|
|
16
9
|
end
|
|
17
|
-
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def needs_init?
|
|
13
|
+
Hyperloop.transport != :none && Hyperloop.on_server? && !table_exists?
|
|
18
14
|
end
|
|
19
15
|
|
|
20
16
|
def create_table(*args, &block)
|
|
@@ -58,14 +54,14 @@ module Hyperloop
|
|
|
58
54
|
extend AutoCreate
|
|
59
55
|
|
|
60
56
|
def self.build_tables
|
|
61
|
-
create_table(force:
|
|
57
|
+
create_table(force: :cascade) do |t|
|
|
62
58
|
t.string :channel
|
|
63
59
|
t.string :session
|
|
64
60
|
t.datetime :created_at
|
|
65
61
|
t.datetime :expires_at
|
|
66
62
|
t.datetime :refresh_at
|
|
67
63
|
end
|
|
68
|
-
QueuedMessage.create_table(force:
|
|
64
|
+
QueuedMessage.create_table(force: :cascade) do |t|
|
|
69
65
|
t.text :data
|
|
70
66
|
t.integer :connection_id
|
|
71
67
|
end
|
|
@@ -106,6 +102,10 @@ module Hyperloop
|
|
|
106
102
|
attr_accessor :transport
|
|
107
103
|
|
|
108
104
|
def active
|
|
105
|
+
# if table doesn't exist then we are either calling from within
|
|
106
|
+
# a migration or from a console before the server has ever started
|
|
107
|
+
# in these cases there are no channels so we return nothing
|
|
108
|
+
return [] unless table_exists?
|
|
109
109
|
if Hyperloop.on_server?
|
|
110
110
|
expired.delete_all
|
|
111
111
|
refresh_connections if needs_refresh?
|
|
@@ -153,9 +153,10 @@ module Hyperloop
|
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
def root_path
|
|
156
|
-
QueuedMessage
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
# if the QueuedMessage table doesn't exist then we are either calling from within
|
|
157
|
+
# a migration or from a console before the server has ever started
|
|
158
|
+
# in these cases there is no root path to the server
|
|
159
|
+
QueuedMessage.root_path if QueuedMessage.table_exists?
|
|
159
160
|
end
|
|
160
161
|
|
|
161
162
|
def refresh_connections
|
|
@@ -163,7 +164,8 @@ module Hyperloop
|
|
|
163
164
|
channels = transport.refresh_channels
|
|
164
165
|
next_refresh = refresh_started_at + transport.refresh_channels_every
|
|
165
166
|
channels.each do |channel|
|
|
166
|
-
find_by(channel: channel, session: nil)
|
|
167
|
+
connection = find_by(channel: channel, session: nil)
|
|
168
|
+
connection.update(refresh_at: next_refresh) if connection
|
|
167
169
|
end
|
|
168
170
|
inactive.delete_all
|
|
169
171
|
end
|
|
@@ -17,7 +17,7 @@ module Hyperloop
|
|
|
17
17
|
unless method_defined? :pre_hyperloop_call
|
|
18
18
|
alias pre_hyperloop_call call
|
|
19
19
|
def call(env)
|
|
20
|
-
if
|
|
20
|
+
if Hyperloop.transport == :simple_poller && env['PATH_INFO'] && env['PATH_INFO'].include?('/hyperloop-read/')
|
|
21
21
|
Rails.logger.silence do
|
|
22
22
|
pre_hyperloop_call(env)
|
|
23
23
|
end
|
|
@@ -115,6 +115,7 @@ module Hyperloop
|
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
def pusher_auth
|
|
118
|
+
raise unless Hyperloop.transport == :pusher
|
|
118
119
|
channel = regulate params[:channel_name].gsub(/^#{Regexp.quote(Hyperloop.channel)}\-/,'').gsub('==', '::')
|
|
119
120
|
response = Hyperloop.pusher.authenticate(params[:channel_name], params[:socket_id])
|
|
120
121
|
render json: response
|
|
@@ -123,6 +124,7 @@ module Hyperloop
|
|
|
123
124
|
end
|
|
124
125
|
|
|
125
126
|
def action_cable_auth
|
|
127
|
+
raise unless Hyperloop.transport == :action_cable
|
|
126
128
|
channel = regulate params[:channel_name].gsub(/^#{Regexp.quote(Hyperloop.channel)}\-/,'')
|
|
127
129
|
salt = SecureRandom.hex
|
|
128
130
|
authorization = Hyperloop.authorization(salt, channel, client_id)
|
|
@@ -134,6 +136,8 @@ module Hyperloop
|
|
|
134
136
|
def connect_to_transport
|
|
135
137
|
root_path = request.original_url.gsub(/hyperloop-connect-to-transport.*$/, '')
|
|
136
138
|
render json: Hyperloop::Connection.connect_to_transport(params[:channel], client_id, root_path)
|
|
139
|
+
rescue Exception => e
|
|
140
|
+
render status: :service_unavailable, json: {error: e}
|
|
137
141
|
end
|
|
138
142
|
|
|
139
143
|
def execute_remote
|
|
@@ -154,6 +158,7 @@ module Hyperloop
|
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
def console_update # TODO this should just become an execute-remote-api call
|
|
161
|
+
raise unless Rails.env.development?
|
|
157
162
|
authorization = Hyperloop.authorization(params[:salt], params[:channel], params[:data][1][:broadcast_id]) #params[:data].to_json)
|
|
158
163
|
return head :unauthorized if authorization != params[:authorization]
|
|
159
164
|
Hyperloop::Connection.send_to_channel(params[:channel], params[:data])
|
|
@@ -3,6 +3,16 @@ module Hyperloop
|
|
|
3
3
|
class InternalClassPolicy
|
|
4
4
|
|
|
5
5
|
def initialize(regulated_klass)
|
|
6
|
+
unless regulated_klass.is_a?(Class)
|
|
7
|
+
# attempt to constantize the class in case eager_loading in production
|
|
8
|
+
# has loaded the policy before the class. THis will insure that if
|
|
9
|
+
# there is a class being regulated, it is loaded first.
|
|
10
|
+
begin
|
|
11
|
+
regulated_klass.constantize
|
|
12
|
+
rescue NameError
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
6
16
|
@regulated_klass = regulated_klass
|
|
7
17
|
end
|
|
8
18
|
|
|
@@ -49,11 +59,17 @@ module Hyperloop
|
|
|
49
59
|
end
|
|
50
60
|
|
|
51
61
|
def dispatch_to(*args, ®ulation)
|
|
52
|
-
actual_klass = regulated_klass.is_a?(Class)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
actual_klass = if regulated_klass.is_a?(Class)
|
|
63
|
+
regulated_klass
|
|
64
|
+
else
|
|
65
|
+
begin
|
|
66
|
+
regulated_klass.constantize
|
|
67
|
+
rescue NameError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
raise 'you can only dispatch_to Operation classes' unless actual_klass.respond_to? :dispatch_to
|
|
72
|
+
actual_klass.dispatch_to(actual_klass)
|
|
57
73
|
actual_klass.dispatch_to(*args, ®ulation)
|
|
58
74
|
end
|
|
59
75
|
|
|
@@ -82,14 +98,40 @@ module Hyperloop
|
|
|
82
98
|
end
|
|
83
99
|
end
|
|
84
100
|
|
|
101
|
+
def self.ar_base_descendants_map_cache
|
|
102
|
+
@ar_base_descendants_map_cache ||= ActiveRecord::Base.descendants.map(&:name)
|
|
103
|
+
end
|
|
104
|
+
|
|
85
105
|
def get_ar_model(str)
|
|
86
|
-
str.is_a?(Class)
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
if str.is_a?(Class)
|
|
107
|
+
unless str <= ActiveRecord::Base
|
|
108
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:non_ar_class, "#{str} is not a subclass of ActiveRecord::Base")
|
|
109
|
+
end
|
|
110
|
+
str
|
|
111
|
+
else
|
|
112
|
+
# we used to cache this here, but during eager loading the cache may get partially filled and never updated
|
|
113
|
+
# so this guard will fail, now performance will be suckish, as this guard, required for security, takes some ms
|
|
114
|
+
# def self.ar_base_descendants_map_cache
|
|
115
|
+
# @ar_base_descendants_map_cache ||= ActiveRecord::Base.descendants.map(&:name)
|
|
116
|
+
# end
|
|
117
|
+
# if Rails.env.production? && !Hyperloop::InternalClassPolicy.ar_base_descendants_map_cache.include?(str)
|
|
118
|
+
if Rails.application.config.eager_load && !ActiveRecord::Base.descendants.map(&:name).include?(str)
|
|
119
|
+
# AR::Base.descendants is eager loaded in production -> this guard works.
|
|
120
|
+
# In development it may be empty or partially filled -> this guard may fail.
|
|
121
|
+
# Thus guarded here only in production.
|
|
122
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:non_ar_class, "#{str} is either not defined or is not a subclass of ActiveRecord::Base")
|
|
123
|
+
end
|
|
124
|
+
Object.const_get(str)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def self.regulated_klasses
|
|
129
|
+
@regulated_klasses ||= Set.new
|
|
89
130
|
end
|
|
90
131
|
|
|
91
132
|
def regulate(regulation_klass, policy, args, ®ulation)
|
|
92
133
|
process_args(policy, regulation_klass.allowed_opts, args, regulation) do |regulated_klass, opts|
|
|
134
|
+
self.class.regulated_klasses << regulated_klass.to_s
|
|
93
135
|
regulation_klass.add_regulation regulated_klass, opts, ®ulation
|
|
94
136
|
end
|
|
95
137
|
end
|
|
@@ -201,8 +243,23 @@ module Hyperloop
|
|
|
201
243
|
class ClassConnectionRegulation < Regulation
|
|
202
244
|
|
|
203
245
|
def self.add_regulation(klass, opts={}, ®ulation)
|
|
204
|
-
actual_klass = klass.is_a?(Class)
|
|
205
|
-
|
|
246
|
+
actual_klass = if klass.is_a?(Class)
|
|
247
|
+
klass
|
|
248
|
+
else
|
|
249
|
+
begin
|
|
250
|
+
klass.constantize
|
|
251
|
+
rescue NameError
|
|
252
|
+
nil
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
if actual_klass && actual_klass.respond_to?(:dispatch_to)
|
|
256
|
+
begin
|
|
257
|
+
actual_klass.dispatch_to(actual_klass)
|
|
258
|
+
rescue NoMethodError
|
|
259
|
+
# this is the case for ClassPolicy where the instance method :dispatch_to has been deleted.
|
|
260
|
+
nil
|
|
261
|
+
end
|
|
262
|
+
end
|
|
206
263
|
super
|
|
207
264
|
end
|
|
208
265
|
|
|
@@ -321,10 +378,18 @@ module Hyperloop
|
|
|
321
378
|
@obj
|
|
322
379
|
end
|
|
323
380
|
|
|
381
|
+
def self.raise_operation_access_violation(message, details)
|
|
382
|
+
Hyperloop.on_error(Hyperloop::AccessViolation, message, details)
|
|
383
|
+
raise Hyperloop::AccessViolation
|
|
384
|
+
end
|
|
385
|
+
|
|
324
386
|
def self.regulate_connection(acting_user, channel_string)
|
|
325
387
|
channel = channel_string.split("-")
|
|
326
388
|
if channel.length > 1
|
|
327
389
|
id = channel[1..-1].join("-")
|
|
390
|
+
unless Hyperloop::InternalClassPolicy.regulated_klasses.include?(channel[0])
|
|
391
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation(:not_a_channel, "#{channel[0]} is not regulated channel class")
|
|
392
|
+
end
|
|
328
393
|
object = Object.const_get(channel[0]).find(id)
|
|
329
394
|
InstanceConnectionRegulation.connect(object, acting_user)
|
|
330
395
|
else
|
|
@@ -486,7 +551,7 @@ module Hyperloop
|
|
|
486
551
|
|
|
487
552
|
module PolicyAutoLoader
|
|
488
553
|
def self.load(name, value)
|
|
489
|
-
const_get("#{name}Policy") if name && !(
|
|
554
|
+
const_get("#{name}Policy") if name && !name.end_with?("Policy".freeze) && value.is_a?(Class)
|
|
490
555
|
rescue Exception => e
|
|
491
556
|
raise e if e.is_a?(LoadError) && e.message =~ /Unable to autoload constant #{name}Policy/
|
|
492
557
|
end
|
|
@@ -515,8 +580,8 @@ class Class
|
|
|
515
580
|
|
|
516
581
|
Hyperloop::ClassPolicyMethods.instance_methods.each do |method|
|
|
517
582
|
define_method method do |*args, &block|
|
|
518
|
-
if name
|
|
519
|
-
@hyperloop_internal_policy_object = Hyperloop::InternalClassPolicy.new(name.
|
|
583
|
+
if name.end_with?("Policy".freeze)
|
|
584
|
+
@hyperloop_internal_policy_object = Hyperloop::InternalClassPolicy.new(name.sub(/Policy$/,""))
|
|
520
585
|
include Hyperloop::PolicyMethods
|
|
521
586
|
send method, *args, &block
|
|
522
587
|
else
|