readthis 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f5a03b37918d41dfd0e30151cb3049638df50aa8
4
- data.tar.gz: 9c80690714866bc6992eafa9c137e785cf72a88d
3
+ metadata.gz: d1763594017ac9b87a099ca476d01c9f3823a0b0
4
+ data.tar.gz: 23e9363a5cc0c997c70f923f0ea21c50477843ff
5
5
  SHA512:
6
- metadata.gz: 9297ce7d8525ecdc2715399b3b657fb3cc12e581e4e9d067d762f7e9a134ffe6a35e48f6867bf7a26902f5dc48b6106e5c6dd012bc5e16372ad1b715fa6faa60
7
- data.tar.gz: d431742cd9f908aabdd06dfeaab2ed99b7165158fff51c57c947d9857d336e2a5c82fe42edae15a41c7cdd8043b3fc950c09e6fd60feb4d69c571fb71e2fe6e3
6
+ metadata.gz: 0c3868ec4afab92154c4a9bc542de628224086760a6c8c5275d9ba83ede290b4800f209eb3c3b5349d11cd2f2f63e0aa5858e587308ac9d2988ecc6a35989511
7
+ data.tar.gz: 3ffd2cc9546da805453a27d5ca33a4a35525582eb7905973a45293e4093ed47c70ebe37b4e3fb8f16035074cb558de08e271b2de712b8f52e237a8f386db9ca3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## v0.5.0 2014-12-12
2
+
3
+ - Added: All read and write operations are marshalled to and from storage. This
4
+ allows hashes, arrays, etc. to be restored instead of always returning a
5
+ string. Unlike `ActiveSupport::Store::Entity`, no new objects are allocated
6
+ for each entity, reducing GC and improving performance.
7
+ - Fixed: Increment/Decrement interface was only accepting two params instead of
8
+ three. Now accepts `amount` as the second parameter.
9
+ - Changed: Increment/Decrement no longer use `incby` and `decby`, as they don't
10
+ work with marshalled values. This means they are not entirely atomic, so race
11
+ conditions are possible.
12
+
1
13
  ## v0.4.0 2014-12-11
2
14
 
3
15
  - Added: Force the use of `hiredis` as the adapter. It is dramatically faster,
data/Gemfile CHANGED
@@ -2,8 +2,11 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'coveralls', require: false
6
+
5
7
  group :benchmarking do
6
8
  gem 'benchmark-ips'
7
9
  gem 'dalli'
10
+ gem 'oj'
8
11
  gem 'redis-activesupport', github: 'sorentwo/redis-activesupport'
9
12
  end
