moneta 0.8.1 → 1.0.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: 1ffd0417515470f90fc239f70af5f5f5c1458343
4
- data.tar.gz: 1d20edcf756595fbf503741b41f4cc4ca4d6243a
3
+ metadata.gz: 0e375eca1c232dfe6bf7732660b158162bd5b887
4
+ data.tar.gz: 30219ec6a3496650ea09927d1d6140d377f48730
5
5
  SHA512:
6
- metadata.gz: 5ab6f8072a7800d0c1313a7e2bb17988b8f1f301dc48d6ba8b7749c13e00effad24beca9ac711d173c837fc2d392e654c12b5d1e65fc977360329b958c2cd598
7
- data.tar.gz: 7a58e5765b1eca76c1d65863732c74b4d46d88fe1eb49dd67b482a028b071ef13ce8a0607509b9173d67fbafe7aba437152b4de32e31c7d3f51a556d43bb7267
6
+ metadata.gz: 39edafb80920ff47c04b215fd74d77f9e49363588bdf988649637d24219e1e164b8887c46289be4455a0fbaf5315fe5b768fd0b56f5a594594ba44f28ff1ece9
7
+ data.tar.gz: ba16585476906490eaa5df9be59c24cd5d49cd82eacdc9b6960f59456bc461a603334abce9c70894e9de12af1f55b13d16710137171a47a4588670e72bf0f4f7
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ 1.0.0
2
+
3
+ * Adapters::Sequel - allow usage of Sequel extensions and connection validation
4
+ * ActiveSupport::Cache::MonetaStore - dup options before mutating them
5
+ * ActiveSupport::Cache::MonetaStore - allow writing raw values
6
+
1
7
  0.8.1
2
8
 
3
9
  * Adapters::TokyoTyrant - more consistent error handling
@@ -26,6 +26,8 @@ Potapov Sergey <blake131313@gmail.com>
26
26
  Quin Hoxie <quin@aboutus.org>
27
27
  Ryan T. Hosford <tad.hosford@gmail.com>
28
28
  Scott Wadden <scott.wadden@gmail.com>
29
+ Timo Goebel <timo.goebel@dm.de>
29
30
  Tom Meier <ozmeier@yahoo.co.uk>
31
+ Tony Han <h.bing612@gmail.com>
30
32
  Xavier Shay <xavier@rhnh.net>
31
33
  Yehuda Katz <wycats@gmail.com>
data/Gemfile CHANGED
@@ -16,6 +16,7 @@ gem 'msgpack', platforms: :ruby
16
16
  gem 'msgpack-jruby', platforms: :jruby
17
17
  gem 'bert', platforms: :ruby
18
18
  gem 'php_serialize'
19
+ gem 'nokogiri', '~> 1.6.0'
19
20
 
20
21
  # Compressors used by Transformer
21
22
  gem 'rbzip2'
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 - 2014 Daniel Mendler, Yehuda Katz
1
+ Copyright (c) 2009 - 2017 Daniel Mendler, Yehuda Katz, Alastair Pharo
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -38,7 +38,8 @@ module ActiveSupport
38
38
  end
39
39
 
40
40
  def write_entry(key, entry, options)
41
- @store.store(key, entry, moneta_options(options))
41
+ value = options[:raw] ? entry.value.to_s : entry
42
+ @store.store(key, value, moneta_options(options))
42
43
  true
43
44
  end
44
45
 
@@ -50,9 +51,9 @@ module ActiveSupport
50
51
  private
51
52
 
52
53
  def moneta_options(options)
53
- options ||= {}
54
- options[:expires] = options.delete(:expires_in).to_i if options.include?(:expires_in)
55
- options
54
+ new_options = options ? options.dup : {}
55
+ new_options[:expires] = new_options.delete(:expires_in).to_i if new_options.include?(:expires_in)
56
+ new_options
56
57
  end
57
58
  end
58
59
  end
@@ -17,15 +17,24 @@ module Moneta
17
17
  # @param [Hash] options
18
18
  # @option options [String] :db Sequel database
19
19
  # @option options [String/Symbol] :table (:moneta) Table name
