memcached_store 0.12.7 → 0.12.8

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: 705667e198b18bcd4d65f50390e5b739e12065b2
4
- data.tar.gz: 238f5c0a7cbcbdec4000fe7d753eb439e2868765
3
+ metadata.gz: 3e458052cfc2aac92c629b37e02fc7ae66077dcd
4
+ data.tar.gz: af048ce101065d5bc3463a9e118b6c3fb45c7763
5
5
  SHA512:
6
- metadata.gz: a50651983aa2380199e6ccb94b912e0d24f99791977cea55951fbd286adbb305f7e230308e6efcd27a7393775ede1e4dc4a78cb4cbf8eef974269e445f042d3a
7
- data.tar.gz: bf9f4f47644e8e73d5d0a4bf3b0f7679902b457ada045f0f0c5c4a8b6736a3f2958b3f60eb0f90b9b77d94fe735327ce86cf0345fec87a011035e0562d7bb34a
6
+ metadata.gz: 376570dd86abe4ca47cff7cb74aa934c7b26dac30ad24eeab5ee02b8310a24d9866d8c92ba2c85364c9c7bec426fe842e282b65d84742f2ba371db2cef478254
7
+ data.tar.gz: 8d958e238be593341eb041916ee4b47648a841ef3c87f45a3ebeeb1a2bca09808c43b1cf16aaf0ae5dba0b842ce49300ea9a3d600621e7f26d3deaeaa223b761
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
2
  services: memcache
3
+ before_install:
4
+ - gem update bundler
3
5
  rvm:
4
6
  - 1.9.3
5
7
  - 2.0.0
data/Gemfile CHANGED
@@ -11,5 +11,11 @@ else
11
11
  "~> #{version}"
12
12
  end
13
13
 
14
- gem 'minitest', '~> 4.0' if version == "3.2.18"
15
14
  gem "activesupport", as_version
15
+
16
+ group :test do
17
+ gem "minitest", '~> 4.0' if version == "3.2.18"
18
+ gem "mocha"
19
+ gem "timecop"
20
+ gem "snappy"
21
+ end
@@ -20,6 +20,11 @@ module ActiveSupport
20
20
  raise UnsupportedOperation.new("decrement is not supported by: #{self.class.name}")
21
21
  end
22
22
 
23
+ # IdentityCache has its own handling for read only.
24
+ def read_only
25
+ false
26
+ end
27
+
23
28
  private
24
29
  def serialize_entry(entry, options)
25
30
  value = options[:raw] ? entry.value.to_s : Marshal.dump(entry)
@@ -30,6 +30,8 @@ module ActiveSupport
30
30
 
31
31
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
32
32
 
33
+ attr_accessor :read_only
34
+
33
35
  def initialize(*addresses)
34
36
  addresses = addresses.flatten
35
37
  options = addresses.extract_options!
@@ -46,6 +48,21 @@ module ActiveSupport
46
48
  extend Strategy::LocalCache
47
49
  end
48
50
 
51
+ def logger
52
+ return @logger if defined?(@logger)
53
+ @logger = ::Rails.logger if defined?(::Rails)
54
+ end
55
+
56
+ def write(*args)
57
+ return true if read_only
58
+ super(*args)
59
+ end
60
+
61
+ def delete(*args)
62
+ return true if read_only
63
+ super(*args)
64
+ end
65
+
49
66
  def read_multi(*names)
50
67
  options = names.extract_options!
51
68
  options = merged_options(options)
@@ -74,6 +91,7 @@ module ActiveSupport
74
91
  @data.cas(key, expiration(options), cas_raw?(options)) do |raw_value|
75
92
  entry = deserialize_entry(raw_value)
76
93
  value = yield entry.value
94
+ break true if read_only
77
95
  serialize_entry(Entry.new(value, options), options).first
78
96
  end
79
97
  end
@@ -83,6 +101,7 @@ module ActiveSupport
83
101
  end
84
102
 
85
103
  def cas_multi(*names)
104
+
86
105
  options = names.extract_options!
87
106
  options = merged_options(options)
88
107
  keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
@@ -95,6 +114,7 @@ module ActiveSupport
95
114
  values[keys_to_names[key]] = entry.value unless entry.expired?
96
115
  end
97
116
  values = yield values
117
+ break true if read_only
98
118
  Hash[values.map{|name, value| [escape_key(namespaced_key(name, options)), serialize_entry(Entry.new(value, options), options).first]}]
99
119
  end
100
120
  end
@@ -151,6 +171,7 @@ module ActiveSupport
151
171
  end
152
172
 
153
173
  def write_entry(key, entry, options) # :nodoc:
174
+ return true if read_only
154
175
  method = options && options[:unless_exist] ? :add : :set
155
176
  expires_in = expiration(options)
156
177
  value, raw = serialize_entry(entry, options)
@@ -161,6 +182,7 @@ module ActiveSupport
161
182
  end
162
183
 
163
184
  def delete_entry(key, options) # :nodoc:
185
+ return true if read_only
164
186
  @data.delete(escape_key(key))
165
187
  true
166
188
  rescue *NONFATAL_EXCEPTIONS => e
@@ -23,6 +23,7 @@ module MemcachedStore
23
23
  def exist_with_rescue?(*args)
24
24
  exist_without_rescue?(*args)
25
25
  rescue *NONFATAL_EXCEPTIONS
26
+ report_exception($!)
26
27
  end
27
28
  alias_method :exist_without_rescue?, :exist?
28
29
  alias_method :exist?, :exist_with_rescue?
@@ -30,6 +31,7 @@ module MemcachedStore
30
31
  def cas_with_rescue(*args)
31
32
  cas_without_rescue(*args)
32
33
  rescue *NONFATAL_EXCEPTIONS
34
+ report_exception($!)
33
35
  false
34
36
  end
35
37
  alias_method_chain :cas, :rescue
@@ -37,6 +39,7 @@ module MemcachedStore
37
39
  def get_multi_with_rescue(*args)
38
40
  get_multi_without_rescue(*args)
39
41
  rescue *NONFATAL_EXCEPTIONS
42
+ report_exception($!)
40
43
  {}
41
44
  end
42
45
  alias_method_chain :get_multi, :rescue
@@ -44,6 +47,7 @@ module MemcachedStore
44
47
  def set_with_rescue(*args)
45
48
  set_without_rescue(*args)
46
49
  rescue *NONFATAL_EXCEPTIONS
50
+ report_exception($!)
47
51
  false
48
52
  end
49
53
  alias_method_chain :set, :rescue
@@ -51,6 +55,7 @@ module MemcachedStore
51
55
  def add_with_rescue(*args)
52
56
  add_without_rescue(*args)
53
57
  rescue *NONFATAL_EXCEPTIONS
58
+ report_exception($!)
54
59
  @string_return_types? "NOT STORED\r\n" : true
55
60
  end
56
61
  alias_method_chain :add, :rescue
@@ -60,9 +65,24 @@ module MemcachedStore
60
65
  def #{meth}_with_rescue(*args)
61
66
  #{meth}_without_rescue(*args)
62
67
  rescue *NONFATAL_EXCEPTIONS
68
+ report_exception($!)
63
69
  end
64
70
  alias_method_chain :#{meth}, :rescue
65
71
  ENV
66
72
  end
73
+
74
+ def logger
75
+ return @logger if @logger
76
+ @logger = ::Rails.logger if defined?(::Rails)
77
+ end
78
+
79
+ private
80
+
81
+ def report_exception(exception)
82
+ if defined?(::Rails)
83
+ logger.error "[#{self.class}] exception=#{exception}"
84
+ end
85
+ nil # make sure return value is nil
86
+ end
67
87
  end
68
88
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module MemcachedStore
3
- VERSION = "0.12.7"
3
+ VERSION = "0.12.8"
4
4
  end
@@ -19,8 +19,4 @@ Gem::Specification.new do |gem|
19
19
  gem.add_runtime_dependency "memcached", "~> 1.8.0"
20
20
 
21
21
  gem.add_development_dependency "rake"
22
- gem.add_development_dependency "minitest"
23
- gem.add_development_dependency "mocha"
24
- gem.add_development_dependency "timecop"
25
- gem.add_development_dependency "snappy"
26
22
  end