data/PERFORMANCE.md CHANGED
@@ -19,55 +19,55 @@ Performance compared to `dalli` and `redis-activesupport`:
19
19
  ```
20
20
  Raw Read Multi:
21
21
  Calculating -------------------------------------
22
- readthis:read-multi 500.000 i/100ms
23
- redisas:read-multi 95.000 i/100ms
24
- dalli:read-multi 97.000 i/100ms
22
+ readthis:read-multi 358.000 i/100ms
23
+ redisas:read-multi 94.000 i/100ms
24
+ dalli:read-multi 99.000 i/100ms
25
25
  -------------------------------------------------
26
- readthis:read-multi 5.286k (± 2.7%) i/s - 26.500k
27
- redisas:read-multi 959.4054.2%) i/s - 4.845k
28
- dalli:read-multi 978.8032.1%) i/s - 4.947k
26
+ readthis:read-multi 3.800k (± 2.3%) i/s - 19.332k
27
+ redisas:read-multi 962.1993.6%) i/s - 4.888k
28
+ dalli:read-multi 995.3531.1%) i/s - 5.049k
29
29
 
30
30
  Comparison:
31
- readthis:read-multi: 5286.0 i/s
32
- dalli:read-multi: 978.8 i/s - 5.40x slower
33
- redisas:read-multi: 959.4 i/s - 5.51x slower
31
+ readthis:read-multi: 3799.8 i/s
32
+ dalli:read-multi: 995.4 i/s - 3.82x slower
33
+ redisas:read-multi: 962.2 i/s - 3.95x slower
34
34
 
35
35
  Raw Fetch Multi:
36
36
  Calculating -------------------------------------
37
- readthis:fetch-multi 448.000 i/100ms
38
- redisas:fetch-multi 84.000 i/100ms
39
- dalli:fetch-multi 99.000 i/100ms
37
+ readthis:fetch-multi 336.000 i/100ms
38
+ redisas:fetch-multi 86.000 i/100ms
39
+ dalli:fetch-multi 102.000 i/100ms
40
40
  -------------------------------------------------
41
- readthis:fetch-multi 4.682k (± 2.4%) i/s - 23.744k
42
- redisas:fetch-multi 848.1013.2%) i/s - 4.284k
43
- dalli:fetch-multi 1.006k (± 2.4%) i/s - 5.049k
41
+ readthis:fetch-multi 3.424k (± 2.6%) i/s - 17.136k
42
+ redisas:fetch-multi 874.8032.7%) i/s - 4.386k
43
+ dalli:fetch-multi 1.028k1.2%) i/s - 5.202k
44
44
 
45
45
  Comparison:
46
- readthis:fetch-multi: 4682.4 i/s
47
- dalli:fetch-multi: 1005.6 i/s - 4.66x slower
48
- redisas:fetch-multi: 848.1 i/s - 5.52x slower
46
+ readthis:fetch-multi: 3424.2 i/s
47
+ dalli:fetch-multi: 1027.7 i/s - 3.33x slower
48
+ redisas:fetch-multi: 874.8 i/s - 3.91x slower
49
49
 
50
50
  Compressed Writes:
51
51
  Calculating -------------------------------------
52
- readthis:write 1.003k i/100ms
53
- dalli:write 913.000 i/100ms
52
+ readthis:write 924.000 i/100ms
53
+ dalli:write 903.000 i/100ms
54
54
  -------------------------------------------------
55
- readthis:write 11.095k5.7%) i/s - 56.168k
56
- dalli:write 9.829k (± 1.8%) i/s - 49.302k
55
+ readthis:write 10.105k4.9%) i/s - 50.820k
56
+ dalli:write 9.662k (± 1.6%) i/s - 48.762k
57
57
 
58
58
  Comparison:
59
- readthis:write: 11095.5 i/s
60
- dalli:write: 9828.8 i/s - 1.13x slower
59
+ readthis:write: 10105.3 i/s
60
+ dalli:write: 9662.4 i/s - 1.05x slower
61
61
 
62
62
  Compressed Read Multi:
63
63
  Calculating -------------------------------------
64
- readthis:read_multi 446.000 i/100ms
65
- dalli:read_multi 97.000 i/100ms
64
+ readthis:read_multi 325.000 i/100ms
65
+ dalli:read_multi 100.000 i/100ms
66
66
  -------------------------------------------------
67
- readthis:read_multi 4.728k4.6%) i/s - 23.638k
68
- dalli:read_multi 985.986 (± 3.9%) i/s - 4.947k
67
+ readthis:read_multi 3.357k2.3%) i/s - 16.900k
68
+ dalli:read_multi 1.014k (± 3.1%) i/s - 5.100k
69
69
 
70
70
  Comparison:
71
- readthis:read_multi: 4728.3 i/s
72
- dalli:read_multi: 986.0 i/s - 4.80x slower
71
+ readthis:read_multi: 3356.5 i/s
72
+ dalli:read_multi: 1014.1 i/s - 3.31x slower
73
73
  ```
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/readthis.svg)](http://badge.fury.io/rb/readthis)
2
2
  [![Build Status](https://travis-ci.org/sorentwo/readthis.svg?branch=master)](https://travis-ci.org/sorentwo/readthis)
3
3
  [![Code Climate](https://codeclimate.com/github/sorentwo/readthis/badges/gpa.svg)](https://codeclimate.com/github/sorentwo/readthis)
4
+ [![Coverage Status](https://img.shields.io/coveralls/sorentwo/readthis.svg)](https://coveralls.io/r/sorentwo/readthis?branch=master)
4
5
 
5
6
  # Readthis
6
7
 
@@ -13,7 +14,7 @@ behavior for `fetch_multi`.
13
14
 
14
15
  ## Footprint & Performance
15
16
 
16
- See [Performance][PERFORMANCE.md]
17
+ See [Performance](PERFORMANCE.md)
17
18
 
18
19
  ## Installation
19
20
 
@@ -51,6 +52,8 @@ redis database, which defaults to 0. For example, using database 2:
51
52
  REDIS_URL=redis://localhost:6379/2
52
53
  ```
53
54
 
55
+ ### Compression
56
+
54
57
  Compression can be enabled for all actions by passing the `compress` flag. By
55
58
  default all values greater than 1024k will be compressed automatically. If there
56
59
  is any content has not been stored with compression, or perhaps was compressed
@@ -65,6 +68,24 @@ config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
65
68
  }
66
69
  ```
67
70
 
71
+ ### Marshalling
72
+
73
+ Readthis uses Ruby's `Marshal` module for dumping and loading all values by
74
+ default. This isn't always the fastest option, depending on your use case it may
75
+ be desirable to use a faster but more flexible marshaller.
76
+
77
+ Use Oj for JSON marshalling, extremely fast, limited types:
78
+
79
+ ```ruby
80
+ Readthis::Cache.new(marshal: Oj)
81
+ ```
82
+
83
+ If you don't mind everything being a string then use the Passthrough marshal:
84
+
85
+ ```ruby
86
+ Readthis::Cache.new(marshal: Readthis::Passthrough)
87
+ ```
88
+
68
89
  ## Differences
69
90
 
70
91
  Readthis supports all of standard cache methods except for the following:
@@ -0,0 +1,37 @@
1
+ require 'bundler'
2
+
3
+ Bundler.setup
4
+
5
+ require 'benchmark/ips'
6
+ require 'json'
7
+ require 'oj'
8
+ require 'readthis'
9
+ require 'readthis/passthrough'
10
+
11
+ REDIS_URL = 'redis://localhost:6379/11'
12
+ OPTIONS = { compressed: false }
13
+
14
+ readthis_pass = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Readthis::Passthrough))
15
+ readthis_oj = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Oj))
16
+ readthis_json = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: JSON))
17
+ readthis_ruby = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Marshal))
18
+
19
+ HASH = ('a'..'z').each_with_object({}) { |key, memo| memo[key] = key }
20
+
21
+ Benchmark.ips do |x|
22
+ x.report('pass:hash:dump') { readthis_oj.write('pass', HASH) }
23
+ x.report('oj:hash:dump') { readthis_oj.write('oj', HASH) }
24
+ x.report('json:hash:dump') { readthis_json.write('json', HASH) }
25
+ x.report('ruby:hash:dump') { readthis_ruby.write('ruby', HASH) }
26
+
27
+ x.compare!
28
+ end
29
+
30
+ Benchmark.ips do |x|
31
+ x.report('pass:hash:load') { readthis_oj.read('pass') }
32
+ x.report('oj:hash:load') { readthis_oj.read('oj') }
33
+ x.report('json:hash:load') { readthis_json.read('json') }
34
+ x.report('ruby:hash:load') { readthis_ruby.read('ruby') }
35
+
36
+ x.compare!
37
+ end
@@ -1,19 +1,14 @@
1
- require 'readthis/compressor'
1
+ require 'readthis/entity'
2
2
  require 'readthis/expanders'