20
+ # @option options [Array] :extensions ([]) List of Sequel extensions
21
+ # @option options [Integer] :connection_validation_timeout (nil) Sequel connection_validation_timeout
20
22
  # @option options All other options passed to `Sequel#connect`
21
23
  # @option options [Sequel connection] :backend Use existing backend instance
22
24
  def initialize(options = {})
23
25
  table = (options.delete(:table) || :moneta).to_sym
26
+ extensions = options.delete(:extensions) || []
27
+ raise ArgumentError, 'Option :extensions must be an Array' unless extensions.is_a?(Array)
28
+ connection_validation_timeout = options.delete(:connection_validation_timeout)
24
29
  @backend = options[:backend] ||
25
30
  begin
26
31
  raise ArgumentError, 'Option :db is required' unless db = options.delete(:db)
27
32
  ::Sequel.connect(db, options)
28
33
  end
34
+ extensions.each do |extension|
35
+ @backend.extension(extension.to_sym)
36
+ end
37
+ @backend.pool.connection_validation_timeout = connection_validation_timeout if connection_validation_timeout
29
38
  @backend.create_table?(table) do
30
39
  String :k, null: false, primary_key: true
31
40
  File :v
@@ -1,5 +1,5 @@
1
1
  module Moneta
2
2
  # Moneta version number
3
3
  # @api public
4
- VERSION = '0.8.1'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = 'moneta'
7
7
  s.version = Moneta::VERSION
8
8
  s.date = Date.today.to_s
9
- s.authors = ['Daniel Mendler', 'Yehuda Katz', 'Hannes Georg']
10
- s.email = %w{mail@daniel-mendler.de wycats@gmail.com hannes.georg@googlemail.com}
9
+ s.authors = ['Daniel Mendler', 'Yehuda Katz', 'Hannes Georg', 'Alastair Pharo']
10
+ s.email = %w{mail@daniel-mendler.de wycats@gmail.com hannes.georg@googlemail.com asppsa@gmail.com}
11
11
  s.description = 'A unified interface to key/value stores'
12
12
  s.extra_rdoc_files = %w{README.md SPEC.md LICENSE}
13
13
  s.files = `git ls-files`.split("\n")
@@ -68,6 +68,18 @@ parallel = []
68
68
  parallel << serial
69
69
  end
70
70
  end
71
+
72
+ # The activesupport cache specs also use memcached
73
+ activesupport_cache_specs = specs.grep(/active_support.+cache/)
74
+ specs -= activesupport_cache_specs
75
+
76
+ if memcache_specs = parallel.find{ |serial| serial.all?{ |s| s.match /memcached/ } }
77
+ activesupport_cache_specs.each(&memcache_specs.method(:push))
78
+ else
79
+ parallel += activesupport_cache_specs
80
+ end
81
+
82
+ # All the left-overs
71
83
  parallel += specs.map {|s| [s] }
72
84
 
73
85
  threads = []
@@ -3,7 +3,20 @@ require 'active_support'
3
3
  require 'active_support/cache/moneta_store'
4
4
  require 'ostruct'
5
5
 
6
- describe ActiveSupport::Cache::MonetaStore do
6
+ module MonetaStoreHelpers
7
+ def with_notifications
8
+ described_class.instrument = true
9
+ yield
10
+ ensure
11
+ described_class.instrument = false
12
+ end
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ config.include(MonetaStoreHelpers)
17
+ end
18
+
19
+ describe "cache_moneta_store" do
7
20
  before(:all) do
8
21
  @events = []
9
22
  ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
@@ -13,185 +26,234 @@ describe ActiveSupport::Cache::MonetaStore do
13
26
 
14
27
  before(:each) do
15
28
  @events.clear
16
- @store = ActiveSupport::Cache::MonetaStore.new(store: Moneta.new(:Memory))
17
- @rabbit = OpenStruct.new name: 'bunny'
18
- @white_rabbit = OpenStruct.new color: 'white'
19
-
20
- @store.write 'rabbit', @rabbit
21
- @store.delete 'counter'
22
- @store.delete 'rub-a-dub'
23
29
  end
24
30
 
25
- it 'reads the data' do
26
- @store.read('rabbit').should == @rabbit
27
- end
31
+ # All stores should implement this basic behavior.
32
+ shared_examples :basic_store do
33
+ before(:each) do
34
+ @rabbit = OpenStruct.new name: 'bunny'
35
+ @white_rabbit = OpenStruct.new color: 'white'
28
36
 
