unpoly-rails 0.30.0 → 0.30.1

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.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8c90c3524cc53bceb112f2a7fe498d4a7eda786
4
- data.tar.gz: 979eaa1084743303e20c6ecd7f430e64751797bd
3
+ metadata.gz: 1e53ab2c1cbdfe09b80e31d76371d5382ad40f70
4
+ data.tar.gz: 7ba79eb21d2494bf69b3bc222a4b7e89778671a1
5
5
  SHA512:
6
- metadata.gz: 42c4bd9af359d17e34a0741e0a06a1074ae90ee87143cd150679f47b2035a18f2b613e12df74c4ba313b5eddf5abdf84710f5c324bd030a80ca9e33802f71caa
7
- data.tar.gz: 74539855e8eadf62b68ca6a88e09b2ef2b6f5227eedb616c0395579e1d48eda87e4b1ca998fe9bdf5c13b48114caee59db83377c96892d8cfd6578d65b445b43
6
+ metadata.gz: 5789b11f34c69787dbf6fdd9fe9ed03d6c25c5c76fbf32b7028fcba36a6ecad14072c10d1cf5b4945d7edd104d11e581a302f6ca45b1c15baf4b5aa868c90691
7
+ data.tar.gz: 3d07d31ce7a01814badad2a7e16fbe4bbf283dc574a6b27818ea9a2ba5a6cff9b9fdcf6a9acfd19b6d505d12f8c84486ea12b7322fae39fa7000cb9af3492c1d
data/CHANGELOG.md CHANGED
@@ -14,6 +14,16 @@ Unreleased
14
14
  ### Breaking changes
15
15
 
16
16
 
17
+ 0.30.1
18
+ ------
19
+
20
+ ### Compatible changes
21
+
22
+ - Fix [`up.observe`](/up.observe) not honoring `{ delay }` option
23
+ - Fix [`[up-observe]`](/up-observe) not honoring `[up-delay]` modifier
24
+ - Fix many issues with concurrency and slow server responses for [`up.observe`](/up.observe) and [`[up-observe]`](/up-observe)
25
+
26
+
17
27
 
18
28
  0.30.0
19
29
  ------
