hyper-operation 1.0.alpha1.4 → 1.0.alpha1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a53a1488f00a9d27e8522384b6d3f3ac62048a26f218b13bb40422c8ebc3f255
4
- data.tar.gz: 3caa24d2d23cb2e84216ec85bb636f8da3dcd3acc722e5aabed4b7f77fd04df5
3
+ metadata.gz: 65b352cb39f23e41aa0f4adc29ba892a8d9cf269c7345314851f2a68bccddf86
4
+ data.tar.gz: 71f10e8f1dfc3f38b968b694c0535169211a374dc404fa3da79a864791e075f2
5
5
  SHA512:
6
- metadata.gz: 4d939b37c53b280ae4e29fedf525e02b3badd3469619bd0d7957f753aa556451462dbe8c700fd629a81251e56217fa821e3328d0e6e4c5d5c2eb6f3f488554fe
7
- data.tar.gz: 817800180407bfaec631fb91fadc7d595f15cba5a50c4dec4563cb9d5fa426679264684cebabc80b450919234279cede3a4745d9c6c78b43c7eaa197d7923d30
6
+ metadata.gz: ba63a89809105c28b96d246a9958d0aab4ea121bcd59d5ee0f7fb1f96d88b1832dcafe35421f8220a13b401acba73d08cf7b27ce798c382336a2c9d844e7533f
7
+ data.tar.gz: afaea58dfe7c97007f3d70cebe53d02258c938e3fa4c863788a259f2478fbf45dcf7bc8472fc46967c60b797b693594d1e2468b45671b58281bcaed2925e7e0f
@@ -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 = {})
@@ -164,8 +183,15 @@ class Promise
164
183
  end
165
184
 
166
185
  def exception!(error)
167
- @exception = true
168
-
186
+ # If the error is a Promise::Fail, then
187
+ # the error becomes the error.result value
188
+ # this allows code to raise an error on an
189
+ # object that is not an error.
190
+ if error.is_a? Promise::Fail
191
+ error = error.result
192
+ else
193
+ @exception = true
194
+ end
169
195
  reject!(error)
170
196
  end
171
197
 
@@ -21,7 +21,7 @@ module Hyperstack
21
21
  @tracks ||= []
22
22
  end
23
23
 
24
- def to_opts(tie, args, block)
24
+ def build_tie(tie, args, block)
25
25
  if args.count.zero?
26
26
  { run: block }
27
27
  elsif args[0].is_a?(Hash)
@@ -41,7 +41,7 @@ module Hyperstack
41
41
 
42
42
  [:step, :failed, :async].each do |tie|
43
43
  define_method :"add_#{tie}" do |*args, &block|
44
- tracks << to_opts(tie, args, block)
44
+ tracks << build_tie(tie, args, block)
45
45
  end
46
46
  end
47
47
 
@@ -55,44 +55,33 @@ module Hyperstack
55
55
  end
56
56
 
57
57
  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
58
+ @promise_chain = @promise_chain
59
+ .then { |result| apply(result, :success, opts) }
66
60
  end
67
61
 
68
62
  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
75
- end
76
- elsif @state == :failed
77
- apply(opts)
78
- end
63
+ @promise_chain = @promise_chain
64
+ .always { |result| apply(result, :failed, opts) }
79
65
  end
80
66
 
81
67
  def async(opts)
82
- apply(opts) if @state != :failed
68
+ @promise_chain = @promise_chain_start = Promise.new
69
+ @promise_chain.resolve(@last_async_result)
70
+ step(opts)
83
71
  end
84
72
 
85
- def apply(opts, in_promise = nil)
73
+ def apply(result, state, opts)
74
+ return result unless @state == state
86
75
  if opts[:scope] == :class
87
- args = [@operation, *@last_result]
76
+ args = [@operation, *result]
88
77
  instance = @operation.class
89
78
  else
90
- args = @last_result
79
+ args = result
91
80
  instance = @operation
92
81
  end
93
82
  block = opts[:run]
94
83
  block = instance.method(block) if block.is_a? Symbol
95
- @last_result =
84
+ last_result =
96
85
  if block.arity.zero?
97
86
  instance.instance_exec(&block)
98
87
  elsif args.is_a?(Array) && block.arity == args.count
@@ -100,40 +89,54 @@ module Hyperstack
100
89
  else
101
90
  instance.instance_exec(args, &block)
102
91
  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
92
+ @last_async_result = last_result unless last_result.is_a? Promise
93
+ last_result
107
94
  rescue Exit => e
108
- @state = e.state
109
- @last_result = (e.state != :failed || e.result.is_a?(Exception)) ? e.result : e
110
- raise e
95
+ # the promise chain ends with an always block which will process
96
+ # any immediate exits by checking the value of @state. All other
97
+ # step/failed/async blocks will be skipped because state will not equal
98
+ # :succeed or :failed
99
+ if e.state == :failed
100
+ @state = :abort
101
+ # exit via the final always block with the exception
102
+ raise e.result.is_a?(Exception) ? e.result : e
103
+ else
104
+ @state = :succeed
105
+ # exit via the final then block with the success value
106
+ e.result
107
+ end
111
108
  rescue Exception => e