29
- it 'writes the data' do
30
- @store.write 'rabbit', @white_rabbit
31
- @store.read('rabbit').should == @white_rabbit
32
- end
37
+ store.clear
38
+ store.write 'rabbit', @rabbit
39
+ end
33
40
 
34
- it 'writes the data with expiration time' do
35
- @store.write 'rabbit', @white_rabbit, expires_in: 1.second
36
- @store.read('rabbit').should == @white_rabbit
37
- sleep 2
38
- @store.read('rabbit').should be_nil
39
- end
41
+ it 'reads the data' do
42
+ store.read('rabbit').should == @rabbit
43
+ end
40
44
 
41
- it 'deletes data' do
42
- @store.delete 'rabbit'
43
- @store.read('rabbit').should be_nil
44
- end
45
+ it 'writes the data' do
46
+ store.write 'rabbit', @white_rabbit
47
+ store.read('rabbit').should == @white_rabbit
48
+ end
45
49
 
46
- it 'verifies existence of an object in the store' do
47
- @store.exist?('rabbit').should == true
48
- (!!@store.exist?('rab-a-dub')).should == false
49
- end
50
+ it 'deletes data' do
51
+ store.delete 'rabbit'
52
+ store.read('rabbit').should be_nil
53
+ end
50
54
 
51
- it 'fetches data' do
52
- @store.fetch('rabbit').should == @rabbit
53
- @store.fetch('rub-a-dub').should be_nil
54
- @store.fetch('rub-a-dub') { 'Flora de Cana' }
55
- @store.fetch('rub-a-dub').should == 'Flora de Cana'
56
- @store.fetch('rabbit', force: true) # force cache miss
57
- @store.fetch('rabbit', force: true, expires_in: 1.second) { @white_rabbit }
58
- @store.fetch('rabbit').should == @white_rabbit
59
- sleep 2
60
- @store.fetch('rabbit').should be_nil
61
- end
55
+ it 'verifies existence of an object in the store' do
56
+ store.exist?('rabbit').should be true
57
+ (!!store.exist?('rab-a-dub')).should be false
58
+ end
62
59
 
63
- it 'reads multiple keys' do
64
- @store.write 'irish whisky', 'Jameson'
65
- result = @store.read_multi 'rabbit', 'irish whisky'
66
- result['rabbit'].should == @rabbit
67
- result['irish whisky'].should == 'Jameson'
68
- end
60
+ it 'fetches data' do
61
+ store.fetch('rabbit').should == @rabbit
62
+ store.fetch('rub-a-dub').should be_nil
63
+ store.fetch('rub-a-dub') { 'Flora de Cana' }
64
+ store.fetch('rub-a-dub').should == 'Flora de Cana'
65
+ end
69
66
 
70
- it 'reads multiple keys and returns only the matched ones' do
71
- @store.delete 'irish whisky'
72
- result = @store.read_multi 'rabbit', 'irish whisky'
73
- result.should_not include('irish whisky')
74
- result.should include('rabbit')
75
- end
67
+ it 'reads multiple keys' do
68
+ store.write 'irish whisky', 'Jameson'
69
+ result = store.read_multi 'rabbit', 'irish whisky'
70
+ result['rabbit'].should == @rabbit
71
+ result['irish whisky'].should == 'Jameson'
72
+ end
76
73
 
77
- it 'increments a key' do
78
- 3.times { @store.increment 'counter' }
79
- @store.read('counter', raw: true).should == '3'
74
+ it 'reads multiple keys and returns only the matched ones' do
75
+ store.delete 'irish whisky'
76
+ result = store.read_multi 'rabbit', 'irish whisky'
77
+ result.should_not include('irish whisky')
78
+ result.should include('rabbit')
79
+ end
80
80
  end
81
81
 
