picky 3.6.7 → 3.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/lib/picky/backends/file/basic.rb +1 -1
  2. data/lib/picky/backends/file/json.rb +5 -1
  3. data/lib/picky/backends/file.rb +7 -0
  4. data/lib/picky/backends/memory.rb +7 -0
  5. data/lib/picky/backends/redis/basic.rb +3 -11
  6. data/lib/picky/backends/redis/directly_manipulable.rb +48 -0
  7. data/lib/picky/backends/redis/list.rb +39 -15
  8. data/lib/picky/backends/redis/string.rb +17 -9
  9. data/lib/picky/backends/redis.rb +102 -66
  10. data/lib/picky/backends/sqlite/array.rb +38 -0
  11. data/lib/picky/backends/sqlite/basic.rb +100 -0
  12. data/lib/picky/backends/sqlite/directly_manipulable.rb +42 -0
  13. data/lib/picky/backends/sqlite/value.rb +34 -0
  14. data/lib/picky/backends/sqlite.rb +14 -4
  15. data/lib/picky/bundle.rb +12 -5
  16. data/lib/picky/bundle_indexed.rb +15 -2
  17. data/lib/picky/bundle_indexing.rb +6 -5
  18. data/lib/picky/bundle_realtime.rb +22 -31
  19. data/lib/picky/categories_realtime.rb +1 -1
  20. data/lib/picky/category_indexed.rb +1 -1
  21. data/lib/picky/category_indexing.rb +7 -5
  22. data/lib/picky/category_realtime.rb +17 -5
  23. data/lib/picky/generators/strategy.rb +4 -0
  24. data/lib/picky/index_indexing.rb +1 -4
  25. data/lib/picky/index_realtime.rb +16 -6
  26. data/lib/picky/indexers/base.rb +7 -1
  27. data/lib/picky/indexes.rb +1 -0
  28. data/lib/picky/loader.rb +11 -7
  29. data/lib/picky/query/allocation.rb +1 -1
  30. data/lib/picky/query/indexes.rb +2 -2
  31. data/lib/picky/query/token.rb +1 -1
  32. data/lib/picky/search.rb +20 -8
  33. data/lib/picky/tokenizer.rb +6 -6
  34. data/lib/picky/wrappers/bundle/delegators.rb +3 -1
  35. data/spec/category_realtime_spec.rb +33 -0
  36. data/spec/functional/backends/file_spec.rb +98 -0
  37. data/spec/functional/backends/memory_spec.rb +96 -0
  38. data/spec/functional/backends/redis_spec.rb +107 -0
  39. data/spec/functional/backends/sqlite_spec.rb +104 -0
  40. data/spec/{specific → functional}/dynamic_weights_spec.rb +0 -0
  41. data/spec/{specific → functional}/exact_first_spec.rb +2 -4
  42. data/spec/functional/max_allocations_spec.rb +33 -0
  43. data/spec/{specific → functional}/realtime_spec.rb +0 -0
  44. data/spec/{specific → functional}/regression_spec.rb +0 -0
  45. data/spec/{specific → functional}/speed_spec.rb +0 -0
  46. data/spec/lib/backends/file/basic_spec.rb +1 -1
  47. data/spec/lib/backends/redis/basic_spec.rb +12 -13
  48. data/spec/lib/backends/redis/directly_manipulable_spec.rb +91 -0
  49. data/spec/lib/backends/redis/float_spec.rb +17 -17
  50. data/spec/lib/backends/redis/list_spec.rb +9 -9
  51. data/spec/lib/backends/sqlite/array_spec.rb +143 -0
  52. data/spec/lib/backends/sqlite/directly_manipulable_spec.rb +65 -0
  53. data/spec/lib/backends/sqlite/{db_spec.rb → value_spec.rb} +2 -7
  54. data/spec/lib/backends/sqlite_spec.rb +22 -20
  55. data/spec/lib/category_indexed_spec.rb +1 -1
  56. data/spec/lib/category_indexing_spec.rb +2 -2
  57. data/spec/lib/index_indexing_spec.rb +0 -7
  58. data/spec/lib/index_realtime_spec.rb +34 -0
  59. data/spec/lib/indexed/bundle_realtime_spec.rb +166 -75
  60. data/spec/lib/indexers/base_spec.rb +13 -1
  61. data/spec/lib/search_spec.rb +31 -20
  62. metadata +58 -34
  63. data/lib/picky/backends/sqlite/db.rb +0 -84
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Picky::Backends::Redis::DirectlyManipulable do
4
+
5
+ let(:client) { stub :client }
6
+ let(:backend) { stub :backend, client: client, namespace: 'some:namespace' }
7
+ let(:list) do
8
+ list = [1,2]
9
+ described_class.make backend, list, 'some:key'
10
+ list
11
+ end
12
+
13
+ context 'stubbed backend' do
14
+ before(:each) do
15
+ backend.stub! :[]
16
+ end
17
+ it 'calls the right client method' do
18
+ num = described_class.class_variable_get(:@@append_index)
19
+
20
+ client.should_receive(:zadd).once.with "some:namespace:some:key", num+1, 3
21
+
22
+ list << 3
23
+ end
24
+ it 'calls the right client method' do
25
+ num = described_class.class_variable_get(:@@unshift_index)
26
+
27
+ client.should_receive(:zadd).once.with "some:namespace:some:key", num-1, 3
28
+
29
+ list.unshift 3
30
+ end
31
+ it 'calls the right client method' do
32
+ client.should_receive(:zrem).once.with "some:namespace:some:key", 1
33
+
34
+ list.delete 1
35
+ end
36
+ it 'calls the right client method' do
37
+ client.should_receive(:zrem).never
38
+
39
+ list.delete 5
40
+ end
41
+ end
42
+
43
+ context 'stubbed client' do
44
+ before(:each) do
45
+ client.stub! :zadd
46
+ client.stub! :zrem
47
+ end
48
+ it 'calls [] at the end of the method' do
49
+ backend.should_receive(:[]).once.with 'some:key'
50
+
51
+ list << 3
52
+ end
53
+ it 'calls [] at the end of the method' do
54
+ backend.should_receive(:[]).once.with 'some:key'
55
+
56
+ list.unshift 3
57
+ end
58
+ it 'returns the result' do
59
+ list.delete(1).should == 1
60
+ end
61
+ end
62
+
63
+ context 'stubbed client/backend' do
64
+ before(:each) do
65
+ backend.stub! :[]
66
+ client.stub! :zadd
67
+ client.stub! :zrem
68
+ end
69
+ it 'behaves like an ordinary Array' do
70
+ list << 3
71
+
72
+ list.should == [1,2,3]
73
+ end
74
+ it 'behaves like an ordinary Array' do
75
+ list.unshift 3
76
+
77
+ list.should == [3,1,2]
78
+ end
79
+ it 'behaves like an ordinary Array' do
80
+ list.delete 1
81
+
82
+ list.should == [2]
83
+ end
84
+ it 'behaves like an ordinary Array' do
85
+ list.delete 5
86
+
87
+ list.should == [1,2]
88
+ end
89
+ end
90
+
91
+ end
@@ -1,38 +1,38 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Picky::Backends::Redis::Float do
4
-
4
+
5
5
  let(:client) { stub :client }