3
3
  require 'readthis/notifications'
4
+ require 'readthis/passthrough'
4
5
  require 'redis'
5
6
  require 'hiredis'
6
7
  require 'connection_pool'
7
8
 
8
9
  module Readthis
9
10
  class Cache
10
- attr_reader :compress,
11
- :compression_threshold,
12
- :expires_in,
13
- :namespace,
14
- :pool
15
-
16
- alias_method :compress?, :compress
11
+ attr_reader :entity, :expires_in, :namespace, :pool
17
12
 
18
13
  # Provide a class level lookup of the proper notifications module.
19
14
  # Instrumention is expected to occur within applications that have
@@ -30,11 +25,14 @@ module Readthis
30
25
  # Creates a new Readthis::Cache object with the given redis URL. The URL
31
26
  # is parsed by the redis client directly.
32
27
  #
33
- # @param url [String] A redis compliant url with necessary connection details
34
- # @option options [String] :namespace Prefix used to namespace entries
35
- # @option options [Number] :expires_in The number of seconds until an entry expires
36
- # @option options [Boolean] :compress Enable or disable automatic compression
37
- # @option options [Number] :compression_threshold The size a string must be for compression
28
+ # @param [String] A redis compliant url with necessary connection details
29
+ # @option [Boolean] :compress (false) Enable or disable automatic compression
30
+ # @option [Number] :compression_threshold (8k) The size a string must be for compression
31
+ # @option [Number] :expires_in The number of seconds until an entry expires
32
+ # @option [Module] :marshal (Marshal) Any module that responds to `dump` and `load`
33
+ # @option [String] :namespace Prefix used to namespace entries
34
+ # @option [Number] :pool_size (5) The number of threads in the pool
35
+ # @option [Number] :pool_timeout (5) How long before a thread times out
38
36
  #