82
- it 'decrements a key' do
83
- 3.times { @store.increment 'counter' }
84
- 2.times { @store.decrement 'counter' }
85
- @store.read('counter', raw: true).should == '1'
86
- end
82
+ shared_examples :expiry do
83
+ it 'writes the data with expiration time' do
84
+ store.write 'rabbit', @white_rabbit, expires_in: 1.second
85
+ store.read('rabbit').should == @white_rabbit
86
+ sleep 2
87
+ store.read('rabbit').should be_nil
88
+ end
89
+
90
+ it "sets expiry on cache miss" do
91
+ store.fetch('rabbit', force: true) # force cache miss
92
+ store.fetch('rabbit', force: true, expires_in: 1.second) { @white_rabbit }
93
+ store.fetch('rabbit').should == @white_rabbit
94
+ sleep 2
95
+ store.fetch('rabbit').should be_nil
96
+ end
87
97
 
88
- it 'increments a key by given value' do
89
- @store.increment 'counter', 3
90
- @store.read('counter', raw: true).should == '3'
98
+ it 'does not set expiry on cache hit' do
99
+ store.fetch('rabbit', expires_in: 1.second) { @white_rabbit }.should == @rabbit
100
+ sleep 2
101
+ store.fetch('rabbit').should == @rabbit
102
+ end
91
103
  end
92
104
 
93
- it 'decrements a key by given value' do
94
- 3.times { @store.increment 'counter' }
95
- @store.decrement 'counter', 2
96
- @store.read('counter', raw: true).should == '1'
105
+ # A store *may* implement this
106
+ shared_examples :increment_decrement do
107
+ it 'increments a key' do
108
+ store.write 'counter', 0, raw: true
109
+ 3.times { store.increment 'counter' }
110
+ store.read('counter', raw: true).to_i.should == 3
111
+ end
112
+
113
+ it 'decrements a key' do
114
+ store.write 'counter', 0, raw: true
115
+ 3.times { store.increment 'counter' }
116
+ 2.times { store.decrement 'counter' }
117
+ store.read('counter', raw: true).to_i.should == 1
118
+ end
119
+
120
+ it 'increments a key by given value' do
121
+ store.write 'counter', 0, raw: true
122
+ store.increment 'counter', 3
123
+ store.read('counter', raw: true).to_i.should == 3
124
+ end
125
+
126
+ it 'decrements a key by given value' do
127
+ store.write 'counter', 0, raw: true
128
+ 3.times { store.increment 'counter' }
129
+ store.decrement 'counter', 2
130
+ store.read('counter', raw: true).to_i.should == 1
131
+ end
97
132
  end
98
133
 
99
- describe 'notifications' do
134
+ shared_examples :basic_instrumentation do
100
135
  it 'notifies on #fetch' do
101
136
  with_notifications do
102
- @store.fetch('radiohead') { 'House Of Cards' }
137
+ store.fetch('radiohead') { 'House Of Cards' }
103
138
  end
104
139
 
105
- read, generate, write = @events
106
-
140
+ read = @events.shift
107
141
  read.name.should == 'cache_read.active_support'
108
142
  read.payload.should == { key: 'radiohead', super_operation: :fetch }
109
143
 
144
+ generate = @events.shift
110
145
  generate.name.should == 'cache_generate.active_support'
111
146
  generate.payload.should == { key: 'radiohead' }
112
147
 
148
+ write = @events.shift
113
149
  write.name.should == 'cache_write.active_support'
114
150
  write.payload.should == { key: 'radiohead' }
115
151
  end
116
152
 
117
153
  it 'notifies on #read' do
118
154
  with_notifications do
119
- @store.read 'metallica'
155
+ store.read 'metallica'
120
156
  end
121
157
 
122
- read = @events.first
158
+ read = @events.shift
123
159
  read.name.should == 'cache_read.active_support'
124
160
  read.payload.should == { key: 'metallica', hit: false }
125
161
  end
126
162
 
127
163
  it 'notifies on #write' do
128
164
  with_notifications do
129
- @store.write 'depeche mode', 'Enjoy The Silence'
165
+ store.write 'depeche mode', 'Enjoy The Silence'
130
166
  end
131
167
 
132
- write = @events.first
168
+ write = @events.shift
133
169
  write.name.should == 'cache_write.active_support'
134
170
  write.payload.should == { key: 'depeche mode' }
135
171
  end
136
172
 
137
173
  it 'notifies on #delete' do
138
174
  with_notifications do
139
- @store.delete 'the new cardigans'
175
+ store.delete 'the new cardigans'
140
176
  end
