readthis 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 076d20cf4f8bffa4b9a6b6ced603f65020bea46c
4
- data.tar.gz: bcb7899c7aa4f98d206ae7fea3e6fc388c7036b4
3
+ metadata.gz: 513263f9ac89cdc3d8bfc354404778e49902460b
4
+ data.tar.gz: 525ab985b197507e19c000bcb6dc21683ba068d9
5
5
  SHA512:
6
- metadata.gz: be88ade839408848ba227ece979dc2d4bb5b4621cdca98eb1f6a3b61fae3d00468940cb9c32eb02b3ca6438e882c680019035583f65c54ed58bd69e33f2c5e3d
7
- data.tar.gz: dcb48683aaa8ef72cf0e6770f0334245db4f6198fa244486115123a497ee9bc897568687727ef582ec40b220f81f6b1c1c1221b9c1527a80df714f090a1c4fa5
6
+ metadata.gz: f9207fe0c1d9746337d17ac05a23acd4bfde8c9d943d1814daa7426160a39fb8d932ea2fd7c69e898097afdf0427eab59cfad320362053547d4e40b3f2a4d4ee
7
+ data.tar.gz: 14ae7680da85fe6591cc6b623c9f13e2c11dd7905add114288d6ed4138490b4a63a67d0832d0069706246cac73f5fe9de14774f0316e4a4db933ed418e6c60aa
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ services:
5
+ - redis-server
6
+ script: bin/rspec
@@ -1,3 +1,10 @@
1
+ ## v0.2.0 2014-11-24
2
+
3
+ - Instrument all caching methods. Will use `ActiveSupport::Notifications`
4
+ if available, otherwise falls back to a polyfill.
5
+ - Expand objects with a `cache_key` method and arrays of strings or objects into
6
+ consistent naespaced keys.
7
+
1
8
  ## v0.1.0 2014-11-22
2
9
 