6
- let(:backend) { described_class.new client, :some_namespace }
7
-
6
+ let(:backend) { described_class.new client, 'some_namespace' }
7
+
8
8
  describe 'dump' do
9
9
  it 'dumps correctly' do
10
- client.should_receive(:del).once.ordered.with :some_namespace
11
- client.should_receive(:hset).once.ordered.with :some_namespace, :a, 1
12
- client.should_receive(:hset).once.ordered.with :some_namespace, :b, 2
13
- client.should_receive(:hset).once.ordered.with :some_namespace, :c, 3
14
-
15
- backend.dump a: 1, b: 2, c: 3
10
+ client.should_receive(:del).once.ordered.with 'some_namespace'
11
+ client.should_receive(:hset).once.ordered.with 'some_namespace', 'a', 1
12
+ client.should_receive(:hset).once.ordered.with 'some_namespace', 'b', 2
13
+ client.should_receive(:hset).once.ordered.with 'some_namespace', 'c', 3
14
+
15
+ backend.dump 'a' => 1, 'b' => 2, 'c' => 3
16
16
  end
17
17
  end
18
-
18
+
19
19
  describe 'member' do
20
20
  it 'delegates to the backend' do
21
- client.should_receive(:hget).once.with :some_namespace, :some_symbol
22
-
23
- backend[:some_symbol]
21
+ client.should_receive(:hget).once.with 'some_namespace', 'some'
22
+
23
+ backend['some']
24
24
  end
