cached 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Rakefile +15 -0
- data/bin/cached +27 -0
- data/bin/kvstore +74 -0
- data/gem.yml +11 -0
- data/hooks/post_install.rb +1 -0
- data/hooks/pre_uninstall.rb +1 -0
- data/lib/cached.rb +84 -0
- data/lib/db.rb +40 -0
- data/lib/fast_gem.rb +56 -0
- data/script/console +19 -0
- data/test/test.rb +5 -0
- data/vex/gem.rake +36 -0
- data/vex/gem.rb +95 -0
- metadata +95 -0
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
load "#{File.dirname __FILE__}/vex/gem.rake"
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
|
5
|
+
task :test do
|
6
|
+
sh "ruby test/test.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
task :rcov do
|
10
|
+
sh "cd test; rcov -o ../coverage -x ruby/.*/gems -x ^test.rb test.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :rdoc do
|
14
|
+
sh "rdoc -o doc/rdoc"
|
15
|
+
end
|
data/bin/cached
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGV == %w(--install)
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
FileUtils.rm($0)
|
7
|
+
FileUtils.symlink(__FILE__, $0)
|
8
|
+
exit
|
9
|
+
elsif defined?(Gem)
|
10
|
+
STDERR.puts "Note: please run improved installation via\n\n\tsudo #{$0} --install"
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
path = File.readlink(__FILE__)
|
15
|
+
rescue Errno::EINVAL
|
16
|
+
path = __FILE__
|
17
|
+
end
|
18
|
+
|
19
|
+
$: << File.expand_path(File.dirname(path) + "/../lib")
|
20
|
+
|
21
|
+
require "cached"
|
22
|
+
|
23
|
+
load "db.rb"
|
24
|
+
|
25
|
+
Db.database ||= "#{ENV["HOME"]}/.cached.sql"
|
26
|
+
|
27
|
+
puts Cached.exec *ARGV
|
data/bin/kvstore
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGV == %w(--install)
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
FileUtils.rm($0)
|
7
|
+
FileUtils.symlink(__FILE__, $0)
|
8
|
+
exit
|
9
|
+
elsif defined?(Gem)
|
10
|
+
STDERR.puts "Note: please run improved installation via\n\n\tsudo #{$0} --install"
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
path = File.readlink(__FILE__)
|
15
|
+
rescue Errno::EINVAL
|
16
|
+
path = __FILE__
|
17
|
+
end
|
18
|
+
|
19
|
+
$: << File.expand_path(File.dirname(path) + "/../lib")
|
20
|
+
|
21
|
+
require "db"
|
22
|
+
|
23
|
+
Db.database = "#{ENV["HOME"]}/.kvstore.sql"
|
24
|
+
|
25
|
+
class App
|
26
|
+
include Db
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
unless ask "SELECT name FROM sqlite_master WHERE type='table' AND name='kvstore'"
|
30
|
+
ask "CREATE TABLE kvstore(key, value)"
|
31
|
+
ask "CREATE UNIQUE INDEX IF NOT EXISTS kvstore_key ON kvstore(key)"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def set(key, value)
|
36
|
+
ask("INSERT OR REPLACE INTO kvstore VALUES(?, ?)", key, value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get(key)
|
40
|
+
puts ask("SELECT value FROM kvstore WHERE key=?", key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(key)
|
44
|
+
ask("DELETE FROM kvstore WHERE key=?", key)
|
45
|
+
end
|
46
|
+
|
47
|
+
def list
|
48
|
+
sql("SELECT key, value FROM kvstore") do |row|
|
49
|
+
puts row.first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def help(*args)
|
54
|
+
STDERR.puts <<-MSG
|
55
|
+
|
56
|
+
Usage:
|
57
|
+
|
58
|
+
#{$0} set key value
|
59
|
+
#{$0} get key
|
60
|
+
#{$0} delete key
|
61
|
+
#{$0} list
|
62
|
+
MSG
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
app = App.new
|
67
|
+
|
68
|
+
cmd = ARGV.shift || "help"
|
69
|
+
begin
|
70
|
+
app.send cmd, *ARGV
|
71
|
+
rescue NoMethodError, ArgumentError
|
72
|
+
puts $!
|
73
|
+
app.help
|
74
|
+
end
|
data/gem.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#
|
2
|
+
# A vex/gem config file.
|
3
|
+
version: 0.2
|
4
|
+
authors: ["radiospiel"]
|
5
|
+
email: eno@open-lab.org
|
6
|
+
homepage: http://radiospiel.org
|
7
|
+
summary: cached command line tool
|
8
|
+
description: |
|
9
|
+
The 'cached' command line tool provides an easy to use *nix caching tool.
|
10
|
+
|
11
|
+
dependencies: sqlite3
|
@@ -0,0 +1 @@
|
|
1
|
+
STDERR.puts "*** THIS IS THE POST INSTALL SCRIPT"
|
@@ -0,0 +1 @@
|
|
1
|
+
STDERR.puts "*** THIS IS THE PRE UNINSTALL SCRIPT"
|
data/lib/cached.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
load "db.rb"
|
2
|
+
Db.database ||= "#{ENV["HOME"]}/.cached.sql"
|
3
|
+
|
4
|
+
require 'digest/sha1'
|
5
|
+
|
6
|
+
class Entry
|
7
|
+
extend Db
|
8
|
+
|
9
|
+
def self.init
|
10
|
+
unless ask "SELECT name FROM sqlite_master WHERE type='table' AND name='cached'"
|
11
|
+
ask "CREATE TABLE cached(key, value, valid_until)"
|
12
|
+
end
|
13
|
+
|
14
|
+
ask "CREATE UNIQUE INDEX IF NOT EXISTS cached_key ON cached(key)"
|
15
|
+
ask "CREATE INDEX IF NOT EXISTS cached_valid_until ON cached(valid_until)"
|
16
|
+
end
|
17
|
+
|
18
|
+
init
|
19
|
+
|
20
|
+
def self.cleanup
|
21
|
+
ask "DELETE FROM cached WHERE valid_until < ?", now
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.now
|
25
|
+
Time.now.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.get(key)
|
29
|
+
value, valid_until = *row("SELECT value, valid_until FROM cached WHERE key=?", key)
|
30
|
+
return value if value && valid_until.to_i >= now
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.set(key, value, options)
|
34
|
+
ask "INSERT OR REPLACE INTO cached (key, value, valid_until) VALUES(?, ?, ?)", key, value, now + options[:ttl]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Cached
|
39
|
+
def self.exec(*args)
|
40
|
+
ttl = 3600
|
41
|
+
|
42
|
+
case args.first
|
43
|
+
when /^--ttl=([0-9]+)$/
|
44
|
+
args.shift
|
45
|
+
ttl = $1.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
key = Digest::SHA1.hexdigest(Dir.getwd + ":" + args.inspect)
|
49
|
+
Entry.get(key) || begin
|
50
|
+
value = run(*args)
|
51
|
+
Entry.set(key, value, :ttl => ttl)
|
52
|
+
value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.shell_escape(*args)
|
57
|
+
args.map do |str|
|
58
|
+
if str.empty?
|
59
|
+
"''"
|
60
|
+
elsif %r{\A[0-9A-Za-z+,./:=@_-]+\z} =~ str
|
61
|
+
str
|
62
|
+
else
|
63
|
+
result = ''
|
64
|
+
str.scan(/('+)|[^']+/) {
|
65
|
+
if $1
|
66
|
+
result << %q{\'} * $1.length
|
67
|
+
else
|
68
|
+
result << "'#{$&}'"
|
69
|
+
end
|
70
|
+
}
|
71
|
+
result
|
72
|
+
end
|
73
|
+
end.join(" ")
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.run(*args)
|
77
|
+
command = args.join(" ")
|
78
|
+
# command = shell_escape args
|
79
|
+
`#{command}`
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# TODO: start cleanup in background... Entry.cleanup
|
data/lib/db.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
load "fast_gem.rb"
|
2
|
+
|
3
|
+
FastGem.load "sqlite3"
|
4
|
+
|
5
|
+
module Db
|
6
|
+
def self.database=(database)
|
7
|
+
@database = database
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.database
|
11
|
+
@database
|
12
|
+
end
|
13
|
+
|
14
|
+
def db
|
15
|
+
@db ||= SQLite3::Database.new(Db.database)
|
16
|
+
end
|
17
|
+
|
18
|
+
def log(query, *args)
|
19
|
+
# STDERR.puts "[sqlite] #{query}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def sql(query, *args, &block)
|
23
|
+
log query, *args
|
24
|
+
r = db.execute query, *args, &block
|
25
|
+
return r if r
|
26
|
+
return [ db.changes ] if query =~ /^\s*(DELETE|INSERT)\b/i
|
27
|
+
end
|
28
|
+
|
29
|
+
def ask(query, *args)
|
30
|
+
log query, *args
|
31
|
+
r = db.get_first_value(query, *args)
|
32
|
+
return r if r
|
33
|
+
return db.changes if query =~ /^\s*(DELETE|INSERT)\b/i
|
34
|
+
end
|
35
|
+
|
36
|
+
def row(query, *args)
|
37
|
+
log query, *args
|
38
|
+
db.get_first_row(query, *args)
|
39
|
+
end
|
40
|
+
end
|
data/lib/fast_gem.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Gem
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
if !defined?(ConfigMap)
|
5
|
+
|
6
|
+
ConfigMap = {
|
7
|
+
:EXEEXT => RbConfig::CONFIG["EXEEXT"],
|
8
|
+
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
|
9
|
+
:arch => RbConfig::CONFIG["arch"],
|
10
|
+
:bindir => RbConfig::CONFIG["bindir"],
|
11
|
+
:datadir => RbConfig::CONFIG["datadir"],
|
12
|
+
:libdir => RbConfig::CONFIG["libdir"],
|
13
|
+
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"],
|
14
|
+
:ruby_version => RbConfig::CONFIG["ruby_version"],
|
15
|
+
:rubylibprefix => RbConfig::CONFIG["rubylibprefix"],
|
16
|
+
:sitedir => RbConfig::CONFIG["sitedir"],
|
17
|
+
:sitelibdir => RbConfig::CONFIG["sitelibdir"],
|
18
|
+
:vendordir => RbConfig::CONFIG["vendordir"] ,
|
19
|
+
:vendorlibdir => RbConfig::CONFIG["vendorlibdir"]
|
20
|
+
}
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.user_home
|
25
|
+
ENV["HOME"]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'rubygems/defaults'
|
30
|
+
|
31
|
+
module FastGem
|
32
|
+
extend self
|
33
|
+
|
34
|
+
def gem_paths
|
35
|
+
[ Gem.default_dir, Gem.user_dir ]
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Try ro find the file "name" in any of the available paths, and return the path
|
40
|
+
def find(name)
|
41
|
+
# look into each gempath for a matching file, sort by version (roughly),
|
42
|
+
# and return the last hit
|
43
|
+
gem_paths.
|
44
|
+
map { |gem_path| Dir.glob("#{gem_path}/gems/#{name}-[0-9]*") }.
|
45
|
+
flatten.
|
46
|
+
sort_by { |gem_path| gem_path.gsub(/.*\/gems\/[^-]+-/, "") }.
|
47
|
+
last
|
48
|
+
end
|
49
|
+
|
50
|
+
def load(name)
|
51
|
+
path = find(name)
|
52
|
+
# STDERR.puts "Load #{name} from #{path}"
|
53
|
+
$: << "#{path}/lib"
|
54
|
+
require name
|
55
|
+
end
|
56
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Start a console that initializes the gem
|
4
|
+
#
|
5
|
+
require "irb"
|
6
|
+
require "rubygems"
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'wirble'
|
10
|
+
Wirble.init
|
11
|
+
Wirble.colorize
|
12
|
+
rescue LoadError
|
13
|
+
STDERR.puts "To enable colorized and tab completed run 'gem install wirble'"
|
14
|
+
end
|
15
|
+
|
16
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
17
|
+
$: << "#{File.dirname(__FILE__)}/../init.rb"
|
18
|
+
|
19
|
+
IRB.start
|
data/test/test.rb
ADDED
data/vex/gem.rake
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
def sys(*args)
|
2
|
+
STDERR.puts "#{args.join(" ")}"
|
3
|
+
system *args
|
4
|
+
end
|
5
|
+
|
6
|
+
namespace :gem do
|
7
|
+
task :build => :spec do
|
8
|
+
options = []
|
9
|
+
sys "gem build .gemspec #{options.join(" ")} && mkdir -p pkg && mv *.gem pkg"
|
10
|
+
end
|
11
|
+
|
12
|
+
task :spec do
|
13
|
+
File.open ".gemspec", "w" do |file|
|
14
|
+
file.write <<-TXT
|
15
|
+
require "vex/gem"
|
16
|
+
Gem.spec File.dirname(__FILE__)
|
17
|
+
TXT
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task :install => :build do
|
22
|
+
file = Dir.glob("pkg/*.gem").sort.last
|
23
|
+
sys "gem install #{file}"
|
24
|
+
end
|
25
|
+
|
26
|
+
task :push => :build do
|
27
|
+
file = Dir.glob("pkg/*.gem").sort.last
|
28
|
+
puts "To push the gem to gemcutter please run"
|
29
|
+
puts
|
30
|
+
puts "\tgem push #{file}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Build gem"
|
35
|
+
# task :gem => %w(test gem:install)
|
36
|
+
task :gem => %w(gem:install gem:push)
|
data/vex/gem.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Gem
|
2
|
+
(class << self; self; end).class_eval do
|
3
|
+
def default_attributes
|
4
|
+
%w(name version date files executables)
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_root(root)
|
8
|
+
@root = File.expand_path(root)
|
9
|
+
@name = File.basename(@root)
|
10
|
+
@conf ||= YAML.load File.read("#{@root}/gem.yml")
|
11
|
+
@attributes ||= (default_attributes + @conf.keys.map(&:to_s)).uniq
|
12
|
+
end
|
13
|
+
|
14
|
+
# attr_reader :root, :conf, :attributes,
|
15
|
+
attr_reader :name
|
16
|
+
|
17
|
+
def attribute(name)
|
18
|
+
if @conf.key?(name) && !%w(dependencies).include?(name)
|
19
|
+
@conf[name]
|
20
|
+
else
|
21
|
+
self.send(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# create a new Gem::Specification object for this gem.
|
27
|
+
def spec(root)
|
28
|
+
self.set_root root
|
29
|
+
|
30
|
+
Gem::Specification.new do |s|
|
31
|
+
@attributes.each do |attr|
|
32
|
+
v = attribute(attr)
|
33
|
+
next if v.nil?
|
34
|
+
|
35
|
+
log attr, v
|
36
|
+
|
37
|
+
s.send attr + "=", v
|
38
|
+
end
|
39
|
+
|
40
|
+
%w(pre_uninstall post_install).each do |hook|
|
41
|
+
next unless File.exists?("#{root}/hooks/#{hook}.rb")
|
42
|
+
log hook, "yes"
|
43
|
+
Gem.send(hook) {
|
44
|
+
load "hooks/#{hook}.rb"
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def log(attr, v)
|
51
|
+
v = case attr
|
52
|
+
when "files" then "#{v.length} files"
|
53
|
+
else v.inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
STDERR.puts "#{"%20s" % attr}:\t#{v}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def dependencies
|
60
|
+
return nil unless @conf["dependencies"]
|
61
|
+
|
62
|
+
@conf["dependencies"].map do |d|
|
63
|
+
Gem::Dependency.new d, ">= 0"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def head(file)
|
68
|
+
File.open(file) do |f|
|
69
|
+
f.readline
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def executables
|
74
|
+
r = Dir.glob("#{@root}/bin/**/*").map do |file|
|
75
|
+
next unless head(file) =~ /^#!/
|
76
|
+
file[@root.length + 5 .. -1]
|
77
|
+
end.compact
|
78
|
+
|
79
|
+
return nil if r.empty?
|
80
|
+
r
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# get files from git
|
85
|
+
def files
|
86
|
+
r = `git ls-files`.split("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# return the date
|
91
|
+
def date
|
92
|
+
Date.today.to_s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cached
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: "0.2"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- radiospiel
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-16 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: sqlite3
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: |
|
35
|
+
The 'cached' command line tool provides an easy to use *nix caching tool.
|
36
|
+
|
37
|
+
email: eno@open-lab.org
|
38
|
+
executables:
|
39
|
+
- cached
|
40
|
+
- kvstore
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- .gitignore
|
47
|
+
- Rakefile
|
48
|
+
- bin/cached
|
49
|
+
- bin/kvstore
|
50
|
+
- gem.yml
|
51
|
+
- hooks/post_install.rb
|
52
|
+
- hooks/pre_uninstall.rb
|
53
|
+
- lib/cached.rb
|
54
|
+
- lib/db.rb
|
55
|
+
- lib/fast_gem.rb
|
56
|
+
- script/console
|
57
|
+
- test/test.rb
|
58
|
+
- vex/gem.rake
|
59
|
+
- vex/gem.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://radiospiel.org
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
requirements: []
|
88
|
+
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.3.7
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: cached command line tool
|
94
|
+
test_files: []
|
95
|
+
|