39
37
  # @example Create a new cache instance
40
38
  # Readthis::Cache.new('redis://localhost:6379/0', namespace: 'cache')
@@ -45,19 +43,35 @@ module Readthis
45
43
  def initialize(url, options = {})
46
44
  @expires_in = options.fetch(:expires_in, nil)
47
45
  @namespace = options.fetch(:namespace, nil)
48
- @compress = options.fetch(:compress, false)
49
- @compression_threshold = options.fetch(:compression_threshold, 1024)
46
+
47
+ @entity = Readthis::Entity.new(
48
+ marshal: options.fetch(:marshal, Marshal),
49
+ compress: options.fetch(:compress, false),
50
+ threshold: options.fetch(:compression_threshold, 1024)
51
+ )
50
52
 
51
53
  @pool = ConnectionPool.new(pool_options(options)) do
52
54
  Redis.new(url: url, driver: :hiredis)
53
55
  end
54
56
  end
55
57
 
58
+ # Fetches data from the cache, using the given key. If there is data in
59
+ # the cache with the given key, then that data is returned. Otherwise, nil
60
+ # is returned.
61
+ #
62
+ # @param [String] Key for lookup
63
+ # @param [Hash] Optional overrides
64
+ #
65
+ # @example
66
+ #
67
+ # cache.read('missing') # => nil
68
+ # cache.read('matched') # => 'some value'
69
+ #
56
70
  def read(key, options = {})
57
71
  invoke(:read, key) do |store|
58
72
  value = store.get(namespaced_key(key, merged_options(options)))
59
73
 
60
- decompressed(value)
74
+ entity.load(value)
61
75
  end
62
76
  end
63
77
 
@@ -67,9 +81,9 @@ module Readthis
67
81
 
68
82
  invoke(:write, key) do |store|
69
83
  if expiration = options[:expires_in]
70
- store.setex(namespaced, expiration, compressed(value))
84
+ store.setex(namespaced, expiration, entity.dump(value))
71
85
  else
72
- store.set(namespaced, compressed(value))
86
+ store.set(namespaced, entity.dump(value))
73
87
  end
74
88
  end
75
89
  end
@@ -91,15 +105,45 @@ module Readthis
91
105
  value
92
106
  end
93
107
 