25
25
  it 'returns whatever it gets from the backend' do
26
26
  client.should_receive(:hget).any_number_of_times.and_return '1.23'
27
-
28
- backend[:anything].should == 1.23
27
+
28
+ backend['anything'].should == 1.23
29
29
  end
30
30
  end
31
-
31
+
32
32
  describe 'to_s' do
33
33
  it 'returns the cache path with the default file extension' do
34
34
  backend.to_s.should == 'Picky::Backends::Redis::Float(some_namespace:*)'
35
35
  end
36
36
  end
37
-
37
+
38
38
  end
@@ -1,27 +1,27 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Picky::Backends::Redis::List do
4
-
4
+
5
5
  let(:client) { stub :client }
6
6
  let(:index) { described_class.new client, :some_namespace }
7
-
7
+
8
8
  describe '[]' do
9
9
  it 'returns whatever comes back from the backend' do
10
- client.stub! :zrange => :some_lrange_result
11
-
12
- index[:anything].should == :some_lrange_result
10
+ client.stub! :zrange => [:some_lrange_result]
11
+
12
+ index[:anything].should == [:some_lrange_result]
13
13
  end
14
14
  it 'calls the right method on the backend' do
15
- client.should_receive(:zrange).once.with "some_namespace:some_sym", 0, -1
16
-
15
+ client.should_receive(:zrange).once.with "some_namespace:some_sym", :"0", :"-1"
16
+
17
17
  index[:some_sym]
18
18
  end
19
19
  end
20
-
20
+
21
21
  describe 'to_s' do
22
22
  it 'returns the cache path with the default file extension' do
23
23
  index.to_s.should == 'Picky::Backends::Redis::List(some_namespace:*)'
24
24
  end
25
25
  end
26
-
26
+
27
27
  end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+
