hyper-operation 1.0.alpha1.2 → 1.0.alpha1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +1 -0
  4. data/hyper-operation.gemspec +6 -4
  5. data/lib/hyper-operation.rb +4 -1
  6. data/lib/hyper-operation/api.rb +6 -2
  7. data/lib/hyper-operation/async_sleep.rb +23 -0
  8. data/lib/hyper-operation/exception.rb +29 -3
  9. data/lib/hyper-operation/promise.rb +32 -2
  10. data/lib/hyper-operation/railway/dispatcher.rb +0 -1
  11. data/lib/hyper-operation/railway/run.rb +57 -48
  12. data/lib/hyper-operation/railway/validations.rb +9 -2
  13. data/lib/hyper-operation/server_op.rb +31 -9
  14. data/lib/hyper-operation/transport/client_drivers.rb +45 -11
  15. data/lib/hyper-operation/transport/connection.rb +58 -136
  16. data/lib/hyper-operation/transport/connection_adapter/active_record.rb +113 -0
  17. data/lib/hyper-operation/transport/connection_adapter/active_record/auto_create.rb +26 -0
  18. data/lib/hyper-operation/transport/connection_adapter/active_record/connection.rb +47 -0
  19. data/lib/hyper-operation/transport/connection_adapter/active_record/queued_message.rb +42 -0
  20. data/lib/hyper-operation/transport/connection_adapter/redis.rb +94 -0
  21. data/lib/hyper-operation/transport/connection_adapter/redis/connection.rb +85 -0
  22. data/lib/hyper-operation/transport/connection_adapter/redis/queued_message.rb +34 -0
  23. data/lib/hyper-operation/transport/connection_adapter/redis/redis_record.rb +158 -0
  24. data/lib/hyper-operation/transport/hyperstack.rb +15 -2
  25. data/lib/hyper-operation/transport/hyperstack_controller.rb +6 -2
  26. data/lib/hyper-operation/transport/policy.rb +16 -26
  27. data/lib/hyper-operation/transport/policy_diagnostics.rb +106 -0
  28. data/lib/hyper-operation/version.rb +1 -1
  29. metadata +79 -38
  30. data/Gemfile.lock +0 -385
  31. 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: d47ac42a99f25a99dc9885760fc423e2c7e10eb8bf7c0e2a364ba46875befe3e
4
- data.tar.gz: 8dc9076df7d602707f48395175f565c53aa1fda133c3a5755cd2b75b67204a49
3
+ metadata.gz: a56623fa556f5390dc8771aa7364bcf1b28915b29cdefc7bff643815cdeb23f8
4
+ data.tar.gz: 592ca1144190525c14f98f2e38155942583489b62b94b3605f6707462a1b15f5
5
5
  SHA512:
6
- metadata.gz: 77dcf8399dbad50f748448754283ef8ee4e357912e0b2aba3a5fd028d750fe492aeeca6edd24b653dec2f5e4604392aac90b98802050806d9dd1d38d4e282a47
7
- data.tar.gz: 838921510db41e89786f21be04d67ffdc54404d048bd2fac01d381cd07f57d30b8073b7ff1a8768a632a5ae34098b683c460d4c9f417f4df8e6002b1016e1c24
6
+ metadata.gz: 592851310258a5036585eb9c6781955070696810cedd90cdfdaa3ee7f69313cdeb9166fdd0c5e81c37fe63be1074fb275bb7bb522650c473ea2dfc4280f3a0e4
7
+ data.tar.gz: 34249a102c0f16febeb00e998368d3f358c726045af29ebc8b78431bee96230381ddc6e0fae0576d4a955c40e2bb83b135d9723c33f7af5e62738dd42d5ccdca
data/.gitignore CHANGED
@@ -53,3 +53,7 @@ bower.json
53
53
  .idea
54
54
  .vscode
55
55
  *.iml
56
+
57
+ # ignore Gemfile.locks https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
58
+ /spec/test_app/Gemfile.lock
59
+ /Gemfile.lock
data/.travis.yml CHANGED
@@ -7,6 +7,7 @@ rvm:
7
7
  - ruby-head
8
8
  services:
9
9
  - mysql
10
+ - redis-server
10
11
  env:
11
12
  - DRIVER=google-chrome TZ=Europe/Berlin
12
13
  matrix:
@@ -27,25 +27,27 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency 'hyper-component', Hyperstack::Operation::VERSION
28
28
  spec.add_dependency 'mutations'
29
29
  spec.add_dependency 'opal-activesupport', '~> 0.3.1'
