hyper-operation 1.0.alpha1.5 → 1.0.alpha1.6
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/.travis.yml +1 -0
- data/hyper-operation.gemspec +6 -5
- data/lib/hyper-operation.rb +2 -1
- data/lib/hyper-operation/api.rb +6 -2
- data/lib/hyper-operation/async_sleep.rb +23 -0
- data/lib/hyper-operation/railway/dispatcher.rb +0 -1
- data/lib/hyper-operation/railway/run.rb +4 -1
- data/lib/hyper-operation/server_op.rb +1 -1
- data/lib/hyper-operation/transport/client_drivers.rb +2 -2
- data/lib/hyper-operation/transport/connection.rb +58 -136
- data/lib/hyper-operation/transport/connection_adapter/active_record.rb +113 -0
- data/lib/hyper-operation/transport/connection_adapter/active_record/auto_create.rb +26 -0
- data/lib/hyper-operation/transport/connection_adapter/active_record/connection.rb +47 -0
- data/lib/hyper-operation/transport/connection_adapter/active_record/queued_message.rb +42 -0
- data/lib/hyper-operation/transport/connection_adapter/redis.rb +94 -0
- data/lib/hyper-operation/transport/connection_adapter/redis/connection.rb +85 -0
- data/lib/hyper-operation/transport/connection_adapter/redis/queued_message.rb +34 -0
- data/lib/hyper-operation/transport/connection_adapter/redis/redis_record.rb +158 -0
- data/lib/hyper-operation/transport/hyperstack.rb +9 -1
- data/lib/hyper-operation/transport/hyperstack_controller.rb +5 -1
- data/lib/hyper-operation/transport/policy.rb +9 -4
- data/lib/hyper-operation/version.rb +1 -1
- metadata +64 -42
- data/lib/hyper-operation/delay_and_interval.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf7ea211cf5d34d735df9a5318691ff4261b26e15816c6529138da58ce23ead5
|
4
|
+
data.tar.gz: bbce8cdf73f646fe7abd922a0667739f486368c723800b7f6efecbbec9ad7468
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afdb25d53a08ed95794a3419f0d3ea341f04c8da594d68189029f2c5c87568b4c0edd9fe4b57df4a42042f63f66de585e4a1a65dde8efb56063d79019fddee75
|
7
|
+
data.tar.gz: c5fbd8014773c171dee7d8556f84da60fcdfc77509a7ae6f4380c3b7b729a2c438faa4cd5c1d09a92dc19ceed2b77203154314ebd75b6e3b01988383485caf88
|
data/.travis.yml
CHANGED
data/hyper-operation.gemspec
CHANGED
@@ -29,24 +29,25 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_dependency 'opal-activesupport', '~> 0.3.1'
|
30
30
|
spec.add_dependency 'tty-table'
|
31
31
|
|
32
|
-
spec.add_development_dependency 'bundler'
|
32
|
+
spec.add_development_dependency 'bundler'
|
33
33
|
spec.add_development_dependency 'chromedriver-helper'
|
34
34
|
spec.add_development_dependency 'database_cleaner'
|
35
35
|
spec.add_development_dependency 'hyper-spec', Hyperstack::Operation::VERSION
|
36
36
|
spec.add_development_dependency 'mysql2'
|
37
|
-
spec.add_development_dependency 'opal', '>= 0.11.0', '< 0.12.0'
|
38
37
|
spec.add_development_dependency 'opal-browser', '~> 0.2.0'
|
39
|
-
spec.add_development_dependency 'opal-rails', '
|
38
|
+
spec.add_development_dependency 'opal-rails', '>= 0.9.4', '< 2.0'
|
40
39
|
spec.add_development_dependency 'pry-rescue'
|
40
|
+
spec.add_development_dependency 'pry-stack_explorer'
|
41
41
|
spec.add_development_dependency 'puma'
|
42
42
|
spec.add_development_dependency 'pusher'
|
43
43
|
spec.add_development_dependency 'pusher-fake'
|
44
|
-
spec.add_development_dependency 'rails', '>=
|
44
|
+
spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
|
45
45
|
spec.add_development_dependency 'rake'
|
46
46
|
spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
|
47
|
+
spec.add_development_dependency 'redis'
|
47
48
|
spec.add_development_dependency 'rspec-rails'
|
48
49
|
spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
|
49
50
|
spec.add_development_dependency 'rspec-wait'
|
50
|
-
spec.add_development_dependency 'sqlite3', '~> 1.
|
51
|
+
spec.add_development_dependency 'sqlite3', '~> 1.4.2' # see https://github.com/rails/rails/issues/35153
|
51
52
|
spec.add_development_dependency 'timecop', '~> 0.8.1'
|
52
53
|
end
|
data/lib/hyper-operation.rb
CHANGED
@@ -28,6 +28,7 @@ if RUBY_ENGINE == 'opal'
|
|
28
28
|
require 'hyper-operation/railway/validations'
|
29
29
|
require 'hyper-operation/server_op'
|
30
30
|
require 'hyper-operation/boot'
|
31
|
+
require 'hyper-operation/async_sleep'
|
31
32
|
else
|
32
33
|
require 'tty-table'
|
33
34
|
require 'hyperstack-config'
|
@@ -45,7 +46,7 @@ else
|
|
45
46
|
require 'hyper-operation/transport/client_drivers'
|
46
47
|
require 'hyper-operation/transport/acting_user'
|
47
48
|
require 'opal-activesupport'
|
48
|
-
require 'hyper-operation/
|
49
|
+
require 'hyper-operation/async_sleep'
|
49
50
|
require 'hyper-operation/exception'
|
50
51
|
require 'hyper-operation/promise'
|
51
52
|
require 'hyper-operation/railway'
|
data/lib/hyper-operation/api.rb
CHANGED
@@ -46,8 +46,12 @@ module Hyperstack
|
|
46
46
|
@_railway.process_params(args)
|
47
47
|
@_railway.process_validations
|
48
48
|
@_railway.run
|
49
|
-
|
50
|
-
@_railway.
|
49
|
+
# return the result from dispatch in case there is an error
|
50
|
+
if (dispatch_result = @_railway.dispatch).rejected?
|
51
|
+
dispatch_result
|
52
|
+
else
|
53
|
+
@_railway.result
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module AsyncSleep
|
3
|
+
if RUBY_ENGINE == 'opal'
|
4
|
+
def self.every(*args, &block)
|
5
|
+
every(*args, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.after(*args, &block)
|
9
|
+
after(*args, &block)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def every(time, &block)
|
15
|
+
Thread.new { loop { sleep time; block.call } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def after(time, &block)
|
19
|
+
Thread.new { sleep time; block.call }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -4,10 +4,13 @@ module Hyperstack
|
|
4
4
|
class Exit < StandardError
|
5
5
|
attr_reader :state
|
6
6
|
attr_reader :result
|
7
|
-
def initialize(state, result)
|
7
|
+
def initialize(state, result = nil)
|
8
8
|
@state = state
|
9
9
|
@result = result
|
10
10
|
end
|
11
|
+
def to_s
|
12
|
+
@state
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
class Railway
|
@@ -13,7 +13,7 @@ module Hyperstack
|
|
13
13
|
hash = serialize_params(hash)
|
14
14
|
Hyperstack::HTTP.post(
|
15
15
|
"#{`window.HyperstackEnginePath`}/execute_remote",
|
16
|
-
payload: {
|
16
|
+
payload: {hyperstack_secured_json: {operation: name, params: hash}.to_json},
|
17
17
|
headers: headers.merge('X-CSRF-Token' => Hyperstack::ClientDrivers.opts[:form_authenticity_token])
|
18
18
|
)
|
19
19
|
.then do |response|
|
@@ -97,7 +97,7 @@ module Hyperstack
|
|
97
97
|
%x{
|
98
98
|
var channel = #{ClientDrivers.opts[:pusher_api]}.subscribe(#{channel.gsub('::', '==')});
|
99
99
|
channel.bind('dispatch', #{ClientDrivers.opts[:dispatch]})
|
100
|
-
channel.bind('pusher:subscription_succeeded', #{
|
100
|
+
channel.bind('pusher:subscription_succeeded', #{->(*) { ClientDrivers.get_queued_data("connect-to-transport", channel_string)}})
|
101
101
|
}
|
102
102
|
@pusher_dispatcher_registered = true
|
103
103
|
elsif ClientDrivers.opts[:transport] == :action_cable
|
@@ -169,7 +169,7 @@ module Hyperstack
|
|
169
169
|
config_hash = {
|
170
170
|
transport: Hyperstack.transport,
|
171
171
|
id: id,
|
172
|
-
acting_user_id: (controller.acting_user && controller.acting_user.id),
|
172
|
+
acting_user_id: (controller.acting_user.respond_to?(:id) && controller.acting_user.id),
|
173
173
|
env: ::Rails.env,
|
174
174
|
client_logging: Hyperstack.client_logging,
|
175
175
|
pusher_fake_js: pusher_fake_js,
|
@@ -1,173 +1,95 @@
|
|
1
|
-
|
2
|
-
module AutoCreate
|
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)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def needs_init?
|
13
|
-
Hyperstack.transport != :none && Hyperstack.on_server? && !table_exists?
|
14
|
-
end
|
15
|
-
|
16
|
-
def create_table(*args, &block)
|
17
|
-
connection.create_table(table_name, *args, &block) if needs_init?
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Connection < ActiveRecord::Base
|
22
|
-
class QueuedMessage < ActiveRecord::Base
|
23
|
-
|
24
|
-
extend AutoCreate
|
25
|
-
|
26
|
-
self.table_name = 'hyperstack_queued_messages'
|
27
|
-
|
28
|
-
do_not_synchronize
|
29
|
-
|
30
|
-
serialize :data
|
1
|
+
# frozen_string_literal: true
|
31
2
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
3
|
+
module Hyperstack
|
4
|
+
class Connection
|
5
|
+
class << self
|
6
|
+
attr_accessor :transport, :connection_adapter, :show_diagnostics
|
7
|
+
|
8
|
+
def adapter
|
9
|
+
adapter_name = Hyperstack.connection[:adapter].to_s
|
10
|
+
adapter_path = "hyper-operation/transport/connection_adapter/#{adapter_name}"
|
11
|
+
|
12
|
+
begin
|
13
|
+
require adapter_path
|
14
|
+
rescue LoadError => e
|
15
|
+
if e.path == adapter_path
|
16
|
+
raise e.class, "Could not load the '#{adapter_name}' adapter. Make sure the adapter is spelled correctly in your Hyperstack config, and the necessary gems are in your Gemfile.", e.backtrace
|
17
|
+
|
18
|
+
# Bubbled up from the adapter require. Prefix the exception message
|
19
|
+
# with some guidance about how to address it and reraise.
|
20
|
+
else
|
21
|
+
raise e.class, "Error loading the '#{adapter_name}' adapter. Missing a gem it depends on? #{e.message}", e.backtrace
|
22
|
+
end
|
23
|
+
end
|
44
24
|
|
45
|
-
|
46
|
-
|
25
|
+
adapter_name = adapter_name.camelize
|
26
|
+
"Hyperstack::ConnectionAdapter::#{adapter_name}".constantize
|
47
27
|
end
|
48
28
|
|
49
|
-
def
|
50
|
-
|
29
|
+
def build_tables
|
30
|
+
adapter.build_tables
|
51
31
|
end
|
52
|
-
end
|
53
|
-
|
54
|
-
extend AutoCreate
|
55
32
|
|
56
|
-
|
57
|
-
|
58
|
-
t.string :channel
|
59
|
-
t.string :session
|
60
|
-
t.datetime :created_at
|
61
|
-
t.datetime :expires_at
|
62
|
-
t.datetime :refresh_at
|
63
|
-
end
|
64
|
-
QueuedMessage.create_table(force: :cascade) do |t|
|
65
|
-
t.text :data
|
66
|
-
t.integer :connection_id
|
33
|
+
def build_tables?
|
34
|
+
adapter.respond_to?(:build_tables)
|
67
35
|
end
|
68
|
-
end
|
69
|
-
|
70
|
-
do_not_synchronize
|
71
|
-
|
72
|
-
self.table_name = 'hyperstack_connections'
|
73
|
-
|
74
|
-
has_many :messages,
|
75
|
-
foreign_key: 'connection_id',
|
76
|
-
class_name: 'Hyperstack::Connection::QueuedMessage',
|
77
|
-
dependent: :destroy
|
78
|
-
scope :expired,
|
79
|
-
-> { where('expires_at IS NOT NULL AND expires_at < ?', Time.zone.now) }
|
80
|
-
scope :pending_for,
|
81
|
-
->(channel) { where(channel: channel).where('session IS NOT NULL') }
|
82
|
-
scope :inactive,
|
83
|
-
-> { where('session IS NULL AND refresh_at < ?', Time.zone.now) }
|
84
|
-
|
85
|
-
def self.needs_refresh?
|
86
|
-
exists?(['refresh_at IS NOT NULL AND refresh_at < ?', Time.zone.now])
|
87
|
-
end
|
88
|
-
|
89
|
-
def transport
|
90
|
-
self.class.transport
|
91
|
-
end
|
92
|
-
|
93
|
-
before_create do
|
94
|
-
if session
|
95
|
-
self.expires_at = Time.now + transport.expire_new_connection_in
|
96
|
-
elsif transport.refresh_channels_every != :never
|
97
|
-
self.refresh_at = Time.now + transport.refresh_channels_every
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class << self
|
102
|
-
attr_accessor :transport
|
103
36
|
|
104
37
|
def active
|
105
|
-
|
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
|
-
if Hyperstack.on_server?
|
110
|
-
expired.delete_all
|
111
|
-
refresh_connections if needs_refresh?
|
112
|
-
end
|
113
|
-
all.pluck(:channel).uniq
|
38
|
+
adapter.active
|
114
39
|
end
|
115
40
|
|
116
41
|
def open(channel, session = nil, root_path = nil)
|
117
|
-
|
118
|
-
|
42
|
+
puts "open(#{channel}, #{session}, #{root_path})" if show_diagnostics
|
43
|
+
|
44
|
+
adapter.open(channel, session, root_path).tap do |c|
|
45
|
+
puts " - open returning #{c}" if show_diagnostics
|
46
|
+
end
|
119
47
|
end
|
120
48
|
|
121
49
|
def send_to_channel(channel, data)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
transport.send_data(channel, data) if exists?(channel: channel, session: nil)
|
50
|
+
puts "send_to_channel(#{channel}, #{data})" if show_diagnostics
|
51
|
+
|
52
|
+
adapter.send_to_channel(channel, data)
|
126
53
|
end
|
127
54
|
|
128
55
|
def read(session, root_path)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
QueuedMessage.for_session(session).destroy_all.pluck(:data)
|
56
|
+
puts "read(#{session}, #{root_path})" if show_diagnostics
|
57
|
+
|
58
|
+
adapter.read(session, root_path)
|
133
59
|
end
|
134
60
|
|
135
61
|
def connect_to_transport(channel, session, root_path)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
connection.destroy
|
140
|
-
else
|
141
|
-
messages = []
|
142
|
-
end
|
143
|
-
open(channel)
|
144
|
-
messages
|
62
|
+
puts "connect_to_transport(#{channel}, #{session}, #{root_path})" if show_diagnostics
|
63
|
+
|
64
|
+
adapter.connect_to_transport(channel, session, root_path)
|
145
65
|
end
|
146
66
|
|
147
67
|
def disconnect(channel)
|
148
|
-
|
68
|
+
adapter.disconnect(channel)
|
149
69
|
end
|
150
70
|
|
151
71
|
def root_path=(path)
|
152
|
-
|
72
|
+
adapter.root_path = path
|
153
73
|
end
|
154
74
|
|
155
75
|
def root_path
|
156
|
-
|
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?
|
76
|
+
adapter.root_path
|
160
77
|
end
|
161
78
|
|
162
79
|
def refresh_connections
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
80
|
+
adapter.refresh_connections
|
81
|
+
end
|
82
|
+
|
83
|
+
def method_missing(method_name, *args, &block)
|
84
|
+
if adapter::Connection.respond_to?(method_name)
|
85
|
+
adapter::Connection.send(method_name, *args, &block)
|
86
|
+
else
|
87
|
+
super
|
169
88
|
end
|
170
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def respond_to_missing?(method_name, include_private = false)
|
92
|
+
adapter::Connection.respond_to?(method_name)
|
171
93
|
end
|
172
94
|
end
|
173
95
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'active_record/connection'
|
4
|
+
require_relative 'active_record/queued_message'
|
5
|
+
|
6
|
+
module Hyperstack
|
7
|
+
module ConnectionAdapter
|
8
|
+
module ActiveRecord
|
9
|
+
class << self
|
10
|
+
def build_tables
|
11
|
+
Connection.create_table(force: :cascade) do |t|
|
12
|
+
t.string :channel
|
13
|
+
t.string :session
|
14
|
+
t.datetime :created_at
|
15
|
+
t.datetime :expires_at
|
16
|
+
t.datetime :refresh_at
|
17
|
+
end
|
18
|
+
|
19
|
+
QueuedMessage.create_table(force: :cascade) do |t|
|
20
|
+
t.text :data
|
21
|
+
t.integer :connection_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def transport
|
26
|
+
Hyperstack::Connection.transport
|
27
|
+
end
|
28
|
+
|
29
|
+
def active
|
30
|
+
# if table doesn't exist then we are either calling from within
|
31
|
+
# a migration or from a console before the server has ever started
|
32
|
+
# in these cases there are no channels so we return nothing
|
33
|
+
return [] unless Connection.table_exists?
|
34
|
+
|
35
|
+
if Hyperstack.on_server?
|
36
|
+
Connection.expired.delete_all
|
37
|
+
refresh_connections if Connection.needs_refresh?
|
38
|
+
end
|
39
|
+
|
40
|
+
Connection.all.pluck(:channel).uniq
|
41
|
+
rescue ::ActiveRecord::StatementInvalid
|
42
|
+
[]
|
43
|
+
end
|
44
|
+
|
45
|
+
def open(channel, session = nil, root_path = nil)
|
46
|
+
self.root_path = root_path
|
47
|
+
|
48
|
+
Connection.find_or_create_by(channel: channel, session: session)
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_to_channel(channel, data)
|
52
|
+
Connection.pending_for(channel).each do |connection|
|
53
|
+
QueuedMessage.create(data: data, hyperstack_connection: connection)
|
54
|
+
end
|
55
|
+
|
56
|
+
transport.send_data(channel, data) if Connection.exists?(channel: channel, session: nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
def read(session, root_path)
|
60
|
+
self.root_path = root_path
|
61
|
+
|
62
|
+
Connection.where(session: session)
|
63
|
+
.update_all(expires_at: Time.current + transport.expire_polled_connection_in)
|
64
|
+
|
65
|
+
QueuedMessage.for_session(session).destroy_all.pluck(:data)
|
66
|
+
end
|
67
|
+
|
68
|
+
def connect_to_transport(channel, session, root_path)
|
69
|
+
self.root_path = root_path
|
70
|
+
|
71
|
+
if (connection = Connection.find_by(channel: channel, session: session))
|
72
|
+
messages = connection.messages.pluck(:data)
|
73
|
+
connection.destroy
|
74
|
+
else
|
75
|
+
messages = []
|
76
|
+
end
|
77
|
+
|
78
|
+
open(channel)
|
79
|
+
|
80
|
+
messages
|
81
|
+
end
|
82
|
+
|
83
|
+
def disconnect(channel)
|
84
|
+
Connection.find_by(channel: channel, session: nil).destroy
|
85
|
+
end
|
86
|
+
|
87
|
+
def root_path=(path)
|
88
|
+
QueuedMessage.root_path = path if path
|
89
|
+
end
|
90
|
+
|
91
|
+
def root_path
|
92
|
+
# if the QueuedMessage table doesn't exist then we are either calling from within
|
93
|
+
# a migration or from a console before the server has ever started
|
94
|
+
# in these cases there is no root path to the server
|
95
|
+
QueuedMessage.root_path if QueuedMessage.table_exists?
|
96
|
+
end
|
97
|
+
|
98
|
+
def refresh_connections
|
99
|
+
refresh_started_at = Time.current
|
100
|
+
channels = transport.refresh_channels
|
101
|
+
next_refresh = refresh_started_at + transport.refresh_channels_every
|
102
|
+
|
103
|
+
channels.each do |channel|
|
104
|
+
connection = Connection.find_by(channel: channel, session: nil)
|
105
|
+
connection.update(refresh_at: next_refresh) if connection
|
106
|
+
end
|
107
|
+
|
108
|
+
Connection.inactive.delete_all
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|