moneta 0.8.1 → 1.0.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: 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: