juno 0.2.5 → 0.2.6

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 (130) hide show
  1. data/.travis.yml +5 -1
  2. data/Gemfile +27 -21
  3. data/README.md +41 -2
  4. data/Rakefile +3 -3
  5. data/juno.gemspec +12 -12
  6. data/lib/juno.rb +4 -4
  7. data/lib/juno/adapters/couch.rb +1 -1
  8. data/lib/juno/adapters/datamapper.rb +1 -1
  9. data/lib/juno/adapters/dbm.rb +1 -1
  10. data/lib/juno/adapters/file.rb +1 -1
  11. data/lib/juno/adapters/fog.rb +1 -1
  12. data/lib/juno/adapters/gdbm.rb +1 -1
  13. data/lib/juno/adapters/localmemcache.rb +1 -1
  14. data/lib/juno/adapters/pstore.rb +1 -1
  15. data/lib/juno/adapters/redis.rb +3 -2
  16. data/lib/juno/adapters/sdbm.rb +1 -1
  17. data/lib/juno/adapters/sequel.rb +1 -1
  18. data/lib/juno/adapters/sqlite.rb +1 -1
  19. data/lib/juno/adapters/tokyocabinet.rb +1 -1
  20. data/lib/juno/builder.rb +10 -0
  21. data/lib/juno/cache.rb +1 -0
  22. data/lib/juno/expires.rb +1 -1
  23. data/lib/juno/proxy.rb +2 -0
  24. data/lib/juno/stack.rb +1 -0
  25. data/lib/juno/transformer.rb +34 -18
  26. data/lib/juno/version.rb +1 -1
  27. data/lib/rack/cache/juno.rb +93 -0
  28. data/lib/rack/session/juno.rb +63 -0
  29. data/spec/generate.rb +41 -2
  30. data/spec/{adapter_activerecord_spec.rb → juno/adapter_activerecord_spec.rb} +0 -0
  31. data/spec/{adapter_cassandra_spec.rb → juno/adapter_cassandra_spec.rb} +0 -0
  32. data/spec/{adapter_couch_spec.rb → juno/adapter_couch_spec.rb} +0 -0
  33. data/spec/{adapter_datamapper_spec.rb → juno/adapter_datamapper_spec.rb} +0 -0
  34. data/spec/{adapter_dbm_spec.rb → juno/adapter_dbm_spec.rb} +0 -0
  35. data/spec/{adapter_file_spec.rb → juno/adapter_file_spec.rb} +0 -0
  36. data/spec/{adapter_fog_spec.rb → juno/adapter_fog_spec.rb} +0 -0
  37. data/spec/{adapter_gdbm_spec.rb → juno/adapter_gdbm_spec.rb} +0 -0
  38. data/spec/{adapter_localmemcache_spec.rb → juno/adapter_localmemcache_spec.rb} +0 -0
  39. data/spec/{adapter_lruhash_spec.rb → juno/adapter_lruhash_spec.rb} +0 -0
  40. data/spec/{adapter_memcached_dalli_spec.rb → juno/adapter_memcached_dalli_spec.rb} +0 -0
  41. data/spec/{adapter_memcached_native_spec.rb → juno/adapter_memcached_native_spec.rb} +0 -0
  42. data/spec/{adapter_memcached_spec.rb → juno/adapter_memcached_spec.rb} +0 -0
  43. data/spec/{adapter_memory_spec.rb → juno/adapter_memory_spec.rb} +0 -0
  44. data/spec/{adapter_mongo_spec.rb → juno/adapter_mongo_spec.rb} +0 -0
  45. data/spec/{adapter_pstore_spec.rb → juno/adapter_pstore_spec.rb} +0 -0
  46. data/spec/{adapter_redis_spec.rb → juno/adapter_redis_spec.rb} +0 -0
  47. data/spec/{adapter_riak_spec.rb → juno/adapter_riak_spec.rb} +0 -0
  48. data/spec/{adapter_sdbm_spec.rb → juno/adapter_sdbm_spec.rb} +0 -0
  49. data/spec/{adapter_sequel_spec.rb → juno/adapter_sequel_spec.rb} +0 -0
  50. data/spec/{adapter_sqlite_spec.rb → juno/adapter_sqlite_spec.rb} +0 -0
  51. data/spec/{adapter_tokyocabinet_bdb_spec.rb → juno/adapter_tokyocabinet_bdb_spec.rb} +0 -0
  52. data/spec/{adapter_tokyocabinet_hdb_spec.rb → juno/adapter_tokyocabinet_hdb_spec.rb} +0 -0
  53. data/spec/{adapter_yaml_spec.rb → juno/adapter_yaml_spec.rb} +0 -0
  54. data/spec/{cache_file_memory_spec.rb → juno/cache_file_memory_spec.rb} +0 -0
  55. data/spec/{cache_memory_null_spec.rb → juno/cache_memory_null_spec.rb} +0 -0
  56. data/spec/{expires_file_spec.rb → juno/expires_file_spec.rb} +0 -0
  57. data/spec/{expires_memory_spec.rb → juno/expires_memory_spec.rb} +0 -0
  58. data/spec/{lock_spec.rb → juno/lock_spec.rb} +0 -0
  59. data/spec/{null_adapter_spec.rb → juno/null_adapter_spec.rb} +0 -0
  60. data/spec/{proxy_expires_memory_spec.rb → juno/proxy_expires_memory_spec.rb} +0 -0
  61. data/spec/{proxy_redis_spec.rb → juno/proxy_redis_spec.rb} +0 -0
  62. data/spec/{simple_activerecord_spec.rb → juno/simple_activerecord_spec.rb} +0 -0
  63. data/spec/{simple_activerecord_with_expires_spec.rb → juno/simple_activerecord_with_expires_spec.rb} +0 -0
  64. data/spec/{simple_cassandra_spec.rb → juno/simple_cassandra_spec.rb} +0 -0
  65. data/spec/{simple_couch_spec.rb → juno/simple_couch_spec.rb} +0 -0
  66. data/spec/{simple_couch_with_expires_spec.rb → juno/simple_couch_with_expires_spec.rb} +0 -0
  67. data/spec/{simple_datamapper_spec.rb → juno/simple_datamapper_spec.rb} +0 -0
  68. data/spec/{simple_datamapper_with_expires_spec.rb → juno/simple_datamapper_with_expires_spec.rb} +0 -0
  69. data/spec/{simple_datamapper_with_repository_spec.rb → juno/simple_datamapper_with_repository_spec.rb} +0 -0
  70. data/spec/{simple_dbm_spec.rb → juno/simple_dbm_spec.rb} +0 -0
  71. data/spec/{simple_dbm_with_expires_spec.rb → juno/simple_dbm_with_expires_spec.rb} +0 -0
  72. data/spec/{simple_file_spec.rb → juno/simple_file_spec.rb} +0 -0
  73. data/spec/{simple_file_with_expires_spec.rb → juno/simple_file_with_expires_spec.rb} +0 -0
  74. data/spec/{simple_fog_spec.rb → juno/simple_fog_spec.rb} +0 -0
  75. data/spec/{simple_fog_with_expires_spec.rb → juno/simple_fog_with_expires_spec.rb} +0 -0
  76. data/spec/{simple_gdbm_spec.rb → juno/simple_gdbm_spec.rb} +0 -0
  77. data/spec/{simple_gdbm_with_expires_spec.rb → juno/simple_gdbm_with_expires_spec.rb} +0 -0
  78. data/spec/{simple_hashfile_spec.rb → juno/simple_hashfile_spec.rb} +0 -0
  79. data/spec/{simple_hashfile_with_expires_spec.rb → juno/simple_hashfile_with_expires_spec.rb} +0 -0
  80. data/spec/{simple_localmemcache_spec.rb → juno/simple_localmemcache_spec.rb} +0 -0
  81. data/spec/{simple_localmemcache_with_expires_spec.rb → juno/simple_localmemcache_with_expires_spec.rb} +0 -0
  82. data/spec/{simple_lruhash_spec.rb → juno/simple_lruhash_spec.rb} +0 -0
  83. data/spec/{simple_lruhash_with_expires_spec.rb → juno/simple_lruhash_with_expires_spec.rb} +0 -0
  84. data/spec/{simple_memcached_dalli_spec.rb → juno/simple_memcached_dalli_spec.rb} +0 -0
  85. data/spec/{simple_memcached_native_spec.rb → juno/simple_memcached_native_spec.rb} +0 -0
  86. data/spec/{simple_memcached_spec.rb → juno/simple_memcached_spec.rb} +0 -0
  87. data/spec/{simple_memory_spec.rb → juno/simple_memory_spec.rb} +0 -0
  88. data/spec/{simple_memory_with_expires_spec.rb → juno/simple_memory_with_expires_spec.rb} +0 -0
  89. data/spec/{simple_mongo_spec.rb → juno/simple_mongo_spec.rb} +0 -0
  90. data/spec/{simple_mongo_with_expires_spec.rb → juno/simple_mongo_with_expires_spec.rb} +0 -0
  91. data/spec/{simple_null_spec.rb → juno/simple_null_spec.rb} +0 -0
  92. data/spec/{simple_pstore_spec.rb → juno/simple_pstore_spec.rb} +0 -0
  93. data/spec/{simple_pstore_with_expires_spec.rb → juno/simple_pstore_with_expires_spec.rb} +0 -0
  94. data/spec/{simple_redis_spec.rb → juno/simple_redis_spec.rb} +0 -0
  95. data/spec/{simple_riak_spec.rb → juno/simple_riak_spec.rb} +0 -0
  96. data/spec/{simple_riak_with_expires_spec.rb → juno/simple_riak_with_expires_spec.rb} +0 -0
  97. data/spec/{simple_sdbm_spec.rb → juno/simple_sdbm_spec.rb} +0 -0
  98. data/spec/{simple_sdbm_with_expires_spec.rb → juno/simple_sdbm_with_expires_spec.rb} +0 -0
  99. data/spec/{simple_sequel_spec.rb → juno/simple_sequel_spec.rb} +0 -0
  100. data/spec/{simple_sequel_with_expires_spec.rb → juno/simple_sequel_with_expires_spec.rb} +0 -0
  101. data/spec/{simple_sqlite_spec.rb → juno/simple_sqlite_spec.rb} +0 -0
  102. data/spec/{simple_sqlite_with_expires_spec.rb → juno/simple_sqlite_with_expires_spec.rb} +0 -0
  103. data/spec/{simple_tokyocabinet_spec.rb → juno/simple_tokyocabinet_spec.rb} +0 -0
  104. data/spec/{simple_tokyocabinet_with_expires_spec.rb → juno/simple_tokyocabinet_with_expires_spec.rb} +0 -0
  105. data/spec/{simple_yaml_spec.rb → juno/simple_yaml_spec.rb} +0 -0
  106. data/spec/{simple_yaml_with_expires_spec.rb → juno/simple_yaml_with_expires_spec.rb} +0 -0
  107. data/spec/{stack_file_memory_spec.rb → juno/stack_file_memory_spec.rb} +0 -0
  108. data/spec/{stack_memory_file_spec.rb → juno/stack_memory_file_spec.rb} +0 -0
  109. data/spec/{transformer_bencode_spec.rb → juno/transformer_bencode_spec.rb} +0 -0
  110. data/spec/{transformer_bert_spec.rb → juno/transformer_bert_spec.rb} +0 -0
  111. data/spec/{transformer_bson_spec.rb → juno/transformer_bson_spec.rb} +0 -0
  112. data/spec/{transformer_compress_spec.rb → juno/transformer_compress_spec.rb} +0 -0
  113. data/spec/{transformer_json_spec.rb → juno/transformer_json_spec.rb} +0 -0
  114. data/spec/juno/transformer_lzma_spec.rb +22 -0
  115. data/spec/juno/transformer_lzo_spec.rb +22 -0
  116. data/spec/{transformer_marshal_base64_spec.rb → juno/transformer_marshal_base64_spec.rb} +0 -0
  117. data/spec/{transformer_marshal_escape_spec.rb → juno/transformer_marshal_escape_spec.rb} +0 -0
  118. data/spec/{transformer_marshal_md5_spec.rb → juno/transformer_marshal_md5_spec.rb} +0 -0
  119. data/spec/{transformer_marshal_md5_spread_spec.rb → juno/transformer_marshal_md5_spread_spec.rb} +0 -0
  120. data/spec/juno/transformer_marshal_prefix_spec.rb +41 -0
  121. data/spec/{transformer_marshal_uuencode_spec.rb → juno/transformer_marshal_uuencode_spec.rb} +0 -0
  122. data/spec/{transformer_msgpack_spec.rb → juno/transformer_msgpack_spec.rb} +0 -0
  123. data/spec/{transformer_ox_spec.rb → juno/transformer_ox_spec.rb} +0 -0
  124. data/spec/juno/transformer_quicklz_spec.rb +22 -0
  125. data/spec/juno/transformer_snappy_spec.rb +22 -0
  126. data/spec/{transformer_tnet_spec.rb → juno/transformer_tnet_spec.rb} +0 -0
  127. data/spec/{transformer_yaml_spec.rb → juno/transformer_yaml_spec.rb} +0 -0
  128. data/spec/rack/cache_juno_spec.rb +355 -0
  129. data/spec/rack/session_juno_spec.rb +305 -0
  130. metadata +206 -190
