opal-browser 0.1.0.beta1 → 0.2.0.beta1

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +60 -0
  3. data/.yardopts +1 -1
  4. data/Gemfile +7 -2
  5. data/LICENSE +19 -0
  6. data/README.md +74 -10
  7. data/config.ru +2 -1
  8. data/index.html.erb +22 -0
  9. data/opal-browser.gemspec +9 -11
  10. data/opal/browser.rb +1 -1
  11. data/opal/browser/animation_frame.rb +66 -9
  12. data/opal/browser/canvas.rb +72 -18
  13. data/opal/browser/canvas/data.rb +1 -1
  14. data/opal/browser/console.rb +3 -37
  15. data/opal/browser/cookies.rb +80 -24
  16. data/opal/browser/css/declaration.rb +0 -5
  17. data/opal/browser/{timeout.rb → delay.rb} +13 -13
  18. data/opal/browser/dom.rb +0 -2
  19. data/opal/browser/dom/attribute.rb +6 -0
  20. data/opal/browser/dom/builder.rb +4 -8
  21. data/opal/browser/dom/character_data.rb +43 -7
  22. data/opal/browser/dom/document.rb +13 -11
  23. data/opal/browser/dom/element.rb +127 -29
  24. data/opal/browser/dom/element/image.rb +23 -0
  25. data/opal/browser/dom/element/offset.rb +27 -10
  26. data/opal/browser/dom/element/scroll.rb +32 -12
  27. data/opal/browser/dom/element/size.rb +29 -0
  28. data/opal/browser/dom/event.rb +88 -75
  29. data/opal/browser/dom/event/animation.rb +16 -4
  30. data/opal/browser/dom/event/audio_processing.rb +6 -4
  31. data/opal/browser/dom/event/base.rb +229 -64
  32. data/opal/browser/dom/event/before_unload.rb +6 -4
  33. data/opal/browser/dom/event/clipboard.rb +6 -4
  34. data/opal/browser/dom/event/close.rb +16 -4
  35. data/opal/browser/dom/event/composition.rb +16 -4
  36. data/opal/browser/dom/event/custom.rb +43 -8
  37. data/opal/browser/dom/event/device_light.rb +6 -4
  38. data/opal/browser/dom/event/device_motion.rb +17 -4
  39. data/opal/browser/dom/event/device_orientation.rb +16 -4
  40. data/opal/browser/dom/event/device_proximity.rb +6 -4
  41. data/opal/browser/dom/event/drag.rb +34 -28
  42. data/opal/browser/dom/event/focus.rb +21 -5
  43. data/opal/browser/dom/event/gamepad.rb +33 -20
  44. data/opal/browser/dom/event/hash_change.rb +6 -4
  45. data/opal/browser/dom/event/keyboard.rb +45 -23
  46. data/opal/browser/dom/event/message.rb +28 -8
  47. data/opal/browser/dom/event/mouse.rb +26 -25
  48. data/opal/browser/dom/event/page_transition.rb +6 -4
  49. data/opal/browser/dom/event/pop_state.rb +16 -4
  50. data/opal/browser/dom/event/progress.rb +16 -4
  51. data/opal/browser/dom/event/sensor.rb +6 -4
  52. data/opal/browser/dom/event/storage.rb +6 -4
  53. data/opal/browser/dom/event/touch.rb +10 -19
  54. data/opal/browser/dom/event/ui.rb +19 -3
  55. data/opal/browser/dom/event/wheel.rb +2 -2
  56. data/opal/browser/dom/mutation_observer.rb +65 -5
  57. data/opal/browser/dom/node.rb +164 -59
  58. data/opal/browser/dom/node_set.rb +4 -0
  59. data/opal/browser/dom/text.rb +16 -1
  60. data/opal/browser/event_source.rb +5 -2
  61. data/opal/browser/history.rb +51 -15
  62. data/opal/browser/http.rb +22 -7
  63. data/opal/browser/http/headers.rb +5 -0
  64. data/opal/browser/http/request.rb +40 -10
  65. data/opal/browser/immediate.rb +123 -9
  66. data/opal/browser/interval.rb +8 -13
  67. data/opal/browser/location.rb +13 -3
  68. data/opal/browser/navigator.rb +9 -6
  69. data/opal/browser/screen.rb +31 -5
  70. data/opal/browser/socket.rb +8 -4
  71. data/opal/browser/storage.rb +118 -33
  72. data/opal/browser/support.rb +232 -0
  73. data/opal/browser/utils.rb +24 -6
  74. data/opal/browser/version.rb +1 -1
  75. data/opal/browser/window.rb +1 -2
  76. data/opal/browser/window/scroll.rb +21 -11
  77. data/opal/browser/window/size.rb +16 -6
  78. data/opal/browser/window/view.rb +23 -5
  79. data/spec/dom/builder_spec.rb +19 -19
  80. data/spec/dom/document_spec.rb +6 -6
  81. data/spec/dom/element_spec.rb +5 -5
  82. data/spec/dom/event_spec.rb +20 -20
  83. data/spec/dom/mutation_observer_spec.rb +5 -5
  84. data/spec/dom/node_spec.rb +39 -27
  85. data/spec/dom_spec.rb +10 -8
  86. data/spec/event_source_spec.rb +12 -12
  87. data/spec/history_spec.rb +24 -15
  88. data/spec/http_spec.rb +18 -17
  89. data/spec/immediate_spec.rb +9 -7
  90. data/spec/runner.rb +114 -0
  91. data/spec/socket_spec.rb +8 -8
  92. data/spec/spec_helper.rb +1 -0
  93. data/spec/storage_spec.rb +6 -6
  94. data/spec/wgxpath.install.js +49 -0
  95. data/spec/window_spec.rb +2 -2
  96. metadata +21 -54
  97. data/opal/browser/compatibility.rb +0 -59
  98. data/opal/browser/compatibility/animation_frame.rb +0 -93
  99. data/opal/browser/compatibility/dom/document/window.rb +0 -15
  100. data/opal/browser/compatibility/dom/element/css.rb +0 -15
  101. data/opal/browser/compatibility/dom/element/matches.rb +0 -31
  102. data/opal/browser/compatibility/dom/element/offset.rb +0 -20
  103. data/opal/browser/compatibility/dom/element/scroll.rb +0 -25
  104. data/opal/browser/compatibility/dom/element/style.rb +0 -15
  105. data/opal/browser/compatibility/dom/mutation_observer.rb +0 -47
  106. data/opal/browser/compatibility/http/request.rb +0 -15
  107. data/opal/browser/compatibility/immediate.rb +0 -107
  108. data/opal/browser/compatibility/window/scroll.rb +0 -27
  109. data/opal/browser/compatibility/window/size.rb +0 -13
  110. data/opal/browser/compatibility/window/view.rb +0 -13
  111. data/opal/browser/dom/compatibility.rb +0 -8
  112. data/opal/browser/http/compatibility.rb +0 -1
  113. data/opal/browser/window/compatibility.rb +0 -3