3
+ require 'sqlite3'
4
+
5
+ describe Picky::Backends::SQLite::Array do
6
+
7
+ context 'hash-based indexes' do
8
+ let(:db) { described_class.new 'some/cache/path/to/file' }
9
+
10
+ describe 'dump' do
11
+ it 'delegates to the given hash' do
12
+ hash = stub :hash
13
+
14
+ db.should_receive(:dump_sqlite).once.with hash
15
+
16
+ db.dump hash
17
+ end
18
+ end
19
+
20
+ describe 'dump_sqlite' do
21
+ let(:client) { stub :client }
22
+ before(:each) do
23
+ db.stub! :db => client
24
+ end
25
+ it 'initializes the client' do
26
+ client.stub! :execute
27
+
28
+ db.should_receive(:lazily_initialize_client).once.with
29
+
30
+ db.dump_sqlite Hash.new
31
+ end
32
+ it 'executes something' do
33
+ db.stub! :lazily_initialize_client
34
+
35
+ client.should_receive(:execute).at_least(1).times
36
+
37
+ db.dump_sqlite Hash.new
38
+ end
39
+ it 'inserts keys and values' do
40
+ db.stub! :lazily_initialize_client
41
+ client.stub! :execute # We only want to test the insert statements.
42
+
43
+ client.should_receive(:execute).once.with 'insert into key_value values (?,?)', 'a', '[1,2,3]'
44
+ client.should_receive(:execute).once.with 'insert into key_value values (?,?)', 'b', '[4,5,6]'
45
+
46
+ db.dump_sqlite :a => [1,2,3], :b => [4,5,6]
47
+ end
48
+ end
49
+
50
+ describe 'load' do
51
+ it 'returns a copy of itself' do
52
+ db.load.should == db
53
+ end
54
+ end
55
+
56
+ describe 'empty' do
57
+ it 'returns the container that is used for indexing' do
58
+ db.empty.should == {}
59
+ end
60
+ end
61
+
62
+ describe 'initial' do
63
+ it 'is correct' do
64
+ db.initial.should == {}
65
+ end
66
+ end
67
+
68
+ describe 'to_s' do
69
+ it 'returns the cache path with the default file extension' do
70
+ db.to_s.should == 'Picky::Backends::SQLite::Array(some/cache/path/to/file.sqlite3)'
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'hash-based indexes' do
76
+ let(:db) { described_class.new 'some/cache/path/to/file', self_indexed: true }
77
+
78
+ describe 'dump' do
79
+ it 'delegates to the given hash' do
80
+ hash = stub :hash
81
+
82
+ db.should_receive(:dump_sqlite).never
83
+
84
+ db.dump hash
85
+ end
86
+ end
87
+
88
+ describe 'dump_sqlite' do
89
+ let(:client) { stub :client }
90
+ before(:each) do
91
+ db.stub! :db => client
92
+ end
93
+ it 'initializes the client' do
94
+ client.stub! :execute
95
+
96
+ db.should_receive(:lazily_initialize_client).once.with
97
+
98
+ db.dump_sqlite Hash.new
99
+ end
100
+ it 'executes something' do
101
+ db.stub! :lazily_initialize_client
102
+
103
+ client.should_receive(:execute).at_least(1).times
104
+
105
+ db.dump_sqlite Hash.new
106
+ end
107
+ it 'inserts keys and values' do
108
+ db.stub! :lazily_initialize_client
109
+ client.stub! :execute # We only want to test the insert statements.
110
+
111
+ client.should_receive(:execute).once.with 'insert into key_value values (?,?)', 'a', '[1,2,3]'
112
+ client.should_receive(:execute).once.with 'insert into key_value values (?,?)', 'b', '[4,5,6]'
113
+
114
+ db.dump_sqlite :a => [1,2,3], :b => [4,5,6]
115
+ end
116
+ end
117
+
118
+ describe 'load' do
119
+ it 'returns a copy of itself' do
120
+ db.load.should == db
121
+ end
122
+ end
123
+
124
+ describe 'empty' do
125
+ it 'returns the container that is used for indexing' do
126
+ db.empty.should == db
127
+ end
128
+ end
129
+
130
+ describe 'initial' do
131
+ it 'is correct' do
132
+ db.initial.should == db
133
+ end
134
+ end
135
+
136
+ describe 'to_s' do
137
+ it 'returns the cache path with the default file extension' do
138
+ db.to_s.should == 'Picky::Backends::SQLite::Array(some/cache/path/to/file.sqlite3)'
139
+ end
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Picky::Backends::SQLite::DirectlyManipulable do
4
+
5
+ let(:client) { stub :client }
6
+ let(:backend) { stub :backend, client: client, namespace: 'some:namespace' }
7
+ let(:array) do
8
+ array = [1,2]
9
+ described_class.make backend, array, 'some:key'
10
+ array
11
+ end
12
+
13
+ context 'stubbed backend' do
14
+ before(:each) do
15
+ backend.stub! :[]
16
+ end
17
+ it 'calls the right client method' do
18
+ backend.should_receive(:[]=).once.with 'some:key', [1,2,3]
19
+
20
+ array << 3
21
+ end
22
+ it 'calls the right client method' do
23
+ backend.should_receive(:[]=).once.with 'some:key', [3,1,2]
24
+
25
+ array.unshift 3
26
+ end
27
+ it 'calls the right client method' do
28
+ backend.should_receive(:[]=).once.with 'some:key', [2]
29
+
30
+ array.delete 1
31
+ end
32
+ it 'calls the right client method' do
33
+ client.should_receive(:zrem).never
34
+
35
+ array.delete 5
36
+ end
37
+ end
38
+
39
+ context 'stubbed client/backend' do
40
+ before(:each) do
41
+ backend.stub! :[]=
42
+ end
43
+ it 'behaves like an ordinary Array' do
44
+ array << 3
45
+
46
+ array.should == [1,2,3]
47
+ end
48
+ it 'behaves like an ordinary Array' do
49
+ array.unshift 3
50
+
51
+ array.should == [3,1,2]
52
+ end
53
+ it 'behaves like an ordinary Array' do
54
+ array.delete 1
55
+
56
+ array.should == [2]
57
+ end
58
+ it 'behaves like an ordinary Array' do
59
+ array.delete 5
60
+
61
+ array.should == [1,2]
62
+ end
63
+ end
64
+
65
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  require 'sqlite3'
4
4
 