112
109
  @state = :failed
113
- @last_result = e
114
- raise e if in_promise
110
+ raise e
115
111
  end
116
112
 
117
113
  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
114
+ # if @operation.has_errors? || @state
115
+ # @last_result ||= ValidationException.new(@operation.instance_variable_get('@errors'))
116
+ # # following handles abort out of validation. if state is already set then we are aborting
117
+ # # otherwise if state is not set but we have errors then we are failed
118
+ # @state ||= :failed
119
+ # else
120
+ # @state = :success
121
+ # end
122
+ @state ||= :success
123
+ @promise_chain_start = @promise_chain = Promise.new
124
+ @promise_chain_start.resolve(@last_result)
125
+ tracks.each { |opts| opts[:tie].bind(self).call(opts) } unless @state == :abort
127
126
  end
128
127
 
129
128
  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)
129
+ @result ||= @promise_chain.always do |e|
130
+ if %i[abort failed].include? @state
131
+ if e.is_a? Exception
132
+ raise e
133
+ else
134
+ raise Promise::Fail.new(e)
135
+ end
134
136
  else
135
- Promise.new.reject(@last_result)
137
+ e
136
138
  end
139
+ end
137
140
  end
138
141
  end
139
142
  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
@@ -14,7 +14,7 @@ module Hyperstack
14
14
  Hyperstack::HTTP.post(
15
15
  "#{`window.HyperstackEnginePath`}/execute_remote",
16
16
  payload: {json: {operation: name, params: hash}.to_json},
17
- headers: {'X-CSRF-Token' => Hyperstack::ClientDrivers.opts[:form_authenticity_token] }
17
+ headers: headers.merge('X-CSRF-Token' => Hyperstack::ClientDrivers.opts[:form_authenticity_token])
18
18
  )
19
19
  .then do |response|
20
20
  deserialize_response response.json[:response]
@@ -129,6 +129,10 @@ module Hyperstack
129
129
  promise.reject e
130
130
  end
131
131
 
132
+ def headers
133
+ {}
134
+ end
135
+
132
136
  def serialize_params(hash)
133
137
  hash
134
138
  end
@@ -35,16 +35,35 @@ module Hyperstack
35
35
 
36
36
 
37
37
  if RUBY_ENGINE == 'opal'
38
+ # Patch in a dummy copy of Model.load in case we are not using models
39
+ # this will be defined properly by hyper-model
40
+ module Model
41
+ def self.load
42
+ Promise.new.tap { |promise| promise.resolve(yield) }
43
+ end unless respond_to?(:load)
44
+ end
45
+
38
46
  def self.connect(*channels)
39
47
  channels.each do |channel|
40
48
  if channel.is_a? Class
41
49
  IncomingBroadcast.connect_to(channel.name)
42
50
  elsif channel.is_a?(String) || channel.is_a?(Array)
43
51
  IncomingBroadcast.connect_to(*channel)
44
- elsif channel.id
45
- IncomingBroadcast.connect_to(channel.class.name, channel.id)
52
+ elsif channel.respond_to?(:id)
53
+ Hyperstack::Model.load do
54
+ channel.id
55
+ end.then do |id|
56
+ raise "Hyperstack.connect cannot connect to #{channel.inspect}. "\
57
+ "The id is nil. This can be caused by connecting to a model "\
58
+ "that is not saved, or that does not exist." unless id
59
+ IncomingBroadcast.connect_to(channel.class.name, id)
60
+ end
46
61
  else
47
- raise "cannot connect to model before it has been saved"
62
+ raise "Hyperstack.connect cannot connect to #{channel.inspect}.\n"\
63
+ "Channels must be either a class, or a class name,\n"\
64
+ "a string in the form 'ClassName-id',\n"\
65
+ "an array in the form [class, id] or [class-name, id],\n"\
66
+ "or an object that responds to the id method with a non-nil value"
48
67
  end
49
68
  end
50
69
  end
@@ -65,12 +84,14 @@ module Hyperstack
65
84
 
66
85
  def self.add_connection(channel_name, id = nil)
67
86
  channel_string = "#{channel_name}#{'-'+id.to_s if id}"
87
+ return if open_channels.include? channel_string
68
88
  open_channels << channel_string
69
89
  channel_string
70
90
  end
71
91
 
72
92
  def self.connect_to(channel_name, id = nil)
73
93
  channel_string = add_connection(channel_name, id)
94
+ return unless channel_string # already connected!
74
95
  if ClientDrivers.opts[:transport] == :pusher
75
96
  channel = "#{ClientDrivers.opts[:channel]}-#{channel_string}"
76
97
  %x{
@@ -78,6 +99,7 @@ module Hyperstack
78
99
  channel.bind('dispatch', #{ClientDrivers.opts[:dispatch]})
79
100
  channel.bind('pusher:subscription_succeeded', #{lambda {ClientDrivers.get_queued_data("connect-to-transport", channel_string)}})
80
101
  }
102
+ @pusher_dispatcher_registered = true
81
103
  elsif ClientDrivers.opts[:transport] == :action_cable
82
104
  channel = "#{ClientDrivers.opts[:channel]}-#{channel_string}"
83
105
  Hyperstack::HTTP.post(ClientDrivers.polling_path('action-cable-auth', channel), headers: { 'X-CSRF-Token' => ClientDrivers.opts[:form_authenticity_token] }).then do |response|
@@ -163,12 +185,17 @@ module Hyperstack
163
185
  # not sure why the second check is needed. It happens in the test app
164
186
  route.app == Hyperstack::Engine or (route.app.respond_to?(:app) and route.app.app == Hyperstack::Engine)
165
187
  end
166
- raise 'Hyperstack::Engine mount point not found. Check your config/routes.rb file' unless path
167
- path = path.path.spec
168
- "<script type='text/javascript'>\n"\
169
- "window.HyperstackEnginePath = '#{path}';\n"\
170
- "window.HyperstackOpts = #{config_hash.to_json}\n"\
171
- "</script>\n"
188
+ if path
189
+ path = path.path.spec
190
+ "<script type='text/javascript'>\n"\
191
+ "window.HyperstackEnginePath = '#{path}';\n"\
192
+ "window.HyperstackOpts = #{config_hash.to_json}\n"\
193
+ "</script>\n"
194
+ else
195
+ "<script type='text/javascript'>\n"\
196
+ "window.HyperstackOpts = #{config_hash.to_json}\n"\
197
+ "</script>\n"
198
+ end
172
199
  end if RUBY_ENGINE != 'opal'
173
200
 
174
201
  class << self
@@ -222,6 +249,9 @@ module Hyperstack
222
249
 
223
250
  @opts = Hash.new(`window.HyperstackOpts`)
224
251
 
252
+ if opts[:transport] != :none && `typeof(window.HyperstackEnginePath) == 'undefined'`
253
+ raise "No hyperstack mount point found!\nCheck your Rails routes.rb file";
254
+ end
225
255
 
226
256
  if opts[:transport] == :pusher
227
257
 
@@ -59,6 +59,8 @@ module Hyperstack
59
59
  end
60
60
  end
61
61
 
62
+ define_setting(:send_to_server_timeout, 10)
63
+
62
64
  define_setting :opts, {}
63
65
  define_setting :channel_prefix, 'synchromesh'
64
66
  define_setting :client_logging, true
@@ -163,7 +165,10 @@ module Hyperstack
163
165
  request.body = {
164
166
  channel: channel, data: data, salt: salt, authorization: authorization
165
167
  }.to_json
166
- http.request(request)
168
+ Timeout::timeout(Hyperstack.send_to_server_timeout) { http.request(request) }
169
+ rescue Timeout::Error
170
+ puts "\n********* FAILED TO RECEIVE RESPONSE FROM SERVER WITHIN #{Hyperstack.send_to_server_timeout} SECONDS. CHANGES WILL NOT BE SYNCED ************\n"
171
+ raise 'no server running'
167
172
  end
168
173
 
169
174
  def self.dispatch(data)
@@ -1,5 +1,5 @@
1
1
  module Hyperstack
2
2
  class Operation
3
- VERSION = '1.0.alpha1.4'
3
+ VERSION = '1.0.alpha1.5'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyper-operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.alpha1.4
4
+ version: 1.0.alpha1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitch VanDuyn
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-02-16 00:00:00.000000000 Z
12
+ date: 2019-06-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - '='
33
33
  - !ruby/object:Gem::Version
34
- version: 1.0.alpha1.4
34
+ version: 1.0.alpha1.5
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - '='
40
40
  - !ruby/object:Gem::Version
41
- version: 1.0.alpha1.4
41
+ version: 1.0.alpha1.5
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: mutations
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -135,14 +135,14 @@ dependencies:
135
135
  requirements:
136
136
  - - '='
137
137
  - !ruby/object:Gem::Version
138
- version: 1.0.alpha1.4
138
+ version: 1.0.alpha1.5
139
139
  type: :development
140
140
  prerelease: false
141
141
  version_requirements: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - '='
144
144
  - !ruby/object:Gem::Version
145
- version: 1.0.alpha1.4
145
+ version: 1.0.alpha1.5
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: mysql2
148
148
  requirement: !ruby/object:Gem::Requirement
@@ -441,7 +441,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
441
441
  - !ruby/object:Gem::Version
442
442
  version: 1.3.1
443
443
  requirements: []
444
- rubygems_version: 3.0.2
444
+ rubygems_version: 3.0.4
445
445
  signing_key:
446
446
  specification_version: 4
447
447
  summary: HyperOperations are the swiss army knife of the Hyperstack