retriever 0.1.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 +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +40 -0
- data/Rakefile +20 -0
- data/lib/retriever/config.rb +74 -0
- data/lib/retriever/instruction.rb +63 -0
- data/lib/retriever/storage/ephemeral.rb +32 -0
- data/lib/retriever/storage/redis.rb +51 -0
- data/lib/retriever/version.rb +4 -0
- data/lib/retriever.rb +149 -0
- data/retriever.gemspec +34 -0
- data/spec/lib/custom_storage_spec.rb +107 -0
- data/spec/lib/default_spec.rb +87 -0
- data/spec/lib/redis_spec.rb +87 -0
- data/spec/spec_helper.rb +19 -0
- metadata +154 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2011 Nelvin Driz <ndriz@exist.com>
|
|
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.rdoc
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
= Retriever
|
|
2
|
+
|
|
3
|
+
A block result caching library.
|
|
4
|
+
|
|
5
|
+
== Installation
|
|
6
|
+
|
|
7
|
+
Either include to your <tt>Gemfile</tt> or install it.
|
|
8
|
+
|
|
9
|
+
# Bash
|
|
10
|
+
$ gem install retriever
|
|
11
|
+
|
|
12
|
+
# Gemfile
|
|
13
|
+
gem 'retriever'
|
|
14
|
+
|
|
15
|
+
== Rails Usage
|
|
16
|
+
|
|
17
|
+
Create file <tt>config/initializer/retriever.rb</tt> with your configuration.
|
|
18
|
+
|
|
19
|
+
=== Example <tt>retriever.rb</tt>
|
|
20
|
+
|
|
21
|
+
Retriever.config.storage(:memory)
|
|
22
|
+
Retreiver.catch! do
|
|
23
|
+
target :bone do |parameters|
|
|
24
|
+
...
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
=== Example Usage (Controller)
|
|
29
|
+
|
|
30
|
+
def index
|
|
31
|
+
@bone = Retriever.fetch(:bone)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
== {Documentation}[http://rubydoc.info/gems/retriever/frames]
|
|
35
|
+
|
|
36
|
+
== License
|
|
37
|
+
|
|
38
|
+
Copyright (c) 2011 Nelvin Driz.
|
|
39
|
+
|
|
40
|
+
Retriever is free software released under the MIT license.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
|
|
4
|
+
gem 'rdoc'
|
|
5
|
+
require 'rdoc/task'
|
|
6
|
+
|
|
7
|
+
RDoc::Task.new do |rdoc|
|
|
8
|
+
rdoc.rdoc_dir = 'doc'
|
|
9
|
+
rdoc.title = "Retriever #{Retriever::VERSION}"
|
|
10
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
11
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
12
|
+
rdoc.options << '--charset' << 'utf-8'
|
|
13
|
+
rdoc.options << '--line-numbers'
|
|
14
|
+
rdoc.main = 'README.rdoc'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
18
|
+
t.pattern = "./spec/**/*_spec.rb"
|
|
19
|
+
t.rspec_opts = ['--color']
|
|
20
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Retriever
|
|
2
|
+
# Configuration Namespace
|
|
3
|
+
module Config
|
|
4
|
+
class << self
|
|
5
|
+
# Boolean value to indicate whether or not key used for cache is encrypted or not.
|
|
6
|
+
attr_reader :encrypt
|
|
7
|
+
|
|
8
|
+
# Set encryption to boolean specified
|
|
9
|
+
#
|
|
10
|
+
# ==== Examples
|
|
11
|
+
#
|
|
12
|
+
# Retriever.config.encrypt = true
|
|
13
|
+
# Retriever.config.encrypt = false
|
|
14
|
+
#
|
|
15
|
+
def encrypt=(boolean)
|
|
16
|
+
require 'digest/sha1' if boolean
|
|
17
|
+
@encrypt = boolean
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Set storage handler to be used by retriever.
|
|
21
|
+
# This is a requirement to be set so that you
|
|
22
|
+
# can use retriever.
|
|
23
|
+
#
|
|
24
|
+
# ==== Storage Handlers (Built-in)
|
|
25
|
+
#
|
|
26
|
+
# * <tt>:ephemeral</tt> - Uses a ruby hash to store cache.
|
|
27
|
+
# * <tt>:redis</tt> - Use redis. (Requires <tt>redis</tt> gem.)
|
|
28
|
+
#
|
|
29
|
+
# ==== Custom Storage Handlers
|
|
30
|
+
# You can supply your own storage handler. In the class you supply,
|
|
31
|
+
# you need to specify the following methods:
|
|
32
|
+
#
|
|
33
|
+
# * <tt>set(key, value)</tt> - Used to set value in the cache store.
|
|
34
|
+
# * <tt>get(key)</tt> - Used to retrieve value from cache store.
|
|
35
|
+
# * <tt>delete(key)</tt> - Used to delete a record from cache store.
|
|
36
|
+
#
|
|
37
|
+
# ==== Examples
|
|
38
|
+
#
|
|
39
|
+
# Retriever.config.storage(:ephemeral) # Set storage to memory.
|
|
40
|
+
# Retriever.config.storage(:redis) # Set storage to redis.
|
|
41
|
+
#
|
|
42
|
+
# class CustomStorageHandler
|
|
43
|
+
# def set(key, value)
|
|
44
|
+
# ...
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# def get(key)
|
|
48
|
+
# ...
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# def delete(key)
|
|
52
|
+
# ...
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# Retriever.config.storage(CustomStorageHandler) # Set storage to a custom storage.
|
|
57
|
+
#
|
|
58
|
+
def storage(type)
|
|
59
|
+
if type.is_a? Class
|
|
60
|
+
::Retriever.storage = type.new if type.is_a?(Class)
|
|
61
|
+
else
|
|
62
|
+
case type
|
|
63
|
+
when :ephemeral
|
|
64
|
+
require File.join(File.dirname(__FILE__), 'storage', 'ephemeral')
|
|
65
|
+
::Retriever.storage = Retriever::Storage::Ephemeral.new
|
|
66
|
+
when :redis
|
|
67
|
+
require File.join(File.dirname(__FILE__), 'storage', 'redis')
|
|
68
|
+
::Retriever.storage = Retriever::Storage::Redis.new
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Retriever
|
|
2
|
+
# Instruction is used by retriever as an object that would
|
|
3
|
+
# store set attributes and options while specifying your
|
|
4
|
+
# targets in <tt>Retriever.catch!</tt>
|
|
5
|
+
#
|
|
6
|
+
class Instruction
|
|
7
|
+
# Key used by instruction for storage manipulation.
|
|
8
|
+
attr_accessor :key
|
|
9
|
+
|
|
10
|
+
# Block executed to retrieve value to be cached.
|
|
11
|
+
attr_accessor :block
|
|
12
|
+
|
|
13
|
+
# Instantiate Instruction
|
|
14
|
+
#
|
|
15
|
+
# ==== Parameters
|
|
16
|
+
#
|
|
17
|
+
# * <tt>:key</tt> - Key used to store and retrieve value from cache.
|
|
18
|
+
# * <tt>:options</tt> - Options defined in <tt>Retriever.catch!</tt>
|
|
19
|
+
# * <tt>:block</tt> - Block is the <tt>Proc</tt> object to be executed.
|
|
20
|
+
# Resulting value would be the cached.
|
|
21
|
+
#
|
|
22
|
+
def initialize(key, options, block)
|
|
23
|
+
@key = key
|
|
24
|
+
@block = block
|
|
25
|
+
@validity = options[:validity] || 365.days
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Checks if value in storage is expired before trying to retrieve
|
|
29
|
+
# a new value. Value in storage is returned if value is still valid.
|
|
30
|
+
#
|
|
31
|
+
# ==== Parameters
|
|
32
|
+
#
|
|
33
|
+
# * <tt>:storage</tt> - Instance of the storage handler.
|
|
34
|
+
# * <tt>:block_parameters</tt> - Arguments sent to the block if value
|
|
35
|
+
# in storage is expired.
|
|
36
|
+
#
|
|
37
|
+
def execute(storage, block_parameters)
|
|
38
|
+
if expired? then execute!(storage, block_parameters) else storage.get(key) end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Cache the result of the block. Just like <tt>execute</tt> but does not
|
|
42
|
+
# check cached value.
|
|
43
|
+
#
|
|
44
|
+
# ==== Parameters
|
|
45
|
+
#
|
|
46
|
+
# * <tt>:storage</tt> - Instance of the storage handler.
|
|
47
|
+
# * <tt>:block_parameters</tt> - Arguments sent to the block if value
|
|
48
|
+
# in storage is expired.
|
|
49
|
+
#
|
|
50
|
+
def execute!(storage, block_parameters)
|
|
51
|
+
@last_execution = Time.now
|
|
52
|
+
storage.set(key, block.call(*block_parameters))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
# Check if cached value is expired
|
|
57
|
+
#
|
|
58
|
+
def expired?
|
|
59
|
+
return true unless @last_execution
|
|
60
|
+
Time.now - @last_execution > @validity
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Storage handler namespace
|
|
2
|
+
module Retriever::Storage
|
|
3
|
+
# Storage handler for a Hash Backend
|
|
4
|
+
class Ephemeral
|
|
5
|
+
# Instance of Hash
|
|
6
|
+
attr_reader :storage
|
|
7
|
+
|
|
8
|
+
# Instantiate hash object as storage of cache.
|
|
9
|
+
#
|
|
10
|
+
def initialize
|
|
11
|
+
@storage = Hash.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Set record in hash.
|
|
15
|
+
#
|
|
16
|
+
def set(key, value)
|
|
17
|
+
storage[key] = value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get record from hash.
|
|
21
|
+
#
|
|
22
|
+
def get(key)
|
|
23
|
+
storage[key]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Delete record in hash.
|
|
27
|
+
#
|
|
28
|
+
def delete(key)
|
|
29
|
+
storage.delete(key)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'redis'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Retriever::Storage
|
|
5
|
+
# Storage handler for a Redis Backend
|
|
6
|
+
class Redis
|
|
7
|
+
# Instance of Redis
|
|
8
|
+
attr_reader :storage
|
|
9
|
+
|
|
10
|
+
# Instantiate Redis Connection (Default Settings Used)
|
|
11
|
+
#
|
|
12
|
+
def initialize
|
|
13
|
+
@storage = ::Redis.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Set record in storage.
|
|
17
|
+
# Serializes value to JSON if applicable.
|
|
18
|
+
#
|
|
19
|
+
def set(key, value)
|
|
20
|
+
value = serialize(value)
|
|
21
|
+
storage.set(key, value)
|
|
22
|
+
JSON.parse(value) rescue value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get record from storage.
|
|
26
|
+
# Deserialize value from JSON if applicable.
|
|
27
|
+
#
|
|
28
|
+
def get(key)
|
|
29
|
+
value = storage.get(key)
|
|
30
|
+
JSON.parse(value) rescue value
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Delete record from storage.
|
|
34
|
+
#
|
|
35
|
+
def delete(key)
|
|
36
|
+
storage.del key
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
# Serialize value by converting it to JSON if possible.
|
|
41
|
+
#
|
|
42
|
+
def serialize(value)
|
|
43
|
+
case value.class.to_s
|
|
44
|
+
when 'String' then value
|
|
45
|
+
else
|
|
46
|
+
value.to_json rescue value.to_s
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/retriever.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require File.join('retriever', 'version')
|
|
2
|
+
require File.join('retriever', 'config')
|
|
3
|
+
require File.join('retriever', 'instruction')
|
|
4
|
+
|
|
5
|
+
# Main Retriever Entry Point
|
|
6
|
+
module Retriever
|
|
7
|
+
class << self
|
|
8
|
+
# Instance of storage handler.
|
|
9
|
+
attr_accessor :storage
|
|
10
|
+
|
|
11
|
+
# Array of instructions.
|
|
12
|
+
attr_accessor :instructions
|
|
13
|
+
|
|
14
|
+
# Setup targets for retriever to cache.
|
|
15
|
+
#
|
|
16
|
+
# ==== Example
|
|
17
|
+
#
|
|
18
|
+
# Retriever.catch! do
|
|
19
|
+
# target :bone do
|
|
20
|
+
# 'wishbone'
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
def catch!(&block)
|
|
25
|
+
@instance_timestamp = Time.now
|
|
26
|
+
@instructions = Hash.new
|
|
27
|
+
self.instance_eval &block
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Setup a new <tt>target</tt> for retriever to catch.
|
|
31
|
+
#
|
|
32
|
+
# ==== Parameters
|
|
33
|
+
#
|
|
34
|
+
# * <tt>:name</tt> - Identifier used to call the block whenever you call
|
|
35
|
+
# <tt>fetch</tt> or <tt>fetch!</tt>.
|
|
36
|
+
# * <tt>:options</tt> - Options that modify basic instruction of simply
|
|
37
|
+
# caching the result of the block.
|
|
38
|
+
# * <tt>:block</tt> - The block to be executed with it's result being cached.
|
|
39
|
+
#
|
|
40
|
+
# ==== Options
|
|
41
|
+
#
|
|
42
|
+
# * <tt>:validity</tt> - Sets length of validity of a certain cache.
|
|
43
|
+
# After the cache expires, retriever simply executes the block again.
|
|
44
|
+
#
|
|
45
|
+
# target :bone, :validity => 10.minutes
|
|
46
|
+
#
|
|
47
|
+
# ==== Example
|
|
48
|
+
#
|
|
49
|
+
# Retriever.catch! do
|
|
50
|
+
# @counter = 0
|
|
51
|
+
# target :bone, :validity => 10.minutes do
|
|
52
|
+
# @counter += 1
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# Retriever.fetch(:bone) # => 1
|
|
57
|
+
# # After 10 minutes
|
|
58
|
+
# Retreiver.fetch(:bone) # => 2
|
|
59
|
+
#
|
|
60
|
+
def target(name, options = {}, &block)
|
|
61
|
+
key = keyify(name)
|
|
62
|
+
instructions[key] = Instruction.new(key, options, block)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Fetch data based upon the execution result of the corresponding
|
|
66
|
+
# instruction identified by the <tt>target_name</tt>. If data cached
|
|
67
|
+
# is still valid, that data would be returned.
|
|
68
|
+
#
|
|
69
|
+
# ==== Parameters
|
|
70
|
+
#
|
|
71
|
+
# * <tt>:target_name</tt> - The name used to identify the instruction.
|
|
72
|
+
# * <tt>:*block_parameters</tt> - Parameters passed on to the block
|
|
73
|
+
# that is part of the instruction.
|
|
74
|
+
#
|
|
75
|
+
# ==== Examples
|
|
76
|
+
#
|
|
77
|
+
# Retriever.fetch(:bone)
|
|
78
|
+
#
|
|
79
|
+
def fetch(target_name, *block_parameters)
|
|
80
|
+
key = keyify(target_name)
|
|
81
|
+
instruction = instructions[key]
|
|
82
|
+
instruction.execute(storage, block_parameters)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Fetch data based upon the execution result of the corresponding
|
|
86
|
+
# instruction identified by the <tt>target_name</tt>.
|
|
87
|
+
#
|
|
88
|
+
# ==== Parameters
|
|
89
|
+
#
|
|
90
|
+
# * <tt>:target_name</tt> - The name used to identify the instruction.
|
|
91
|
+
# * <tt>:*block_parameters</tt> - Parameters passed on to the block
|
|
92
|
+
# that is part of the instruction.
|
|
93
|
+
#
|
|
94
|
+
# ==== Examples
|
|
95
|
+
#
|
|
96
|
+
# Retriever.fetch(:bone)
|
|
97
|
+
#
|
|
98
|
+
def fetch!(target_name, *block_parameters)
|
|
99
|
+
key = keyify(target_name)
|
|
100
|
+
instruction = instructions[key]
|
|
101
|
+
instruction.execute!(storage, block_parameters)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Check if keys used for the cache store is encrypted or not encrypted.
|
|
105
|
+
# Basis of key is namespaced to retriever as well as timestamped.
|
|
106
|
+
# (Timestamp comes from the instance start.)
|
|
107
|
+
#
|
|
108
|
+
def encrypted?
|
|
109
|
+
config.encrypt || false
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns config namespace.
|
|
113
|
+
#
|
|
114
|
+
def config
|
|
115
|
+
Config
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Delete all cache. Automatically run on instance process termination.
|
|
119
|
+
#
|
|
120
|
+
def clean!
|
|
121
|
+
return unless instructions
|
|
122
|
+
instructions.keys.each do |key|
|
|
123
|
+
storage.delete(key)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
# Namespaces <tt>target_name</tt> to retriever as well as timestamped
|
|
129
|
+
# with the instance start timestamp. This key is also encrypted using
|
|
130
|
+
# SHA1 if set to be encrypted.
|
|
131
|
+
#
|
|
132
|
+
# ==== Examples
|
|
133
|
+
#
|
|
134
|
+
# To set encryption:
|
|
135
|
+
# Retriever.config.encrypt = true # Set encryption to true
|
|
136
|
+
# Retriever.config.encrypt = false # Set encryption to false
|
|
137
|
+
#
|
|
138
|
+
def keyify(target_name)
|
|
139
|
+
key = "Retriever::#{target_name}::#{@instance_timestamp}"
|
|
140
|
+
key = Digest::SHA1.hexdigest(key) if encrypted?
|
|
141
|
+
return key
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
at_exit do
|
|
148
|
+
Retriever.clean!
|
|
149
|
+
end
|
data/retriever.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "retriever/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'retriever'
|
|
7
|
+
s.version = Retriever::VERSION
|
|
8
|
+
s.authors = ['Nelvin Driz']
|
|
9
|
+
s.email = ['ndriz@exist.com']
|
|
10
|
+
s.homepage = "http://github.com/Keoven/Retriever"
|
|
11
|
+
s.summary = %q{Retriever is a block result caching library.}
|
|
12
|
+
s.description = %q{
|
|
13
|
+
Retriever allows caching of the result of a block. It currently supports ruby memory storage and redis.
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
s.rubyforge_project = 'retriever'
|
|
17
|
+
|
|
18
|
+
s.files = `git ls-files`.split("\n")
|
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
21
|
+
s.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
# Development
|
|
24
|
+
s.add_development_dependency 'rspec'
|
|
25
|
+
s.add_development_dependency 'redis'
|
|
26
|
+
s.add_development_dependency 'yajl-ruby'
|
|
27
|
+
s.add_development_dependency 'rails'
|
|
28
|
+
s.add_development_dependency 'timecop'
|
|
29
|
+
s.add_development_dependency 'ruby-debug19'
|
|
30
|
+
s.add_development_dependency 'simplecov'
|
|
31
|
+
|
|
32
|
+
# Runtime
|
|
33
|
+
s.add_runtime_dependency 'activesupport'
|
|
34
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class Custom
|
|
4
|
+
attr_reader :storage
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@storage = Hash.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def set(key, value)
|
|
11
|
+
storage[key] = value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(key)
|
|
15
|
+
storage[key]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def delete(key)
|
|
19
|
+
storage.delete(key)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe Retriever do
|
|
24
|
+
before do
|
|
25
|
+
Retriever.config.storage(Custom)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should fetch ball' do
|
|
29
|
+
Retriever.catch! do
|
|
30
|
+
target :ball do
|
|
31
|
+
'ball'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should memoize ball' do
|
|
38
|
+
Retriever.catch! do
|
|
39
|
+
target :ball do
|
|
40
|
+
'ball'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
Retriever.storage.should_receive(:set).once.and_return('ball')
|
|
44
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
45
|
+
Retriever.storage.stub!(:get).and_return('ball')
|
|
46
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'should only be valid in 10 minutes' do
|
|
50
|
+
now = Time.now
|
|
51
|
+
Timecop.freeze(now)
|
|
52
|
+
Retriever.catch! do
|
|
53
|
+
@counter = 0
|
|
54
|
+
target :ball, :validity => 10.minutes do
|
|
55
|
+
@counter += 1
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
59
|
+
Timecop.freeze(now + 5.minutes)
|
|
60
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
61
|
+
Timecop.freeze(now + 10.minutes)
|
|
62
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
63
|
+
Timecop.freeze(now + 10.minutes + 1.second)
|
|
64
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
65
|
+
Timecop.freeze(now + 15.minutes)
|
|
66
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
67
|
+
Timecop.freeze(now)
|
|
68
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
69
|
+
Timecop.freeze(now + 30.minutes)
|
|
70
|
+
Retriever.fetch(:ball).should be_eql(3)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'should be able to pass parameters' do
|
|
74
|
+
Retriever.catch! do
|
|
75
|
+
target :ball do |arg1, arg2|
|
|
76
|
+
arg1 + arg2
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
Retriever.fetch(:ball, 1, 2).should be_eql(3)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'should be able to fetch forcefully' do
|
|
84
|
+
now = Time.now
|
|
85
|
+
Timecop.freeze(now)
|
|
86
|
+
Retriever.catch! do
|
|
87
|
+
@counter = 0
|
|
88
|
+
target :ball, :validity => 10.minutes do
|
|
89
|
+
@counter += 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
93
|
+
Retriever.fetch!(:ball).should be_eql(2)
|
|
94
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'should be encrypted' do
|
|
98
|
+
Retriever.config.encrypt = true
|
|
99
|
+
Retriever.should be_encrypted
|
|
100
|
+
Retriever.catch! do
|
|
101
|
+
target :ball do
|
|
102
|
+
'ball'
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Retriever do
|
|
4
|
+
before do
|
|
5
|
+
Retriever.config.storage(:ephemeral)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should fetch ball' do
|
|
9
|
+
Retriever.catch! do
|
|
10
|
+
target :ball do
|
|
11
|
+
'ball'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should memoize ball' do
|
|
18
|
+
Retriever.catch! do
|
|
19
|
+
target :ball do
|
|
20
|
+
'ball'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
Retriever.storage.should_receive(:set).once.and_return('ball')
|
|
24
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
25
|
+
Retriever.storage.stub!(:get).and_return('ball')
|
|
26
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should only be valid in 10 minutes' do
|
|
30
|
+
now = Time.now
|
|
31
|
+
Timecop.freeze(now)
|
|
32
|
+
Retriever.catch! do
|
|
33
|
+
@counter = 0
|
|
34
|
+
target :ball, :validity => 10.minutes do
|
|
35
|
+
@counter += 1
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
39
|
+
Timecop.freeze(now + 5.minutes)
|
|
40
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
41
|
+
Timecop.freeze(now + 10.minutes)
|
|
42
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
43
|
+
Timecop.freeze(now + 10.minutes + 1.second)
|
|
44
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
45
|
+
Timecop.freeze(now + 15.minutes)
|
|
46
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
47
|
+
Timecop.freeze(now)
|
|
48
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
49
|
+
Timecop.freeze(now + 30.minutes)
|
|
50
|
+
Retriever.fetch(:ball).should be_eql(3)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should be able to pass parameters' do
|
|
54
|
+
Retriever.catch! do
|
|
55
|
+
target :ball do |arg1, arg2|
|
|
56
|
+
arg1 + arg2
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Retriever.fetch(:ball, 1, 2).should be_eql(3)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should be able to fetch forcefully' do
|
|
64
|
+
now = Time.now
|
|
65
|
+
Timecop.freeze(now)
|
|
66
|
+
Retriever.catch! do
|
|
67
|
+
@counter = 0
|
|
68
|
+
target :ball, :validity => 10.minutes do
|
|
69
|
+
@counter += 1
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
Retriever.fetch(:ball).should be_eql(1)
|
|
73
|
+
Retriever.fetch!(:ball).should be_eql(2)
|
|
74
|
+
Retriever.fetch(:ball).should be_eql(2)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should be encrypted' do
|
|
78
|
+
Retriever.config.encrypt = true
|
|
79
|
+
Retriever.should be_encrypted
|
|
80
|
+
Retriever.catch! do
|
|
81
|
+
target :ball do
|
|
82
|
+
'ball'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Retriever do
|
|
4
|
+
before do
|
|
5
|
+
Retriever.config.storage(:redis)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should fetch ball' do
|
|
9
|
+
Retriever.catch! do
|
|
10
|
+
target :ball do
|
|
11
|
+
'ball'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should memoize ball' do
|
|
18
|
+
Retriever.catch! do
|
|
19
|
+
target :ball do
|
|
20
|
+
'ball'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
Retriever.storage.should_receive(:set).once.and_return('ball')
|
|
24
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
25
|
+
Retriever.storage.stub!(:get).and_return('ball')
|
|
26
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should only be valid in 10 minutes' do
|
|
30
|
+
now = Time.now
|
|
31
|
+
Timecop.freeze(now)
|
|
32
|
+
Retriever.catch! do
|
|
33
|
+
@counter = 0
|
|
34
|
+
target :ball, :validity => 10.minutes do
|
|
35
|
+
@counter += 1
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
Retriever.fetch(:ball).should be_eql('1')
|
|
39
|
+
Timecop.freeze(now + 5.minutes)
|
|
40
|
+
Retriever.fetch(:ball).should be_eql('1')
|
|
41
|
+
Timecop.freeze(now + 10.minutes)
|
|
42
|
+
Retriever.fetch(:ball).should be_eql('1')
|
|
43
|
+
Timecop.freeze(now + 10.minutes + 1.second)
|
|
44
|
+
Retriever.fetch(:ball).should be_eql('2')
|
|
45
|
+
Timecop.freeze(now + 15.minutes)
|
|
46
|
+
Retriever.fetch(:ball).should be_eql('2')
|
|
47
|
+
Timecop.freeze(now)
|
|
48
|
+
Retriever.fetch(:ball).should be_eql('2')
|
|
49
|
+
Timecop.freeze(now + 30.minutes)
|
|
50
|
+
Retriever.fetch(:ball).should be_eql('3')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should be able to pass parameters' do
|
|
54
|
+
Retriever.catch! do
|
|
55
|
+
target :ball do |arg1, arg2|
|
|
56
|
+
arg1 + arg2
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Retriever.fetch(:ball, 1, 2).should be_eql('3')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should be able to fetch forcefully' do
|
|
64
|
+
now = Time.now
|
|
65
|
+
Timecop.freeze(now)
|
|
66
|
+
Retriever.catch! do
|
|
67
|
+
@counter = 0
|
|
68
|
+
target :ball, :validity => 10.minutes do
|
|
69
|
+
@counter += 1
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
Retriever.fetch(:ball).should be_eql('1')
|
|
73
|
+
Retriever.fetch!(:ball).should be_eql('2')
|
|
74
|
+
Retriever.fetch(:ball).should be_eql('2')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should be encrypted' do
|
|
78
|
+
Retriever.config.encrypt = true
|
|
79
|
+
Retriever.should be_encrypted
|
|
80
|
+
Retriever.catch! do
|
|
81
|
+
target :ball do
|
|
82
|
+
'ball'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
Retriever.fetch(:ball).should be_eql('ball')
|
|
86
|
+
end
|
|
87
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'simplecov'
|
|
2
|
+
SimpleCov.start do
|
|
3
|
+
add_filter '/spec/'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
require File.join(
|
|
7
|
+
File.dirname(__FILE__),
|
|
8
|
+
'..', 'lib', 'retriever')
|
|
9
|
+
require 'timecop'
|
|
10
|
+
require 'active_support'
|
|
11
|
+
require 'active_support/time'
|
|
12
|
+
|
|
13
|
+
RSpec.configure do |config|
|
|
14
|
+
[:all, :each].each do |x|
|
|
15
|
+
config.after(x) do
|
|
16
|
+
Retriever.clean!
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: retriever
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Nelvin Driz
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-09-25 00:00:00.000000000Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: rspec
|
|
16
|
+
requirement: &70342454584380 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *70342454584380
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: redis
|
|
27
|
+
requirement: &70342454583340 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *70342454583340
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: yajl-ruby
|
|
38
|
+
requirement: &70342454582340 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *70342454582340
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: rails
|
|
49
|
+
requirement: &70342454580180 !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ! '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: *70342454580180
|
|
58
|
+
- !ruby/object:Gem::Dependency
|
|
59
|
+
name: timecop
|
|
60
|
+
requirement: &70342454579060 !ruby/object:Gem::Requirement
|
|
61
|
+
none: false
|
|
62
|
+
requirements:
|
|
63
|
+
- - ! '>='
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0'
|
|
66
|
+
type: :development
|
|
67
|
+
prerelease: false
|
|
68
|
+
version_requirements: *70342454579060
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: ruby-debug19
|
|
71
|
+
requirement: &70342454578320 !ruby/object:Gem::Requirement
|
|
72
|
+
none: false
|
|
73
|
+
requirements:
|
|
74
|
+
- - ! '>='
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: *70342454578320
|
|
80
|
+
- !ruby/object:Gem::Dependency
|
|
81
|
+
name: simplecov
|
|
82
|
+
requirement: &70342454577260 !ruby/object:Gem::Requirement
|
|
83
|
+
none: false
|
|
84
|
+
requirements:
|
|
85
|
+
- - ! '>='
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
type: :development
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: *70342454577260
|
|
91
|
+
- !ruby/object:Gem::Dependency
|
|
92
|
+
name: activesupport
|
|
93
|
+
requirement: &70342454576500 !ruby/object:Gem::Requirement
|
|
94
|
+
none: false
|
|
95
|
+
requirements:
|
|
96
|
+
- - ! '>='
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
type: :runtime
|
|
100
|
+
prerelease: false
|
|
101
|
+
version_requirements: *70342454576500
|
|
102
|
+
description: ! "\n Retriever allows caching of the result of a block. It currently
|
|
103
|
+
supports ruby memory storage and redis.\n "
|
|
104
|
+
email:
|
|
105
|
+
- ndriz@exist.com
|
|
106
|
+
executables: []
|
|
107
|
+
extensions: []
|
|
108
|
+
extra_rdoc_files: []
|
|
109
|
+
files:
|
|
110
|
+
- .gitignore
|
|
111
|
+
- Gemfile
|
|
112
|
+
- LICENSE.txt
|
|
113
|
+
- README.rdoc
|
|
114
|
+
- Rakefile
|
|
115
|
+
- lib/retriever.rb
|
|
116
|
+
- lib/retriever/config.rb
|
|
117
|
+
- lib/retriever/instruction.rb
|
|
118
|
+
- lib/retriever/storage/ephemeral.rb
|
|
119
|
+
- lib/retriever/storage/redis.rb
|
|
120
|
+
- lib/retriever/version.rb
|
|
121
|
+
- retriever.gemspec
|
|
122
|
+
- spec/lib/custom_storage_spec.rb
|
|
123
|
+
- spec/lib/default_spec.rb
|
|
124
|
+
- spec/lib/redis_spec.rb
|
|
125
|
+
- spec/spec_helper.rb
|
|
126
|
+
homepage: http://github.com/Keoven/Retriever
|
|
127
|
+
licenses: []
|
|
128
|
+
post_install_message:
|
|
129
|
+
rdoc_options: []
|
|
130
|
+
require_paths:
|
|
131
|
+
- lib
|
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
133
|
+
none: false
|
|
134
|
+
requirements:
|
|
135
|
+
- - ! '>='
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
|
+
none: false
|
|
140
|
+
requirements:
|
|
141
|
+
- - ! '>='
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
requirements: []
|
|
145
|
+
rubyforge_project: retriever
|
|
146
|
+
rubygems_version: 1.8.10
|
|
147
|
+
signing_key:
|
|
148
|
+
specification_version: 3
|
|
149
|
+
summary: Retriever is a block result caching library.
|
|
150
|
+
test_files:
|
|
151
|
+
- spec/lib/custom_storage_spec.rb
|
|
152
|
+
- spec/lib/default_spec.rb
|
|
153
|
+
- spec/lib/redis_spec.rb
|
|
154
|
+
- spec/spec_helper.rb
|