unpoly-rails 0.56.7 → 0.57.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  3. data/dist/unpoly.js +1569 -793
  4. data/dist/unpoly.min.js +4 -4
  5. data/lib/assets/javascripts/unpoly.coffee +2 -0
  6. data/lib/assets/javascripts/unpoly/browser.coffee.erb +25 -41
  7. data/lib/assets/javascripts/unpoly/bus.coffee.erb +20 -6
  8. data/lib/assets/javascripts/unpoly/classes/cache.coffee +23 -13
  9. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +87 -0
  10. data/lib/assets/javascripts/unpoly/classes/focus_tracker.coffee +29 -0
  11. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +7 -4
  12. data/lib/assets/javascripts/unpoly/classes/record.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/request.coffee +38 -45
  14. data/lib/assets/javascripts/unpoly/classes/response.coffee +16 -1
  15. data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +26 -0
  16. data/lib/assets/javascripts/unpoly/classes/store/session.coffee +59 -0
  17. data/lib/assets/javascripts/unpoly/cookie.coffee +56 -0
  18. data/lib/assets/javascripts/unpoly/dom.coffee.erb +67 -39
  19. data/lib/assets/javascripts/unpoly/feedback.coffee +2 -2
  20. data/lib/assets/javascripts/unpoly/form.coffee.erb +23 -12
  21. data/lib/assets/javascripts/unpoly/history.coffee +2 -2
  22. data/lib/assets/javascripts/unpoly/layout.coffee.erb +118 -99
  23. data/lib/assets/javascripts/unpoly/link.coffee.erb +12 -5
  24. data/lib/assets/javascripts/unpoly/log.coffee +6 -5
  25. data/lib/assets/javascripts/unpoly/modal.coffee.erb +9 -2
  26. data/lib/assets/javascripts/unpoly/motion.coffee.erb +2 -6
  27. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +2 -2
  28. data/lib/assets/javascripts/unpoly/params.coffee.erb +522 -0
  29. data/lib/assets/javascripts/unpoly/popup.coffee.erb +3 -3
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +42 -34
  31. data/lib/assets/javascripts/unpoly/{syntax.coffee → syntax.coffee.erb} +59 -117
  32. data/lib/assets/javascripts/unpoly/{util.coffee → util.coffee.erb} +206 -171
  33. data/lib/unpoly/rails/version.rb +1 -1
  34. data/package.json +1 -1
  35. data/spec_app/Gemfile.lock +1 -1
  36. data/spec_app/app/assets/javascripts/integration_test.coffee +0 -4
  37. data/spec_app/app/assets/stylesheets/integration_test.sass +7 -1
  38. data/spec_app/app/controllers/pages_controller.rb +4 -0
  39. data/spec_app/app/views/form_test/basics/new.erb +34 -5
  40. data/spec_app/app/views/form_test/submission_result.erb +2 -2
  41. data/spec_app/app/views/form_test/uploads/new.erb +15 -2
  42. data/spec_app/app/views/hash_test/unpoly.erb +30 -0
  43. data/spec_app/app/views/pages/start.erb +2 -1
  44. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +17 -2
  45. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +5 -0
  46. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +1 -1
  47. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +5 -0
  48. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +8 -8
  49. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +58 -20
  50. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +78 -0
  51. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +31 -0
  52. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +50 -0
  53. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +67 -0
  54. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +113 -0
  55. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +133 -45
  56. data/spec_app/spec/javascripts/up/form_spec.js.coffee +13 -13
  57. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +110 -26
  58. data/spec_app/spec/javascripts/up/link_spec.js.coffee +1 -1
  59. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +1 -0
  60. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +52 -51
  61. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +2 -2
  62. data/spec_app/spec/javascripts/up/params_spec.coffee +768 -0
  63. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +75 -36
  64. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +48 -15
  65. data/spec_app/spec/javascripts/up/util_spec.js.coffee +148 -131
  66. metadata +17 -5
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
@@ -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.56.7'
7
+ VERSION = '0.57.0'
8
8
  end
9
9
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unpoly",
3
- "version": "0.56.7",
3
+ "version": "0.57.0",
4
4
  "description": "Unobtrusive JavaScript framework",
