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

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