@@ -0,0 +1,22 @@
1
+ # Generated by generate.rb
2
+ require 'helper'
3
+
4
+ describe_juno "transformer_lzo" do
5
+ def new_store
6
+ Juno.build do
7
+ use :Transformer, :value => :lzo
8
+ adapter :Memory
9
+ end
10
+ end
11
+
12
+ include_context 'setup_store'
13
+ it_should_behave_like 'null_objectkey_stringvalue'
14
+ it_should_behave_like 'null_stringkey_stringvalue'
15
+ it_should_behave_like 'null_hashkey_stringvalue'
16
+ it_should_behave_like 'store_objectkey_stringvalue'
17
+ it_should_behave_like 'store_stringkey_stringvalue'
18
+ it_should_behave_like 'store_hashkey_stringvalue'
19
+ it_should_behave_like 'returndifferent_objectkey_stringvalue'
20
+ it_should_behave_like 'returndifferent_stringkey_stringvalue'
21
+ it_should_behave_like 'returndifferent_hashkey_stringvalue'
22
+ end
@@ -0,0 +1,41 @@
1
+ # Generated by generate.rb
2
+ require 'helper'
3
+
4
+ describe_juno "transformer_marshal_prefix" do
5
+ def new_store
6
+ Juno.build do
7
+ use :Transformer, :key => [:marshal, :prefix], :value => :marshal, :prefix => 'juno'
8
+ adapter :Memory
9
+ end
10
+ end
11
+
12
+ include_context 'setup_store'
13
+ it_should_behave_like 'null_objectkey_objectvalue'
14
+ it_should_behave_like 'null_objectkey_stringvalue'
15
+ it_should_behave_like 'null_objectkey_hashvalue'
16
+ it_should_behave_like 'null_stringkey_objectvalue'
17
+ it_should_behave_like 'null_stringkey_stringvalue'
18
+ it_should_behave_like 'null_stringkey_hashvalue'
19
+ it_should_behave_like 'null_hashkey_objectvalue'
20
+ it_should_behave_like 'null_hashkey_stringvalue'
21
+ it_should_behave_like 'null_hashkey_hashvalue'
22
+ it_should_behave_like 'store_objectkey_objectvalue'
23
+ it_should_behave_like 'store_objectkey_stringvalue'
24
+ it_should_behave_like 'store_objectkey_hashvalue'
25
+ it_should_behave_like 'store_stringkey_objectvalue'
26
+ it_should_behave_like 'store_stringkey_stringvalue'
27
+ it_should_behave_like 'store_stringkey_hashvalue'
28
+ it_should_behave_like 'store_hashkey_objectvalue'
29
+ it_should_behave_like 'store_hashkey_stringvalue'
30
+ it_should_behave_like 'store_hashkey_hashvalue'
31
+ it_should_behave_like 'returndifferent_objectkey_objectvalue'
32
+ it_should_behave_like 'returndifferent_objectkey_stringvalue'
33
+ it_should_behave_like 'returndifferent_objectkey_hashvalue'
34
+ it_should_behave_like 'returndifferent_stringkey_objectvalue'
35
+ it_should_behave_like 'returndifferent_stringkey_stringvalue'
36
+ it_should_behave_like 'returndifferent_stringkey_hashvalue'
37
+ it_should_behave_like 'returndifferent_hashkey_objectvalue'
38
+ it_should_behave_like 'returndifferent_hashkey_stringvalue'
39
+ it_should_behave_like 'returndifferent_hashkey_hashvalue'
40
+ it_should_behave_like 'marshallable_key'
41
+ end
@@ -0,0 +1,22 @@
1
+ # Generated by generate.rb
2
+ require 'helper'
3
+
4
+ describe_juno "transformer_quicklz" do
5
+ def new_store
6
+ Juno.build do
7
+ use :Transformer, :value => :quicklz
8
+ adapter :Memory
9
+ end
10
+ end
11
+
12
+ include_context 'setup_store'
13
+ it_should_behave_like 'null_objectkey_stringvalue'
14
+ it_should_behave_like 'null_stringkey_stringvalue'
15
+ it_should_behave_like 'null_hashkey_stringvalue'
16
+ it_should_behave_like 'store_objectkey_stringvalue'
17
+ it_should_behave_like 'store_stringkey_stringvalue'
18
+ it_should_behave_like 'store_hashkey_stringvalue'
19
+ it_should_behave_like 'returndifferent_objectkey_stringvalue'
20
+ it_should_behave_like 'returndifferent_stringkey_stringvalue'
21
+ it_should_behave_like 'returndifferent_hashkey_stringvalue'
22
+ end
@@ -0,0 +1,22 @@
1
+ # Generated by generate.rb
2
+ require 'helper'
3
+
4
+ describe_juno "transformer_snappy" do
5
+ def new_store
6
+ Juno.build do
7
+ use :Transformer, :value => :snappy
8
+ adapter :Memory
9
+ end
10
+ end
11
+
12
+ include_context 'setup_store'
13
+ it_should_behave_like 'null_objectkey_stringvalue'
14
+ it_should_behave_like 'null_stringkey_stringvalue'
15
+ it_should_behave_like 'null_hashkey_stringvalue'
16
+ it_should_behave_like 'store_objectkey_stringvalue'
17
+ it_should_behave_like 'store_stringkey_stringvalue'
18
+ it_should_behave_like 'store_hashkey_stringvalue'
19
+ it_should_behave_like 'returndifferent_objectkey_stringvalue'
20
+ it_should_behave_like 'returndifferent_stringkey_stringvalue'
21
+ it_should_behave_like 'returndifferent_hashkey_stringvalue'
22
+ end
@@ -0,0 +1,355 @@
1
+ require 'rack/cache/juno'
2
+ require 'rack/mock'
3
+ require 'rack/cache'
4
+
5
+ class Object
6
+ def sha_like?
7
+ length == 40 && self =~ /^[0-9a-z]+$/
8
+ end
9
+ end
10
+
11
+ describe Rack::Cache::MetaStore::Juno do
12
+ before do
13
+ Rack::Cache::Juno['meta'] = Juno.new(:Memory, :expires => true)
14
+ Rack::Cache::Juno['entity'] = Juno.new(:Memory, :expires => true)
15
+ @store = Rack::Cache::MetaStore::Juno.resolve uri('juno://entity')
16
+ @entity_store = Rack::Cache::EntityStore::Juno.resolve uri('juno://meta')
17
+ @request = mock_request('/', {})
18
+ @response = mock_response(200, {}, ['hello world'])
19
+ end
20
+
21
+ after do
22
+ Rack::Cache::Juno['meta'].clear
23
+ Rack::Cache::Juno['entity'].clear
24
+ end
25
+
26
+ it "has the class referenced by homonym constant" do
27
+ Rack::Cache::MetaStore::JUNO.should == Rack::Cache::MetaStore::Juno
28
+ end
29
+
30
+ it "instantiates the store" do
31
+ @store.should be_kind_of(Rack::Cache::MetaStore::Juno)
32
+ end
33
+
34
+ it "resolves the connection uri" do
35
+ Rack::Cache::MetaStore::Juno.resolve(uri('juno://Memory?expires=true')).should be_kind_of(Rack::Cache::MetaStore::Juno)
36
+ end
37
+
38
+ # Low-level implementation methods ===========================================
39
+
40
+ it 'writes a list of negotation tuples with #write' do
41
+ # lambda {
42
+ @store.write('/test', [[{}, {}]])
43
+ # }.should_not raise Exception
44
+ end
45
+
46
+ it 'reads a list of negotation tuples with #read' do
47
+ @store.write('/test', [[{},{}],[{},{}]])
48
+ tuples = @store.read('/test')
49
+ tuples.should == [ [{},{}], [{},{}] ]
50
+ end
51
+
52
+ it 'reads an empty list with #read when nothing cached at key' do
53
+ @store.read('/nothing').should be_empty
54
+ end
55
+
56
+ it 'removes entries for key with #purge' do
57
+ @store.write('/test', [[{},{}]])
58
+ @store.read('/test').should_not be_empty
59
+
60
+ @store.purge('/test')
61
+ @store.read('/test').should be_empty
62
+ end
63
+
64
+ it 'succeeds when purging non-existing entries' do
65
+ @store.read('/test').should be_empty
66
+ @store.purge('/test')
67
+ end
68
+
69
+ it 'returns nil from #purge' do
70
+ @store.write('/test', [[{},{}]])
71
+ @store.purge('/test').should be_nil
72
+ @store.read('/test').should == []
73
+ end
74
+
75
+ %w[/test http://example.com:8080/ /test?x=y /test?x=y&p=q].each do |key|
76
+ it "can read and write key: '#{key}'" do
77
+ # lambda {
78
+ @store.write(key, [[{},{}]])
79
+ # }.should_not raise Exception
80
+ @store.read(key).should == [[{},{}]]
81
+ end
82
+ end
83
+
84
+ it "can read and write fairly large keys" do
85
+ key = "b" * 4096
86
+ # lambda {
87
+ @store.write(key, [[{},{}]])
88
+ # }.should_not raise Exception
89
+ @store.read(key).should == [[{},{}]]
90
+ end
91
+
92
+ it "allows custom cache keys from block" do
93
+ request = mock_request('/test', {})
94
+ request.env['rack-cache.cache_key'] =
95
+ lambda { |request| request.path_info.reverse }
96
+ @store.cache_key(request).should == 'tset/'
97
+ end
98
+
99
+ it "allows custom cache keys from class" do
100
+ request = mock_request('/test', {})
101
+ request.env['rack-cache.cache_key'] = Class.new do
102
+ def self.call(request); request.path_info.reverse end
103
+ end
104
+ @store.cache_key(request).should == 'tset/'
105
+ end
106
+
107
+ it 'does not blow up when given a non-marhsalable object with an ALL_CAPS key' do
108
+ store_simple_entry('/bad', { 'SOME_THING' => Proc.new {} })
109
+ end
110
+
111
+ # Abstract methods ===========================================================
112
+
113
+ it 'stores a cache entry' do
114
+ cache_key = store_simple_entry
115
+ @store.read(cache_key).should_not be_empty
116
+ end
117
+
118
+ it 'sets the X-Content-Digest response header before storing' do
119
+ cache_key = store_simple_entry
120
+ req, res = @store.read(cache_key).first
121
+ res['X-Content-Digest'].should == 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'
122
+ end
123
+
124
+ it 'finds a stored entry with #lookup' do
125
+ store_simple_entry
126
+ response = @store.lookup(@request, @entity_store)
127
+ response.should_not be_nil
128
+ response.should be_kind_of(Rack::Cache::Response)
129
+ end
130
+
131
+ it 'does not find an entry with #lookup when none exists' do
132
+ req = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
133
+ @store.lookup(req, @entity_store).should be_nil
134
+ end
135
+
136
+ it "canonizes urls for cache keys" do
137
+ store_simple_entry(path='/test?x=y&p=q')
138
+
139
+ hits_req = mock_request(path, {})
140
+ miss_req = mock_request('/test?p=x', {})
141
+
142
+ @store.lookup(hits_req, @entity_store).should_not be_nil
143
+ @store.lookup(miss_req, @entity_store).should be_nil
144
+ end
145
+
146
+ it 'does not find an entry with #lookup when the body does not exist' do
147
+ store_simple_entry
148
+ @response.headers['X-Content-Digest'].should_not be_nil
149
+ @entity_store.purge(@response.headers['X-Content-Digest'])
150
+ @store.lookup(@request, @entity_store).should be_nil
151
+ end
152
+
153
+ it 'restores response headers properly with #lookup' do
154
+ store_simple_entry
155
+ response = @store.lookup(@request, @entity_store)
156
+ response.headers.should == @response.headers.merge('Content-Length' => '4')
157
+ end
158
+
159
+ it 'restores response body from entity store with #lookup' do
160
+ store_simple_entry
161
+ response = @store.lookup(@request, @entity_store)
162
+ body = '' ; response.body.each {|p| body << p}
163
+ body.should == 'test'
164
+ end
165
+
166
+ it 'invalidates meta and entity store entries with #invalidate' do
167
+ store_simple_entry
168
+ @store.invalidate(@request, @entity_store)
169
+ response = @store.lookup(@request, @entity_store)
170
+ response.should be_kind_of(Rack::Cache::Response)
171
+ response.should_not be :fresh?
172
+ end
173
+
174
+ it 'succeeds quietly when #invalidate called with no matching entries' do
175
+ req = mock_request('/test', {})
176
+ @store.invalidate(req, @entity_store)
177
+ @store.lookup(@request, @entity_store).should be_nil
178
+ end
179
+
180
+ # Vary =======================================================================
181
+
182
+ it 'does not return entries that Vary with #lookup' do
183
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
184
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
185
+ res = mock_response(200, {'Vary' => 'Foo Bar'}, ['test'])
186
+ @store.store(req1, res, @entity_store)
187
+
188
+ @store.lookup(req2, @entity_store).should be_nil
189
+ end
190
+
191
+ it 'stores multiple responses for each Vary combination' do
192
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
193
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
194
+ key = @store.store(req1, res1, @entity_store)
195
+
196
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
197
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
198
+ @store.store(req2, res2, @entity_store)
199
+
200
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom'})
201
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
202
+ @store.store(req3, res3, @entity_store)
203
+
204
+ slurp(@store.lookup(req3, @entity_store).body).should == 'test 3'
205
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
206
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
207
+
208
+ @store.read(key).length.should == 3
209
+ end
210
+
211
+ it 'overwrites non-varying responses with #store' do
212
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
213
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
214
+ key = @store.store(req1, res1, @entity_store)
215
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
216
+
217
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
218
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
219
+ @store.store(req2, res2, @entity_store)
220
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
221
+
222
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
223
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
224
+ @store.store(req3, res3, @entity_store)
225
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 3'
226
+
227
+ @store.read(key).length.should == 2
228
+ end
229
+
230
+ private
231
+ def mock_request(uri, opts)
232
+ env = Rack::MockRequest.env_for(uri, opts || {})
233
+ Rack::Cache::Request.new(env)
234
+ end
235
+
236
+ def mock_response(status, headers, body)
237
+ headers ||= {}
238
+ body = Array(body).compact
239
+ Rack::Cache::Response.new(status, headers, body)
240
+ end
241
+
242
+ def slurp(body)
243
+ buf = ''
244
+ body.each { |part| buf << part }
245
+ buf
246
+ end
247
+
248
+ # Stores an entry for the given request args, returns a url encoded cache key
249
+ # for the request.
250
+ def store_simple_entry(*request_args)
251
+ path, headers = request_args
252
+ @request = mock_request(path || '/test', headers || {})
253
+ @response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test'])
254
+ body = @response.body
255
+ cache_key = @store.store(@request, @response, @entity_store)
256
+ @response.body.should == body
257
+ cache_key
258
+ end
259
+
260
+ def uri(uri)
261
+ URI.parse uri
262
+ end
263
+ end
264
+
265
+ describe Rack::Cache::EntityStore::Juno do
266
+ before do
267
+ @store = Rack::Cache::EntityStore::Juno.resolve(uri('juno://Memory?expires=true'))
268
+ end
269
+
270
+ it 'has the class referenced by homonym constant' do
271
+ Rack::Cache::EntityStore::JUNO.should == Rack::Cache::EntityStore::Juno
272
+ end
273
+
274
+ it 'resolves the connection uri' do
275
+ Rack::Cache::EntityStore::Juno.resolve(uri('juno://Memory?expires=true')).should be_kind_of(Rack::Cache::EntityStore::Juno)
276
+ end
277
+
278
+ it 'responds to all required messages' do
279
+ %w[read open write exist?].each do |message|
280
+ @store.should respond_to message
281
+ end
282
+ end
283
+
284
+ it 'stores bodies with #write' do
285
+ key, size = @store.write(['My wild love went riding,'])
286
+ key.should_not be_nil
287
+ key.should be_sha_like
288
+
289
+ data = @store.read(key)
290
+ data.should == 'My wild love went riding,'
291
+ end
292
+
293
+ it 'takes a ttl parameter for #write' do
294
+ key, size = @store.write(['My wild love went riding,'], 0)
295
+ key.should_not be_nil
296
+ key.should be_sha_like
297
+
298
+ data = @store.read(key)
299
+ data.should == 'My wild love went riding,'
300
+ end
301
+
302
+ it 'correctly determines whether cached body exists for key with #exist?' do
303
+ key, size = @store.write(['She rode to the devil,'])
304
+ @store.exist?(key).should be_true
305
+ @store.exist?('938jasddj83jasdh4438021ksdfjsdfjsdsf').should be_false
306
+ end
307
+
308
+ it 'can read data written with #write' do
309
+ key, size = @store.write(['And asked him to pay.'])
310
+ data = @store.read(key)
311
+ data.should == 'And asked him to pay.'
312
+ end
313
+
314
+ it 'gives a 40 character SHA1 hex digest from #write' do
315
+ key, size = @store.write(['she rode to the sea;'])
316
+ key.should_not be_nil
317
+ key.length.should == 40
318
+ key.should match(/^[0-9a-z]+$/)
319
+ key.should == '90a4c84d51a277f3dafc34693ca264531b9f51b6'
320
+ end
321
+
322
+ it 'returns the entire body as a String from #read' do
323
+ key, size = @store.write(['She gathered together'])
324
+ @store.read(key).should == 'She gathered together'
325
+ end
326
+
327
+ it 'returns nil from #read when key does not exist' do
328
+ @store.read('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
329
+ end
330
+
331
+ it 'returns a Rack compatible body from #open' do
332
+ key, size = @store.write(['Some shells for her hair.'])
333
+ body = @store.open(key)
334
+ body.should respond_to :each
335
+ buf = ''
336
+ body.each { |part| buf << part }
337
+ buf.should == 'Some shells for her hair.'
338
+ end
339
+
340
+ it 'returns nil from #open when key does not exist' do
341
+ @store.open('87fe0a1ae82a518592f6b12b0183e950b4541c62').should be_nil
342
+ end
343
+
344
+ it 'deletes stored entries with #purge' do
345
+ key, size = @store.write(['My wild love went riding,'])
346
+ @store.purge(key).should be_nil
347
+ @store.read(key).should be_nil
348
+ end
349
+
350
+ private
351
+
352
+ def uri(uri)
353
+ URI.parse uri
354
+ end
355
+ end