5
5
  "main": "dist/unpoly.js",
6
6
  "files": [
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- unpoly-rails (0.56.6)
4
+ unpoly-rails (0.56.7)
5
5
  rails (>= 3)
6
6
 
7
7
  GEM
@@ -3,7 +3,3 @@
3
3
  #= require es6-promise.auto
4
4
  #= require helpers/knife
5
5
  #= require unpoly
6
-
7
- up.compiler '.compiler', ($element) ->
8
- console.error('*** Compiler called for %o', $element.get(0))
9
- -> console.error('*** Destructor called for %o', $element.get(0))
@@ -11,6 +11,7 @@ $block-spacing: 25px
11
11
  body
12
12
  margin: 0
13
13
  background: image-url('grid.png') repeat
14
+
14
15
  font-family: arial, sans-serif
15
16
 
16
17
  h1, h2, h3, h4, p
@@ -29,7 +30,8 @@ a
29
30
  height: 150px
30
31
  content: 'Free space'
31
32
 
32
- .fixed-top-bar
33
+ .fixed-top-bar,
34
+ .fixed-bottom-bar
33
35
  position: fixed
34
36
  z-index: 9999999
35
37
  top: 0
@@ -38,6 +40,10 @@ a
38
40
  background-color: transparentize(blue, 0.5)
39
41
  padding: 50px
40
42
 
43
+ .fixed-bottom-bar
44
+ top: auto
45
+ bottom: 0
46
+
41
47
  .example
42
48
  margin: 50px
43
49
  +clear-after
@@ -2,4 +2,8 @@ class PagesController < ApplicationController
2
2
 
3
3
  layout 'integration_test'
4
4
 
5
+ # def start
6
+ # cookies['baram'] = { value: 'baz;bam', expires: 2.days.from_now }
7
+ # end
8
+
5
9
  end
@@ -5,17 +5,46 @@
5
5
  <%= form_tag "/form_test/basic", method: :post, 'up-target' => '.result' do %>
6
6
 
7
7
  <p>
8
- <input type="text" name="text-param" value="text-value" />
8
+ <label>
9
+ input[type=text]
10
+ <input type="text" name="text-param" value="text-value" />
11
+ </label>
9
12
  </p>
10
13
 
11
14
  <p>
12
- <input type="password" name="password-param" value="password-value" />
15
+ <label>
16
+ input[type=password]
17
+ <input type="password" name="password-param" value="password-value" />
18
+ </label>
13
19
  </p>
14
20
 
15
21
  <p>
16
- <select name="select-param">
17
- <option value="select-value">select-label</option>
18
- </select>
22
+ <label>
23
+ input[type=checkbox]
24
+ <select name="checkbox-param">
25
+ <option value="checkbox-value">checkbox-label</option>
26
+ </select>
27
+ </label>
28
+ </p>
29
+
30
+ <p>
31
+ <label>
32
+ select
33
+ <select name="select-param">
34
+ <option value="select-value">select-label</option>
35
+ </select>
36
+ </label>
37
+ </p>
38
+
39
+ <p>
40
+ <label>
41
+ select[multiple]
42
+ <select name="multiple-select-param[]" multiple>
43
+ <option value="multiple-select-value-1">multiple-select-label-1</option>
44
+ <option value="multiple-select-value-2">multiple-select-label-2</option>
45
+ <option value="multiple-select-value-3">multiple-select-label-3</option>
46
+ </select>
47
+ </label>
19
48
  </p>
20
49
 
21
50
  <p>
@@ -11,7 +11,7 @@
11
11
  <dt><code><%= key %></code></dt>
12
12
  <dd>
13
13
  <% if value.is_a?(String) %>
14
- <pre><code><%= value %></code></pre>
14
+ <code><%= value %></code>
15
15
  <% elsif value.is_a?(ActionDispatch::Http::UploadedFile) %>
16
16
  <% if value.original_filename.ends_with?('.jpg') %>
17
17
  <img src="data:image/jpeg;base64,<%= Base64.encode64(value.read) %>">
@@ -21,7 +21,7 @@
21
21
  <%= value.original_filename %> (<%= value.size %> bytes)
22
22
  <% end %>
23
23
  <% else %>
24
- <%= value.inspect %>
24
+ <code><%= value.inspect %></code>
25
25
  <% end %>
26
26
  </dd>
27
27
  <% end %>
@@ -5,11 +5,24 @@
5
5
  <%= form_tag "/form_test/upload", method: :post, multipart: true, 'up-target' => '.result' do %>
6
6
 
7
7
  <p>
8
- <input type="text" name="text-param" value="text-value" />
8
+ <label>
9
+ input[type=text]
10
+ <input type="text" name="text-param" value="text-value" />
11
+ </label>
9
12
  </p>
10
13
 
11
14
  <p>
12
- <input type="file" name="file-param" />
15
+ <label>
16
+ input[type=file]
17
+ <input type="file" name="file-param" />
18
+ </label>
19
+ </p>
20
+
21
+ <p>
22
+ <label>
23
+ input[type=file][multiple]
24
+ <input type="file" name="multiple-file-param" multiple />
25
+ </label>
13
26
  </p>
14
27
 
15
28
  <p>
@@ -0,0 +1,30 @@
1
+ <div class="fixed-top-bar" up-fixed="top" style="background-color: rgba(40, 40, 40, 0.85)">
2
+ Obstruction
3
+ </div>
4
+
5
+ <div class="spacer">
6
+ </div>
7
+
8
+ <h1 style="margin: 0">Hash links (with Unpoly)</h1>
9
+
10
+ <div class="example" id="one" style="height: 1000px; background-color: yellow; margin: 0">
11
+ <h2 style="margin: 0">This is #one</h2>
12
+
13
+ <a href="#two" up-target=".page">Scroll down to #two</a>
14
+ </div>
15
+
16
+ <div class="example" id="two" style="height: 200px; background-color: green; margin: 0">
17
+ <h2 style="margin: 0">This is #two</h2>
18
+
19
+ <a href="#three" up-target=".page">Scroll down to #three</a>
20
+ </div>
21
+
22
+ <div class="example" id="three" style="height: 1000px; background-color: cyan; margin: 0">
23
+ <h2 style="margin: 0">This is #three</h2>
24
+
25
+ <a href="#one" up-target=".page">Scroll up to #one</a>
26
+ </div>
27
+
28
+ <div class="fixed-bottom-bar" up-fixed="bottom" style="background-color: rgba(40, 40, 40, 0.85)">
29
+ Obstruction
30
+ </div>
@@ -60,7 +60,8 @@
60
60
  <li><%= link_to 'Error', '/error_test/trigger' %></li>
61
61
  <li><%= link_to 'Booting with non-GET method', '/method_test/page1' %></li>
62
62
  <li><%= link_to 'Fragment update', '/replace_test/page1' %></li>
63
- <li><%= link_to 'Hash links (without Unpoly)', '/hash_test/vanilla' %></li>
63
+ <li><%= link_to 'Hash links (without Unpoly)', '/hash_test/vanilla#one' %></li>
64
+ <li><%= link_to 'Hash links (with Unpoly)', '/hash_test/unpoly#one' %></li>
64
65
  <li><%= link_to 'Revealing long pages', '/reveal_test/long1' %></li>
65
66
  <li><%= link_to 'Animations', '/motion_test/animations' %></li>
66
67
  <li><%= link_to 'Transitions', '/motion_test/transitions' %></li>
@@ -5,5 +5,20 @@ beforeEach ->
5
5
  up.util.isFormData(xhr.params)
6
6
 
7
7
  parse: (params) ->
8
- # Just return it
9
- params
8
+ if up.browser.canInspectFormData()
9
+ array = up.params.toArray(params)
10
+ else if params.originalArray
11
+ # In browser that don't support FormData#entries(),
12
+ # up.params.toArray() stores the original array with the generated
13
+ # FormData object.
14
+ array = params.originalArray
15
+ else
16
+ throw "Cannot parse FormData for inspection in tests"
17
+
18
+ obj = {}
19
+
20
+ for entry in array
21
+ obj[entry.name] ||= []
22
+ obj[entry.name].push(entry.value)
23
+
24
+ obj
@@ -1,10 +1,15 @@
1
1
  afterEach (done) ->
2
+ # If the spec has installed the Jasmine clock, uninstall it so
3
+ # the timeout below will actually happen.
4
+ jasmine.clock().uninstall()
5
+
2
6
  # Wait one more frame so pending callbacks have a chance to run.
3
7
  # Pending callbacks might change the URL or cause errors that bleed into
4
8
  # the next example.
5
9
 
6
10
  up.util.nextFrame =>
7
11
  up.reset()
12
+ up.browser.popCookie(up.protocol.config.methodCookie)
8
13
 
9
14
  # Give async reset behavior another frame to play out,
10
15
  # then start the next example.
@@ -2,4 +2,4 @@ beforeEach ->
2
2
  jasmine.addMatchers
3
3
  toBeError: (util, customEqualityTesters) ->
4
4
  compare: (actual, message) ->
5
- pass: (actual instanceof Error) && (!message || (message instanceof RegExp && actual.message =~ message) || actual.message == message)
5
+ pass: (actual instanceof Error) && (!message || (message instanceof RegExp && message.test(actual.message)) || actual.message == message)
@@ -0,0 +1,5 @@
1
+ beforeEach ->
2
+ jasmine.addMatchers
3
+ toMatchSelector: (util, customEqualityTesters) ->
4
+ compare: (actualElement, expectedSelector) ->
5
+ pass: $(actualElement).is(expectedSelector)
@@ -13,9 +13,9 @@ describe 'up.browser', ->
13
13
 
14
14
  describe "for GET requests", ->
15
15
 
16
- it "creates a GET form, adds all { data } params as hidden fields and submits the form", ->
16
+ it "creates a GET form, adds all { params } as hidden fields and submits the form", ->
17
17
  submitForm = spyOn(up.browser, 'submitForm')
18
- up.browser.navigate('/foo', method: 'GET', data: { param1: 'param1 value', param2: 'param2 value' })
18
+ up.browser.navigate('/foo', method: 'GET', params: { param1: 'param1 value', param2: 'param2 value' })
19
19
  expect(submitForm).toHaveBeenCalled()
20
20
  $form = $('form.up-page-loader')
21
21
  expect($form).toExist()
@@ -25,9 +25,9 @@ describe 'up.browser', ->
25
25
  expect($form.find('input[name="param1"][value="param1 value"]')).toExist()
26
26
  expect($form.find('input[name="param2"][value="param2 value"]')).toExist()
27
27
 
28
- it 'merges params from the given URL and the { data } option', ->
28
+ it 'merges params from the given URL and the { params } option', ->
29
29
  submitForm = spyOn(up.browser, 'submitForm')
30
- up.browser.navigate('/foo?param1=param1%20value', method: 'GET', data: { param2: 'param2 value' })
30
+ up.browser.navigate('/foo?param1=param1%20value', method: 'GET', params: { param2: 'param2 value' })
31
31
  expect(submitForm).toHaveBeenCalled()
32
32
  $form = $('form.up-page-loader')
33
33
  expect($form).toExist()
@@ -39,9 +39,9 @@ describe 'up.browser', ->
39
39
 
40
40
  describe "for POST requests", ->
41
41
 
42
- it "creates a POST form, adds all { data } params as hidden fields and submits the form", ->
42
+ it "creates a POST form, adds all { params } params as hidden fields and submits the form", ->
43
43
  submitForm = spyOn(up.browser, 'submitForm')
44
- up.browser.navigate('/foo', method: 'POST', data: { param1: 'param1 value', param2: 'param2 value' })
44
+ up.browser.navigate('/foo', method: 'POST', params: { param1: 'param1 value', param2: 'param2 value' })
45
45
  expect(submitForm).toHaveBeenCalled()
46
46
  $form = $('form.up-page-loader')
47
47
  expect($form).toExist()
@@ -50,9 +50,9 @@ describe 'up.browser', ->
50
50
  expect($form.find('input[name="param1"][value="param1 value"]')).toExist()
51
51
  expect($form.find('input[name="param2"][value="param2 value"]')).toExist()
52
52
 
53
- it 'merges params from the given URL and the { data } option', ->
53
+ it 'merges params from the given URL and the { params } option', ->
54
54
  submitForm = spyOn(up.browser, 'submitForm')
55
- up.browser.navigate('/foo?param1=param1%20value', method: 'POST', data: { param2: 'param2 value' })
55
+ up.browser.navigate('/foo?param1=param1%20value', method: 'POST', params: { param2: 'param2 value' })
56
56
  expect(submitForm).toHaveBeenCalled()
57
57
  $form = $('form.up-page-loader')
58
58
  expect($form).toExist()
@@ -31,30 +31,68 @@ describe 'up.bus', ->
31
31
  next =>
32
32
  expect(clickSpy.calls.count()).toEqual(1)
33
33
 
34
- it 'parses an up-data attribute as JSON and passes the parsed object as a third argument to the initializer', asyncSpec (next) ->
35
- $child = affix('.child')
36
- observeArgs = jasmine.createSpy()
37
- up.on 'click', '.child', (event, $element, data) ->
38
- observeArgs($element.attr('class'), data)
34
+ it 'throws an error when trying to register the same callback multiple times', ->
35
+ callback = ->
36
+ register = -> up.on 'foo', callback
37
+ register()
38
+ expect(register).toThrowError(/cannot be registered more than once/i)
39
+
40
+ it 'does not throw an error if a callback is registered, unregistered and registered a second time', ->
41
+ callback = ->
42
+ register = -> up.on 'foo', callback
43
+ unregister = -> up.off 'foo', callback
44
+ register()
45
+ unregister()
46
+ expect(register).not.toThrowError()
47
+
48
+ describe 'passing of [up-data]', ->
49
+
50
+ it 'parses an [up-data] attribute as JSON and passes the parsed object as a third argument to the listener', asyncSpec (next) ->
51
+ $child = affix('.child')
52
+ observeArgs = jasmine.createSpy()
53
+ up.on 'click', '.child', (event, $element, data) ->
54
+ observeArgs($element.attr('class'), data)
55
+
56
+ data = { key1: 'value1', key2: 'value2' }
57
+ $tag = affix(".child").attr('up-data', JSON.stringify(data))
39
58
 
40
- data = { key1: 'value1', key2: 'value2' }
41
- $tag = affix(".child").attr('up-data', JSON.stringify(data))
59
+ Trigger.click($('.child'))
42
60
 
43
- Trigger.click($('.child'))
61
+ next =>
62
+ expect(observeArgs).toHaveBeenCalledWith('child', data)
44
63
 
45
- next =>
46
- expect(observeArgs).toHaveBeenCalledWith('child', data)
64
+ it 'passes an empty object as a second argument to the listener if there is no [up-data] attribute', asyncSpec (next) ->
65
+ $child = affix('.child')
66
+ observeArgs = jasmine.createSpy()
67
+ up.on 'click', '.child', (event, $element, data) ->
68
+ observeArgs($element.attr('class'), data)
47
69
 
48
- it 'passes an empty object as a second argument to the listener if there is no up-data attribute', asyncSpec (next) ->
49
- $child = affix('.child')
50
- observeArgs = jasmine.createSpy()
51
- up.on 'click', '.child', (event, $element, data) ->
52
- observeArgs($element.attr('class'), data)
70
+ Trigger.click($('.child'))
53
71
 
54
- Trigger.click($('.child'))
72
+ next =>
73
+ expect(observeArgs).toHaveBeenCalledWith('child', {})
55
74
 
56
- next =>
57
- expect(observeArgs).toHaveBeenCalledWith('child', {})
75
+ it 'does not parse an [up-data] attribute if the listener function only takes one argument', asyncSpec (next) ->
76
+ parseDataSpy = spyOn(up.syntax, 'data').and.returnValue({})
77
+
78
+ $child = affix('.child')
79
+ up.on 'click', '.child', (event) -> # no-op
80
+
81
+ Trigger.click($('.child'))
82
+
83
+ next =>
84
+ expect(parseDataSpy).not.toHaveBeenCalled()
85
+
86
+ it 'does not parse an [up-data] attribute if the listener function only takes two arguments', asyncSpec (next) ->
87
+ parseDataSpy = spyOn(up.syntax, 'data').and.returnValue({})
88
+
89
+ $child = affix('.child')
90
+ up.on 'click', '.child', (event, $element) -> # no-op
91
+
92
+ Trigger.click($('.child'))
93
+
94
+ next =>
95
+ expect(parseDataSpy).not.toHaveBeenCalled()
58
96
 
59
97
  describe 'up.off', ->
60
98
 
@@ -132,10 +170,10 @@ describe 'up.bus', ->
132
170
 
133
171
  expect(emittedEvent.$element).toEqual($element)
134
172
 
135
- describe 'up.bus.renamedEvent', ->
173
+ describe 'up.bus.deprecateRenamedEvent', ->
136
174
 
137
175
  it 'prints a warning and registers the event listener for the new event name', ->
138
- warnSpy = spyOn(up.log, 'warn')
176
+ warnSpy = spyOn(up, 'warn')
139
177
  listener = jasmine.createSpy('listener')
140
178
 
141
179
  # Reister listener for the old event name
@@ -1 +1,79 @@
1
1
  describe 'up.Cache', ->
2
+
3
+ describe '#get', ->
4
+
5
+ it 'returns an item that was previously set', ->
6
+ store = new up.Cache()
7
+ store.set('foo', 'value of foo')
8
+ store.set('bar', 'value of bar')
9
+
10
+ expect(store.get('foo')).toEqual('value of foo')
11
+ expect(store.get('bar')).toEqual('value of bar')
12
+
13
+ it 'returns undefined if no item with that key was set', ->
14
+ store = new up.Cache()
15
+ store.set('foo', 'value of foo')
16
+
17
+ expect(store.get('bar')).toBeUndefined()
18
+
19
+ it 'returns undefined if the item has expired', ->
20
+ jasmine.clock().install()
21
+ jasmine.clock().mockDate(new Date())
22
+
23
+ store = new up.Cache(expiry: 100, logPrefix: 'cache')
24
+ store.set('foo', 'value of foo')
25
+
26
+ jasmine.clock().tick(80)
27
+ expect(store.get('foo')).toEqual('value of foo')
28
+
29
+ jasmine.clock().tick(40)
30
+ expect(store.get('foo')).toBeUndefined()
31
+
32
+ describe '#set', ->
33
+
34
+ it 'removes the oldest item if setting a new item would exceed the cache size', ->
35
+ jasmine.clock().install()
36
+ store = new up.Cache(size: 2, logPrefix: 'cache')
37
+
38
+ store.set('foo', 'value of foo')
39
+ jasmine.clock().tick(10)
40
+ store.set('bar', 'value of bar')
41
+ jasmine.clock().tick(10)
42
+ store.set('baz', 'value of baz')
43
+
44
+ expect(store.get('foo')).toBeUndefined()
45
+ expect(store.get('bar')).toEqual('value of bar')
46
+ expect(store.get('baz')).toEqual('value of baz')
47
+
48
+ describe '#keys', ->
49
+
50
+ it 'returns an array of keys in the store', ->
51
+ store = new up.Cache()
52
+ store.set('foo', 'value of foo')
53
+ store.set('bar', 'value of bar')
54
+
55
+ expect(store.keys().sort()).toEqual ['bar', 'foo']
56
+
57
+ describe '#clear', ->
58
+
59
+ it 'removes all keys from the store', ->
60
+ store = new up.Cache()
61
+ store.set('foo', 'value of foo')
62
+ store.set('bar', 'value of bar')
63
+
64
+ store.clear()
65
+
66
+ expect(store.get('foo')).toBeUndefined()
67
+ expect(store.get('bar')).toBeUndefined()
68
+
69
+ describe '#remove', ->
70
+
71
+ it 'removes the given key from the store', ->
72
+ store = new up.Cache()
73
+ store.set('foo', 'value of foo')
74
+ store.set('bar', 'value of bar')
75
+
76
+ store.remove('foo')
77
+
78
+ expect(store.get('foo')).toBeUndefined()
79
+ expect(store.get('bar')).toEqual('value of bar')