94
- def increment(key, options = {})
108
+ # Increment a key in the store.
109
+ #
110
+ # If the key doesn't exist it will be initialized at 0. If the key exists
111
+ # but it isn't a Fixnum it will be initialized at 0.
112
+ #
113
+ # @param [String] Key for lookup
114
+ # @param [Fixnum] Value to increment by
115
+ # @param [Hash] Optional overrides
116
+ #
117
+ # @example
118
+ #
119
+ # cache.increment('counter') # => 0
120
+ # cache.increment('counter') # => 1
121
+ # cache.increment('counter', 2) # => 3
122
+ #
123
+ def increment(key, amount = 1, options = {})
95
124
  invoke(:incremenet, key) do |store|
96
- store.incr(namespaced_key(key, merged_options(options)))
125
+ alter(key, amount, options)
97
126
  end
98
127
  end
99
128
 
100
- def decrement(key, options = {})
129
+ # Decrement a key in the store.
130
+ #
131
+ # If the key doesn't exist it will be initialized at 0. If the key exists
132
+ # but it isn't a Fixnum it will be initialized at 0.
133
+ #
134
+ # @param [String] Key for lookup
135
+ # @param [Fixnum] Value to decrement by
136
+ # @param [Hash] Optional overrides
137
+ #
138
+ # @example
139
+ #
140
+ # cache.write('counter', 20) # => 20
141
+ # cache.decrement('counter') # => 19
142
+ # cache.decrement('counter', 2) # => 17
143
+ #
144
+ def decrement(key, amount = 1, options = {})
101
145
  invoke(:decrement, key) do |store|
102
- store.decr(namespaced_key(key, merged_options(options)))
146
+ alter(key, amount * -1, options)
103
147
  end
104
148
  end
105
149
 
@@ -108,7 +152,7 @@ module Readthis
108
152
  mapping = keys.map { |key| namespaced_key(key, options) }
109
153
 
110
154
  invoke(:read_multi, keys) do |store|
111
- values = decompressed_multi(store.mget(mapping))
155
+ values = store.mget(mapping).map { |value| entity.load(value) }
112
156
 
113
157
  keys.zip(values).to_h
114
158
  end
@@ -152,32 +196,11 @@ module Readthis
152
196
 
153
197
  private
154
198
 
155
- def compressed(value)
156
- if compress?
157
- compressor.compress(value)
158
- else
159
- value
160
- end
161
- end
162
-
163
- def decompressed(value)
164
- if compress?
165
- compressor.decompress(value)
166
- else
167
- value
168
- end
169
- end
170
-
171
- def decompressed_multi(values)
172
- if compress?
173
- values.map { |value| compressor.decompress(value) }
174
- else
175
- values
176
- end
177
- end
178
-
179
- def compressor
180
- @compressor ||= Readthis::Compressor.new(threshold: compression_threshold)
199
+ def alter(key, amount, options)
200
+ number = read(key, options)
201
+ delta = number.to_i + amount
202
+ write(key, delta, options)
203
+ delta
181
204
  end
182
205
 
183
206
  def instrument(operation, key)
@@ -0,0 +1,51 @@
1
+ require 'zlib'
2
+
3
+ module Readthis
4
+ class Entity
5
+ DEFAULT_THRESHOLD = 8 * 1024
6
+
7
+ attr_reader :marshal, :compression, :threshold
8
+
9
+ def initialize(marshal: Marshal, compress: false, threshold: DEFAULT_THRESHOLD)
10
+ @marshal = marshal
11
+ @compression = compress
12
+ @threshold = threshold
13
+ end
14
+
15
+ def dump(value)
16
+ return value if value.nil?
17
+
18
+ if compress?(value)
19
+ compress(value)
20
+ else
21
+ marshal.dump(value)
22
+ end
23
+ end
24
+
25
+ def load(value)
26
+ return value if value.nil?
27
+
28
+ if compress?(value)
29
+ decompress(value)
30
+ else
31
+ marshal.load(value)
32
+ end
33
+ rescue TypeError, Zlib::Error
34
+ value
35
+ end
36
+
37
+ def compress(value)
38
+ Zlib::Deflate.deflate(marshal.dump(value))
39
+ end
40
+
41
+ def decompress(value)
42
+ marshal.load(Zlib::Inflate.inflate(value))
43
+ end
44
+
45
+ private
46
+
47
+ def compress?(value)
48
+ compression && value.bytesize >= threshold
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ module Readthis
2
+ module Passthrough
3
+ def self.dump(value)
4
+ value
5
+ end
6
+
7
+ def self.load(value)
8
+ value
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Readthis
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -20,17 +20,6 @@ RSpec.describe Readthis::Cache do
20
20
 
