expiring_memory_store 0.1.2 → 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 57f921197804eacb997b64aef1c87565d8adc47cfba73c98538f2f7d2ec68336
4
+ data.tar.gz: 0d9dbb97ff8d42f575573709adc440c1eb27df8abb7150b0268da785cf8bfa26
5
+ SHA512:
6
+ metadata.gz: b2d3debd19a816a55be0457d8eeb12449ab70ea329cc499925603c28c2ca7e4053e5935abd2bcd19a4fdb234a094590f97f2b1532d1bd32778e9edb51bedf681
7
+ data.tar.gz: '048b7c734a7febb2cdd6d5fb05f0f4b1686be9885888dfa771fe4c716c29971af725b774b08bd309ff8073900fdaac7c0c5663ec1f8fd0cac441b26b2fa93b5f'
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 [Matthew Rudy Jacobs]
1
+ Copyright (C) 2013 Michael Grosser <michael@grosser.it>
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
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ class ExpiringMemoryStore
3
+ MAX_EXPIRES_IN = 2**32
4
+
5
+ def initialize
6
+ @data = {}
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ def get(key)
11
+ _get(key)[1]
12
+ end
13
+
14
+ def set(key, value, expires_in: MAX_EXPIRES_IN)
15
+ @data[key] = { value: value, expires_at: now + expires_in }
16
+ value
17
+ end
18
+
19
+ def add(key, value, **args)
20
+ @mutex.synchronize do
21
+ return false if _get(key)[0]
22
+ set(key, value, **args)
23
+ return true
24
+ end
25
+ end
26
+
27
+ def fetch(key, **args)
28
+ @mutex.synchronize do
29
+ success, value = _get(key)
30
+ return value if success
31
+ value = yield
32
+ set key, value, **args
33
+ end
34
+ end
35
+
36
+ # remove everything that is expired to prevent leaking memory over time
37
+ def cleanup
38
+ @mutex.synchronize do
39
+ now = now()
40
+ @data.delete_if { |_key, info| info[:expires_at] < now }
41
+ true
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def _get(key)
48
+ return [false, nil] unless (info = @data[key])
49
+ return [false, nil] unless info[:expires_at] > now
50
+ [true, info[:value]]
51
+ end
52
+
53
+ def now
54
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
55
+ end
56
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ class ExpiringMemoryStore
3
+ VERSION = "1.0.0"
4
+ end
metadata CHANGED
@@ -1,69 +1,46 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: expiring_memory_store
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 2
9
- version: 0.1.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
10
5
  platform: ruby
11
- authors:
12
- - Matthew Rudy Jacobs
6
+ authors:
7
+ - Michael Grosser
13
8
  autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
-
17
- date: 2010-06-28 00:00:00 +02:00
18
- default_executable:
11
+ date: 2019-05-03 00:00:00.000000000 Z
19
12
  dependencies: []
20
-
21
13
  description:
22
- email: dunno@dunno.com
14
+ email: michael@grosser.it
23
15
  executables: []
24
-
25
16
  extensions: []
26
-
27
17
  extra_rdoc_files: []
28
-
29
- files:
18
+ files:
30
19
  - MIT-LICENSE
31
- - Rakefile
32
- - Readme.md
33
- - VERSION
34
- - expiring_memory_store.gemspec
35
- - init.rb
36
- - lib/active_support/cache/expiring_memory_store.rb
37
- - test/expiring_memory_store_test.rb
38
- has_rdoc: true
39
- homepage: http://github.com/matthewrudy/expiring_memory_store
40
- licenses: []
41
-
20
+ - lib/expiring_memory_store.rb
21
+ - lib/expiring_memory_store/version.rb
22
+ homepage: https://github.com/grosser/expiring_memory_store
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
42
26
  post_install_message:
43
- rdoc_options:
44
- - --charset=UTF-8
45
- require_paths:
27
+ rdoc_options: []
28
+ require_paths:
46
29
  - lib
47
- required_ruby_version: !ruby/object:Gem::Requirement
48
- requirements:
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
49
32
  - - ">="
50
- - !ruby/object:Gem::Version
51
- segments:
52
- - 0
53
- version: "0"
54
- required_rubygems_version: !ruby/object:Gem::Requirement
55
- requirements:
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
56
37
  - - ">="
57
- - !ruby/object:Gem::Version
58
- segments:
59
- - 0
60
- version: "0"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
61
40
  requirements: []
62
-
63
41
  rubyforge_project:
64
- rubygems_version: 1.3.6
42
+ rubygems_version: 2.7.6
65
43
  signing_key:
