Class: Promise

Inherits:
Object show all
Defined in:
lib/volt/utils/promise.rb,
lib/volt/utils/promise_extensions.rb

Overview

A temp patch for promises until github.com/opal/opal/pull/725 is released.

Direct Known Subclasses

Trace, When

Defined Under Namespace

Classes: Trace, When

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(action = {}) ⇒ Promise

Returns a new instance of Promise.



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/volt/utils/promise.rb', line 117

def initialize(action = {})
  @action = action

  @realized  = false
  @exception = false
  @value     = nil
  @error     = nil
  @delayed   = false

  @prev = nil
  @next = nil
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/volt/utils/promise_extensions.rb', line 16

def method_missing(method_name, *args, &block)
  if respond_to_missing?(method_name)
    promise = self.then do |value|
      value.send(method_name, *args, &block)
    end

    promise
  else
    super
  end
end

Instance Attribute Details

#errorObject (readonly)

Returns the value of attribute error.



115
116
117
# File 'lib/volt/utils/promise.rb', line 115

def error
  @error
end

#nextObject (readonly)

Returns the value of attribute next.



115
116
117
# File 'lib/volt/utils/promise.rb', line 115

def next
  @next
end

#prevObject (readonly)

Returns the value of attribute prev.



115
116
117
# File 'lib/volt/utils/promise.rb', line 115

def prev
  @prev
end

Class Method Details

.error(value) ⇒ Object



107
108
109
# File 'lib/volt/utils/promise.rb', line 107

def self.error(value)
  new.reject(value)
end

.value(value) ⇒ Object



103
104
105
# File 'lib/volt/utils/promise.rb', line 103

def self.value(value)
  new.resolve(value)
end

.when(*promises) ⇒ Object



111
112
113
# File 'lib/volt/utils/promise.rb', line 111

def self.when(*promises)
  When.new(promises)
end

Instance Method Details

#<<(promise) ⇒ Object



169
170
171
172
173
# File 'lib/volt/utils/promise.rb', line 169

def <<(promise)
  @prev = promise

  self
end

#>>(promise) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/volt/utils/promise.rb', line 175

def >>(promise)
  @next = promise

  if exception?
    promise.reject(@delayed[0])
  elsif resolved?
    promise.resolve(@delayed ? @delayed[0] : value)
  elsif rejected?
    if !@action.has_key?(:failure) || Promise === (@delayed ? @delayed[0] : @error)
      promise.reject(@delayed ? @delayed[0] : error)
    elsif promise.action.include?(:always)
      promise.reject(@delayed ? @delayed[0] : error)
    end
  end

  self
end

#^(promise) ⇒ Object



162
163
164
165
166
167
# File 'lib/volt/utils/promise.rb', line 162

def ^(promise)
  promise << self
  self    >> promise

  promise
end

#act?Boolean

Returns:



138
139
140
# File 'lib/volt/utils/promise.rb', line 138

def act?
  @action.has_key?(:success) || @action.has_key?(:always)
end

#actionObject



142
143
144
# File 'lib/volt/utils/promise.rb', line 142

def action
  @action.keys
end

#always(&block) ⇒ Object Also known as: finally, ensure



290
291
292
293
294
295
296
# File 'lib/volt/utils/promise.rb', line 290

def always(&block)
  if @next
    raise ArgumentError, 'a promise has already been chained'
  end

  self ^ Promise.new(always: block)
end

#each(&block) ⇒ Object

Allow .each to be called directly on promises

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
# File 'lib/volt/utils/promise_extensions.rb', line 29

def each(&block)
  raise ArgumentError, 'no block given' unless block

  self.then do |val|
    val.each(&block)
  end
end

#exception!(error) ⇒ Object



263
264
265
266
267
# File 'lib/volt/utils/promise.rb', line 263

def exception!(error)
  @exception = true

  reject!(error)
end

#exception?Boolean

Returns:



146
147
148
# File 'lib/volt/utils/promise.rb', line 146

def exception?
  @exception
end

#fail(&block) ⇒ Object Also known as: rescue, catch



279
280
281
282
283
284
285
# File 'lib/volt/utils/promise.rb', line 279

def fail(&block)
  if @next
    raise ArgumentError, 'a promise has already been chained'
  end

  self ^ Promise.new(failure: block)
end

#inspectObject

Improve #inspect to not show nested promises.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/volt/utils/promise_extensions.rb', line 38