21
21
  expect(cache.expires_in).to eq(10)
22
22
  end
23
-
24
- it 'stores compression parameters' do
25
- cache = Readthis::Cache.new(
26
- url,
27
- compress: true,
28
- compression_threshold: 8
29
- )
30
-
31
- expect(cache.compress).to be_truthy
32
- expect(cache.compression_threshold).to eq(8)
33
- end
34
23
  end
35
24
 
36
25
  describe '#write' do
@@ -47,6 +36,14 @@ RSpec.describe Readthis::Cache do
47
36
  expect(cache.read('some-key', namespace: 'cache')).to eq('some-value')
48
37
  end
49
38
 
39
+ it 'roundtrips values as their original type' do
40
+ object = { a: 1, b: 2 }
41
+
42
+ cache.write('obj-key', object)
43
+
44
+ expect(cache.read('obj-key')).to eq(object)
45
+ end
46
+
50
47
  it 'uses a custom expiration' do
51
48
  cache.write('some-key', 'some-value', expires_in: 1)
52
49
 
@@ -70,7 +67,7 @@ RSpec.describe Readthis::Cache do
70
67
  end
71
68
  end
72
69
 
73
- describe '#compress' do
70
+ describe 'compression' do
74
71
  it 'round trips entries when compression is enabled' do
75
72
  com_cache = Readthis::Cache.new(url, compress: true, compression_threshold: 8)
76
73
  raw_cache = Readthis::Cache.new(url)
@@ -129,11 +126,11 @@ RSpec.describe Readthis::Cache do
129
126
  it 'maps multiple values to keys' do
130
127
  cache.write('a', 1)
131
128
  cache.write('b', 2)
132
- cache.write('c', 3)
129
+ cache.write('c', '3')
133
130
 
134
131
  expect(cache.read_multi('a', 'b', 'c')).to eq(
135
- 'a' => '1',
136
- 'b' => '2',
132
+ 'a' => 1,
133
+ 'b' => 2,
137
134
  'c' => '3',
138
135
  )
139
136
  end
@@ -143,8 +140,8 @@ RSpec.describe Readthis::Cache do
143
140
  cache.write('e', 2, namespace: 'cache')
144
141
 
145
142
  expect(cache.read_multi('d', 'e', namespace: 'cache')).to eq(
146
- 'd' => '1',
147
- 'e' => '2',
143
+ 'd' => 1,
144
+ 'e' => 2,
148
145
  )
149
146
  end
150
147
  end
@@ -157,9 +154,9 @@ RSpec.describe Readthis::Cache do
157
154
  results = cache.fetch_multi('a', 'b', 'c') { |key| key + key }
158
155
 
159
156
  expect(results).to eq(
160
- 'a' => '1',
157
+ 'a' => 1,
161
158
  'b' => 'bb',
162
- 'c' => '3',
159
+ 'c' => 3,
163
160
  )
164
161
  end
165
162
  end
@@ -188,7 +185,7 @@ RSpec.describe Readthis::Cache do
188
185
  it 'atomically increases the stored integer' do
189
186
  cache.write('counter', 10)
190
187
  expect(cache.increment('counter')).to eq(11)
191
- expect(cache.read('counter')).to eq('11')
188
+ expect(cache.read('counter')).to eq(11)
192
189
  end