@@ -1 +1,4 @@
1
- # use default config
1
+ dependencies:
2
+ bundler:
3
+ without:
4
+ - test
@@ -5,3 +5,17 @@ require 'timecop'
5
5
  require 'active_support/test_case'
6
6
 
7
7
  require 'memcached_store'
8
+
9
+ class Rails
10
+ def self.logger
11
+ @logger ||= Logger.new("/dev/null")
12
+ end
13
+
14
+ def self.env
15
+ Struct.new("Env") do
16
+ def self.test?
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -125,6 +125,11 @@ class TestMemcachedSafety < ActiveSupport::TestCase
125
125
  @cache.prepend("a-key", "other")
126
126
  end
127
127
  end
128
+
129
+ test "logger defaults to rails logger" do
130
+ assert_equal Rails.logger, @cache.logger
131
+ end
132
+
128
133
  private
129
134
 
130
135
  def expect_nonfatal(sym)
@@ -1,9 +1,13 @@
1
1
  require 'test_helper'
2
+ require 'logger'
2
3
 
3
4
  class TestMemcachedStore < ActiveSupport::TestCase
4
5
  setup do
5
6
  @cache = ActiveSupport::Cache.lookup_store(:memcached_store, expires_in: 60, support_cas: true)
6
7
  @cache.clear
8
+
9
+ # Enable ActiveSupport notifications. Can be disabled in Rails 5.
10
+ Thread.current[:instrument_cache_store] = true
7
11
  end
8
12
 
9
13
  def test_write_not_found
@@ -153,26 +157,11 @@ class TestMemcachedStore < ActiveSupport::TestCase
153
157
  assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
154
158
  end
155
159
 
156
- def test_read_multi
157
- @cache.write('foo', 'bar')
158
- @cache.write('fu', 'baz')
159
- @cache.write('fud', 'biz')
160
- assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
161
- end
162
-
163
160
  def test_read_multi_not_found
164
161
  expect_not_found
165
162
  assert_equal({}, @cache.read_multi('foe', 'fue'))
166
163
  end
167
164
 
168
- def test_read_multi_with_expires
169
- @cache.write('foo', 'bar', :expires_in => 0.001)
170
- @cache.write('fu', 'baz')
171
- @cache.write('fud', 'biz')
172
- sleep(0.002)
173
- assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
174
- end
175
-
176
165
  def test_read_and_write_compressed_small_data
177
166
  @cache.write('foo', 'bar', :compress => true)
178
167
  assert_equal 'bar', @cache.read('foo')
@@ -388,15 +377,224 @@ class TestMemcachedStore < ActiveSupport::TestCase
388
377
  assert_equal "", client.prefix_key, "should not send the namespace to the client"
389
378
  assert_equal "foo::key", cache.send(:namespaced_key, "key", cache.options)
390
379
  end
391
-
380
+
392
381
  def test_reset
393
382
  client = @cache.instance_variable_get(:@data)
394
383
  client.expects(:reset).once
395
384
  @cache.reset
396
385
  end
397
386
 