30
+ spec.add_dependency 'tty-table'
30
31
 
31
32
  spec.add_development_dependency 'bundler'
32
33
  spec.add_development_dependency 'chromedriver-helper'
33
34
  spec.add_development_dependency 'database_cleaner'
34
35
  spec.add_development_dependency 'hyper-spec', Hyperstack::Operation::VERSION
35
36
  spec.add_development_dependency 'mysql2'
36
- spec.add_development_dependency 'opal', '>= 0.11.0', '< 0.12.0'
37
37
  spec.add_development_dependency 'opal-browser', '~> 0.2.0'
38
- spec.add_development_dependency 'opal-rails', '~> 0.9.4'
38
+ spec.add_development_dependency 'opal-rails', '>= 0.9.4', '< 2.0'
39
39
  spec.add_development_dependency 'pry-rescue'
40
+ spec.add_development_dependency 'pry-stack_explorer'
40
41
  spec.add_development_dependency 'puma'
41
42
  spec.add_development_dependency 'pusher'
42
43
  spec.add_development_dependency 'pusher-fake'
43
- spec.add_development_dependency 'rails', '>= 4.0.0'
44
+ spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
44
45
  spec.add_development_dependency 'rake'
45
46
  spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
47
+ spec.add_development_dependency 'redis'
46
48
  spec.add_development_dependency 'rspec-rails'
47
49
  spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
48
50
  spec.add_development_dependency 'rspec-wait'
49
- spec.add_development_dependency 'sqlite3'
51
+ spec.add_development_dependency 'sqlite3', '~> 1.4.2' # see https://github.com/rails/rails/issues/35153
50
52
  spec.add_development_dependency 'timecop', '~> 0.8.1'
51
53
  end
@@ -28,7 +28,9 @@ 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
33
+ require 'tty-table'
32
34
  require 'hyperstack-config'
33
35
  require 'mutations'
34
36
  Mutations::HashFilter.register_additional_filter(Mutations::DuckFilter, :duck)
@@ -40,10 +42,11 @@ else
40
42
  require 'hyper-operation/transport/connection'
41
43
  require 'hyper-operation/transport/hyperstack'
42
44
  require 'hyper-operation/transport/policy'
45
+ require 'hyper-operation/transport/policy_diagnostics'
43
46
  require 'hyper-operation/transport/client_drivers'
44
47
  require 'hyper-operation/transport/acting_user'
45
48
  require 'opal-activesupport'
46
- require 'hyper-operation/delay_and_interval'
49
+ require 'hyper-operation/async_sleep'
47
50
  require 'hyper-operation/exception'
48
51
  require 'hyper-operation/promise'
49
52
  require 'hyper-operation/railway'
@@ -46,8 +46,12 @@ module Hyperstack
46
46
  @_railway.process_params(args)
47
47
  @_railway.process_validations
48
48
  @_railway.run
49
- @_railway.dispatch
50
- @_railway.result
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
@@ -1,13 +1,39 @@
1
+ module Mutations
2
+ class ErrorArray
3
+ def self.new_from_error_hash(errors)
4
+ new(errors.collect do |key, values|
5
+ ErrorAtom.new(key, values[:symbol], values)
6
+ end)
7
+ end
8
+ end
9
+ end
10
+
1
11
  module Hyperstack
2
12
  class AccessViolation < StandardError
3
- def message
4
- "Hyperstack::Operation::AccessViolation: #{super}"
13
+ attr_accessor :details
14
+
15
+ def initialize(message = nil, details = nil)
16
+ super("Hyperstack::AccessViolation#{':' + message.to_s if message}")
17
+ @details = details
18
+ end
19
+
20
+ def __hyperstack_on_error(operation, params, fmted_message)
21
+ Hyperstack.on_error(operation, self, params, fmted_message)
5
22
  end
6
23
  end
7
24
 
8
25
  class Operation
9
26
  class ValidationException < Mutations::ValidationException
27
+ def as_json(*)
28
+ errors.as_json
29
+ end
30
+
31
+ def initialize(errors)
32
+ unless errors.is_a? Mutations::ErrorHash
33
+ errors = Mutations::ErrorArray.new_from_error_hash(errors)
34
+ end
35
+ super(errors)
36
+ end
10
37
  end
11
38
  end
12
-
13
39
  end