193
190
 
194
191
  it 'defaults a missing key to 1' do
@@ -0,0 +1,73 @@
1
+ require 'readthis/entity'
2
+ require 'json'
3
+
4
+ RSpec.describe Readthis::Entity do
5
+ describe '#dump' do
6
+ it 'marshals the object as a ruby string' do
7
+ string = 'some string'
8
+ entity = Readthis::Entity.new
9
+
10
+ expect(entity.dump(string)).to eq(Marshal.dump(string))
11
+ end
12
+
13
+ it 'marshals using a custom marshaller' do
14
+ string = 'some string'
15
+ entity = Readthis::Entity.new(marshal: JSON)
16
+
17
+ expect(entity.dump(string)).to eq(JSON.dump(string))
18
+ end
19
+
20
+ it 'applies compression when enabled' do
21
+ string = 'a very large string, huge I tell you'
22
+ entity = Readthis::Entity.new(compress: true, threshold: 8)
23
+ dumped = Marshal.dump(string)
24
+
25
+ expect(entity.dump(string)).not_to eq(dumped)
26
+ end
27
+
28
+ it 'does not dump nil values' do
29
+ entity = Readthis::Entity.new
30
+
31
+ expect(entity.dump(nil)).to be_nil
32
+ end
33
+ end
34
+
35
+ describe '#load' do
36
+ it 'unmarshals a value' do
37
+ object = { a: 1, b: '2' }
38
+ dumped = Marshal.dump(object)
39
+ entity = Readthis::Entity.new
40
+
41
+ expect(entity.load(dumped)).to eq(object)
42
+ end
43
+
44
+ it 'uncompresses when compression is enabled' do
45
+ string = 'another one of those huge strings'
46
+ entity = Readthis::Entity.new(compress: true, threshold: 0)
47
+ dumped = Marshal.dump(string)
48
+
49
+ compressed = entity.compress(dumped)
50
+
51
+ expect(entity.load(compressed)).not_to eq(string)
52
+ end
53
+
54
+ it 'does not try to load a nil value' do
55
+ entity = Readthis::Entity.new
56
+
57
+ expect(entity.load(nil)).to be_nil
58
+ end
59
+
60
+ it 'passes through the value when it fails to marshal' do
61
+ entity = Readthis::Entity.new
62
+
63
+ expect { entity.load('not marshalled') }.not_to raise_error
64
+ end
65
+
66
+ it 'passes through the value when it fails to decompress' do
67
+ entity = Readthis::Entity.new(compress: true, threshold: 0)
68
+ dumped = Marshal.dump('some sizable string')
69
+
70
+ expect { entity.load(dumped) }.not_to raise_error
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,17 @@
1
+ require 'readthis/passthrough'
2
+
3
+ RSpec.describe Readthis::Passthrough do
4
+ describe '.load' do
5
+ it 'passes through the provided value' do
6
+ value = Object.new
7
+ expect(Readthis::Passthrough.load(value)).to eq(value)
8
+ end
9
+ end
10
+
11
+ describe '.dump' do
12
+ it 'passes through the provided value' do
13
+ value = Object.new
14
+ expect(Readthis::Passthrough.dump(value)).to eq(value)
15
+ end
16
+ end
17
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,7 @@
1
+ require 'coveralls'
2
+
3
+ Coveralls.wear!
4
+
1
5
  RSpec.configure do |config|
2
6
  config.expect_with :rspec do |expectations|
3
7
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readthis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Parker Selbert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-11 00:00:00.000000000 Z
11
+ date: 2014-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -112,21 +112,24 @@ files:
112
112
  - README.md
113
113
  - Rakefile
114
114
  - benchmarks/compressed.rb
115
+ - benchmarks/marshalling.rb
115
116
  - benchmarks/memory.rb
116
117
  - benchmarks/multi.rb
117
118
  - bin/rspec