3
10
  - Initial release! Working as a drop in replacement for `redis_store`.
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ [![Gem Version](https://badge.fury.io/rb/readthis.svg)](http://badge.fury.io/rb/readthis)
2
+ [![Build Status](https://travis-ci.org/sorentwo/readthis.svg?branch=master)](https://travis-ci.org/sorentwo/readthis)
3
+
1
4
  # Readthis
2
5
 
3
6
  An ActiveSupport::Cache compatible redis based cache focused on speed,
@@ -1,3 +1,5 @@
1
+ require 'readthis/expanders'
2
+ require 'readthis/notifications'
1
3
  require 'redis'
2
4
  require 'connection_pool'
3
5
 
@@ -5,8 +7,22 @@ module Readthis
5
7
  class Cache
6
8
  attr_reader :expires_in, :namespace, :pool
7
9
 
10
+ # Provide a class level lookup of the proper notifications module.
11
+ # Instrumention is expected to occur within applications that have
12
+ # ActiveSupport::Notifications available, but needs to work even when it
13
+ # isn't.
14
+ def self.notifications
15
+ if Object.const_defined?('ActiveSupport::Notifications')
16
+ ActiveSupport::Notifications
17
+ else
18
+ Readthis::Notifications
19
+ end
20
+ end
21
+
8
22
  # Creates a new Readthis::Cache object with the given redis URL. The URL
9
23
  # is parsed by the redis client directly.
24
+ #
25
+ # Readthis::Cache.new('redis://localhost:6379/0', namespace: 'cache')
10
26
  def initialize(url, options = {})
11
27
  @expires_in = options.fetch(:expires_in, nil)
12
28
  @namespace = options.fetch(:namespace, nil)
@@ -17,8 +33,10 @@ module Readthis
17
33
  end
18
34
 
19
35
  def read(key, options = {})
20
- with do |store|
21
- store.get(namespaced_key(key, merged_options(options)))
36
+ instrument(:read, key) do
37
+ with do |store|
38
+ store.get(namespaced_key(key, merged_options(options)))
39
+ end
22
40
  end
23
41
  end
24
42
 
@@ -26,18 +44,22 @@ module Readthis
26
44
  options = merged_options(options)
27
45
  namespaced = namespaced_key(key, options)
28
46
 
29
- with do |store|
30
- if expiration = options[:expires_in]
31
- store.setex(namespaced, expiration, value)
32
- else
33
- store.set(namespaced, value)
47
+ instrument(:write, key) do
48
+ with do |store|
49
+ if expiration = options[:expires_in]
50
+ store.setex(namespaced, expiration, value)
51
+ else
52
+ store.set(namespaced, value)
53
+ end
34
54
  end
35
55
  end
36
56
  end
37
57
 
38
58
  def delete(key, options = {})
39
- with do |store|
40
- store.del(namespaced_key(key, merged_options(options)))
59
+ instrument(:delete, key) do
60
+ with do |store|
61
+ store.del(namespaced_key(key, merged_options(options)))
62
+ end
41
63
  end
42
64
  end
43
65
 
@@ -53,14 +75,18 @@ module Readthis
53
75
  end
54
76
 
55
77
  def increment(key, options = {})
56
- with do |store|
57
- store.incr(namespaced_key(key, merged_options(options)))
78
+ instrument(:incremenet, key) do
79
+ with do |store|
80
+ store.incr(namespaced_key(key, merged_options(options)))
81
+ end
58
82
  end
59
83
  end
60
84
 
61
85
  def decrement(key, options = {})
62
- with do |store|
63
- store.decr(namespaced_key(key, merged_options(options)))
86
+ instrument(:decrement, key) do
87
+ with do |store|
88
+ store.decr(namespaced_key(key, merged_options(options)))
89
+ end
64
90
  end
65
91
  end
66
92
 
@@ -68,50 +94,70 @@ module Readthis
68
94
  options = merged_options(extract_options!(keys))
69
95
  results = []
70
96
 
71
- with do |store|
72
- results = store.pipelined do
73
- keys.each { |key| store.get(namespaced_key(key, options)) }
97
+ instrument(:read_multi, keys) do
98
+ with do |store|
99
+ results = store.pipelined do
100
+ keys.each { |key| store.get(namespaced_key(key, options)) }
101
+ end
74
102
  end
75
- end
76
103
 
77
- keys.zip(results).to_h
104
+ keys.zip(results).to_h
105
+ end
78
106
  end
79
107
 
80
- # This must be done in two separate blocks. Redis pipelines return
81
- # futures, which can not be resolved until the pipeline has exited.
108
+ # Fetches multiple keys from the cache using a single call to the server
109
+ # and filling in any cache misses. All read and write operations are
110
+ # executed atomically.
111
+ #
112
+ # cache.fetch_multi('alpha', 'beta') do |key|
113
+ # "#{key}-was-missing"
114
+ # end
82
115
  def fetch_multi(*keys)
83
116
  results = read_multi(*keys)
84
117
  options = merged_options(extract_options!(keys))
85
118
 
86
- with do |store|
87
- store.pipelined do
88
- results.each do |key, value|
89
- if value.nil?
90
- value = yield key
91
- write(key, value, options)
92
- results[key] = value
119
+ instrument(:fetch_multi, keys) do
120
+ with do |store|
121
+ store.pipelined do
122
+ results.each do |key, value|
123
+ if value.nil?
124
+ value = yield key
125
+ write(key, value, options)
126
+ results[key] = value
127
+ end
93
128
  end
94
129
  end
95
130
  end
96
- end
97
131
 
98
- results
132
+ results
133
+ end
99
134
  end
100
135
 
101
136
  def exist?(key, options = {})
102
- with do |store|
103
- store.exists(namespaced_key(key, merged_options(options)))
137
+ instrument(:exist?, key) do
138
+ with do |store|
139
+ store.exists(namespaced_key(key, merged_options(options)))
140
+ end
104
141
  end
105
142
  end
106
143
 
107
144
  def clear
108
- with do |store|
109
- store.flushdb
145
+ instrument(:clear, '*') do
146
+ with do |store|
147
+ store.flushdb
148
+ end
110
149
  end
111
150
  end
112
151
 
113
152
  private
114
153
 
154
+ def instrument(operation, key)
155
+ name = "cache_#{operation}.active_support"
156
+ payload = { key: key }
157
+
158
+ self.class.notifications.instrument(name, key) { yield(payload) }
159
+ end
160
+
115
161
  def with(&block)
116
162
  pool.with(&block)
117
163
  end
@@ -132,9 +178,7 @@ module Readthis
132
178
  end
133
179
 
134
180
  def namespaced_key(key, options)
135
- namespace = options[:namespace]
136
-
137
- [namespace, key].compact.join(':')
181
+ Readthis::Expanders.expand(key, options[:namespace])
138
182
  end
139
183
  end
140
184
  end
@@ -0,0 +1,15 @@
1
+ module Readthis
2
+ module Expanders
3
+ def self.expand(key, namespace = nil)
4
+ expanded = if key.respond_to?(:cache_key)
5
+ key.cache_key
6
+ elsif key.is_a?(Array)
7
+ key.flat_map { |elem| expand(elem) }.join(':')
8
+ else
9
+ key
10
+ end
11
+
12
+ namespace ? "#{namespace}:#{expanded}" : expanded
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Readthis
2
+ module Notifications
3
+ def self.instrument(name, payload)
4
+ yield(payload)
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module Readthis
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.add_dependency 'redis', '~> 3.0'
21
21
  spec.add_dependency 'connection_pool', '~> 2.1'
22
22
 
23
- spec.add_development_dependency 'bundler', '~> 1.7'
24
- spec.add_development_dependency 'rake', '~> 10.0'
25
- spec.add_development_dependency 'rspec', '~> 3.1'
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec', '~> 3.1'
26
26
  end
@@ -43,6 +43,14 @@ RSpec.describe Readthis::Cache do
43
43
  sleep 1.01
44
44
  expect(cache.read('some-key')).to be_nil
45
45
  end
46
+
47
+ it 'expands non-string keys' do
48
+ key_obj = double(cache_key: 'custom-key')
49
+
50
+ cache.write(key_obj, 'some-value')
51
+
52
+ expect(cache.read('custom-key')).to eq('some-value')
53
+ end
46
54
  end
47
55
 
48
56
  describe '#fetch' do
@@ -0,0 +1,26 @@
1
+ require 'readthis/expanders'
2
+
3
+ RSpec.describe Readthis::Expanders do
4
+ def expand(key, namespace = nil)
5
+ Readthis::Expanders.expand(key, namespace)
6
+ end
7
+
8
+ describe '#expand' do
9
+ it 'namespaces a plain string' do
10
+ expect(expand('thing', 'space')).to eq('space:thing')
11
+ end
12
+
13
+ it 'expands an object that has a cache_key method' do
14
+ object = double(cache_key: 'custom-key')
15
+
16
+ expect(expand(object)).to eq('custom-key')
17
+ end
18
+
19
+ it 'expands an array of objects' do
20
+ object = double(cache_key: 'gamma')
21
+
22
+ expect(expand(['alpha', 'beta'])).to eq('alpha:beta')
23
+ expect(expand([object, object])).to eq('gamma:gamma')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require 'readthis/notifications'
2
+
3
+ RSpec.describe Readthis::Notifications do
4
+ describe '#instrument' do
5
+ it 'yields the provided block' do
6
+ inner = double(:inner)
7
+
8
+ expect(inner).to receive(:call)
9
+
10
+ Readthis::Notifications.instrument('operation', key: 'key') do
11
+ inner.call
12
+ end
13
+ end
14
+ end
15
+ end
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.1.0
4
+ version: 0.2.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-11-23 00:00:00.000000000 Z
11
+ date: 2014-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -42,30 +42,30 @@ dependencies:
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.7'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.7'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -89,6 +89,7 @@ extra_rdoc_files: []
89
89
  files:
90
90
  - ".gitignore"
91
91
  - ".rspec"
92
+ - ".travis.yml"
92
93
  - CHANGELOG.md
93
94
  - Gemfile
94
95
  - LICENSE.txt
@@ -98,9 +99,13 @@ files:
98
99
  - lib/active_support/cache/readthis_store.rb
99
100
  - lib/readthis.rb
100
101
  - lib/readthis/cache.rb
102
+ - lib/readthis/expanders.rb
103
+ - lib/readthis/notifications.rb
101
104
  - lib/readthis/version.rb
102
105
  - readthis.gemspec
103
106
  - spec/readthis/cache_spec.rb
107
+ - spec/readthis/expanders_spec.rb
108
+ - spec/readthis/notifications_spec.rb
104
109
  - spec/spec_helper.rb
105
110
  homepage: https://github.com/sorentwo/readthis
106
111
  licenses:
@@ -128,4 +133,6 @@ specification_version: 4
128
133
  summary: Pooled active support compliant caching with redis
129
134
  test_files:
130
135
  - spec/readthis/cache_spec.rb
136
+ - spec/readthis/expanders_spec.rb
137
+ - spec/readthis/notifications_spec.rb
131
138
  - spec/spec_helper.rb