dcache 0.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/.autotest +23 -0
- data/.yardopts +12 -0
- data/History.rdoc +3 -0
- data/Manifest.txt +13 -0
- data/README.rdoc +109 -0
- data/Rakefile +77 -0
- data/ext/dcache_ary/dcache_ary.c +276 -0
- data/ext/dcache_ary/extconf.rb +4 -0
- data/ext/dcache_list/dcache_list.c +460 -0
- data/ext/dcache_list/extconf.rb +4 -0
- data/lib/dcache.rb +243 -0
- data/spec/dcache_spec.rb +171 -0
- data/yard/reader.rb +33 -0
- metadata +110 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/.yardopts
ADDED
data/History.rdoc
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
= DCache
|
2
|
+
|
3
|
+
* http://github.com/DirtYiCE/DCache
|
4
|
+
* Documentation (git): http://yardoc.org/docs/DirtYiCE-DCache/file:README.rdoc
|
5
|
+
* Documentation (release): http://dirty-ice.web44.net/doc/dcache
|
6
|
+
|
7
|
+
== DESCRIPTION:
|
8
|
+
|
9
|
+
A simple caching gem.
|
10
|
+
|
11
|
+
== FEATURES:
|
12
|
+
|
13
|
+
* Two different backends
|
14
|
+
* Cached entries can have a timeout
|
15
|
+
* Cache structures are either allocated at once (+:ary+ backend) or grow/shrink
|
16
|
+
dynamically (+:list+ backend)
|
17
|
+
* Backends are written in C for speed
|
18
|
+
|
19
|
+
== PROBLEMS:
|
20
|
+
|
21
|
+
* Should use #hash for faster comparisions.
|
22
|
+
|
23
|
+
== SYNOPSIS:
|
24
|
+
|
25
|
+
Initialize a cache using +:ary+ backend, and storing 1000 values
|
26
|
+
require "dcache"
|
27
|
+
c = DCache.new :ary, 1000
|
28
|
+
Put some value into it:
|
29
|
+
c.add :key, "value"
|
30
|
+
c.add :key2, 34
|
31
|
+
c.add [1, 2, 3], [4, 5, 6] # key/value can be any ruby type
|
32
|
+
c.add :timeout, "of course", 60 # will be purged after 1 minute
|
33
|
+
Retrieve values:
|
34
|
+
c.get :key # => "value"
|
35
|
+
c.get :not_stored # => nil
|
36
|
+
c.get :not_stored, "default value" # => "default_value"
|
37
|
+
c.get(:not_stored) { puts "Ouch" } # will print "Ouch"
|
38
|
+
c.get_or_add :foo, 3 # => 3
|
39
|
+
c.get :foo # => 3
|
40
|
+
c.get_or_raise :not_stored # will raise ArgumentError
|
41
|
+
c.get :timeout # => "of course"
|
42
|
+
sleep 60
|
43
|
+
c.get :timeout # => nil
|
44
|
+
Remove an element:
|
45
|
+
c.delete :key
|
46
|
+
c.get :key # => nil
|
47
|
+
c.get :key2 # => 34
|
48
|
+
Clear cache:
|
49
|
+
c.clear
|
50
|
+
c.get :key2 # => nil
|
51
|
+
Get statistics:
|
52
|
+
[c.hits, c.misses] # => [4, 8]
|
53
|
+
# also [c.stored, c.removed] with :list backend
|
54
|
+
|
55
|
+
|
56
|
+
== REQUIREMENTS:
|
57
|
+
|
58
|
+
* Ruby 1.9 (may work with 1.8)
|
59
|
+
* Development dependencies:
|
60
|
+
* Hoe-2.4
|
61
|
+
* hoe-git-1.3.0
|
62
|
+
* RSpec-1.2
|
63
|
+
* yard (git version) (to generate docs)
|
64
|
+
|
65
|
+
== INSTALL:
|
66
|
+
|
67
|
+
Intall the latest release:
|
68
|
+
$ sudo gem install dcache
|
69
|
+
|
70
|
+
Or check out the git repository from Github, then run:
|
71
|
+
$ rake install_gem
|
72
|
+
This will install it into the system, so you'll need the root
|
73
|
+
password. Alternatively you can run
|
74
|
+
$ rake gem
|
75
|
+
which will build a gem, and put it into pkg/.
|
76
|
+
|
77
|
+
== DEVELOPERS:
|
78
|
+
|
79
|
+
After checking out the source, run:
|
80
|
+
|
81
|
+
$ rake newb
|
82
|
+
|
83
|
+
This task will install any missing dependencies, run the tests/specs,
|
84
|
+
and generate the RDoc.
|
85
|
+
|
86
|
+
== LICENSE:
|
87
|
+
|
88
|
+
(The MIT License)
|
89
|
+
|
90
|
+
Copyright (c) 2009 Kővágó, Zoltán
|
91
|
+
|
92
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
93
|
+
a copy of this software and associated documentation files (the
|
94
|
+
'Software'), to deal in the Software without restriction, including
|
95
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
96
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
97
|
+
permit persons to whom the Software is furnished to do so, subject to
|
98
|
+
the following conditions:
|
99
|
+
|
100
|
+
The above copyright notice and this permission notice shall be
|
101
|
+
included in all copies or substantial portions of the Software.
|
102
|
+
|
103
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
104
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
105
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
106
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
107
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
108
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
109
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
SOEXT = Config::CONFIG['DLEXT']
|
7
|
+
EXTS = [:ary, :list]
|
8
|
+
EXT_FILES = EXTS.map do |i|
|
9
|
+
"lib/dcache_#{i}.#{SOEXT}"
|
10
|
+
end
|
11
|
+
|
12
|
+
Hoe.plugin :git
|
13
|
+
Hoe.spec 'dcache' do
|
14
|
+
developer("Kővágó, Zoltán", "DirtY.iCE.hu@gmail.com")
|
15
|
+
|
16
|
+
self.readme_file = 'README.rdoc'
|
17
|
+
self.history_file = 'History.rdoc'
|
18
|
+
self.post_install_message = "** Please build documentation with `yard'"
|
19
|
+
|
20
|
+
spec_extras[:extensions] = EXTS.map do |i|
|
21
|
+
"ext/dcache_#{i}/extconf.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
extra_dev_deps << ['rspec', '>=1.2']
|
25
|
+
extra_dev_deps << ['hoe-git', '>=1.3.0']
|
26
|
+
extra_dev_deps << ['yard', '>=0.5'] # for documentation
|
27
|
+
|
28
|
+
clean_globs << '.yardoc'
|
29
|
+
# clean globs for native extensions
|
30
|
+
clean_globs << EXT_FILES
|
31
|
+
EXTS.each do |i|
|
32
|
+
clean_globs << "ext/dcache_#{i}/Makefile"
|
33
|
+
clean_globs << "ext/dcache_#{i}/dcache_#{i}.o"
|
34
|
+
clean_globs << "ext/dcache_#{i}/dcache_#{i}.#{SOEXT}"
|
35
|
+
clean_globs << "lib/dcache_#{i}.#{SOEXT}"
|
36
|
+
end
|
37
|
+
clean_globs.flatten!
|
38
|
+
|
39
|
+
self.rspec_options = [ '--color' ]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Workaround !
|
43
|
+
file 'History.txt' => 'History.rdoc'
|
44
|
+
file 'README.txt' => 'README.rdoc'
|
45
|
+
|
46
|
+
# Native extension
|
47
|
+
task :spec => EXT_FILES
|
48
|
+
desc "Builds native extensions"
|
49
|
+
task :build => EXT_FILES
|
50
|
+
|
51
|
+
EXTS.each do |e|
|
52
|
+
files = [ "ext/dcache_#{e}/extconf.rb", "ext/dcache_#{e}/dcache_#{e}.c" ]
|
53
|
+
tfile = "lib/dcache_#{e}.#{SOEXT}"
|
54
|
+
|
55
|
+
file tfile => files do
|
56
|
+
Dir.chdir "ext/dcache_#{e}" do
|
57
|
+
ruby 'extconf.rb'
|
58
|
+
sh 'make'
|
59
|
+
end
|
60
|
+
FileUtils.copy "ext/dcache_#{e}/dcache_#{e}.#{SOEXT}", tfile
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# YARD documentation
|
65
|
+
Rake.application.instance_variable_get('@tasks').delete("docs")
|
66
|
+
begin
|
67
|
+
require 'yard'
|
68
|
+
|
69
|
+
task :docs => :yard
|
70
|
+
YARD::Rake::YardocTask.new do |t|
|
71
|
+
# options and files read from '.yardopts'
|
72
|
+
end
|
73
|
+
rescue LoadError
|
74
|
+
puts "** Install `yard' to generate docs."
|
75
|
+
end
|
76
|
+
|
77
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,276 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <time.h>
|
3
|
+
|
4
|
+
static ID CMP;
|
5
|
+
|
6
|
+
typedef struct
|
7
|
+
{
|
8
|
+
VALUE key;
|
9
|
+
VALUE value;
|
10
|
+
time_t time;
|
11
|
+
} SubStruct;
|
12
|
+
|
13
|
+
typedef struct
|
14
|
+
{
|
15
|
+
unsigned int head;
|
16
|
+
unsigned int items;
|
17
|
+
unsigned long long int hits;
|
18
|
+
unsigned long long int misses;
|
19
|
+
SubStruct s[];
|
20
|
+
} Struct;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Mark elements in cache for Ruby's GC.
|
24
|
+
* @param[in] s struct associated with self.
|
25
|
+
*/
|
26
|
+
static void struct_mark(Struct * s)
|
27
|
+
{
|
28
|
+
time_t tim = time(NULL);
|
29
|
+
int i;
|
30
|
+
|
31
|
+
for(i = 0; i < s->items; ++i)
|
32
|
+
{
|
33
|
+
SubStruct * it = &(s->s[i]);
|
34
|
+
|
35
|
+
if (it->time > tim || it->time == 0)
|
36
|
+
{
|
37
|
+
rb_gc_mark(it->key);
|
38
|
+
rb_gc_mark(it->value);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Gets the associated structure.
|
45
|
+
* @param[in] self self
|
46
|
+
* @param[out] var name of the variable that'll contain the pointer to the
|
47
|
+
* structure.
|
48
|
+
*/
|
49
|
+
#define GET_STRUCT(self, var) \
|
50
|
+
Struct * var; \
|
51
|
+
Data_Get_Struct(self, Struct, var);
|
52
|
+
|
53
|
+
/*
|
54
|
+
* @return [Number] maximum number of items cache can store.
|
55
|
+
*/
|
56
|
+
static VALUE method_get_max_items(VALUE self)
|
57
|
+
{
|
58
|
+
GET_STRUCT(self, s);
|
59
|
+
return INT2NUM(s->items);
|
60
|
+
}
|
61
|
+
|
62
|
+
/*
|
63
|
+
* @return [Number] number of cache hits.
|
64
|
+
*/
|
65
|
+
static VALUE method_get_hits(VALUE self)
|
66
|
+
{
|
67
|
+
GET_STRUCT(self, s);
|
68
|
+
return INT2NUM(s->hits);
|
69
|
+
}
|
70
|
+
|
71
|
+
/*
|
72
|
+
* @return [Number] number of cache misses.
|
73
|
+
*/
|
74
|
+
static VALUE method_get_misses(VALUE self)
|
75
|
+
{
|
76
|
+
GET_STRUCT(self, s);
|
77
|
+
return INT2NUM(s->misses);
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* @return [Number] number of currently cached items.
|
82
|
+
*/
|
83
|
+
static VALUE method_get_stored_items(VALUE self)
|
84
|
+
{
|
85
|
+
GET_STRUCT(self, s);
|
86
|
+
time_t tim = time(NULL);
|
87
|
+
int cnt = 0;
|
88
|
+
|
89
|
+
int i;
|
90
|
+
for (i = 0; i < s->items; ++i)
|
91
|
+
if(s->s[i].time > tim || s->s[i].time == 0)
|
92
|
+
++cnt;
|
93
|
+
|
94
|
+
return INT2NUM(cnt);
|
95
|
+
}
|
96
|
+
|
97
|
+
/* @overload add(key, value, timeout)
|
98
|
+
* Add a new value to cache.
|
99
|
+
* @param [#eql?] key an unique identifier (in the cache)
|
100
|
+
* @param value the value
|
101
|
+
* @param [Number, nil] timeout time in seconds after the value will be purged
|
102
|
+
* from cache. +nil+ if there's no such limit.
|
103
|
+
* @return *value*
|
104
|
+
*/
|
105
|
+
static VALUE method_add(VALUE self, VALUE key, VALUE val, VALUE timeout)
|
106
|
+
{
|
107
|
+
GET_STRUCT(self, s);
|
108
|
+
if (++s->head >= s->items)
|
109
|
+
s->head = 0;
|
110
|
+
|
111
|
+
SubStruct * it = &(s->s[s->head]);
|
112
|
+
it->key = key;
|
113
|
+
it->value = val;
|
114
|
+
if (timeout == Qnil)
|
115
|
+
it->time = 0;
|
116
|
+
else
|
117
|
+
it->time = time(NULL) + NUM2INT(timeout);
|
118
|
+
|
119
|
+
return val;
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Decreases the item pointer, wrapping around the start if needed.
|
124
|
+
* @param[in,out] n pointer to the current pointer, will be changed directly.
|
125
|
+
* @param[in] cnt size of the cache
|
126
|
+
*/
|
127
|
+
static void dec(int * n, int cnt)
|
128
|
+
{
|
129
|
+
--(*n);
|
130
|
+
if (*n < 0) *n = cnt - 1;
|
131
|
+
}
|
132
|
+
|
133
|
+
/*
|
134
|
+
* Tries to get a value from the cache.
|
135
|
+
* @overload get(key, default = nil)
|
136
|
+
* Tries to get a value from cache. If it fails, returns a default value.
|
137
|
+
* @param [#eql?] key the key of the value to get
|
138
|
+
* @param default default return value.
|
139
|
+
* @return the value from cache if found, *default* otherwise
|
140
|
+
* @overload get(key)
|
141
|
+
* Tries to get a value from cache. If it fails, executes the block.
|
142
|
+
* @param [#eql?] key the key of the value to get
|
143
|
+
* @yield if key is not found.
|
144
|
+
* @return the value from cache if found, or yielded block's return value.
|
145
|
+
*/
|
146
|
+
static VALUE method_get(int argc, VALUE * argv, VALUE self)
|
147
|
+
{
|
148
|
+
GET_STRUCT(self, s);
|
149
|
+
VALUE key, default_val;
|
150
|
+
rb_scan_args(argc, argv, "11", &key, &default_val);
|
151
|
+
|
152
|
+
time_t tim = time(NULL);
|
153
|
+
int end = s->head + 1;
|
154
|
+
if (end >= s->items) end = 0;
|
155
|
+
|
156
|
+
int i;
|
157
|
+
for (i = s->head; i != end; dec(&i, s->items))
|
158
|
+
{
|
159
|
+
SubStruct * it = &(s->s[i]);
|
160
|
+
if (it->time <= tim && it->time != 0) continue;
|
161
|
+
if (rb_funcall(it->key, CMP, 1, key) == Qtrue)
|
162
|
+
{
|
163
|
+
++s->hits;
|
164
|
+
return it->value;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
++s->misses;
|
169
|
+
if (rb_block_given_p())
|
170
|
+
{
|
171
|
+
return rb_yield(Qnil);
|
172
|
+
}
|
173
|
+
|
174
|
+
return default_val;
|
175
|
+
}
|
176
|
+
|
177
|
+
/*
|
178
|
+
* Removes a given key or keys from the cache.
|
179
|
+
* @overload remove(key)
|
180
|
+
* @param key remove this key from the cache
|
181
|
+
* @overload remove() { |key, value| ... }
|
182
|
+
* @yield [key, value] for each item in the cache,
|
183
|
+
* @yieldparam key key of the current item.
|
184
|
+
* @yieldparam value value associated with the *key*.
|
185
|
+
* @yieldreturn [Boolean] +true+ if you want to delete this item
|
186
|
+
* @return [nil]
|
187
|
+
*/
|
188
|
+
static VALUE mehod_remove(int argc, VALUE * argv, VALUE self)
|
189
|
+
{
|
190
|
+
GET_STRUCT(self, s);
|
191
|
+
VALUE todel;
|
192
|
+
rb_scan_args(argc, argv, "01", &todel);
|
193
|
+
time_t tim = time(NULL);
|
194
|
+
|
195
|
+
int has_block = rb_block_given_p();
|
196
|
+
|
197
|
+
int i;
|
198
|
+
for (i = 0; i < s->items; ++i)
|
199
|
+
{
|
200
|
+
SubStruct * it = &(s->s[i]);
|
201
|
+
if (it->time > tim || it->time == 0)
|
202
|
+
{
|
203
|
+
VALUE to_del;
|
204
|
+
if (has_block)
|
205
|
+
{
|
206
|
+
VALUE ary = rb_ary_new3(2, it->key, it->value);
|
207
|
+
to_del = rb_yield(ary);
|
208
|
+
}
|
209
|
+
else
|
210
|
+
to_del = rb_funcall(it->key, CMP, 1, todel);
|
211
|
+
|
212
|
+
if (to_del != Qfalse && to_del != Qnil)
|
213
|
+
it->time = 1;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
return Qnil;
|
218
|
+
}
|
219
|
+
|
220
|
+
/*
|
221
|
+
* Clears the cache.
|
222
|
+
* @return [nil]
|
223
|
+
*/
|
224
|
+
static VALUE method_clear(VALUE self)
|
225
|
+
{
|
226
|
+
GET_STRUCT(self, s);
|
227
|
+
|
228
|
+
int i;
|
229
|
+
for (i = 0; i < s->items; ++i)
|
230
|
+
s->s[i].time = 1;
|
231
|
+
|
232
|
+
return Qnil;
|
233
|
+
}
|
234
|
+
|
235
|
+
/* @overload new(max_items)
|
236
|
+
* Creates a new array-based DCache backend with fixed size.
|
237
|
+
* @param [Number] max_items number of cache items this cache can store.
|
238
|
+
* @return [DCacheAry] the new object.
|
239
|
+
*/
|
240
|
+
static VALUE method_new(VALUE class, VALUE max_items)
|
241
|
+
{
|
242
|
+
Struct * s;
|
243
|
+
int items = NUM2INT(max_items);
|
244
|
+
if (items < 1)
|
245
|
+
rb_raise(rb_eArgError, "max_items is %d, it should >= 1", items);
|
246
|
+
|
247
|
+
s = ruby_xmalloc(sizeof(Struct) + items * sizeof(SubStruct));
|
248
|
+
s->head = 0;
|
249
|
+
s->items = items;
|
250
|
+
s->hits = s->misses = 0;
|
251
|
+
|
252
|
+
int i;
|
253
|
+
for (i = 0; i < items; ++i)
|
254
|
+
s->s[i].time = 1;
|
255
|
+
|
256
|
+
VALUE x = Data_Wrap_Struct(class, struct_mark, free, s);
|
257
|
+
rb_obj_call_init(x, 0, NULL);
|
258
|
+
|
259
|
+
return x;
|
260
|
+
}
|
261
|
+
|
262
|
+
void Init_dcache_ary()
|
263
|
+
{
|
264
|
+
CMP = rb_intern("eql?");
|
265
|
+
VALUE dcache = rb_define_class("DCacheAry", rb_cObject);
|
266
|
+
rb_define_singleton_method(dcache, "new", method_new, 1);
|
267
|
+
rb_define_method(dcache, "add", method_add, 3);
|
268
|
+
rb_define_method(dcache, "get", method_get, -1);
|
269
|
+
rb_define_method(dcache, "remove", mehod_remove, -1);
|
270
|
+
rb_define_method(dcache, "clear", method_clear, 0);
|
271
|
+
|
272
|
+
rb_define_method(dcache, "hits", method_get_hits, 0);
|
273
|
+
rb_define_method(dcache, "misses", method_get_misses, 0);
|
274
|
+
rb_define_method(dcache, "max_items", method_get_max_items, 0);
|
275
|
+
rb_define_method(dcache, "length", method_get_stored_items, 0);
|
276
|
+
}
|