141
177
 
142
- delete = @events.first
178
+ delete = @events.shift
143
179
  delete.name.should == 'cache_delete.active_support'
144
180
  delete.payload.should == { key: 'the new cardigans' }
145
181
  end
146
182
 
147
183
  it 'notifies on #exist?' do
148
184
  with_notifications do
149
- @store.exist? 'the smiths'
185
+ store.exist? 'the smiths'
150
186
  end
151
187
 
152
- exist = @events.first
188
+ exist = @events.shift
153
189
  exist.name.should == 'cache_exist?.active_support'
154
190
  exist.payload.should == { key: 'the smiths' }
155
191
  end
156
192
 
193
+ end
194
+
195
+ # This doesn't seem to be documented at all, so we follow the
196
+ # behavior of MemCacheStore.
197
+ shared_examples :increment_decrement_instrumentation do
157
198
  it 'notifies on #increment' do
158
199
  with_notifications do
159
- @store.increment 'pearl jam'
200
+ store.increment 'pearl jam'
160
201
  end
161
202
 
162
- increment = @events.first
203
+ increment = @events.shift
163
204
  increment.name.should == 'cache_increment.active_support'
164
205
  increment.payload.should == { key: 'pearl jam', amount: 1 }
165
206
  end
166
207
 
167
208
  it 'notifies on #decrement' do
168
209
  with_notifications do
169
- @store.decrement 'placebo'
210
+ store.decrement 'placebo'
170
211
  end
171
212
 
172
- decrement = @events.first
213
+ decrement = @events.shift
173
214
  decrement.name.should == 'cache_decrement.active_support'
174
215
  decrement.payload.should == { key: 'placebo', amount: 1 }
175
216
  end
217
+ end
176
218
 
177
- it 'should notify on clear' do
219
+ describe ActiveSupport::Cache::MonetaStore do
220
+ let(:store){ described_class.new(store: Moneta.new(:Memory)) }
221
+
222
+ include_examples :basic_store
223
+ include_examples :expiry
224
+ include_examples :increment_decrement
225
+ include_examples :basic_instrumentation
226
+ include_examples :increment_decrement_instrumentation
227
+
228
+ # FIXME: no other store does this -- perhaps this should be
229
+ # removed.
230
+ it 'notifies on #clear' do
178
231
  with_notifications do
179
- @store.clear
232
+ store.clear
180
233
  end
181
234
 
182
- clear = @events.first
235
+ clear = @events.shift
183
236
  clear.name.should == 'cache_clear.active_support'
184
237
  clear.payload.should == { key: nil }
185
238
  end
186
239
  end
187
240
 
188
- private
241
+ describe ActiveSupport::Cache::MemoryStore do
242
+ let(:store){ described_class.new }
189
243
 
190
- def with_notifications
191
- ActiveSupport::Cache::MonetaStore.instrument = true
192
- yield
193
- ensure
194
- ActiveSupport::Cache::MonetaStore.instrument = false
244
+ include_examples :basic_store
245
+ include_examples :expiry
246
+ include_examples :increment_decrement
247
+ include_examples :basic_instrumentation
195
248
  end
196
- end
197
249
 
250
+ describe ActiveSupport::Cache::MemCacheStore do
251
+ let(:store){ described_class.new }
252
+
253
+ include_examples :basic_store
254
+ include_examples :expiry
255
+ include_examples :increment_decrement
256
+ include_examples :basic_instrumentation
257
+ include_examples :increment_decrement_instrumentation
258
+ end
259
+ end
metadata CHANGED
@@ -1,22 +1,24 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moneta
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
8
8
  - Yehuda Katz
9
9
  - Hannes Georg
10
+ - Alastair Pharo
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2016-12-14 00:00:00.000000000 Z
14
+ date: 2017-03-08 00:00:00.000000000 Z
14
15
  dependencies: []
15
16
  description: A unified interface to key/value stores
16
17
  email:
17
18
  - mail@daniel-mendler.de
18
19
  - wycats@gmail.com
19
20
  - hannes.georg@googlemail.com
21
+ - asppsa@gmail.com
20
22
  executables: []
21
23
  extensions: []
22
24
  extra_rdoc_files: