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

Sign up to get free protection for your applications and to get access to all the features.
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