expiring_memory_store 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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