@@ -1,3 +1,15 @@
1
+ # Caution. For now Hyperstack maintains its own copy of the Promise class.
2
+ # Eventually the diff between hyperstacks version and the official Opal version
3
+ # should be put into a PR.
4
+
5
+ # A key feature add is the Fail Exception class which is simply there to allow
6
+ # a `always` block to reject without raising an error. To use this see the run.rb
7
+ # module.
8
+
9
+ # Also see exception! method for the part of the code that detects the Fail exception.
10
+
11
+ # See https://github.com/opal/opal/issues/1967 for details.
12
+
1
13
  class Promise
2
14
  def self.value(value)
3
15
  new.resolve(value)
@@ -11,6 +23,13 @@ class Promise
11
23
  When.new(promises)
12
24
  end
13
25
 
26
+ class Fail < StandardError
27
+ attr_reader :result
28
+ def initialize(result)
29
+ @result = result
30
+ end
31
+ end
32
+
14
33
  attr_reader :error, :prev, :next
15
34
 
16
35
  def initialize(action = {})
@@ -104,6 +123,7 @@ class Promise
104
123
 
105
124
  begin
106
125
  if block = @action[:success] || @action[:always]
126
+ @realized = :resolve
107
127
  value = block.call(value)
108
128
  end
109
129
 
@@ -137,6 +157,9 @@ class Promise
137
157
 
138
158
  begin
139
159
  if block = @action[:failure] || @action[:always]
160
+ # temporarily set values so always can determine if this
161
+ # was a reject or resolve
162
+ @realized = :reject
140
163
  value = block.call(value)
141
164
  end
142
165
 
@@ -164,8 +187,15 @@ class Promise
164
187
  end
165
188
 
166
189
  def exception!(error)
167
- @exception = true
168
-
190
+ # If the error is a Promise::Fail, then
191
+ # the error becomes the error.result value
192
+ # this allows code to raise an error on an
193
+ # object that is not an error.
194
+ if error.is_a? Promise::Fail
195
+ error = error.result
196
+ else
197
+ @exception = true
198
+ end
169
199
  reject!(error)
170
200
  end
171
201
 
@@ -1,7 +1,6 @@
1
1
  module Hyperstack
2
2
  class Operation
3
3
  class Railway
4
-
5
4
  def receivers
6
5
  self.class.receivers
7
6
  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
@@ -21,7 +24,7 @@ module Hyperstack
21
24
  @tracks ||= []
22
25
  end
23
26
 
24
- def to_opts(tie, args, block)
27
+ def build_tie(tie, args, block)
25
28
  if args.count.zero?
26
29
  { run: block }
27
30
  elsif args[0].is_a?(Hash)
@@ -41,7 +44,7 @@ module Hyperstack
41
44
 
42
45
  [:step, :failed, :async].each do |tie|
43
46
  define_method :"add_#{tie}" do |*args, &block|
44
- tracks << to_opts(tie, args, block)
47
+ tracks << build_tie(tie, args, block)
45
48
  end
46
49
  end
47
50
 
@@ -55,44 +58,36 @@ module Hyperstack
55
58
  end
56
59
 
57
60
  def step(opts)
58
- if @last_result.is_a? Promise
59
- @last_result = @last_result.then do |*result|
60
- @last_result = result
61
- apply(opts, :in_promise)
62
- end
63
- elsif @state == :success
64
- apply(opts)
65
- end
61
+ @promise_chain = @promise_chain
62
+ .then { |result| apply(result, :success, opts) }
66
63
  end
67
64
 
68
65
  def failed(opts)
69
- if @last_result.is_a? Promise
70
- @last_result = @last_result.fail do |e|
71
- @last_result = e
72
- apply(opts, :in_promise)
73
- raise @last_result if @last_result.is_a? Exception
74
- raise e
66
+ @promise_chain = @promise_chain
67
+ .always do |result|
68
+ @state = :failed if @promise_chain.rejected? && @state != :abort
69
+ apply(result, :failed, opts)
75
70
  end
76
- elsif @state == :failed
77
- apply(opts)
78
- end
79
71
  end
80
72
 
81
73
  def async(opts)
82
- apply(opts) if @state != :failed
74
+ @promise_chain = @promise_chain_start = Promise.new
75
+ @promise_chain.resolve(@last_async_result)
76
+ step(opts)
83
77
  end
84
78
 
85
- def apply(opts, in_promise = nil)
79
+ def apply(result, state, opts)
80
+ return result unless @state == state
86
81
  if opts[:scope] == :class
87
- args = [@operation, *@last_result]
82
+ args = [@operation, *result]
88
83
  instance = @operation.class
89
84
  else