5
- describe Picky::Backends::SQLite::DB do
5
+ describe Picky::Backends::SQLite::Value do
6
6
 
7
7
  context 'hash-based indexes' do
8
8
  let(:db) { described_class.new 'some/cache/path/to/file' }
@@ -51,11 +51,6 @@ describe Picky::Backends::SQLite::DB do
51
51
  it 'returns a copy of itself' do
52
52
  db.load.should == db
53
53
  end
54
- it 'initializes the client' do
55
- db.should_receive(:lazily_initialize_client).once.with
56
-
57
- db.load
58
- end
59
54
  end
60
55
 
61
56
  describe 'empty' do
@@ -72,7 +67,7 @@ describe Picky::Backends::SQLite::DB do
72
67
 
73
68
  describe 'to_s' do
74
69
  it 'returns the cache path with the default file extension' do
75
- db.to_s.should == 'Picky::Backends::SQLite::DB(some/cache/path/to/file.sqlite3)'
70
+ db.to_s.should == 'Picky::Backends::SQLite::Value(some/cache/path/to/file.sqlite3)'
76
71
  end
77
72
  end
78
73
  end
@@ -1,23 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
+ require 'sqlite3'
4
+
3
5
  describe Picky::Backends::SQLite do
4
6
 
5
7
  context 'with options' do
6
8
  before(:each) do
7
- @backend = described_class.new inverted: Picky::Backends::SQLite::DB.new(:unimportant),
8
- weights: Picky::Backends::SQLite::DB.new(:unimportant),
9
- similarity: Picky::Backends::SQLite::DB.new(:unimportant),
10
- configuration: Picky::Backends::SQLite::DB.new(:unimportant)
9
+ @backend = described_class.new inverted: Picky::Backends::SQLite::Value.new(:unimportant),
10
+ weights: Picky::Backends::SQLite::Array.new(:unimportant),
11
+ similarity: Picky::Backends::SQLite::Value.new(:unimportant),
12
+ configuration: Picky::Backends::SQLite::Array.new(:unimportant)
11
13
 
12
14
  @backend.stub! :timed_exclaim
13
15
  end
14
16
 
15
17
  describe 'create_...' do
16
18
  [
17
- [:inverted, Picky::Backends::SQLite::DB],
18
- [:weights, Picky::Backends::SQLite::DB],
19
- [:similarity, Picky::Backends::SQLite::DB],
20
- [:configuration, Picky::Backends::SQLite::DB]
19
+ [:inverted, Picky::Backends::SQLite::Value],
20
+ [:weights, Picky::Backends::SQLite::Array],
21
+ [:similarity, Picky::Backends::SQLite::Value],
22
+ [:configuration, Picky::Backends::SQLite::Array]
21
23
  ].each do |type, kind|
22
24
  it "creates and returns a(n) #{type} index" do
