cache_tree 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/.document +5 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/cache_tree.rb +155 -0
- data/test/helper.rb +18 -0
- data/test/test_cache_tree.rb +7 -0
- metadata +178 -0
data/.document
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
source "http://rubygems.org"
|
|
2
|
+
# Add dependencies required to use your gem here.
|
|
3
|
+
# Example:
|
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
|
5
|
+
|
|
6
|
+
# Add dependencies to develop your gem here.
|
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
|
8
|
+
group :development do
|
|
9
|
+
gem "shoulda", ">= 0"
|
|
10
|
+
gem "rdoc", "~> 3.12"
|
|
11
|
+
gem "bundler", "~> 1.0.0"
|
|
12
|
+
gem "jeweler", "~> 1.8.3"
|
|
13
|
+
gem "rcov", ">= 0"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
gem 'isna'
|
|
17
|
+
gem 'tree_node'
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2012 kazuyoshi tlacaelel
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
= cache_tree
|
|
2
|
+
|
|
3
|
+
Btree based cache
|
|
4
|
+
|
|
5
|
+
== Contributing to cache_tree
|
|
6
|
+
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'cache_tree'
|
|
9
|
+
|
|
10
|
+
class Record
|
|
11
|
+
def id
|
|
12
|
+
1
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
CacheTree::Node.directory = 'cache_tree'
|
|
17
|
+
record = CacheTree::Node.new(Record.new)
|
|
18
|
+
CacheTree::Manager.instance.use(record)
|
|
19
|
+
unless CacheTree::Manager.instance.exists?
|
|
20
|
+
CacheTree::Manager.instance.save('data')
|
|
21
|
+
end
|
|
22
|
+
puts CacheTree::Manager.instance.read
|
|
23
|
+
|
|
24
|
+
== Copyright
|
|
25
|
+
|
|
26
|
+
Copyright (c) 2012 kazuyoshi tlacaelel. See LICENSE.txt for
|
|
27
|
+
further details.
|
|
28
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'bundler'
|
|
5
|
+
begin
|
|
6
|
+
Bundler.setup(:default, :development)
|
|
7
|
+
rescue Bundler::BundlerError => e
|
|
8
|
+
$stderr.puts e.message
|
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
10
|
+
exit e.status_code
|
|
11
|
+
end
|
|
12
|
+
require 'rake'
|
|
13
|
+
|
|
14
|
+
require 'jeweler'
|
|
15
|
+
Jeweler::Tasks.new do |gem|
|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
|
17
|
+
gem.name = "cache_tree"
|
|
18
|
+
gem.homepage = "http://github.com/ktlacaelel/cache_tree"
|
|
19
|
+
gem.license = "MIT"
|
|
20
|
+
gem.summary = %Q{Simple cache tree}
|
|
21
|
+
gem.description = %Q{Cache based on btrees, for large ammounts of cache.}
|
|
22
|
+
gem.email = "kazu.dev@gmail.com"
|
|
23
|
+
gem.authors = ["kazuyoshi tlacaelel"]
|
|
24
|
+
# dependencies defined in Gemfile
|
|
25
|
+
end
|
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
|
27
|
+
|
|
28
|
+
require 'rake/testtask'
|
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
|
30
|
+
test.libs << 'lib' << 'test'
|
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
|
32
|
+
test.verbose = true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
require 'rcov/rcovtask'
|
|
36
|
+
Rcov::RcovTask.new do |test|
|
|
37
|
+
test.libs << 'test'
|
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
|
39
|
+
test.verbose = true
|
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task :default => :test
|
|
44
|
+
|
|
45
|
+
require 'rdoc/task'
|
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
48
|
+
|
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
50
|
+
rdoc.title = "cache_tree #{version}"
|
|
51
|
+
rdoc.rdoc_files.include('README*')
|
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
53
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.0
|
data/lib/cache_tree.rb
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
require 'md5'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'isna'
|
|
5
|
+
require 'tree_node'
|
|
6
|
+
|
|
7
|
+
module CacheTree
|
|
8
|
+
|
|
9
|
+
class Manager
|
|
10
|
+
|
|
11
|
+
include Singleton
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@current_link = nil
|
|
15
|
+
@configuration = { :perform_caching => true }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def configure(options)
|
|
19
|
+
@configuration.merge(options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def use(cache_link)
|
|
23
|
+
@current_link = cache_link
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def exists?
|
|
27
|
+
return false unless @configuration[:perform_caching]
|
|
28
|
+
btree_key_files = @current_link.map(:up) { |node| node.btree_key }
|
|
29
|
+
return false unless btree_key_files.all? { |btree_key| File.exists?(btree_key) }
|
|
30
|
+
@current_link.map(:up) { |node| node.load_btree_key }
|
|
31
|
+
File.exists?(@current_link.cache_file)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def expire!
|
|
35
|
+
@current_link.expire
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def read
|
|
39
|
+
@current_link.map(:up) { |node| node.load_btree_key }
|
|
40
|
+
File.read(@current_link.cache_file)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def save(data)
|
|
44
|
+
@current_link.map(:up) { |node| node.save }
|
|
45
|
+
FileUtils.mkdir_p(File.dirname(@current_link.cache_file))
|
|
46
|
+
File.open(@current_link.cache_file, 'w+') { |file| file.write data }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class Node
|
|
52
|
+
|
|
53
|
+
class << self
|
|
54
|
+
attr_accessor :directory
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
SEPARATOR = '/'
|
|
58
|
+
|
|
59
|
+
attr_accessor :name, :value, :stamp
|
|
60
|
+
|
|
61
|
+
include Tree::Node
|
|
62
|
+
|
|
63
|
+
def initialize(target)
|
|
64
|
+
@name = target.class.name.gsub(/([A-Z])/) { "_#{$1}" }.gsub(/^./, '').downcase
|
|
65
|
+
@value = target.id
|
|
66
|
+
@stamp = generate_stamp
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def directory
|
|
70
|
+
self.class.directory
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def generate_stamp
|
|
74
|
+
(Time.now.to_f * 1.0).to_s + '-' + Kernel.rand(1000000).to_s
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Path of the cache node
|
|
78
|
+
def path
|
|
79
|
+
"#{name}#{SEPARATOR}#{value}#{SEPARATOR}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Resolves final name for btree_key file.
|
|
83
|
+
def btree_key
|
|
84
|
+
directory + SEPARATOR + (map(:up) { |node| node.path } * '') + 'btree.key'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Updates current node stamp from btree_key
|
|
88
|
+
def load_btree_key
|
|
89
|
+
@stamp = eval(File.read(btree_key))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Sums up all stamps and generates a checksum.
|
|
93
|
+
def checksum
|
|
94
|
+
MD5.hexdigest(map(:up) { |node| node.stamp } * '')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Resolves final name for cache file.
|
|
98
|
+
def cache_file
|
|
99
|
+
directory + SEPARATOR + (map(:up) { |node| node.path } * '') + checksum + '.cache'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Writes node stamp from to btree_key file
|
|
103
|
+
def save
|
|
104
|
+
FileUtils.mkdir_p File.dirname(btree_key)
|
|
105
|
+
File.open(btree_key, 'w+') { |file| file.write stamp.inspect }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Quickly expire a whole cache-tree or a single node.
|
|
109
|
+
# Expiring a node in the middle will automatically expire all
|
|
110
|
+
# children nodes. no need to expire each one individually.
|
|
111
|
+
def expire
|
|
112
|
+
@stamp = generate_stamp
|
|
113
|
+
save
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Cleans up expired cache files for a specific node.
|
|
117
|
+
#
|
|
118
|
+
# You need to be specific the engine will not walk down the tree
|
|
119
|
+
# for you to prevent iterating through large trees.
|
|
120
|
+
#
|
|
121
|
+
# @return [Array] of items that were deleted.
|
|
122
|
+
def clean
|
|
123
|
+
diagnose[:dead].each { |file| FileUtils.rm file }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Runs an analysis on a given node and returns its status.
|
|
127
|
+
#
|
|
128
|
+
# @return [Hash] with detailed diagnosis of curret status for that
|
|
129
|
+
# node's cache
|
|
130
|
+
def diagnose
|
|
131
|
+
file_alive = cache_file
|
|
132
|
+
base = File.basename(file_alive)
|
|
133
|
+
dir = File.dirname(file_alive)
|
|
134
|
+
diagnostic = {}
|
|
135
|
+
diagnostic[:btree_key] = btree_key
|
|
136
|
+
diagnostic[:alive] = file_alive
|
|
137
|
+
diagnostic[:dead] = []
|
|
138
|
+
Dir["#{dir}/*.cache"].each do |cached_file|
|
|
139
|
+
next if File.basename(cached_file) == base
|
|
140
|
+
diagnostic[:dead] << cached_file
|
|
141
|
+
end
|
|
142
|
+
diagnostic
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Prints out active cache in green, and expired files in red.
|
|
146
|
+
def debug
|
|
147
|
+
puts cache_file.to_ansi.green
|
|
148
|
+
diagnose[:dead].each { |file| puts file.to_ansi.red }
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
end
|
|
155
|
+
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler'
|
|
3
|
+
begin
|
|
4
|
+
Bundler.setup(:default, :development)
|
|
5
|
+
rescue Bundler::BundlerError => e
|
|
6
|
+
$stderr.puts e.message
|
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
8
|
+
exit e.status_code
|
|
9
|
+
end
|
|
10
|
+
require 'test/unit'
|
|
11
|
+
require 'shoulda'
|
|
12
|
+
|
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
15
|
+
require 'cache_tree'
|
|
16
|
+
|
|
17
|
+
class Test::Unit::TestCase
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cache_tree
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 31
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 0
|
|
10
|
+
version: 0.0.0
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- kazuyoshi tlacaelel
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2012-11-01 00:00:00 -06:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
name: isna
|
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
|
26
|
+
none: false
|
|
27
|
+
requirements:
|
|
28
|
+
- - ">="
|
|
29
|
+
- !ruby/object:Gem::Version
|
|
30
|
+
hash: 3
|
|
31
|
+
segments:
|
|
32
|
+
- 0
|
|
33
|
+
version: "0"
|
|
34
|
+
requirement: *id001
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
name: tree_node
|
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
hash: 3
|
|
45
|
+
segments:
|
|
46
|
+
- 0
|
|
47
|
+
version: "0"
|
|
48
|
+
requirement: *id002
|
|
49
|
+
- !ruby/object:Gem::Dependency
|
|
50
|
+
type: :development
|
|
51
|
+
prerelease: false
|
|
52
|
+
name: shoulda
|
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
|
54
|
+
none: false
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
hash: 3
|
|
59
|
+
segments:
|
|
60
|
+
- 0
|
|
61
|
+
version: "0"
|
|
62
|
+
requirement: *id003
|
|
63
|
+
- !ruby/object:Gem::Dependency
|
|
64
|
+
type: :development
|
|
65
|
+
prerelease: false
|
|
66
|
+
name: rdoc
|
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
|
68
|
+
none: false
|
|
69
|
+
requirements:
|
|
70
|
+
- - ~>
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
hash: 31
|
|
73
|
+
segments:
|
|
74
|
+
- 3
|
|
75
|
+
- 12
|
|
76
|
+
version: "3.12"
|
|
77
|
+
requirement: *id004
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
|
+
type: :development
|
|
80
|
+
prerelease: false
|
|
81
|
+
name: bundler
|
|
82
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
|
83
|
+
none: false
|
|
84
|
+
requirements:
|
|
85
|
+
- - ~>
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
hash: 23
|
|
88
|
+
segments:
|
|
89
|
+
- 1
|
|
90
|
+
- 0
|
|
91
|
+
- 0
|
|
92
|
+
version: 1.0.0
|
|
93
|
+
requirement: *id005
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
type: :development
|
|
96
|
+
prerelease: false
|
|
97
|
+
name: jeweler
|
|
98
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
|
99
|
+
none: false
|
|
100
|
+
requirements:
|
|
101
|
+
- - ~>
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
hash: 49
|
|
104
|
+
segments:
|
|
105
|
+
- 1
|
|
106
|
+
- 8
|
|
107
|
+
- 3
|
|
108
|
+
version: 1.8.3
|
|
109
|
+
requirement: *id006
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
type: :development
|
|
112
|
+
prerelease: false
|
|
113
|
+
name: rcov
|
|
114
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
|
115
|
+
none: false
|
|
116
|
+
requirements:
|
|
117
|
+
- - ">="
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
hash: 3
|
|
120
|
+
segments:
|
|
121
|
+
- 0
|
|
122
|
+
version: "0"
|
|
123
|
+
requirement: *id007
|
|
124
|
+
description: Cache based on btrees, for large ammounts of cache.
|
|
125
|
+
email: kazu.dev@gmail.com
|
|
126
|
+
executables: []
|
|
127
|
+
|
|
128
|
+
extensions: []
|
|
129
|
+
|
|
130
|
+
extra_rdoc_files:
|
|
131
|
+
- LICENSE.txt
|
|
132
|
+
- README.rdoc
|
|
133
|
+
files:
|
|
134
|
+
- .document
|
|
135
|
+
- Gemfile
|
|
136
|
+
- LICENSE.txt
|
|
137
|
+
- README.rdoc
|
|
138
|
+
- Rakefile
|
|
139
|
+
- VERSION
|
|
140
|
+
- lib/cache_tree.rb
|
|
141
|
+
- test/helper.rb
|
|
142
|
+
- test/test_cache_tree.rb
|
|
143
|
+
has_rdoc: true
|
|
144
|
+
homepage: http://github.com/ktlacaelel/cache_tree
|
|
145
|
+
licenses:
|
|
146
|
+
- MIT
|
|
147
|
+
post_install_message:
|
|
148
|
+
rdoc_options: []
|
|
149
|
+
|
|
150
|
+
require_paths:
|
|
151
|
+
- lib
|
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
153
|
+
none: false
|
|
154
|
+
requirements:
|
|
155
|
+
- - ">="
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
hash: 3
|
|
158
|
+
segments:
|
|
159
|
+
- 0
|
|
160
|
+
version: "0"
|
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
|
+
none: false
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
hash: 3
|
|
167
|
+
segments:
|
|
168
|
+
- 0
|
|
169
|
+
version: "0"
|
|
170
|
+
requirements: []
|
|
171
|
+
|
|
172
|
+
rubyforge_project:
|
|
173
|
+
rubygems_version: 1.3.7
|
|
174
|
+
signing_key:
|
|
175
|
+
specification_version: 3
|
|
176
|
+
summary: Simple cache tree
|
|
177
|
+
test_files: []
|
|
178
|
+
|