118
119
  - lib/active_support/cache/readthis_store.rb
119
120
  - lib/readthis.rb
120
121
  - lib/readthis/cache.rb
121
- - lib/readthis/compressor.rb
122
+ - lib/readthis/entity.rb
122
123
  - lib/readthis/expanders.rb
123
124
  - lib/readthis/notifications.rb
125
+ - lib/readthis/passthrough.rb
124
126
  - lib/readthis/version.rb
125
127
  - readthis.gemspec
126
128
  - spec/readthis/cache_spec.rb
127
- - spec/readthis/compressor_spec.rb
129
+ - spec/readthis/entity_spec.rb
128
130
  - spec/readthis/expanders_spec.rb
129
131
  - spec/readthis/notifications_spec.rb
132
+ - spec/readthis/passthrough_spec.rb
130
133
  - spec/spec_helper.rb
131
134
  homepage: https://github.com/sorentwo/readthis
132
135
  licenses:
@@ -154,7 +157,8 @@ specification_version: 4
154
157
  summary: Pooled active support compliant caching with redis
155
158
  test_files:
156
159
  - spec/readthis/cache_spec.rb
157
- - spec/readthis/compressor_spec.rb
160
+ - spec/readthis/entity_spec.rb
158
161
  - spec/readthis/expanders_spec.rb
159
162
  - spec/readthis/notifications_spec.rb
163
+ - spec/readthis/passthrough_spec.rb
160
164
  - spec/spec_helper.rb
@@ -1,41 +0,0 @@
1
- require 'zlib'
2
-
3
- module Readthis
4
- class Compressor
5
- attr_reader :threshold
6
-
7
- # Create a new Readthis::Compressor object that pivots on the provided
8
- # threshold value.
9
- #
10
- # @param threshold [Number] the threshold size required for compression
11
- def initialize(threshold: 1024)
12
- @threshold = threshold
13
- end
14
-
15
- # Compress a value if its size is greater or equal to the current threshold.
16
- #
17
- # @param value [String] a string to compress
18
- def compress(value)
19
- if value.size >= threshold
20
- Zlib::Deflate.deflate(value)
21
- else
22
- value
23
- end
24
- end
25
-
26
- # Decompress a previously compressed object. It will attempt to decode a
27
- # value regardless of whether it has been compressed, but will rescue
28
- # decoding errors.
29
- #
30
- # @param value [String] a possibly compressed string to decompress
31
- def decompress(value)
32
- if value.size >= threshold
33
- Zlib::Inflate.inflate(value)
34
- else
35
- value
36
- end
37
- rescue Zlib::Error
38
- value
39
- end
40
- end
41
- end
@@ -1,38 +0,0 @@
1
- require 'readthis/compressor'
2
-
3
- RSpec.describe Readthis::Compressor do
4
- describe '#compress' do
5
- it 'compresses the input' do
6
- compressor = Readthis::Compressor.new(threshold: 0)
7
- input = 'aaa bbb ccc'
8
- output = compressor.compress(input)
9
-
10
- expect(input).not_to eq(output)
11
- end
12
-
13
- it 'passes input below the threshold size through uncompressed' do
14
- compressor = Readthis::Compressor.new(threshold: 1024)
15
- input = 'abcdefg'
16
-
17
- expect(compressor.compress(input)).to eq(input)
18
- end
19
- end
20
-
21
- describe '#decompress' do
22
- it 'decompresses compressed data' do
23
- compressor = Readthis::Compressor.new(threshold: 0)
24
- input = 'aaa bbb ccc'
25
- compressed = compressor.compress(input)
26
- decompressed = compressor.decompress(compressed)
27
-
28
- expect(decompressed).to eq(input)
29
- end
30
-
31
- it 'passes through decompression failures' do
32
- compressor = Readthis::Compressor.new(threshold: 0)
33
- input = 'abcdefg'
34
-
35
- expect(compressor.decompress(input)).to eq(input)
36
- end
37
- end
38
- end