66
- specification_version: 3
67
- summary: Fallback when original is not present or somethings not right.
68
- test_files:
69
- - test/expiring_memory_store_test.rb
44
+ specification_version: 4
45
+ summary: Fast & Simple Ruby In-Memory Store with expiration
46
+ test_files: []
data/Rakefile DELETED
@@ -1,38 +0,0 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
-
5
- desc 'Default: run unit tests.'
6
- task :default => :test
7
-
8
- desc 'Test the expiring_memory_store plugin.'
9
- Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.pattern = 'test/**/*_test.rb'
12
- t.verbose = true
13
- end
14
-
15
- desc 'Generate documentation for the expiring_memory_store plugin.'
16
- Rake::RDocTask.new(:rdoc) do |rdoc|
17
- rdoc.rdoc_dir = 'rdoc'
18
- rdoc.title = 'ExpiringMemoryStore'
19
- rdoc.options << '--line-numbers' << '--inline-source'
20
- rdoc.rdoc_files.include('README')
21
- rdoc.rdoc_files.include('lib/**/*.rb')
22
- end
23
-
24
- begin
25
- require 'jeweler'
26
- project_name = 'expiring_memory_store'
27
- Jeweler::Tasks.new do |gem|
28
- gem.name = project_name
29
- gem.summary = "Fallback when original is not present or somethings not right."
30
- gem.email = "dunno@dunno.com"
31
- gem.homepage = "http://github.com/matthewrudy/#{project_name}"
32
- gem.authors = ["Matthew Rudy Jacobs"]
33
- end
34
-
35
- Jeweler::GemcutterTasks.new
36
- rescue LoadError
37
- puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
38
- end
data/Readme.md DELETED
@@ -1,28 +0,0 @@
1
- ExpiringMemoryStore
2
- ===================
3
-
4
- Expiring MemoryStore is a modification to the default Rails MemoryStore which adds the :expires_in functionality that is so useful with Memcache.
5
- Although this increases the memory overhead slightly,
6
- it ensures that caches can be expired easily, without restarting your webserver.
7
-
8
- Just add;
9
- config.cache_store = :expiring_memory_store
10
- to your environment.rb.
11
-
12
- Example
13
- =======
14
-
15
- >> Rails.cache.write('are you there?', :im_still_here, :expires_in => 30)
16
- => :im_still_here
17
-
18
- >> Rails.cache.read('are you there?')
19
- => :im_still_here
20
-
21
- wait 30 seconds
22
-
23
- >> Rails.cache.read('are you there?')
24
- => nil
25
-
26
- WICKED!!!
27
-
28
- Copyright (c) 2008 [Matthew Rudy Jacobs], released under the MIT license
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.2
@@ -1,43 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = %q{expiring_memory_store}
8
- s.version = "0.1.2"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Matthew Rudy Jacobs"]
12
- s.date = %q{2010-06-28}
13
- s.email = %q{dunno@dunno.com}
14
- s.files = [
15
- "MIT-LICENSE",
16
- "Rakefile",
17
- "Readme.md",
18
- "VERSION",
19
- "expiring_memory_store.gemspec",
20
- "init.rb",
21
- "lib/active_support/cache/expiring_memory_store.rb",
22
- "test/expiring_memory_store_test.rb"
23
- ]
24
- s.homepage = %q{http://github.com/matthewrudy/expiring_memory_store}
25
- s.rdoc_options = ["--charset=UTF-8"]
26
- s.require_paths = ["lib"]
27
- s.rubygems_version = %q{1.3.6}
28
- s.summary = %q{Fallback when original is not present or somethings not right.}
29
- s.test_files = [
30
- "test/expiring_memory_store_test.rb"
31
- ]
32
-
33
- if s.respond_to? :specification_version then
34
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
35
- s.specification_version = 3
36
-
37
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
38
- else
39
- end
40
- else
41
- end
42
- end
43
-
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'active_support/cache/expiring_memory_store'
@@ -1,33 +0,0 @@
1
- require 'active_support'
2
- module ActiveSupport
3
- module Cache
4
- # Like MemoryStore, but caches are expired after the period specified in the :expires_in option.
5
- class ExpiringMemoryStore < MemoryStore
6
-
7
- def read(name, options = nil)
8
- super
9
- value, expiry = @data[name]
10
- if expiry && expiry < Time.now
11
- delete(name)
12
- return nil
13
- end
14
- return value
15
- end
16
-
17
- def write(name, value, options = nil)
18
- super
19
- @data[name] = [value.freeze, expires_at(options)].freeze
20
- return value
21
- end
22
-
23
- private
24
-
25
- def expires_at(options)
26
- if expires_in = options && options[:expires_in]
27
- return expires_in.from_now if expires_in != 0
28
- end
29
- return nil
30
- end
31
- end
32
- end
33
- end
@@ -1,140 +0,0 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'mocha'
4
-
5
- gem 'activesupport', '>=2.3'
6
- require File.dirname(__FILE__) + '/../init'
7
-
8
- class CacheStoreTest < Test::Unit::TestCase
9
- def setup
10
- @cache = ActiveSupport::Cache.lookup_store(:expiring_memory_store)
11
- end
12
-
13
- def test_fetch_without_cache_miss
14
- @cache.stubs(:read).with('foo', {}).returns('bar')
15
- @cache.expects(:write).never
16
- assert_equal 'bar', @cache.fetch('foo') { 'baz' }
17
- end
18
-
19
- def test_fetch_with_cache_miss
20
- @cache.stubs(:read).with('foo', {}).returns(nil)
21
- @cache.expects(:write).with('foo', 'baz', {})
22
- assert_equal 'baz', @cache.fetch('foo') { 'baz' }
23
- end
24
-
25
- def test_fetch_with_forced_cache_miss
26
- @cache.expects(:read).never
27
- @cache.expects(:write).with('foo', 'bar', :force => true)
28
- @cache.fetch('foo', :force => true) { 'bar' }
29
- end
30
- end
31
-
32
- # Tests the base functionality that should be identical across all cache stores.
33
- module CacheStoreBehavior
34
- def test_should_read_and_write_strings
35
- @cache.write('foo', 'bar')
36
- assert_equal 'bar', @cache.read('foo')
37
- end
38
-
39
- def test_should_read_and_write_hash
40
- @cache.write('foo', {:a => "b"})
41
- assert_equal({:a => "b"}, @cache.read('foo'))
42
- end
43
-
44
- def test_should_read_and_write_nil
45
- @cache.write('foo', nil)
46
- assert_equal nil, @cache.read('foo')
47
- end
48
-
49
- def test_fetch_without_cache_miss
50
- @cache.write('foo', 'bar')
51
- assert_equal 'bar', @cache.fetch('foo') { 'baz' }
52
- end
53
-
54
- def test_fetch_with_cache_miss
55
- assert_equal 'baz', @cache.fetch('foo') { 'baz' }
56
- end
57
-
58
- def test_fetch_with_forced_cache_miss
59
- @cache.fetch('foo', :force => true) { 'bar' }
60
- end
61
-
62
- def test_increment
63
- @cache.write('foo', 1, :raw => true)
64
- assert_equal 1, @cache.read('foo', :raw => true).to_i
65
- assert_equal 2, @cache.increment('foo')
66
- assert_equal 2, @cache.read('foo', :raw => true).to_i
67
- assert_equal 3, @cache.increment('foo')
68
- assert_equal 3, @cache.read('foo', :raw => true).to_i
69
- end
70
-
71
- def test_decrement
72
- @cache.write('foo', 3, :raw => true)
73
- assert_equal 3, @cache.read('foo', :raw => true).to_i
74
- assert_equal 2, @cache.decrement('foo')
75
- assert_equal 2, @cache.read('foo', :raw => true).to_i
76
- assert_equal 1, @cache.decrement('foo')
77
- assert_equal 1, @cache.read('foo', :raw => true).to_i
78
- end
79
- end
80
-
81
- class ExpiringMemoryStoreTest < Test::Unit::TestCase
82
- def setup
83
- @cache = ActiveSupport::Cache.lookup_store(:expiring_memory_store)
84
- end
85
-
86
- include CacheStoreBehavior
87
-
88
- def test_store_objects_should_be_immutable
89
- @cache.write('foo', 'bar')
90
- assert_raise(ActiveSupport::FrozenObjectError) { @cache.read('foo').gsub!(/.*/, 'baz') }
91
- assert_equal 'bar', @cache.read('foo')
92
- end
93
-
94
- def test_by_default_values_should_not_expire
95
- time_now = Time.now
96
- Time.stubs(:now).returns(time_now)
97
-
98
- @cache.write('foo', 'bar')
99
- assert_equal 'bar', @cache.read('foo')
100
-
101
- Time.stubs(:now).returns(time_now + 5.years )
102
- assert_equal 'bar', @cache.read('foo')
103
- end
104
-
105
- def test_values_should_not_expire_if_expires_in_is_set_to_zero
106
- time_now = Time.now
107
- Time.stubs(:now).returns(time_now)
108
-
109
- @cache.write('foo', 'bar', :expires_in => 0)
110
- assert_equal 'bar', @cache.read('foo')
111
-
112
- Time.stubs(:now).returns(time_now + 5.years )
113
- assert_equal 'bar', @cache.read('foo')
114
- end
115
-
116
- def test_values_should_expire_is_param_is_set
117
- time_now = Time.now
118
- Time.stubs(:now).returns(time_now)
119
-
120
- @cache.write('foo', 'bar', :expires_in => 1.year)
121
- assert_equal 'bar', @cache.read('foo')
122
-
123
- Time.stubs(:now).returns(time_now + 5.years )
124
- assert_equal nil, @cache.read('foo')
125
- end
126
-
127
- def test_values_should_expire_when_the_param_tells_it_to
128
- time_now = Time.now
129
- Time.stubs(:now).returns(time_now)
130
-
131
- @cache.write('foo', 'bar', :expires_in => 1.year)
132
- assert_equal 'bar', @cache.read('foo')
133
-
134
- Time.stubs(:now).returns(time_now + 1.year - 1 )
135
- assert_equal 'bar', @cache.read('foo')
136
-
137
- Time.stubs(:now).returns(time_now + 1.year + 1 )
138
- assert_equal nil, @cache.read('foo')
139
- end
140
- end