cachely 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -2
- data/lib/cachely/mechanics.rb +32 -10
- data/lib/cachely/version.rb +1 -1
- data/test/models/dummy_class.rb +5 -0
- data/test/unit/cachely_test.rb +12 -0
- data/test/unit/mechanics_test.rb +20 -6
- metadata +9 -9
data/README.md
CHANGED
@@ -64,8 +64,8 @@ will still work, it will cache both the instance method foo and the class method
|
|
64
64
|
keyed locations, of course. To specify instance or class method, do
|
65
65
|
|
66
66
|
cachely :foo, :type => "instance"
|
67
|
-
cachely :foo, :type => "class"
|
68
|
-
|
67
|
+
cachely :foo, :type => "class"
|
68
|
+
|
69
69
|
Cachely is able to figure out whether or not to use the cache by looking at the arguments you pass in.
|
70
70
|
Generally, if your arguments are the same, and the object you're calling the method on is the same(Cachely uses
|
71
71
|
the to_json method on the object you call the method on to determine this), then it assumes the result
|
@@ -74,6 +74,11 @@ will be the same and uses the cached result.
|
|
74
74
|
Cachely expects that any objects you return or pass in to methods as arguments have to_json methods. Without
|
75
75
|
this, it won't work.
|
76
76
|
|
77
|
+
Because of this, you can expire method results by their "signatures", that is, the object the method is being called on,
|
78
|
+
the method name, and it's arguments. To expire a cached result by this signature, call
|
79
|
+
|
80
|
+
Cachely::Mechanics.expire(object, method, *args)
|
81
|
+
|
77
82
|
CAVEAT: Do NOT use Cachely for functions that depend on time of day or random numbers, as these are inherently uncachable.
|
78
83
|
If you check the tests out, you'll see random number functions are used exhaustively to test the caching ability of cachely,
|
79
84
|
because we know the function wasn't called if the second call yields the same number.
|
data/lib/cachely/mechanics.rb
CHANGED
@@ -37,7 +37,7 @@ module Cachely
|
|
37
37
|
end
|
38
38
|
return @redis
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Defines the method using my hacky eval script. So far, this is the only way I've found that
|
42
42
|
# allows me to define variable argument length definitions. If you have a better idea, please
|
43
43
|
# refactor it!
|
@@ -48,34 +48,56 @@ module Cachely
|
|
48
48
|
context = (is_class_method ? klazz : klazz.new)
|
49
49
|
args_str = context.method("#{name.to_s}_old".to_sym).parameters.map { |k| k.last}.join(',')
|
50
50
|
args_to_use_in_def = args_str.empty? ? "" : "," + args_str
|
51
|
+
time_exp_str = time_to_expire_in_s.nil? ? ",nil" : ",time_to_expire_in_s"
|
51
52
|
eval("klazz.define_#{is_class_method ? "singleton_" : ""}method(:#{name}) do #{args_str.empty? ? "" : "|#{args_str}|"}; " +
|
52
|
-
"result = Cachely::Mechanics.get(context,:#{name}#{args_to_use_in_def});" +
|
53
|
+
"result = Cachely::Mechanics.get(context,:#{name}#{time_exp_str}#{args_to_use_in_def});" +
|
53
54
|
"return result.first if result.is_a?(Array);" +
|
54
55
|
"result = context.send(:#{"#{name.to_s}_old"}#{args_to_use_in_def});" +
|
55
|
-
"Cachely::Mechanics.store(context,:#{"#{name.to_s}"}, result#{
|
56
|
+
"Cachely::Mechanics.store(context,:#{"#{name.to_s}"}, result#{time_exp_str}#{args_to_use_in_def});" +
|
56
57
|
"return result;" +
|
57
58
|
"end"
|
58
59
|
)
|
59
60
|
end
|
60
|
-
|
61
|
+
|
62
|
+
# Force-expires a result to a method with this signature given by obj, method, args.
|
63
|
+
#
|
64
|
+
# @obj [Object] the object you're calling method on
|
65
|
+
# @method [String,Symbol] the method name
|
66
|
+
# @args The arguments of the method
|
67
|
+
# @return The original response of the method back, whatever it may be.
|
68
|
+
def self.expire(obj, method, *args)
|
69
|
+
key = redis_key(obj, method, *args)
|
70
|
+
result = get(obj,method,1,*args)
|
71
|
+
redis.del(key)
|
72
|
+
return result
|
73
|
+
end
|
74
|
+
|
61
75
|
# Gets a cached response to a method.
|
62
76
|
#
|
77
|
+
# @obj [Object] the object you're calling method on
|
78
|
+
# @time_to_exp_in_s [Fixnum] num of seconds to set expire on key. Needs to be reset.
|
63
79
|
# @method [String,Symbol] the method name
|
64
80
|
# @args The arguments of the method
|
65
81
|
# @return The original response of the method back, whatever it may be.
|
66
|
-
def self.get(obj, method, *args)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
82
|
+
def self.get(obj, method, time_to_exp_in_s, *args)
|
83
|
+
key = redis_key(obj, method, *args)
|
84
|
+
result = redis.get(key)
|
85
|
+
if result
|
86
|
+
redis.expire(key, time_to_exp_in_s) if time_to_exp_in_s #reset the expiry
|
87
|
+
#return an array, bc if the result stored was nil, it looks the same as if
|
88
|
+
#we got no result back(which we would return nil) so we differentiate by putting
|
89
|
+
#our return value always in an array. Easy to check.
|
90
|
+
return [map_s_to_param(result)]
|
91
|
+
end
|
72
92
|
end
|
73
93
|
|
74
94
|
# Stores the result in it's proper cached location on redis by getting the redis key and parsing
|
75
95
|
# The result into a storable string, mostly made up of recursive json.
|
76
96
|
#
|
97
|
+
# @obj [Object] object you call method on
|
77
98
|
# @method [Symbol] Method name
|
78
99
|
# @result Anything, really.
|
100
|
+
# @time_to_exp_in_s time in seconds before it expires
|
79
101
|
# @args Arguments of the method
|
80
102
|
# @return [String] Should be "Ok" or something similar.
|
81
103
|
def self.store(obj, method, result, time_to_exp_in_sec, *args)
|
data/lib/cachely/version.rb
CHANGED
data/test/models/dummy_class.rb
CHANGED
@@ -3,6 +3,7 @@ class DummyClass
|
|
3
3
|
attr_accessor :random_no
|
4
4
|
|
5
5
|
cachely :cache_expiry, time_to_expiry: 1.seconds
|
6
|
+
cachely :cache_expiry_3, time_to_expiry: 3.seconds
|
6
7
|
|
7
8
|
cachely :class_diff, time_to_expiry: 3.minutes
|
8
9
|
|
@@ -127,6 +128,10 @@ class DummyClass
|
|
127
128
|
rand(500)
|
128
129
|
end
|
129
130
|
|
131
|
+
def cache_expiry_3
|
132
|
+
rand(500)
|
133
|
+
end
|
134
|
+
|
130
135
|
def cache_expiry
|
131
136
|
rand(500)
|
132
137
|
end
|
data/test/unit/cachely_test.rb
CHANGED
@@ -142,5 +142,17 @@ class CachelyTest < BaseTest
|
|
142
142
|
sleep(3)
|
143
143
|
assert_not_equal(old, obj.cache_expiry)
|
144
144
|
end
|
145
|
+
|
146
|
+
test "cache with 3 s expiry doesnt expire in 3 s if we keep hitting it" do
|
147
|
+
obj = DummyClass.new
|
148
|
+
old = obj.cache_expiry_3
|
149
|
+
5.times do #should still be present on fourth and fifth times
|
150
|
+
sleep(1)
|
151
|
+
assert_equal(old, obj.cache_expiry_3)
|
152
|
+
end
|
145
153
|
|
154
|
+
sleep(4)
|
155
|
+
assert_not_equal(old, obj.cache_expiry_3)
|
156
|
+
end
|
157
|
+
|
146
158
|
end
|
data/test/unit/mechanics_test.rb
CHANGED
@@ -5,12 +5,12 @@ require_relative 'base_test.rb'
|
|
5
5
|
class MechanicsTest < BaseTest
|
6
6
|
test "get/store array" do
|
7
7
|
Cachely::Mechanics.store(DummyClass,:foo, [1,2,3], nil, [3,4])
|
8
|
-
assert_equal(0, [1,2,3] <=> Cachely::Mechanics.get(DummyClass,:foo, [3,4]).first)
|
8
|
+
assert_equal(0, [1,2,3] <=> Cachely::Mechanics.get(DummyClass,:foo,nil, [3,4]).first)
|
9
9
|
end
|
10
10
|
|
11
11
|
test "get/store hash" do
|
12
12
|
Cachely::Mechanics.store(DummyClass,:foo, {:bar => "baz"}, nil, [3,4])
|
13
|
-
respawned = Cachely::Mechanics.get(DummyClass,:foo, [3,4]).first
|
13
|
+
respawned = Cachely::Mechanics.get(DummyClass,:foo,nil, [3,4]).first
|
14
14
|
assert_equal(1, respawned.keys.size)
|
15
15
|
assert_equal("baz", respawned[:bar])
|
16
16
|
end
|
@@ -18,7 +18,7 @@ class MechanicsTest < BaseTest
|
|
18
18
|
test "get/store orm" do
|
19
19
|
d = DummyModel.create!(:attr_1 => 1, :attr_2 => 3)
|
20
20
|
Cachely::Mechanics.store(DummyClass,:foo, d,nil,[3,4])
|
21
|
-
respawned = Cachely::Mechanics.get(DummyClass,:foo, [3,4]).first
|
21
|
+
respawned = Cachely::Mechanics.get(DummyClass,:foo, nil, [3,4]).first
|
22
22
|
assert_equal(d.id, respawned.id)
|
23
23
|
assert_equal(d.attr_1, respawned.attr_1)
|
24
24
|
assert_equal(d.attr_2, respawned.attr_2)
|
@@ -27,15 +27,29 @@ class MechanicsTest < BaseTest
|
|
27
27
|
test "get/store to_json" do
|
28
28
|
d = DummyClass.new
|
29
29
|
Cachely::Mechanics.store(DummyClass,:foo, d,nil, [3,4])
|
30
|
-
respawned = Cachely::Mechanics.get(DummyClass,:foo, [3,4]).first
|
30
|
+
respawned = Cachely::Mechanics.get(DummyClass,:foo, nil, [3,4]).first
|
31
31
|
assert_equal(d.random_no, respawned.random_no)
|
32
32
|
end
|
33
33
|
|
34
34
|
test "get/store primitive" do
|
35
35
|
[3, "3", true, false, nil, 3.5, :stuff].each do |result|
|
36
36
|
Cachely::Mechanics.store(DummyClass,:foo, result, nil, [3,4])
|
37
|
-
respawned = Cachely::Mechanics.get(DummyClass,:foo, [3,4]).first
|
37
|
+
respawned = Cachely::Mechanics.get(DummyClass,:foo, nil, [3,4]).first
|
38
38
|
assert_equal(result, respawned)
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
|
+
test "expire method works" do
|
43
|
+
old = DummyClass.class_fixnum
|
44
|
+
assert_equal(old, DummyClass.class_fixnum)
|
45
|
+
Cachely::Mechanics.expire(DummyClass, :class_fixnum)
|
46
|
+
assert_not_equal(old, DummyClass.class_fixnum)
|
47
|
+
|
48
|
+
obj = DummyClass.new
|
49
|
+
old = obj.instance_fixnum_one(5)
|
50
|
+
assert_equal(old, obj.instance_fixnum_one(5))
|
51
|
+
Cachely::Mechanics.expire(obj, :instance_fixnum_one,5)
|
52
|
+
assert_not_equal(old, obj.instance_fixnum_one(5))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cachely
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2013-03-04 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &9945220 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *9945220
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hiredis
|
27
|
-
requirement: &
|
27
|
+
requirement: &9958720 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.4.5
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *9958720
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: em-synchrony
|
38
|
-
requirement: &
|
38
|
+
requirement: &9954440 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *9954440
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: json
|
49
|
-
requirement: &
|
49
|
+
requirement: &9960660 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *9960660
|
58
58
|
description: Transparently cache the results of methods using redis.
|
59
59
|
email:
|
60
60
|
- jordanmprince@gmail.com
|