23
25
  @backend.send(:"create_#{type}",
@@ -30,20 +32,20 @@ describe Picky::Backends::SQLite do
30
32
 
31
33
  context 'with lambda options' do
32
34
  before(:each) do
33
- @backend = described_class.new inverted: ->(bundle){ Picky::Backends::SQLite::DB.new(bundle.index_path(:inverted)) },
34
- weights: ->(bundle){ Picky::Backends::SQLite::DB.new(bundle.index_path(:weights)) },
35
- similarity: ->(bundle){ Picky::Backends::SQLite::DB.new(bundle.index_path(:similarity)) },
36
- configuration: ->(bundle){ Picky::Backends::SQLite::DB.new(bundle.index_path(:configuration)) }
35
+ @backend = described_class.new inverted: ->(bundle){ Picky::Backends::SQLite::Value.new(bundle.index_path(:inverted)) },
36
+ weights: ->(bundle){ Picky::Backends::SQLite::Array.new(bundle.index_path(:weights)) },
37
+ similarity: ->(bundle){ Picky::Backends::SQLite::Value.new(bundle.index_path(:similarity)) },
38
+ configuration: ->(bundle){ Picky::Backends::SQLite::Array.new(bundle.index_path(:configuration)) }
37
39
 
38
40
  @backend.stub! :timed_exclaim
39
41
  end
40
42
 
41
43
  describe 'create_...' do
42
44
  [
43
- [:inverted, Picky::Backends::SQLite::DB],
44
- [:weights, Picky::Backends::SQLite::DB],
45
- [:similarity, Picky::Backends::SQLite::DB],
46
- [:configuration, Picky::Backends::SQLite::DB]
45
+ [:inverted, Picky::Backends::SQLite::Value],
46
+ [:weights, Picky::Backends::SQLite::Array],
47
+ [:similarity, Picky::Backends::SQLite::Value],
48
+ [:configuration, Picky::Backends::SQLite::Array]
47
49
  ].each do |type, kind|
48
50
  it "creates and returns a(n) #{type} index" do
49
51
  to_a_able_stub = Object.new
@@ -63,10 +65,10 @@ describe Picky::Backends::SQLite do
63
65
 
64
66
  describe 'create_...' do
65
67
  [
66
- [:inverted, Picky::Backends::SQLite::DB],
67
- [:weights, Picky::Backends::SQLite::DB],
68
- [:similarity, Picky::Backends::SQLite::DB],
69
- [:configuration, Picky::Backends::SQLite::DB]
68
+ [:inverted, Picky::Backends::SQLite::Array],
69
+ [:weights, Picky::Backends::SQLite::Value],
70
+ [:similarity, Picky::Backends::SQLite::Array],
71
+ [:configuration, Picky::Backends::SQLite::Value]
70
72
  ].each do |type, kind|
71
73
  it "creates and returns a(n) #{type} index" do
72
74
  @backend.send(:"create_#{type}",
@@ -160,7 +160,7 @@ describe Picky::Category do
160
160
 
161
161
  describe 'load' do
162
162
  it 'should call two methods' do
163
- @category.should_receive(:clear_realtime_mapping).once
163
+ @category.should_receive(:clear_realtime).once
164
164
  @exact.should_receive(:load).once
165
165
  @partial.should_receive(:load).once
166
166
 
@@ -14,8 +14,8 @@ describe Picky::Category do
14
14
 
15
15
  describe 'clear' do
16
16
  it 'delegates to both bundles' do
17
- exact.should_receive(:delete).once.with()
18
- partial.should_receive(:delete).once.with()
17
+ exact.should_receive(:clear).once.with()
18
+ partial.should_receive(:clear).once.with()
19
19
 
20
20
  category.clear
21
21
  end
@@ -97,13 +97,6 @@ describe Picky::Index do
97
97
  @index.define_category :some_category_name1
98
98
  @index.define_category :some_category_name2
99
99
  end
100
- describe "warn_no_source" do
101
- it "should warn" do
102
- @index.should_receive(:warn).once.with "No source given for index some_name."
103
-
104
- @index.warn_no_source
105
- end
106
- end
107
100
  describe 'define_source' do
108
101
  it 'can be set with this method' do
109
102
  source = stub :source, :each => [].each
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Picky::Index, "Realtime API" do
6
+
7
+ Thing = Struct.new :id, :text
8
+
9
+ let(:index) do
10
+ described_class.new :some_index_name do
11
+ category :text
12
+ end
13
+ end
14
+
15
+ it 'offers an add method' do
16
+ index.add Thing.new(1, 'text')
17
+ end
18
+ it 'offers a remove method' do
19
+ index.remove 1
20
+ end
21
+ it 'offers a replace method' do
22
+ index.replace Thing.new(1, 'text')
23
+ end
24
+ it 'offers a << method' do
25
+ index << Thing.new(1, 'text')
26
+ end
27
+ # it 'offers a >> method' do
28
+ # Thing.new(1, 'text') >> index # I mean, as long as we're dreaming.
29
+ # end
30
+ it 'offers an unshift method' do
31
+ index.unshift Thing.new(1, 'text')
32
+ end
33
+
34
+ end