def inspect
  result = "#<#{self.class}(#{object_id})"

  if @next
    result += " >> #{@next.inspect}"
  end

  if realized?
    result += ": #{(@value || @error).inspect}>"
  else
    result += ">"
  end

  result
end

#realized?Boolean

Returns:



150
151
152
# File 'lib/volt/utils/promise.rb', line 150

def realized?
  !!@realized
end

#reject(value = nil) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/volt/utils/promise.rb', line 226

def reject(value = nil)
  if realized?
    raise ArgumentError, 'the promise has already been realized'
  end

  if Promise === value
    return (value << @prev) ^ self
  end

  begin
    if block = @action[:failure] || @action[:always]
      value = block.call(value)
    end

    if @action.has_key?(:always)
      resolve!(value)
    else
      reject!(value)
    end
  rescue Exception => e
    exception!(e)
  end

  self
end

#reject!(value) ⇒ Object



252
253
254
255
256
257
258
259
260
261
# File 'lib/volt/utils/promise.rb', line 252

def reject!(value)
  @realized = :reject
  @error    = value

  if @next
    @next.reject(value)
  else
    @delayed = [value]
  end
end

#rejected?Boolean

Returns:



158
159
160
# File 'lib/volt/utils/promise.rb', line 158

def rejected?
  @realized == :reject
end

#resolve(value = nil) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/volt/utils/promise.rb', line 193

def resolve(value = nil)
  if realized?
    raise ArgumentError, 'the promise has already been realized'
  end

  if Promise === value
    return (value << @prev) ^ self
  end

  begin
    if block = @action[:success] || @action[:always]
      value = block.call(value)
    end

    resolve!(value)
  rescue Exception => e
    exception!(e)
  end

  self
end

#resolve!(value) ⇒ Object



215
216
217
218
219
220
221
222
223
224
# File 'lib/volt/utils/promise.rb', line 215

def resolve!(value)
  @realized = :resolve
  @value    = value

  if @next
    @next.resolve(value)
  else
    @delayed = [value]
  end
end

#resolved?Boolean

Returns:



154
155
156
# File 'lib/volt/utils/promise.rb', line 154

def resolved?
  @realized == :resolve
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

We made a choice not to support comparitors and << and >> on method_missing on Promises. This makes it easier to understand what promise proxying does and how it works. It also prevents confusing situations where you try to

compare two Promises for example. The cost though is more code to do

comparisons, but we feel it is worth it.

Returns:



12
13
14
# File 'lib/volt/utils/promise_extensions.rb', line 12

def respond_to_missing?(method_name, include_private = false)
  !!(method_name =~ /[a-z_]\w*[?!=]?/)
end

#syncObject

Waits for the promise to resolve (assuming it is blocking on the server) and returns the result.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/volt/utils/promise_extensions.rb', line 90

def sync
  raise ".sync can only be used on the client" if Volt.client?

  result = nil
  error = nil

  self.then do |val|
    result = val
  end.fail do |err|
    error = err
  end

  if error
    err_str = "Exception in Promise at .sync: #{error.inspect}"
    err_str += error.backtrace.join("\n") if error.respond_to?(:backtrace)
    Volt.logger.error(err_str)
    fail error
  else
    return result
  end
end

#then(&block) ⇒ Object Also known as: do



269
270
271
272
273
274
275
# File 'lib/volt/utils/promise.rb', line 269

def then(&block)
  if @next
    raise ArgumentError, 'a promise has already been chained'
  end

  self ^ Promise.new(success: block)
end

#to_json(*args, &block) ⇒ Object

Forward to resolved value



82
83
84
# File 'lib/volt/utils/promise_extensions.rb', line 82

def to_json(*args, &block)
  self.then {|v| v.to_json(*args, &block) }
end

#trace(depth = nil, &block) ⇒ Object



301
302
303
304
305
306
307
# File 'lib/volt/utils/promise.rb', line 301

def trace(depth = nil, &block)
  if @next
    raise ArgumentError, 'a promise has already been chained'
  end

  self ^ Trace.new(depth, block)
end

#valueObject



130
131
132
133
134
135
136
# File 'lib/volt/utils/promise.rb', line 130

def value
  if Promise === @value
    @value.value
  else
    @value
  end
end

#value_or_errorObject



64
65
66
# File 'lib/volt/utils/promise_extensions.rb', line 64

def value_or_error
  @value || @error
end