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 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
@@ -0,0 +1,12 @@
1
+ --protected
2
+ --private
3
+ --title
4
+ DCache documentation
5
+ --load
6
+ yard/reader
7
+ --main
8
+ README.rdoc
9
+ lib/**/*.rb
10
+ ext/**/*.c
11
+ -
12
+ History.rdoc
data/History.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ === 0.0.0 / 2009-12-29
2
+
3
+ * First public release.
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ .autotest
2
+ .yardopts
3
+ History.rdoc
4
+ Manifest.txt
5
+ README.rdoc
6
+ Rakefile
7
+ ext/dcache_ary/dcache_ary.c
8
+ ext/dcache_ary/extconf.rb
9
+ ext/dcache_list/dcache_list.c
10
+ ext/dcache_list/extconf.rb
11
+ lib/dcache.rb
12
+ spec/dcache_spec.rb
13
+ yard/reader.rb
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
+ }
@@ -0,0 +1,4 @@
1
+ require "mkmf"
2
+
3
+ dir_config "dcache_ary"
4
+ create_makefile "dcache_ary"