387
+ def test_write_to_read_only_memcached_store_should_not_write
388
+ with_read_only(@cache) do
389
+ assert @cache.write("walrus", "awesome"), "Writing to a disabled memcached
390
+ store should return truthy to make clients not care"
391
+
392
+ assert_nil @cache.read("walrus"), "Key should have nil value in disabled cache"
393
+ end
394
+ end
395
+
396
+ def test_delete_with_read_only_memcached_store_should_not_delete
397
+ assert @cache.write("walrus", "big")
398
+
399
+ with_read_only(@cache) do
400
+ assert @cache.delete("walrus"), "Should return truthy when deleted to not raise in client"
401
+ end
402
+
403
+ assert_equal "big", @cache.read("walrus"), "Cache entry should not have been deleted from read only client"
404
+ end
405
+
406
+ def test_cas_with_read_only_memcached_store_should_not_s
407
+ called_block = false
408
+ @cache.write('walrus', 'slimy')
409
+
410
+ with_read_only(@cache) do
411
+ assert(@cache.cas('walrus') { |value|
412
+ assert_equal 'slimy', value
413
+ called_block = true
414
+ 'full'
415
+ })
416
+ end
417
+
418
+ assert_equal 'slimy', @cache.read('walrus')
419
+ assert called_block, "CAS with read only should have called the inner block with an assertion"
420
+ end
421
+
422
+ def test_cas_multi_with_read_only_memcached_store_should_not_s
423
+ called_block = false
424
+
425
+ @cache.write('walrus', 'cool')
426
+ @cache.write('narwhal', 'horn')
427
+
428
+ with_read_only(@cache) do
429
+ assert(@cache.cas_multi('walrus', 'narwhal') {
430
+ called_block = true
431
+ { "walrus" => "not cool", "narwhal" => "not with horns" }
432
+ })
433
+ end
434
+
435
+ assert_equal 'cool', @cache.read('walrus')
436
+ assert_equal 'horn', @cache.read('narwhal')
437
+ assert called_block, "CAS with read only should have called the inner block with an assertion"
438
+ end
439
+
440
+ def test_write_with_read_only_should_not_send_activesupport_notification
441
+ assert_notifications(/cache/, 0) do
442
+ with_read_only(@cache) do
443
+ assert @cache.write("walrus", "bestest")
444
+ end
445
+ end
446
+ end
447
+
448
+ def test_delete_with_read_only_should_not_send_activesupport_notification
449
+ assert_notifications(/cache/, 0) do
450
+ with_read_only(@cache) do
451
+ assert @cache.delete("walrus")
452
+ end
453
+ end
454
+ end
455
+
456
+ def test_fetch_with_expires_in_with_read_only_should_not_send_activesupport_notification
457
+ expires_in = 10
458
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
459
+
460
+ Timecop.travel(Time.now + expires_in + 1) do
461
+ assert_notifications(/cache_write/, 0) do
462
+ with_read_only(@cache) do
463
+ @cache.fetch("walrus") { "no" }
464
+ end
465
+ end
466
+ end
467
+ end
468
+
469
+ def test_fetch_with_expired_entry_with_read_only_should_return_nil_and_not_delete_from_cache
470
+ expires_in = 10
471
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
472
+
473
+ Timecop.travel(Time.now + expires_in + 1) do
474
+ with_read_only(@cache) do
475
+ value = @cache.fetch("walrus", expires_in: expires_in) { "no" }
476
+
477
+ assert_equal "no", value
478
+ refute @cache.fetch("walrus"), "Client should return nil for expired key"
479
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
480
+ end
481
+ end
482
+ end
483
+
484
+ def test_fetch_with_expired_entry_and_race_condition_ttl_with_read_only_should_return_nil_and_not_delete_from_cache
485
+ expires_in = 10
486
+ race_condition_ttl = 10
487
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
488
+
489
+ Timecop.travel(Time.now + expires_in + 1) do
490
+ with_read_only(@cache) do
491
+ value = @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
492
+
493
+ assert_equal "no", value
494
+ assert_equal "no", @cache.fetch("walrus") { "no" }
495
+ refute @cache.fetch("walrus")
496
+
497
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
498
+ end
499
+ end
500
+ end
501
+
502
+ def test_read_with_expired_with_read_only_entry_should_return_nil_and_not_delete_from_cache
503
+ expires_in = 10
504
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
505
+
506
+ Timecop.travel(Time.now + expires_in + 1) do
507
+ with_read_only(@cache) do
508
+ refute @cache.read("walrus")
509
+
510
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
511
+ end
512
+ end
513
+ end
514
+
515
+ def test_read_multi_with_expired_entry_should_return_nil_and_not_delete_from_cache
516
+ expires_in = 10
517
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
518
+ @cache.fetch("narwhal", expires_in: expires_in) { "yiir" }
519
+
520
+ Timecop.travel(Time.now + expires_in + 1) do
521
+ with_read_only(@cache) do
522
+ assert_predicate @cache.read_multi("walrus", "narwhal"), :empty?
523
+
524
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
525
+ assert_equal "yiir", @cache.instance_variable_get(:@data).get("narwhal").value
526
+ end
527
+ end
528
+ end
529
+
530
+ def test_fetch_with_race_condition_ttl_with_read_only_should_not_send_activesupport_notification
531
+ expires_in = 10
532
+ race_condition_ttl = 10
533
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
534
+
535
+ Timecop.travel(Time.now + expires_in + 1) do
536
+ assert_notifications(/cache_write/, 0) do
537
+ with_read_only(@cache) do
538
+ @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
539
+ end
540
+ end
541
+ end
542
+ end
543
+
544
+ def test_cas_with_read_only_should_send_activesupport_notification
545
+ @cache.write("walrus", "yes")
546
+
547
+ with_read_only(@cache) do
548
+ assert_notifications(/cache_cas/, 1) do
549
+ assert(@cache.cas("walrus") { |value| "no" })
550
+ end
551
+ end
552
+
553
+ assert_equal "yes", @cache.fetch("walrus")
554
+ end
555
+
556
+ def test_cas_multi_with_read_only_should_send_activesupport_notification
557
+ @cache.write("walrus", "yes")
558
+ @cache.write("narwhal", "yes")
559
+
560
+ with_read_only(@cache) do
561
+ assert_notifications(/cache_cas/, 1) do
562
+ assert(@cache.cas_multi("walrus", "narwhal") { |*values|
563
+ { "walrus" => "no", "narwhal" => "no" }
564
+ })
565
+ end
566
+ end
567
+
568
+ assert_equal "yes", @cache.fetch("walrus")
569
+ assert_equal "yes", @cache.fetch("narwhal")
570
+ end
571
+
572
+ def test_logger_defaults_to_rails_logger
573
+ assert_equal Rails.logger, @cache.logger
574
+ end
575
+
398
576
  private
