mini_cache 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.
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