mini_cache 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mini_cache.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Derrick Reimer
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # MiniCache
2
+
3
+ MiniCache is a lightweight in-memory key-value store for Ruby objects.
4
+ This gem has no external dependencies and can be used in a Ruby project
5
+ (including Rails apps).
6
+
7
+ ## Motivation
8
+
9
+ It is common practice to cache certain values on an object that are
10
+ computationally expensive to obtain, such as a property that requires a
11
+ database query.
12
+
13
+ The simplest way to do this is by storing the value in an instance variable:
14
+
15
+ ```ruby
16
+ class Account
17
+ def calculate_balance
18
+ # Do something expensive.
19
+ end
20
+
21
+ def balance
22
+ @balance ||= self.calculate_balance
23
+ end
24
+ end
25
+ ```
26
+
27
+ While this method works in many scenarios, it fails when the value you
28
+ need to cache is:
29
+
30
+ - Either `nil` or `false`
31
+ - Dependent on a particular argument passed to the method
32
+
33
+ Here's a demonstration of how MiniCache solves this problem:
34
+
35
+ ```ruby
36
+ class Account
37
+ def lookup_role(user)
38
+ # Execute a database query to find the user's role.
39
+ end
40
+
41
+ def role(user)
42
+ # Perform the lookup once and cache the value. We can't use
43
+ #
44
+ # @role ||= lookup_user(user)
45
+ #
46
+ # because the value depends on the user argument. Also, the
47
+ # value could be nil if the user does not actually have a role.
48
+ # You can probably see how the solution could get pretty ugly.
49
+ # This is where MiniCache comes into play.
50
+ self.cache.get_or_set("role-#{user.id}") do
51
+ self.lookup_role(user)
52
+ end
53
+ end
54
+
55
+ def cache
56
+ @cache ||= MiniCache::Store.new
57
+ end
58
+ end
59
+ ```
60
+
61
+ The `#get_or_set` method works similarly to the `||=` operator, except it
62
+ knows how to handle `false` and `nil` values and it's keyed off of a unique string ID.
63
+ Problem solved!
64
+
65
+ ## Installation
66
+
67
+ Add this line to your application's Gemfile:
68
+
69
+ gem 'mini_cache'
70
+
71
+ And then execute:
72
+
73
+ $ bundle
74
+
75
+ Or install it yourself as:
76
+
77
+ $ gem install mini_cache
78
+
79
+ ## Usage
80
+
81
+ To create a new MiniCache store object, just initialize it:
82
+
83
+ ```ruby
84
+ store = MiniCache::Store.new
85
+
86
+ # Optionally pass in a Hash of data
87
+ store = MiniCache::Store.new(:name => "Derrick", :occupation => "Developer")
88
+ ```
89
+
90
+ Set and retrieve data using `#get` and `#set`:
91
+
92
+ ```ruby
93
+ # Pass in the value as an argument or block
94
+ store.set("age", 24)
95
+ store.set("birth_year") { 1988 }
96
+
97
+ store.get("birth_year")
98
+ => 1988
99
+ ```
100
+
101
+ Use the `#get_or_set` method to either set the value if it hasn't already been
102
+ set, or get the value that was already set.
103
+
104
+ ```ruby
105
+ store.set("birth_year") { 1988 }
106
+ => 1988
107
+
108
+ store.get_or_set("birth_year") { 1964 }
109
+ => 1988 # Did not overwrite previously set value
110
+ ```
111
+
112
+ Other convenience methods:
113
+
114
+ - `#set?(key)`: Checks to see if a value has been set for a given key
115
+ - `#unset(key)`: Removes a key-value pair for a given key.
116
+ - `#reset`: Clears the cache.
117
+ - `#load(hash)`: Loads a hash of data into the cache (appended to existing data).
118
+
119
+ ## Contributing
120
+
121
+ 1. Fork it
122
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
123
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
124
+ 4. Push to the branch (`git push origin my-new-feature`)
125
+ 5. Create new Pull Request
126
+
127
+ ## License
128
+
129
+ Copyright © 2012 Derrick Reimer
130
+
131
+ MIT License
132
+
133
+ Permission is hereby granted, free of charge, to any person obtaining
134
+ a copy of this software and associated documentation files (the
135
+ "Software"), to deal in the Software without restriction, including
136
+ without limitation the rights to use, copy, modify, merge, publish,
137
+ distribute, sublicense, and/or sell copies of the Software, and to
138
+ permit persons to whom the Software is furnished to do so, subject to
139
+ the following conditions:
140
+
141
+ The above copyright notice and this permission notice shall be
142
+ included in all copies or substantial portions of the Software.
143
+
144
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
145
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
146
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
147
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
148
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
149
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
150
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "lib"
7
+ t.pattern = "test/**/*_test.rb"
8
+ t.verbose = true
9
+ end
10
+
11
+ desc "Run tests"
12
+ task :default => :test
@@ -0,0 +1,145 @@
1
+ module MiniCache
2
+ class Store
3
+ include Enumerable
4
+
5
+ # Public: Returns the hash of key-value pairs.
6
+ attr_reader :data
7
+
8
+ # Public: Initializes a new TinyCache object.
9
+ #
10
+ # data - A Hash of key-value pairs (optional).
11
+ #
12
+ # Returns nothing.
13
+ def initialize(data = {})
14
+ @data = {}
15
+ self.load(data)
16
+ end
17
+
18
+ # Public: Retrieves the value for a given key.
19
+ #
20
+ # key - A String or Symbol representing the key.
21
+ #
22
+ # Returns the value set for the key; if nothing is
23
+ # set, returns nil.
24
+ def get(key)
25
+ check_key!(key)
26
+ @data[key.to_s]
27
+ end
28
+
29
+ # Public: Sets a value for a given key either as
30
+ # an argument or block.
31
+ #
32
+ # key - A String or Symbol representing the key.
33
+ # value - Any object that represents the value (optional).
34
+ # Not used if a block is given.
35
+ # block - A block of code that returns the value to set
36
+ # (optional).
37
+ #
38
+ # Examples
39
+ #
40
+ # cache.set("name", "Derrick")
41
+ # => "Derrick"
42
+ #
43
+ # cache.set("name") { "Joe" }
44
+ # => "Joe"
45
+ #
46
+ # Returns the value given.
47
+ def set(key, value = nil)
48
+ check_key!(key)
49
+ @data[key.to_s] = block_given? ? yield : value
50
+ end
51
+
52
+ # Public: Determines whether a value has been set for
53
+ # a given key.
54
+ #
55
+ # key - A String or Symbol representing the key.
56
+ #
57
+ # Returns a Boolean.
58
+ def set?(key)
59
+ check_key!(key)
60
+ @data.keys.include?(key.to_s)
61
+ end
62
+
63
+ # Public: Retrieves the value for a given key if it
64
+ # has already been set; otherwise, sets the value
65
+ # either as an argument or block.
66
+ #
67
+ # key - A String or Symbol representing the key.
68
+ # value - Any object that represents the value (optional).
69
+ # Not used if a block is given.
70
+ # block - A block of code that returns the value to set
71
+ # (optional).
72
+ #
73
+ # Examples
74
+ #
75
+ # cache.set("name", "Derrick")
76
+ # => "Derrick"
77
+ #
78
+ # cache.get_or_set("name", "Joe")
79
+ # => "Derrick"
80
+ #
81
+ # cache.get_or_set("occupation") { "Engineer" }
82
+ # => "Engineer"
83
+ #
84
+ # cache.get_or_set("occupation") { "Pilot" }
85
+ # => "Engineer"
86
+ #
87
+ # Returns the value.
88
+ def get_or_set(key, value = nil)
89
+ return get(key) if set?(key)
90
+ set(key, block_given? ? yield : value)
91
+ end
92
+
93
+ # Public: Removes the key-value pair from the cache
94
+ # for a given key.
95
+ #
96
+ # key - A String or Symbol representing the key.
97
+ #
98
+ # Returns the value.
99
+ def unset(key)
100
+ check_key!(key)
101
+ @data.delete(key.to_s)
102
+ end
103
+
104
+ # Public: Clears all key-value pairs.
105
+ #
106
+ # Returns nothing.
107
+ def reset
108
+ @data = {}
109
+ end
110
+
111
+ # Public: Iterates over all key-value pairs.
112
+ #
113
+ # block - A block of code that will be send the key
114
+ # and value of each pair.
115
+ #
116
+ # Yields the String key and value.
117
+ def each(&block)
118
+ @data.each { |k, v| yield(k, v) }
119
+ end
120
+
121
+ # Public: Loads a hash of data into the cache.
122
+ #
123
+ # data - A Hash of data with either String or Symbol keys.
124
+ #
125
+ # Returns nothing.
126
+ def load(data)
127
+ data.each do |key, value|
128
+ check_key!(key)
129
+ @data[key.to_s] = value
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ # Internal: Raises an error if the key is not a String
136
+ # or a Symbol.
137
+ #
138
+ # key - A key provided by the user.
139
+ def check_key!(key)
140
+ unless key.is_a?(String) || key.is_a?(Symbol)
141
+ raise TypeError, "key must be a String or Symbol"
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,3 @@
1
+ module MiniCache
2
+ VERSION = "1.0.0"
3
+ end
data/lib/mini_cache.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "mini_cache/version"
2
+ require "mini_cache/store"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mini_cache/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Derrick Reimer"]
6
+ gem.email = ["derrickreimer@gmail.com"]
7
+ gem.description = %q{A lightweight, in-memory cache for Ruby objects}
8
+ gem.summary = %q{MiniCache is a lightweight, in-memory key-value store for Ruby objects}
9
+ gem.homepage = "https://github.com/djreimer/mini_cache"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "mini_cache"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = MiniCache::VERSION
17
+
18
+ gem.add_development_dependency "shoulda-context"
19
+ end
@@ -0,0 +1,138 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class MiniCache::StoreTest < Test::Unit::TestCase
4
+ def setup
5
+ @store = MiniCache::Store.new
6
+ end
7
+
8
+ context "initialize" do
9
+ should "default to empty data" do
10
+ store = MiniCache::Store.new
11
+ assert_equal Hash.new, store.data
12
+ end
13
+
14
+ should "load seed data" do
15
+ data = { "name" => "Derrick" }
16
+ store = MiniCache::Store.new(data)
17
+ assert_equal data, store.data
18
+ end
19
+ end
20
+
21
+ context "#get" do
22
+ should "return a value if set" do
23
+ @store.set("name", "Derrick")
24
+ assert_equal "Derrick", @store.get("name")
25
+ end
26
+
27
+ should "return nil if not set" do
28
+ assert_nil @store.get("name")
29
+ end
30
+
31
+ should "raise a TypeError if key is not valid" do
32
+ assert_raises(TypeError) { @store.get([1, 2]) }
33
+ end
34
+ end
35
+
36
+ context "#set" do
37
+ should "accept the value as an argument" do
38
+ @store.set("name", "Derrick")
39
+ assert_equal "Derrick", @store.get("name")
40
+ end
41
+
42
+ should "accept the value as a block" do
43
+ @store.set("name") { "Derrick" }
44
+ assert_equal "Derrick", @store.get("name")
45
+ end
46
+
47
+ should "raise a TypeError if key is not valid" do
48
+ assert_raises(TypeError) { @store.set([1, 2], "foo") }
49
+ end
50
+ end
51
+
52
+ context "#set?" do
53
+ should "be true if key has been set" do
54
+ @store.set("name", "Derrick")
55
+ assert_equal true, @store.set?("name")
56
+ end
57
+
58
+ should "be false if key has not been set" do
59
+ assert_equal false, @store.set?("foobar")
60
+ end
61
+
62
+ should "raise a TypeError if key is not valid" do
63
+ assert_raises(TypeError) { @store.set?([1, 2]) }
64
+ end
65
+ end
66
+
67
+ context "#get_or_set" do
68
+ should "set the value if it hasn't already been set" do
69
+ @store.get_or_set("name", "Derrick")
70
+ assert_equal "Derrick", @store.get("name")
71
+ end
72
+
73
+ should "not set the value if it has already been set" do
74
+ @store.set("name", "Derrick")
75
+ @store.get_or_set("name", "Joe")
76
+ assert_equal "Derrick", @store.get("name")
77
+ end
78
+
79
+ should "return the value if not already set" do
80
+ assert_equal "Derrick", @store.get_or_set("name", "Derrick")
81
+ end
82
+
83
+ should "return the value if already set" do
84
+ @store.set("name", "Derrick")
85
+ assert_equal "Derrick", @store.get_or_set("name", "Joe")
86
+ end
87
+
88
+ should "accept the value as a block" do
89
+ @store.get_or_set("name") { "Joe" }
90
+ assert_equal "Joe", @store.get("name")
91
+ end
92
+
93
+ should "raise a TypeError if key is not valid" do
94
+ assert_raises(TypeError) { @store.get_or_set([1, 2], "foo") }
95
+ end
96
+ end
97
+
98
+ context "#unset" do
99
+ should "remove the key-value pair" do
100
+ @store.set("name", "Derrick")
101
+ @store.unset("name")
102
+ assert !@store.data.keys.include?("name")
103
+ end
104
+ end
105
+
106
+ context "#reset" do
107
+ should "remove all data" do
108
+ @store.set("name", "Derrick")
109
+ @store.reset
110
+ assert_equal Hash.new, @store.data
111
+ end
112
+ end
113
+
114
+ context "#load" do
115
+ should "append the data to the cache" do
116
+ @store.set("title", "Mr.")
117
+
118
+ data = { "name" => "Derrick", "occupation" => "Developer" }
119
+ @store.load(data)
120
+
121
+ all_data = { "title" => "Mr.",
122
+ "name" => "Derrick", "occupation" => "Developer" }
123
+ assert_equal all_data, @store.data
124
+ end
125
+
126
+ should "stringify the keys" do
127
+ data = { :name => "Derrick" }
128
+ @store.load(data)
129
+ stringified_data = { "name" => "Derrick" }
130
+ assert_equal stringified_data, @store.data
131
+ end
132
+
133
+ should "raise a TypeError if an invalid key is encountered" do
134
+ data = { [1, 2] => "Derrick" }
135
+ assert_raises(TypeError) { @store.load(data) }
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,4 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'mini_cache'
3
+ require 'minitest/autorun'
4
+ require 'shoulda-context'
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_cache
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Derrick Reimer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-07-21 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda-context
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :development
25
+ version_requirements: *id001
26
+ description: A lightweight, in-memory cache for Ruby objects
27
+ email:
28
+ - derrickreimer@gmail.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - LICENSE
39
+ - README.md
40
+ - Rakefile
41
+ - lib/mini_cache.rb
42
+ - lib/mini_cache/store.rb
43
+ - lib/mini_cache/version.rb
44
+ - mini_cache.gemspec
45
+ - test/mini_cache/store_test.rb
46
+ - test/test_helper.rb
47
+ homepage: https://github.com/djreimer/mini_cache
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.24
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: MiniCache is a lightweight, in-memory key-value store for Ruby objects
74
+ test_files:
75
+ - test/mini_cache/store_test.rb
76
+ - test/test_helper.rb