cachely 0.0.3 → 0.0.4
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/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
|