90
- args = @last_result
85
+ args = result
91
86
  instance = @operation
92
87
  end
93
88
  block = opts[:run]
94
89
  block = instance.method(block) if block.is_a? Symbol
95
- @last_result =
90
+ last_result =
96
91
  if block.arity.zero?
97
92
  instance.instance_exec(&block)
98
93
  elsif args.is_a?(Array) && block.arity == args.count
@@ -100,40 +95,54 @@ module Hyperstack
100
95
  else
101
96
  instance.instance_exec(args, &block)
102
97
  end
103
- return @last_result unless @last_result.is_a? Promise
104
- raise @last_result.error if @last_result.rejected?
105
- @last_result = @last_result.value if @last_result.resolved?
106
- @last_result
98
+ @last_async_result = last_result unless last_result.is_a? Promise
99
+ last_result
107
100
  rescue Exit => e
108
- @state = e.state
109
- @last_result = (e.state != :failed || e.result.is_a?(Exception)) ? e.result : e
110
- raise e
101
+ # the promise chain ends with an always block which will process
102
+ # any immediate exits by checking the value of @state. All other
103
+ # step/failed/async blocks will be skipped because state will not equal
104
+ # :succeed or :failed
105
+ if e.state == :failed
106
+ @state = :abort
107
+ # exit via the final always block with the exception
108
+ raise e.result.is_a?(Exception) ? e.result : e
109
+ else
110
+ @state = :succeed
111
+ # exit via the final then block with the success value
112
+ e.result
113
+ end
111
114
  rescue Exception => e
112
115
  @state = :failed
113
- @last_result = e
114
- raise e if in_promise
116
+ raise e
115
117
  end
116
118
 
117
119
  def run
118
- if @operation.has_errors? || @state
119
- @last_result ||= ValidationException.new(@operation.instance_variable_get('@errors'))
120
- return if @state # handles abort out of validation
121
- @state = :failed
122
- else
123
- @state = :success
124
- end
125
- tracks.each { |opts| opts[:tie].bind(self).call(opts) }
126
- rescue Exit
120
+ # if @operation.has_errors? || @state
121
+ # @last_result ||= ValidationException.new(@operation.instance_variable_get('@errors'))
122
+ # # following handles abort out of validation. if state is already set then we are aborting
123
+ # # otherwise if state is not set but we have errors then we are failed
124
+ # @state ||= :failed
125
+ # else
126
+ # @state = :success
127
+ # end
128
+ @state ||= :success
129
+ @promise_chain_start = @promise_chain = Promise.new
130
+ @promise_chain_start.resolve(@last_result)
131
+ tracks.each { |opts| opts[:tie].bind(self).call(opts) } unless @state == :abort
127
132
  end
128
133
 
129
134
  def result
130
- return @last_result if @last_result.is_a? Promise
131
- @last_result =
132
- if @state == :success
133
- Promise.new.resolve(@last_result)
135
+ @result ||= @promise_chain.always do |e|
136
+ if %i[abort failed].include? @state
137
+ if e.is_a? Exception
138
+ raise e
139
+ else
140
+ raise Promise::Fail.new(e)
141
+ end
134
142
  else
135
- Promise.new.reject(@last_result)
143
+ e
136
144
  end
145
+ end
137
146
  end
138
147
  end
139
148
  end
@@ -28,6 +28,8 @@ module Hyperstack
28
28
  rescue Exit => e
29
29
  raise e unless e.state == :failed
30
30
  add_error(param, symbol, message)
31
+ # use a bogus exit state which will skip adding
32
+ # a validation error (see catch block in process_validations method)
31
33
  raise Exit.new(:abort_from_add_error, e.result)
32
34
  end
33
35
  end
@@ -47,17 +49,22 @@ module Hyperstack
47
49
  when :failed
48
50
  add_validation_error(i, "param validation #{i+1} aborted")
49
51
  end
50
- @state = :failed
52
+ @state = :abort
51
53
  return # break does not work in Opal
52
54
  rescue AccessViolation => e
53
55
  add_validation_error(i, e)
54
- @state = :failed
56
+ @state = :abort
55
57
  @last_result = e
56
58
  return # break does not work in Opal
57
59
  rescue Exception => e
58
60
  add_validation_error(i, e)
59
61
  end
60
62
  end
63
+ ensure
64
+ if @operation.has_errors?
65
+ @last_result ||= ValidationException.new(@operation.instance_variable_get('@errors'))
66
+ @state ||= :failed
67
+ end
61
68
  end
62
69
  end
63
70
  end