399
577
 
578
+ def assert_notifications(pattern, num)
579
+ count = 0
580
+ subscriber = ActiveSupport::Notifications.subscribe(pattern) do |name, start, finish, id, payload|
581
+ count += 1
582
+ end
583
+
584
+ yield
585
+
586
+ assert_equal num, count, "Expected #{num} notifications for #{pattern}, but got #{count}"
587
+ ensure
588
+ ActiveSupport::Notifications.unsubscribe(subscriber)
589
+ end
590
+
591
+ def with_read_only(client)
592
+ previous, client.read_only = client.read_only, true
593
+ yield
594
+ ensure
595
+ client.read_only = previous
596
+ end
597
+
400
598
  def expect_not_found
401
599
  @cache.instance_variable_get(:@data).expects(:check_return_code).raises(Memcached::NotFound)
402
600
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memcached_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.7
4
+ version: 0.12.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camilo Lopez
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-06-17 00:00:00.000000000 Z
14
+ date: 2016-11-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -55,62 +55,6 @@ dependencies:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
57
  version: '0'
58
- - !ruby/object:Gem::Dependency
59
- name: minitest
60
- requirement: !ruby/object:Gem::Requirement
61
- requirements:
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- version: '0'
65
- type: :development
66
- prerelease: false
67
- version_requirements: !ruby/object:Gem::Requirement
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: '0'
72
- - !ruby/object:Gem::Dependency
73
- name: mocha
74
- requirement: !ruby/object:Gem::Requirement
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: '0'
79
- type: :development
80
- prerelease: false
81
- version_requirements: !ruby/object:Gem::Requirement
82
- requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- - !ruby/object:Gem::Dependency
87
- name: timecop
88
- requirement: !ruby/object:Gem::Requirement
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- version: '0'
93
- type: :development
94
- prerelease: false
95
- version_requirements: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: '0'
100
- - !ruby/object:Gem::Dependency
101
- name: snappy
102
- requirement: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- version: '0'
107
- type: :development
108
- prerelease: false
109
- version_requirements: !ruby/object:Gem::Requirement
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: '0'
114
58
  description: Plugin-able Memcached adapters to add features (compression, safety)
115
59
  email:
116
60
  - camilo@camilolopez.com
@@ -157,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
101
  version: '0'
158
102
  requirements: []
159
103
  rubyforge_project:
160
- rubygems_version: 2.2.2
104
+ rubygems_version: 2.5.1
161
105
  signing_key:
162
106
  specification_version: 4
163
107
  summary: Plugin-able Memcached adapters to add features (compression, safety)