@@ -16,6 +16,10 @@ class NodeSet
16
16
  }
17
17
  end
18
18
 
19
+ def respond_to_missing?(name)
20
+ @literal.respond_to?(name)
21
+ end
22
+
19
23
  def method_missing(name, *args, &block)
20
24
  unless @literal.respond_to? name
21
25
  each {|el|
@@ -1,16 +1,31 @@
1
1
  module Browser; module DOM
2
2
 
3
+ # Encapsulates a text node.
4
+ #
5
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Text
3
6
  class Text < CharacterData
7
+ # (see Document#create_text)
4
8
  def self.create(*args)
5
9
  $document.create_text(*args)
6
10
  end
7
11
 
12
+ # @!attribute [r] whole
13
+ # @return [String] the whole text
14
+ #
15
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Text.wholeText
8
16
  def whole
9
17
  `#@native.wholeText`
10
18
  end
11
19
 
20
+ # Split the text node at a given offset.
21
+ #
22
+ # @param offset [Integer] the offset where to split the text node
23
+ #
24
+ # @return [Text] the newly created text node
25
+ #
26
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Text.splitText
12
27
  def split(offset)
13
- `#@native.splitText(offset)`
28
+ DOM(`#@native.splitText(offset)`)
14
29
  end
15
30
 
16
31
  def inspect
@@ -1,9 +1,12 @@
1
1
  module Browser
2
2
 
3
- # This class wraps `EventSource`.
3
+ # An {EventSource} allows you to receive events from a server in real-time,
4
+ # similar to long-polling but not exactly.
5
+ #
6
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/EventSource
4
7
  class EventSource
5
8
  def self.supported?
6
- defined? `window.EventSource`
9
+ Browser.supports? :EventSource
7
10
  end
8
11
 
9
12
  include Native
@@ -2,50 +2,86 @@ require 'browser/location'
2
2
 
3
3
  module Browser
4
4
 
5
+ # {History} allows manipulation of the session history.
6
+ #
7
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/History
5
8
  class History
9
+ # Check if HTML5 history is supported.
10
+ def self.supported?
11
+ Browser.supports? 'History'
12
+ end
13
+
6
14
  include Native
7
15
 
16
+ # @!attribute [r] length
17
+ # @return [Integer] how many items are in the history
8
18
  alias_native :length
9
19
 
20
+ # Go back in the history.
21
+ #
22
+ # @param number [Integer] how many items to go back
10
23
  def back(number = 1)
11
24
  `#@native.go(-number)`
12
-
13
- self
14
25
  end
15
26
 
27
+ # Go forward in the history.
28
+ #
29
+ # @param number [Integer] how many items to go forward
16
30
  def forward(number = 1)
17
31
  `#@native.go(number)`
18
-
19
- self
20
32
  end
21
33
 
22
- def push(url, data = nil)
34
+ # Push an item in the history.
35
+ #
36
+ # @param item [String] the item to push in the history
37
+ # @param data [Object] additional state to push
38
+ def push(item, data = nil)
23
39
  data = `null` if data.nil?
24
40
 
25
- `#@native.pushState(data, null, url)`
26
-
27
- self
41
+ `#@native.pushState(data, null, item)`
28
42
  end
29
43
 
30
- def replace(url, data = nil)
44
+ # Replace the current history item with another.
45
+ #
46
+ # @param item [String] the item to replace with
47
+ # @param data [Object] additional state to replace
48
+ def replace(item, data = nil)
31
49
  data = `null` if data.nil?
32
50
 
33
- `#@native.replaceState(data, null, url)`
51
+ `#@native.replaceState(data, null, item)`
34
52
  end
35
53
 
54
+ # @!attribute [r] current
55
+ # @return [String] the current item
36
56
  def current
37
57
  $window.location.path
38
58
  end
39
59
 
40
- def state
41
- `#@native.state`
60
+ # @!attribute [r] state
61
+ # @return [Object] the current state
62
+ if Browser.supports? 'History.state'
63
+ def state
64
+ %x{
65
+ var state = #@native.state;
66
+
67
+ if (state == null) {
68
+ return nil;
69
+ }
70
+ else {
71
+ return state;
72
+ }
73
+ }
74
+ end
75
+ else
76
+ def state
77
+ raise NotImplementedError, 'history state unsupported'
78
+ end
42
79
  end
43
80
  end
44
81
 
45
82
  class Window
46
- # Get the {History} object for this window.
47
- #
48
- # @return [History]
83
+ # @!attribute [r] history
84
+ # @return [History] the history for this window
49
85
  def history
50
86
  History.new(`#@native.history`) if `#@native.history`
51
87
  end
@@ -4,17 +4,21 @@ require 'browser/http/binary'
4
4
  require 'browser/http/headers'
5
5
  require 'browser/http/request'
6
6
  require 'browser/http/response'
7
- require 'browser/http/compatibility'
8
7
 
9
8
  module Browser
10
9
 
11
10
  module HTTP
11
+ def self.supported?
12
+ Browser.supports?('XHR') || Browser.supports?('ActiveXObject')
13
+ end
14
+
12
15
  # Send an asynchronous request.
13
16
  #
14
17
  # @param method [Symbol] the HTTP method to use
15
18
  # @param url [String] the URL to request
16
19
  # @param data [String, Hash] the data to send
17
- # @return [Response] the response
20
+ #
21
+ # @return [Promise] a promise that will be resolved with the response
18
22
  def self.send(method, url, data = nil, &block)
19
23
  Promise.new.tap {|promise|
20
24
  Request.new(&block).tap {|req|
@@ -32,7 +36,8 @@ module HTTP
32
36
  # Send an asynchronous GET request.
33
37
  #
34
38
  # @param url [String] the URL to request
35
- # @return [Response] the response
39
+ #
40
+ # @return [Promise] a promise that will be resolved with the response
36
41
  def self.get(url, &block)
37
42
  send(:get, url, &block)
38
43
  end
@@ -40,7 +45,8 @@ module HTTP
40
45
  # Send an asynchronous HEAD request.
41
46
  #
42
47
  # @param url [String] the URL to request
43
- # @return [Response] the response
48
+ #
49
+ # @return [Promise] a promise that will be resolved with the response
44
50
  def self.head(url, &block)
45
51
  send(:head, url, &block)
46
52
  end
@@ -49,7 +55,8 @@ module HTTP
49
55
  #
50
56
  # @param url [String] the URL to request
51
57
  # @param data [String, Hash] the data to send
52
- # @return [Response] the response
58
+ #
59
+ # @return [Promise] a promise that will be resolved with the response
53
60
  def self.post(url, data = nil, &block)
54
61
  send(:post, url, data, &block)
55
62
  end
@@ -58,7 +65,8 @@ module HTTP
58
65
  #
59
66
  # @param url [String] the URL to request
60
67
  # @param data [String, Hash] the data to send
61
- # @return [Response] the response
68
+ #
69
+ # @return [Promise] a promise that will be resolved with the response
62
70
  def self.put(url, data = nil, &block)
63
71
  send(:put, url, data, &block)
64
72
  end
@@ -67,7 +75,8 @@ module HTTP
67
75
  #
68
76
  # @param url [String] the URL to request
69
77
  # @param data [String, Hash] the data to send
70
- # @return [Response] the response
78
+ #
79
+ # @return [Promise] a promise that will be resolved with the response
71
80
  def self.delete(url, data = nil, &block)
72
81
  send(:delete, url, data, &block)
73
82
  end
@@ -77,6 +86,7 @@ module HTTP
77
86
  # @param method [Symbol] the HTTP method to use
78
87
  # @param url [String] the URL to request
79
88
  # @param data [String, Hash] the data to send
89
+ #
80
90
  # @return [Response] the response
81
91
  def self.send!(method, url, data = nil, &block)
82
92
  Request.new(&block).open(method, url, false).send(data)
@@ -85,6 +95,7 @@ module HTTP
85
95
  # Send a synchronous GET request.
86
96
  #
87
97
  # @param url [String] the URL to request
98
+ #
88
99
  # @return [Response] the response
89
100
  def self.get!(url, &block)
90
101
  send!(:get, url, &block)
@@ -93,6 +104,7 @@ module HTTP
93
104
  # Send a synchronous HEAD request.
94
105
  #
95
106
  # @param url [String] the URL to request
107
+ #
96
108
  # @return [Response] the response
97
109
  def self.head!(url, &block)
98
110
  send!(:head, url, &block)
@@ -102,6 +114,7 @@ module HTTP
102
114
  #
103
115
  # @param url [String] the URL to request
104
116
  # @param data [String, Hash] the data to send
117
+ #
105
118
  # @return [Response] the response
106
119
  def self.post!(url, data = nil, &block)
107
120
  send!(:post, url, data, &block)
@@ -111,6 +124,7 @@ module HTTP
111
124
  #
112
125
  # @param url [String] the URL to request
113
126
  # @param data [String, Hash] the data to send
127
+ #
114
128
  # @return [Response] the response
115
129
  def self.put!(url, data = nil, &block)
116
130
  send!(:put, url, data, &block)
@@ -120,6 +134,7 @@ module HTTP
120
134
  #
121
135
  # @param url [String] the URL to request
122
136
  # @param data [String, Hash] the data to send
137
+ #
123
138
  # @return [Response] the response
124
139
  def self.delete!(url, data = nil, &block)
125
140
  send!(:delete, url, data, &block)
@@ -31,6 +31,11 @@ class Headers
31
31
  @hash = Hash.new
32
32
  end
33
33
 
34
+ # Clear the {Headers}.
35
+ def clear
36
+ @hash.clear
37
+ end
38
+
34
39
  # Iterate over the headers.
35
40
  #
36
41
  # @yield [name, value] the header name and value
@@ -8,7 +8,6 @@ class Request
8
8
  # @param method [Symbol] the HTTP method to use
9
9
  # @param url [String, #to_s] the URL to request
10
10
  # @param parameters [String, Hash] the parameters to send
11
- #
12
11
  def self.open(method, url, parameters = nil, &block)
13
12
  request = new(&block)
14
13
  request.open(method, url)
@@ -44,11 +43,13 @@ class Request
44
43
  def initialize(&block)
45
44
  super(transport)
46
45
 
46
+ @parameters = {}
47
+ @query = {}
47
48
  @headers = Headers[DEFAULT_HEADERS]
48
49
  @method = :get
49
50
  @asynchronous = true
50
51
  @binary = false
51
- @cacheable = true
52
+ @cacheable = true
52
53
  @opened = false
53
54
  @sent = false
54
55
  @completed = false
@@ -61,8 +62,20 @@ class Request
61
62
  end if block
62
63
  end
63
64
 
64
- def transport
65
- `new XMLHttpRequest()`
65
+ # @!method transport
66
+ # @private
67
+ if Browser.supports? :XHR
68
+ def transport
69
+ `new XMLHttpRequest()`
70
+ end
71
+ elsif Browser.supports? :ActiveX
72
+ def transport
73
+ `new ActiveXObject("MSXML2.XMLHTTP.3.0")`
74
+ end
75
+ else
76
+ def transport
77
+ raise NotImplementedError
78
+ end
66
79
  end
67
80
 
68
81
  # Check if the request has been opened.
@@ -183,11 +196,18 @@ class Request
183
196
  #
184
197
  # @param hash [Hash] the parameters
185
198
  #
186
- # @return [self]
187
- def parameters(hash)
188
- @parameters = hash
199
+ # @return [Hash]
200
+ def parameters(hash = nil)
201
+ hash ? @parameters = hash : @parameters
202
+ end
189
203
 
190
- self
204
+ # Set the URI query.
205
+ #
206
+ # @param hash [Hash] the query
207
+ #
208
+ # @return [Hash]
209
+ def query(hash = nil)
210
+ hash ? @query = hash : @query
191
211
  end
192
212
 
193
213
  # Register an event on the request.
@@ -223,7 +243,17 @@ class Request
223
243
  url = @url
224
244
 
225
245
  unless cacheable?
226
- url += (url.include?(??) ? ?& : ??) + "_=#{rand}"
246
+ @query[:_] = rand
247
+ end
248
+
249
+ unless @query.empty?
250
+ if url.include???
251
+ url += ?&
252
+ else
253
+ url += ??
254
+ end
255
+
256
+ url += @query.encode_uri
227
257
  end
228
258
 
229
259
  `#@native.open(#{@method.to_s.upcase}, #{url.to_s}, #{@asynchronous}, #{@user.to_n}, #{@password.to_n})`
@@ -279,7 +309,7 @@ class Request
279
309
 
280
310
  if String === parameters
281
311
  data = parameters
282
- elsif Hash === parameters
312
+ elsif Hash === parameters && !parameters.empty?
283
313
  data = parameters.map {|vals|
284
314
  vals.map(&:encode_uri_component).join(?=)
285
315
  }.join(?&)
@@ -1,9 +1,30 @@
1
- require 'browser/compatibility/immediate'
1
+ require 'promise'
2
2
 
3
3
  module Browser
4
4
 
5
- # FIXME: drop the method_defined? checks when require order is fixed
5
+ # Class to easily create and dispatch an immediate call.
6
+ #
7
+ # Immediate calls are deferred function calls that happen as soon as they can
8
+ # be scheduled.
9
+ #
10
+ # Compatibility
11
+ # -------------
12
+ # The compatibility layer will try various implementations in the following
13
+ # order.
14
+ #
15
+ # + [setImmediate](https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate)
16
+ # + [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)
17
+ # + [readystatechange](https://developer.mozilla.org/en-US/docs/Web/Reference/Events/readystatechange)
18
+ # + [setTimeout](https://developer.mozilla.org/en/docs/Web/API/window.setTimeout)
19
+ #
20
+ # The order has been chosen from best to worst for both performance and
21
+ # preemptiveness.
6
22
  class Immediate
23
+ # Create an immediate for the given function which will be called with the
24
+ # arguments and block.
25
+ #
26
+ # @param func [Proc] the function to call
27
+ # @param args [Array] the arguments to call it with
7
28
  def initialize(func, args, &block)
8
29
  @aborted = false
9
30
  @function = func
@@ -11,14 +32,89 @@ class Immediate
11
32
  @block = block
12
33
  end
13
34
 
14
- def dispatch
15
- raise NotImplementedError
16
- end unless method_defined? :dispatch
35
+ # @!method dispatch
36
+ # Dispatch the immediate.
17
37
 
18
- def prevent
19
- raise NotImplementedError
20
- end unless method_defined? :prevent
38
+ # @!method prevent
39
+ # Prevent the immediate from being called once scheduled.
21
40
 
41
+ if Browser.supports? 'Immediate'
42
+ def dispatch
43
+ @id = `window.setImmediate(function() {
44
+ #{@function.call(*@arguments, &@block)};
45
+ })`
46
+ end
47
+
48
+ def prevent
49
+ `window.clearImmediate(#@id)`
50
+ end
51
+ elsif Browser.supports? 'Immediate (Internet Explorer)'
52
+ def dispatch
53
+ @id = `window.msSetImmediate(function() {
54
+ #{@function.call(*@arguments, &@block)};
55
+ })`
56
+ end
57
+
58
+ def prevent
59
+ `window.msClearImmediate(#@id)`
60
+ end
61
+ elsif Browser.supports? 'Window.send'
62
+ # @private
63
+ @@tasks = {}
64
+
65
+ # @private
66
+ @@prefix = "opal.browser.immediate.#{rand(1_000_000)}."
67
+
68
+ $window.on :message do |e|
69
+ if String === e.data && e.data.start_with?(@@prefix)
70
+ if task = @@tasks.delete(e.data[@@prefix.length .. -1])
71
+ task[0].call(*task[1], &task[2])
72
+ end
73
+ end
74
+ end
75
+
76
+ def dispatch
77
+ @id = rand(1_000_000).to_s
78
+ @@tasks[@id] = [@function, @arguments, @block]
79
+
80
+ $window.send! "#{@@prefix}#{@id}"
81
+ end
82
+
83
+ def prevent
84
+ @@tasks.delete(@id)
85
+ end
86
+ elsif Browser.supports? 'Event.readystatechange'
87
+ def dispatch
88
+ %x{
89
+ var script = document.createElement("script");
90
+
91
+ script.onreadystatechange = function() {
92
+ if (!#{aborted?}) {
93
+ #{@function.call(*@arguments, &@block)};
94
+ }
95
+
96
+ script.onreadystatechange = null;
97
+ script.parentNode.removeChild(script);
98
+ };
99
+
100
+ document.documentElement.appendChild(script);
101
+ }
102
+ end
103
+
104
+ def prevent; end
105
+ else
106
+ def dispatch
107
+ @id = `window.setTimeout(function() {
108
+ #{@function.call(*@arguments, &@block)};
109
+ }, 0)`
110
+ end
111
+
112
+ def prevent
113
+ `window.clearTimeout(#@id)`
114
+ end
115
+ end
116
+
117
+ # Abort the immediate.
22
118
  def abort
23
119
  return if aborted?
24
120
 
@@ -28,6 +124,7 @@ class Immediate
28
124
  self
29
125
  end
30
126
 
127
+ # Check if the immediate has been aborted.
31
128
  def aborted?
32
129
  @aborted
33
130
  end
@@ -36,8 +133,25 @@ end
36
133
  end
37
134
 
38
135
  class Proc
39
- # Defer the function to be called as soon as possible.
136
+ # (see Immediate.new)
40
137
  def defer(*args, &block)
41
138
  Browser::Immediate.new(self, args, &block).tap(&:dispatch)
42
139
  end
43
140
  end
141
+
142
+ class Promise
143
+ # Create a promise which will be resolved with the result of the immediate.
144
+ #
145
+ # @param args [Array] the arguments the block will be called with
146
+ def self.defer(*args, &block)
147
+ new.tap {|promise|
148
+ proc {
149
+ begin
150
+ promise.resolve(block.call(*args))
151
+ rescue Exception => e
152
+ promise.reject(e)
153
+ end
154
+ }.defer
155
+ }
156
+ end
157
+ end