@@ -249,52 +249,35 @@ up.form = (($) ->
249
249
  delay = u.option($fields.attr('up-delay'), options.delay, config.observeDelay)
250
250
  delay = parseInt(delay)
251
251
 
252
+ console.debug("Observing with delay %o", delay)
253
+
252
254
  destructors = u.map $fields, (field) ->
253
- observeField($(field), options, callback)
255
+ observeField($(field), delay, callback)
254
256
 
255
257
  u.sequence(destructors...)
256
258
 
257
259
  observeField = ($field, delay, callback) ->
258
- knownValue = null
259
- callbackTimer = null
260
- callbackPromise = u.resolvedPromise()
261
-
262
- # This holds the next callback function, curried with `value` and `$field`.
263
- # Since we're waiting for callback promises to resolve before running
264
- # another callback, this might be overwritten while we're waiting for a
265
- # previous callback to finish.
266
- nextCallback = null
267
-
268
- runNextCallback = ->
269
- if nextCallback
270
- returnValue = nextCallback()
271
- nextCallback = null
272
- returnValue
260
+ processedValue = u.submittedValue($field)
261
+ timer = undefined
262
+ lastCallbackDone = u.resolvedPromise()
263
+
264
+ runCallback = (value) ->
265
+ processedValue = value
266
+ callbackReturnValue = callback.apply($field.get(0), [value, $field])
267
+ if u.isPromise(callbackReturnValue)
268
+ lastCallbackDone = callbackReturnValue
269
+ else
270
+ lastCallbackDone = callbackReturnValue
273
271
 
274
272
  check = ->
275
273
  value = u.submittedValue($field)
276
274
  # don't run the callback for the check during initialization
277
- skipCallback = u.isNull(knownValue)
278
- if knownValue != value
279
- knownValue = value
280
- unless skipCallback
281
- clearTimer()
282
- nextCallback = -> callback.apply($field.get(0), [value, $field])
283
- runAndChain = ->
284
- # Only run the callback once the previous callback's
285
- # promise resolves.
286
- callbackPromise.then ->
287
- returnValue = runNextCallback()
288
- # If the callback returns a promise, we will remember it
289
- # and chain additional callback invocations to it.
290
- if u.isPromise(returnValue)
291
- callbackPromise = returnValue
292
- else
293
- callbackPromise = u.resolvedPromise()
294
- u.setTimer(delay, runAndChain)
295
-
296
- clearTimer = ->
297
- clearTimeout(callbackTimer)
275
+ if processedValue != value
276
+ nextCallback = -> runCallback(value)
277
+ timer?.cancel()
278
+ timer = u.promiseTimer(delay)
279
+ # We wait until both the delay has passed and a previous callback is done executing
280
+ $.when(timer, lastCallbackDone).then(nextCallback)
298
281
 
299
282
  # Although (depending on the browser) we only need/receive either input or change,
300
283
  # we always bind to both events in case another script manually triggers it.
@@ -307,12 +290,10 @@ up.form = (($) ->
307
290
 
308
291
  $field.on(changeEvents, check)
309
292
 
310
- check()
311
-
312
293
  # return destructor
313
294
  return ->
314
295
  $field.off(changeEvents, check)
315
- clearTimer()
296
+ timer?.cancel()
316
297
 
317
298
  ###*
318
299
  [Observes](/up.observe) a field or form and submits the form when a value changes.
@@ -860,7 +841,7 @@ up.form = (($) ->
860
841
  but not if the checkbox was changed:
861
842
 
862
843
  <form method="GET" action="/search">
863
- <input type="search" name="query" autosubmit>
844
+ <input type="search" name="query" up-autosubmit>
864
845
  <input type="checkbox"> Include archive
865
846
  </form>
866
847
 
@@ -778,6 +778,7 @@ up.util = (($) ->
778
778
  setTimeout(callback, millis)
779
779
  else
780
780
  callback()
781
+ undefined
781
782
 
782
783
 
783
784
  ###*
@@ -1777,14 +1778,21 @@ up.util = (($) ->
1777
1778
  The proxy's `.promise` attribute is available even before the function is called
1778
1779
  and will resolve when the inner function's returned promise resolves.
1779
1780
 
1781
+ If the inner function does not return a promise, the proxy's `.promise` attribute
1782
+ will resolve as soon as the inner function returns.
1783
+
1780
1784
  @function up.util.previewable
1781
1785
  @internal
1782
1786
  ###
1783
1787
  previewable = (fun) ->
1784
1788
  deferred = $.Deferred()
1785
1789
  preview = (args...) ->
1786
- fun(args...).then ->
1787
- deferred.resolve()
1790
+ funValue = fun(args...)
1791
+ if isPromise(funValue)
1792
+ funValue.then -> deferred.resolve(funValue)
1793
+ else
1794
+ deferred.resolve(funValue)
1795
+ funValue
1788
1796
  preview.promise = deferred.promise()
1789
1797
  preview
1790
1798
 
@@ -1844,6 +1852,27 @@ up.util = (($) ->
1844
1852
  ->
1845
1853
  map functions, (f) -> f()
1846
1854
 
1855
+ # ###*
1856
+ # @function up.util.race
1857
+ # @internal
1858
+ # ###
1859
+ # race = (promises...) ->
1860
+ # raceDone = $.Deferred()
1861
+ # each promises, (promise) ->
1862
+ # promise.then -> raceDone.resolve()
1863
+ # raceDone.promise()
1864
+
1865
+ ###*
1866
+ @function up.util.promiseTimer
1867
+ @internal
1868
+ ###
1869
+ promiseTimer = (ms) ->
1870
+ deferred = $.Deferred()
1871
+ timeout = setTimer ms, ->
1872
+ deferred.resolve()
1873
+ deferred.cancel = -> clearTimeout(timeout)
1874
+ deferred
1875
+
1847
1876
  isDetached: isDetached
1848
1877
  requestDataAsArray: requestDataAsArray
1849
1878
  requestDataAsQuery: requestDataAsQuery
@@ -1952,6 +1981,8 @@ up.util = (($) ->
1952
1981
  DivertibleChain: DivertibleChain
1953
1982
  submittedValue: submittedValue
1954
1983
  sequence: sequence
1984
+ promiseTimer: promiseTimer
1985
+ previewable: previewable
1955
1986
 
1956
1987
  )($)
1957
1988
 
@@ -4,6 +4,6 @@ module Unpoly
4
4
  # The current version of the unpoly-rails gem.
5
5
  # This version number is also used for releases of the Unpoly
6
6
  # frontend code.
7
- VERSION = '0.30.0'
7
+ VERSION = '0.30.1'
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- unpoly-rails (0.28.1)
4
+ unpoly-rails (0.30.0)
5
5
  rails (>= 3)
6
6
 
7
7
  GEM
@@ -82,8 +82,8 @@ GEM
82
82
  hpricot (~> 0.8.6)
83
83
  ruby_parser (~> 3.1.1)
84
84
  i18n (0.7.0)
85
- jasmine-core (2.4.1)
86
- jasmine-rails (0.12.2)
85
+ jasmine-core (2.5.2)
86
+ jasmine-rails (0.14.1)
87
87
  jasmine-core (>= 1.3, < 3.0)
88
88
  phantomjs (>= 1.9)
89
89
  railties (>= 3.2.0)
@@ -99,12 +99,14 @@ GEM
99
99
  mail (2.6.3)
100
100
  mime-types (>= 1.16, < 3)
101
101
  mime-types (2.99)
102
- mini_portile2 (2.0.0)
103
- minitest (5.8.4)
104
- multi_json (1.11.2)
105
- nokogiri (1.6.7.2)
106
- mini_portile2 (~> 2.0.0.rc2)
107
- phantomjs (1.9.8.0)
102
+ mini_portile2 (2.1.0)
103
+ minitest (5.9.1)
104
+ multi_json (1.12.1)
105
+ nokogiri (1.6.8)
106
+ mini_portile2 (~> 2.1.0)
107
+ pkg-config (~> 1.1.7)
108
+ phantomjs (2.1.1.0)
109
+ pkg-config (1.1.7)
108
110
  rack (1.6.4)
109
111
  rack-test (0.6.3)
110
112
  rack (>= 1.0)
@@ -132,7 +134,7 @@ GEM
132
134
  activesupport (= 4.2.0)
133
135
  rake (>= 0.8.7)
134
136
  thor (>= 0.18.1, < 2.0)
135
- rake (11.1.2)
137
+ rake (11.3.0)
136
138
  ref (1.0.5)
137
139
  rspec-core (3.4.1)
138
140
  rspec-support (~> 3.4.0)
@@ -1,3 +1,2 @@
1
1
  beforeAll (done) ->
2
2
  $(done)
3
-
@@ -3,7 +3,7 @@ describe 'up.form', ->
3
3
  u = up.util
4
4
 
5
5
  describe 'Javascript functions', ->
6
-
6
+
7
7
  describe 'up.observe', ->
8
8
 
9
9
  beforeEach ->
@@ -23,43 +23,100 @@ describe 'up.form', ->
23
23
 
24
24
  u.each changeEvents, (eventName) ->
25
25
 
26
- it "runs the callback when the input receives a '#{eventName}' event and the value changed", (done) ->
27
- $input = affix('input[value="old-value"]')
28
- callback = jasmine.createSpy('change callback')
29
- up.observe($input, callback)
30
- $input.val('new-value')
31
- u.times 2, -> $input.trigger(eventName)
32
- u.nextFrame ->
33
- expect(callback).toHaveBeenCalledWith('new-value', $input)
34
- expect(callback.calls.count()).toEqual(1)
35
- done()
26
+ describe "when the input receives a #{eventName} event", ->
36
27
 
37
- it "does not run the callback when the input receives a '#{eventName}' event, but the value didn't change", (done) ->
38
- $input = affix('input[value="old-value"]')
39
- callback = jasmine.createSpy('change callback')
40
- up.observe($input, callback)
41
- $input.trigger(eventName)
42
- u.nextFrame ->
43
- expect(callback).not.toHaveBeenCalled()
44
- done()
28
+ it "runs the callback if the value changed", (done) ->
29
+ $input = affix('input[value="old-value"]')
30
+ callback = jasmine.createSpy('change callback')
31
+ up.observe($input, callback)
32
+ $input.val('new-value')
33
+ u.times 2, -> $input.trigger(eventName)
34
+ u.nextFrame ->
35
+ expect(callback).toHaveBeenCalledWith('new-value', $input)
36
+ expect(callback.calls.count()).toEqual(1)
37
+ done()
45
38
 
46
- it "runs a callback in the same frame if the delay is 0 and it receives a '#{eventName}' event", ->
47
- $input = affix('input[value="old-value"]')
48
- callback = jasmine.createSpy('change callback')
49
- up.observe($input, { delay: 0 }, callback)
50
- $input.val('new-value')
51
- $input.trigger(eventName)
52
- expect(callback.calls.count()).toEqual(1)
39
+ it "does not run the callback if the value didn't change", (done) ->
40
+ $input = affix('input[value="old-value"]')
41
+ callback = jasmine.createSpy('change callback')
42
+ up.observe($input, callback)
43
+ $input.trigger(eventName)
44
+ u.nextFrame ->
45
+ expect(callback).not.toHaveBeenCalled()
46
+ done()
53
47
 
54
- it "runs multiple callbacks in the same frame if the delay is 0 and it receives a '#{eventName}' event", ->
55
- $input = affix('input[value="old-value"]')
56
- callback = jasmine.createSpy('change callback')
57
- up.observe($input, { delay: 0 }, callback)
58
- $input.val('new-value')
59
- $input.trigger(eventName)
60
- $input.val('yet-another-value')
61
- $input.trigger(eventName)
62
- expect(callback.calls.count()).toEqual(2)
48
+ it 'debounces the callback when the { delay } option is given', (done) ->
49
+ $input = affix('input[value="old-value"]')
50
+ callback = jasmine.createSpy('change callback')
51
+ up.observe($input, { delay: 100 }, callback)
52
+ $input.val('new-value-1')
53
+ $input.trigger(eventName)
54
+ u.setTimer 50, ->
55
+ # 50 ms after change 1: We're still waiting for the 100ms delay to expire
56
+ expect(callback.calls.count()).toEqual(0)
57
+ u.setTimer 100, ->
58
+ # 150 ms after change 1: The 100ms delay has expired
59
+ expect(callback.calls.count()).toEqual(1)
60
+ expect(callback.calls.mostRecent().args[0]).toEqual('new-value-1')
61
+ $input.val('new-value-2')
62
+ $input.trigger(eventName)
63
+ u.setTimer 40, ->
64
+ # 40 ms after change 2: We change again, resetting the delay
65
+ expect(callback.calls.count()).toEqual(1)
66
+ $input.val('new-value-3')
67
+ $input.trigger(eventName)
68
+ u.setTimer 85, ->
69
+ # 125 ms after change 2, which was superseded by change 3
70
+ # 85 ms after change 3
71
+ expect(callback.calls.count()).toEqual(1)
72
+ u.setTimer 65, ->
73
+ # 190 ms after change 2, which was superseded by change 3
74
+ # 150 ms after change 3
75
+ expect(callback.calls.count()).toEqual(2)
76
+ expect(callback.calls.mostRecent().args[0]).toEqual('new-value-3')
77
+ done()
78
+
79
+ it 'delays a callback if a previous async callback is taking long to execute', (done) ->
80
+ $input = affix('input[value="old-value"]')
81
+ callbackCount = 0
82
+ callback = ->
83
+ callbackCount += 1
84
+ u.promiseTimer(100)
85
+ up.observe($input, { delay: 1 }, callback)
86
+ $input.val('new-value-1')
87
+ $input.trigger(eventName)
88
+ u.setTimer 30, ->
89
+ # Callback has been called and takes 100 ms to complete
90
+ expect(callbackCount).toEqual(1)
91
+ $input.val('new-value-2')
92
+ $input.trigger(eventName)
93
+ u.setTimer 30, ->
94
+ # Second callback is triggerd, but waits for first callback to complete
95
+ expect(callbackCount).toEqual(1)
96
+ u.setTimer 100, ->
97
+ expect(callbackCount).toEqual(2)
98
+ done()
99
+
100
+ it 'does not run multiple callbacks if a long-running callback has been blocking multiple subsequent callbacks'
101
+
102
+ it "runs a callback in the same frame if the delay is 0", ->
103
+ console.debug('*** next example')
104
+ $input = affix('input[value="old-value"]')
105
+ callback = jasmine.createSpy('change callback')
106
+ up.observe($input, { delay: 0 }, callback)
107
+ $input.val('new-value')
108
+ $input.trigger(eventName)
109
+ expect(callback.calls.count()).toEqual(1)
110
+
111
+ it "runs multiple callbacks in the same frame if the delay is 0", ->
112
+ $input = affix('input[value="old-value"]')
113
+ callback = jasmine.createSpy('change callback')
114
+ up.observe($input, { delay: 0 }, callback)
115
+ $input.val('new-value')
116
+ $input.trigger(eventName)
117
+ $input.val('yet-another-value')
118
+ $input.trigger(eventName)
119
+ expect(callback.calls.count()).toEqual(2)
63
120
 
64
121
  describe 'when the first argument is a checkbox', ->
65
122
 
@@ -163,42 +220,44 @@ describe 'up.form', ->
163
220
 
164
221
  u.each changeEvents, (eventName) ->
165
222
 
166
- it "runs the callback when any of the form's inputs receives a '#{eventName}' event and the value changed", (done) ->
167
- $form = affix('form')
168
- $input = $form.affix('input[value="old-value"]')
169
- callback = jasmine.createSpy('change callback')
170
- up.observe($form, callback)
171
- $input.val('new-value')
172
- u.times 2, -> $input.trigger(eventName)
173
- u.nextFrame ->
174
- expect(callback).toHaveBeenCalledWith('new-value', $input)
175
- expect(callback.calls.count()).toEqual(1)
176
- done()
223
+ describe "when any of the form's inputs receives a #{eventName} event", ->
177
224
 
178
- it "does not run the callback when any of the form's inputs receives a '#{eventName}' event, but the value didn't change", (done) ->
179
- $form = affix('form')
180
- $input = $form.affix('input[value="old-value"]')
181
- callback = jasmine.createSpy('change callback')
182
- up.observe($form, callback)
183
- $input.trigger(eventName)
184
- u.nextFrame ->
185
- expect(callback).not.toHaveBeenCalled()
186
- done()
225
+ it "runs the callback if the value changed", (done) ->
226
+ $form = affix('form')
227
+ $input = $form.affix('input[value="old-value"]')
228
+ callback = jasmine.createSpy('change callback')
229
+ up.observe($form, callback)
230
+ $input.val('new-value')
231
+ u.times 2, -> $input.trigger(eventName)
232
+ u.nextFrame ->
233
+ expect(callback).toHaveBeenCalledWith('new-value', $input)
234
+ expect(callback.calls.count()).toEqual(1)
235
+ done()
187
236
 
188
- # it 'runs the callback only once when a radio button group changes its selection', ->
189
- # $form = affix('form')
190
- # $radio1 = $form.affix('input[type="radio"][name="group"][value="1"][checked="checked"]')
191
- # $radio2 = $form.affix('input[type="radio"][name="group"][value="2"]')
192
- # callback = jasmine.createSpy('change callback')
193
- # up.observe($form, callback)
194
- # $radio2.get(0).click()
195
- # u.nextFrame ->
196
- # expect(callback.calls.count()).toEqual(1)
237
+ it "does not run the callback if the value didn't change", (done) ->
238
+ $form = affix('form')
239
+ $input = $form.affix('input[value="old-value"]')
240
+ callback = jasmine.createSpy('change callback')
241
+ up.observe($form, callback)
242
+ $input.trigger(eventName)
243
+ u.nextFrame ->
244
+ expect(callback).not.toHaveBeenCalled()
245
+ done()
246
+
247
+ # it 'runs the callback only once when a radio button group changes its selection', ->
248
+ # $form = affix('form')
249
+ # $radio1 = $form.affix('input[type="radio"][name="group"][value="1"][checked="checked"]')
250
+ # $radio2 = $form.affix('input[type="radio"][name="group"][value="2"]')
251
+ # callback = jasmine.createSpy('change callback')
252
+ # up.observe($form, callback)
253
+ # $radio2.get(0).click()
254
+ # u.nextFrame ->
255
+ # expect(callback.calls.count()).toEqual(1)
197
256
 
198
257
  describe 'up.submit', ->
199
258
 
200
259
  describeCapability 'canPushState', ->
201
-
260
+
202
261
  beforeEach ->
203
262
  @$form = affix('form[action="/path/to"][method="put"][up-target=".response"]')
204
263
  @$form.append('<input name="field1" value="value1">')
@@ -232,11 +291,11 @@ describe 'up.form', ->
232
291
  responseText:
233
292
  """
234
293
  text-before
235
-
294
+
236
295
  <form>
237
296
  error-messages
238
297
  </form>
239
-
298
+
240
299
  text-after
241
300
  """
242
301
  expect(up.browser.url()).toEqual(@hrefBeforeExample)
@@ -300,12 +359,12 @@ describe 'up.form', ->
300
359
  expect(form.submit).toHaveBeenCalled()
301
360
 
302
361
  describeFallback 'canPushState', ->
303
-
362
+
304
363
  it 'falls back to a vanilla form submission', ->
305
364
  $form = affix('form[action="/path/to"][method="put"][up-target=".response"]')
306
365
  form = $form.get(0)
307
366
  spyOn(form, 'submit')
308
-
367
+
309
368
  up.submit($form)
310
369
  expect(form.submit).toHaveBeenCalled()
311
370
 
@@ -358,7 +417,10 @@ describe 'up.form', ->
358
417
 
359
418
  describe '[up-observe]', ->
360
419
 
361
- it 'runs the Javascript code in the attribute value when a change is observed in the field', ->
420
+ afterEach ->
421
+ window.observeCallbackSpy = undefined
422
+
423
+ it 'runs the Javascript code in the attribute value when a change is observed in the field', (done) ->
362
424
  $form = affix('form')
363
425
  window.observeCallbackSpy = jasmine.createSpy('observe callback')
364
426
  $field = $form.affix('input[val="old-value"][up-observe="window.observeCallbackSpy(value, $field.get(0))"]')
@@ -369,6 +431,20 @@ describe 'up.form', ->
369
431
  expect(window.observeCallbackSpy).toHaveBeenCalledWith('new-value', $field.get(0))
370
432
  done()
371
433
 
434
+ describe 'with [up-delay] modifier', ->
435
+
436
+ it 'debounces the callback', (done) ->
437
+ $form = affix('form')
438
+ window.observeCallbackSpy = jasmine.createSpy('observe callback')
439
+ $field = $form.affix('input[val="old-value"][up-observe="window.observeCallbackSpy()"][up-delay="50"]')
440
+ up.hello($form)
441
+ $field.val('new-value')
442
+ $field.trigger('change')
443
+ u.nextFrame ->
444
+ expect(window.observeCallbackSpy).not.toHaveBeenCalled()
445
+ u.setTimer 80, ->
446
+ expect(window.observeCallbackSpy).toHaveBeenCalled()
447
+ done()
372
448
 
373
449
  describe 'input[up-validate]', ->
374
450
 
@@ -1,7 +1,85 @@
1
1
  describe 'up.util', ->
2
-
2
+
3
+ u = up.util
4
+
3
5
  describe 'Javascript functions', ->
4
6
 
7
+ describe 'up.util.previewable', ->
8
+
9
+ it 'wraps a function into a proxy function with an additional .promise attribute', ->
10
+ fun = -> 'return value'
11
+ proxy = up.util.previewable(fun)
12
+ expect(u.isFunction(proxy)).toBe(true)
13
+ expect(u.isPromise(proxy.promise)).toBe(true)
14
+ expect(proxy()).toEqual('return value')
15
+
16
+ it "resolves the proxy's .promise when the inner function returns", (done) ->
17
+ fun = -> 'return value'
18
+ proxy = up.util.previewable(fun)
19
+ callback = jasmine.createSpy('promise callback')
20
+ proxy.promise.then(callback)
21
+ u.nextFrame ->
22
+ expect(callback).not.toHaveBeenCalled()
23
+ proxy()
24
+ u.nextFrame ->
25
+ expect(callback).toHaveBeenCalledWith('return value')
26
+ done()
27
+
28
+ it "delays resolution of the proxy's .promise if the inner function returns a promise", (done) ->
29
+ funDeferred = $.Deferred()
30
+ fun = -> funDeferred
31
+ proxy = up.util.previewable(fun)
32
+ callback = jasmine.createSpy('promise callback')
33
+ proxy.promise.then(callback)
34
+ proxy()
35
+ u.nextFrame ->
36
+ expect(callback).not.toHaveBeenCalled()
37
+ funDeferred.resolve()
38
+ u.nextFrame ->
39
+ expect(callback).toHaveBeenCalled()
40
+ done()
41
+
42
+ describe 'up.util.DivertibleChain', ->
43
+
44
+ it "instantiates a task queue whose (2..n)th tasks can be changed by calling '.asap'", (done) ->
45
+ chain = new up.util.DivertibleChain()
46
+
47
+ timer1Spy = jasmine.createSpy('timer1 has been called')
48
+ timer1 = ->
49
+ timer1Spy()
50
+ u.promiseTimer(50)
51
+
52
+ timer2Spy = jasmine.createSpy('timer2 has been called')
53
+ timer2 = ->
54
+ timer2Spy()
55
+ u.promiseTimer(50)
56
+
57
+ timer3Spy = jasmine.createSpy('timer3 has been called')
58
+ timer3 = ->
59
+ timer3Spy()
60
+ u.promiseTimer(50)
61
+
62
+ timer4Spy = jasmine.createSpy('timer4 has been called')
63
+ timer4 = ->
64
+ timer4Spy()
65
+ u.promiseTimer(50)
66
+
67
+ chain.asap(timer1)
68
+ u.nextFrame ->
69
+ expect(timer1Spy).toHaveBeenCalled()
70
+ chain.asap(timer2)
71
+ u.nextFrame ->
72
+ # timer2 is still waiting for timer1 to finish
73
+ expect(timer2Spy).not.toHaveBeenCalled()
74
+ # Override the (2..n)th tasks. This unschedules timer2.
75
+ chain.asap(timer3, timer4)
76
+ u.setTimer 80, ->
77
+ expect(timer2Spy).not.toHaveBeenCalled()
78
+ expect(timer3Spy).toHaveBeenCalled()
79
+ u.setTimer 70, ->
80
+ expect(timer4Spy).toHaveBeenCalled()
81
+ done()
82
+
5
83
  describe 'up.util.sequence', ->
6
84
 
7
85
  it 'combines the given functions into a single function', ->
@@ -144,10 +222,17 @@ describe 'up.util', ->
144
222
  jasmine.clock().tick(1500)
145
223
  expect(callback).toHaveBeenCalled()
146
224
 
147
- it 'calls the given function in the current execution frame if the delay is zero', ->
148
- callback = jasmine.createSpy()
149
- up.util.setTimer(0, callback)
150
- expect(callback).toHaveBeenCalled()
225
+ describe 'if the delay is zero', ->
226
+
227
+ it 'calls the given function in the current execution frame', ->
228
+ callback = jasmine.createSpy()
229
+ up.util.setTimer(0, callback)
230
+ expect(callback).toHaveBeenCalled()
231
+
232
+ it "returns undefined so the return value won't be mistaken with a Javascript timer ID", ->
233
+ callback = -> 'function return value'
234
+ timerReturnValue = up.util.setTimer(0, callback)
235
+ expect(timerReturnValue).toBeUndefined()
151
236
 
152
237
  # describe 'up.util.argNames', ->
153
238
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unpoly-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.30.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-20 00:00:00.000000000 Z
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails