opod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +36 -0
- data/README +29 -0
- data/lib/opod/cache.rb +24 -0
- data/lib/opod/drb.rb +51 -0
- data/lib/opod/file.rb +79 -0
- data/lib/opod/memcached.rb +69 -0
- data/lib/opod/memory.rb +83 -0
- data/lib/opod/og.rb +61 -0
- data/lib/opod/pstore.rb +187 -0
- data/meta/MANIFEST +21 -0
- data/meta/opod-0.0.1.roll +21 -0
- data/task/config.yaml +9 -0
- data/task/publish +44 -0
- data/task/rdoc +40 -0
- metadata +81 -0
data/COPYING
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
Opod
|
2
|
+
|
3
|
+
Copyright (c) 2007, George Moschovitis & Thomas Sawyer
|
4
|
+
|
5
|
+
All rights reserved.
|
6
|
+
|
7
|
+
This software is distributed under the terms of the BSD license.
|
8
|
+
|
9
|
+
Redistribution and use in source and binary forms, with or without
|
10
|
+
modification, are permitted provided that the following conditions
|
11
|
+
are met:
|
12
|
+
|
13
|
+
* Redistributions of source code must retain the above copyright
|
14
|
+
notice, this list of conditions and the following disclaimer.
|
15
|
+
|
16
|
+
* Redistributions in binary form must reproduce the above
|
17
|
+
copyright notice, this list of conditions and the following
|
18
|
+
disclaimer in the documentation and/or other materials provided
|
19
|
+
with the distribution.
|
20
|
+
|
21
|
+
* Neither the name of the copyright holders nor the names of
|
22
|
+
contributors may be used to endorse or promote products derived
|
23
|
+
from this software without specific prior written permission.
|
24
|
+
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
26
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
27
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
28
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
29
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
30
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
31
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
32
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
33
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
34
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
35
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36
|
+
|
data/README
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= Opod
|
2
|
+
|
3
|
+
Opod is a adaptive cache system for Ruby. It can persist data to
|
4
|
+
a variety of static sources --in memory, to file, or via database.
|
5
|
+
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
Installing via RubyGems:
|
10
|
+
|
11
|
+
gem install opod
|
12
|
+
|
13
|
+
Manual install:
|
14
|
+
|
15
|
+
$ unzip opod-0.0.1.zip
|
16
|
+
$ cd opod-0.0.1.zip
|
17
|
+
$ sudo task/setup
|
18
|
+
|
19
|
+
Windows users the last line will be 'ruby task/setup'.
|
20
|
+
|
21
|
+
|
22
|
+
== Copying
|
23
|
+
|
24
|
+
Copyright (c) 2005-2007 Thomas Sawyer, George Moschovitis
|
25
|
+
|
26
|
+
All rights reserved.
|
27
|
+
|
28
|
+
BSD License
|
29
|
+
|
data/lib/opod/cache.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# TITLE:
|
2
|
+
# Cache
|
3
|
+
#
|
4
|
+
# SUMMARY:
|
5
|
+
# Base classes for all cache adapters.
|
6
|
+
# However it is not used yet.
|
7
|
+
|
8
|
+
module Opod
|
9
|
+
|
10
|
+
# A general cache key.
|
11
|
+
|
12
|
+
class CacheKey
|
13
|
+
end
|
14
|
+
|
15
|
+
# A general cache mechanism.
|
16
|
+
#
|
17
|
+
# This cache system was originaly developed for Nitro. It is
|
18
|
+
# used to cache fragments, og objects (entities), sessions,
|
19
|
+
# application scoped variables and more.
|
20
|
+
|
21
|
+
class Cache
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/opod/drb.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "drb"
|
2
|
+
require "facets/settings"
|
3
|
+
require "nemo/memory"
|
4
|
+
|
5
|
+
module Opod
|
6
|
+
|
7
|
+
# A cached backed in a DRb server.
|
8
|
+
#
|
9
|
+
# === Example
|
10
|
+
#
|
11
|
+
# This cache needs a corresponding DRb server. Here is how you
|
12
|
+
# can setup the standard Nitro Drb server to keep a DrbCache:
|
13
|
+
#
|
14
|
+
# require 'glue/cache/memory'
|
15
|
+
#
|
16
|
+
# class MyDrbServer < Nitro::DrbServer
|
17
|
+
# def setup_drb_objects
|
18
|
+
# ..
|
19
|
+
# @my_cache = SyncHash.new
|
20
|
+
# DRb.start_service("druby://#{my_drb_address}:#{my_drb_port}", @my_cache)
|
21
|
+
# ..
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# MyDrbServer.start
|
26
|
+
|
27
|
+
class DrbCache < MemoryCache
|
28
|
+
|
29
|
+
# Initialize the cache.
|
30
|
+
#
|
31
|
+
# === Options
|
32
|
+
#
|
33
|
+
# :address = The address of the DRb cache object.
|
34
|
+
# :port = The port of the DRb cache object.
|
35
|
+
|
36
|
+
# The address of the Session cache / store (if distibuted).
|
37
|
+
|
38
|
+
setting :address, :default => '127.0.0.1', :doc => 'The address of the Session cache'
|
39
|
+
|
40
|
+
# The port of the Session DRb cache / store (if distributed).
|
41
|
+
|
42
|
+
setting :port, :default => 9069, :doc => 'The port of the Session cache'
|
43
|
+
|
44
|
+
|
45
|
+
def initialize(address = DrbCache.address, port = DrbCache.port)
|
46
|
+
@hash = DRbObject.new(nil, "druby://#{address}:#{port}")
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/opod/file.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "uri"
|
2
|
+
require "fileutils"
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
module Opod
|
6
|
+
|
7
|
+
class FileCache
|
8
|
+
|
9
|
+
setting :basedir, :default => "#{Dir.tmpdir}/nitro_file_cache", :doc => "The directory to store files"
|
10
|
+
|
11
|
+
def initialize(name = "cache", keepalive = nil)
|
12
|
+
@path = File.join(FileCache.basedir, name)
|
13
|
+
@keepalive = keepalive
|
14
|
+
|
15
|
+
FileUtils.mkdir_p(@path, :mode => 0700)
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(k,v)
|
19
|
+
fn = File.join(@path, escape_filename(k.to_s) )
|
20
|
+
encode_file(fn, v)
|
21
|
+
end
|
22
|
+
alias_method :set, :[]=
|
23
|
+
|
24
|
+
def [](k)
|
25
|
+
fn = File.join(@path, escape_filename(k.to_s) )
|
26
|
+
return nil unless File.exists?(fn)
|
27
|
+
decode_file(fn)
|
28
|
+
end
|
29
|
+
alias_method :get, :[]
|
30
|
+
|
31
|
+
def delete(k)
|
32
|
+
f = File.join(@path, escape_filename(k.to_s))
|
33
|
+
File.delete(f) if File.exists?(f)
|
34
|
+
end
|
35
|
+
|
36
|
+
def gc!
|
37
|
+
return unless @keepalive
|
38
|
+
|
39
|
+
now = Time.now
|
40
|
+
all.each do |fn|
|
41
|
+
expire_time = File.stat(fn).atime + @keepalive
|
42
|
+
File.delete(fn) if now > expire_time
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def all
|
47
|
+
Dir.glob( File.join(@path, '*' ) )
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def decode_file(fn)
|
53
|
+
val = nil
|
54
|
+
File.open(fn,"rb") do |f|
|
55
|
+
f.flock(File::LOCK_EX)
|
56
|
+
val = Marshal.load( f.read )
|
57
|
+
f.flock(File::LOCK_UN)
|
58
|
+
end
|
59
|
+
return val
|
60
|
+
end
|
61
|
+
|
62
|
+
def encode_file(fn, value)
|
63
|
+
File.open(fn, "wb") do |f|
|
64
|
+
f.flock(File::LOCK_EX)
|
65
|
+
f.chmod(0600)
|
66
|
+
f.write(Marshal.dump(value))
|
67
|
+
f.flock(File::LOCK_UN)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# need this for fat filesystems
|
72
|
+
def escape_filename(fn)
|
73
|
+
URI.escape(fn, /["\/:;|=,\[\]]/)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# specifications:
|
2
|
+
# http://cvs.danga.com/browse.cgi/wcmtools/memcached/doc/protocol.txt?rev=HEAD&content-type=text/plain
|
3
|
+
#
|
4
|
+
# very simple (= very fast) client for memcached
|
5
|
+
#
|
6
|
+
# i found the Ruby-MemCache library a little bit buggy and complicated, so i made my own before
|
7
|
+
# fixing it ;)
|
8
|
+
#
|
9
|
+
# TODO socket disconnection handling
|
10
|
+
# TODO error handling
|
11
|
+
# TODO multiple servers connections
|
12
|
+
|
13
|
+
require "socket"
|
14
|
+
|
15
|
+
module Opod
|
16
|
+
|
17
|
+
class MemCached
|
18
|
+
|
19
|
+
setting :address, :default => "localhost", :doc => "Server address"
|
20
|
+
setting :port, :default => 11211, :doc => "Server port"
|
21
|
+
|
22
|
+
def initialize(name = "cache", keepalive = nil)
|
23
|
+
@sock = TCPSocket.new(MemCached.address, MemCached.port)
|
24
|
+
@name = name
|
25
|
+
@keepalive = keepalive
|
26
|
+
end
|
27
|
+
|
28
|
+
def []=(k,v)
|
29
|
+
if @keepalive
|
30
|
+
exptime = (Time.now + @keepalive).to_i
|
31
|
+
else
|
32
|
+
exptime = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
data = Marshal.dump(v)
|
36
|
+
@sock.print("set #{@name}:#{k} 0 #{exptime} #{data.size}\r\n#{data}\r\n")
|
37
|
+
response = @sock.gets # "STORED\r\n"
|
38
|
+
v
|
39
|
+
end
|
40
|
+
alias_method :set, :[]=
|
41
|
+
|
42
|
+
def [](k)
|
43
|
+
@sock.print("get #{@name}:#{k}\r\n")
|
44
|
+
resp = @sock.gets
|
45
|
+
if resp == "END\r\n"
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
#dummy, key, flags, size
|
50
|
+
size = resp.split(/ /).last.to_i
|
51
|
+
raw_data = @sock.read(size)
|
52
|
+
@sock.gets # \r\n
|
53
|
+
@sock.gets # END\r\n
|
54
|
+
Marshal.load( raw_data )
|
55
|
+
end
|
56
|
+
alias_method :get, :[]
|
57
|
+
|
58
|
+
def delete(k)
|
59
|
+
@sock.print("delete #{@name}:#{k}\r\n")
|
60
|
+
@sock.gets # "DELETED\r\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
def gc!
|
64
|
+
# garbage collection is handled by the memcache server
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/opod/memory.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'facets/synchash'
|
2
|
+
|
3
|
+
module Opod
|
4
|
+
|
5
|
+
# A cache backed in memory.
|
6
|
+
#--
|
7
|
+
# This implementation is also the base for the Drb Cache.
|
8
|
+
#++
|
9
|
+
|
10
|
+
class MemoryCache
|
11
|
+
attr :hash
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
if options[:sync]
|
15
|
+
@hash = SyncHash
|
16
|
+
else
|
17
|
+
@hash = {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Was orig. in Cache.
|
22
|
+
|
23
|
+
def update(hash)
|
24
|
+
hash.each { |key, value| self[key] = value }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get an object from the cache.
|
28
|
+
|
29
|
+
def get(key, options = nil)
|
30
|
+
@hash[key]
|
31
|
+
end
|
32
|
+
alias_method :read, :get
|
33
|
+
alias_method :[], :get
|
34
|
+
|
35
|
+
# Put an object in the cache.
|
36
|
+
|
37
|
+
def set(key, value = nil, options = nil)
|
38
|
+
@hash[key] = value
|
39
|
+
end
|
40
|
+
alias_method :put, :set
|
41
|
+
alias_method :write, :set
|
42
|
+
alias_method :[]=, :set
|
43
|
+
|
44
|
+
# Delete an object from the cache.
|
45
|
+
|
46
|
+
def delete(key, options = nil)
|
47
|
+
@hash.delete(key)
|
48
|
+
end
|
49
|
+
alias_method :remove, :delete
|
50
|
+
|
51
|
+
def delete_if(&block)
|
52
|
+
@hash.delete_if(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Perform session garbage collection. Typically this method
|
56
|
+
# is called from a cron like mechanism.
|
57
|
+
|
58
|
+
def gc!
|
59
|
+
delete_if { |key, s| s.expired? }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return the mapping.
|
63
|
+
|
64
|
+
def mapping
|
65
|
+
@hash
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return all keys in the cache.
|
69
|
+
|
70
|
+
def keys
|
71
|
+
@hash.keys
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return all objects in the cache.
|
75
|
+
|
76
|
+
def all
|
77
|
+
@hash.values
|
78
|
+
end
|
79
|
+
alias_method :values, :all
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/opod/og.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'og'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Opod
|
5
|
+
|
6
|
+
class OgCached
|
7
|
+
include Og::EntityMixin
|
8
|
+
|
9
|
+
property :unique_id, String, :sql => 'PRIMARY KEY'
|
10
|
+
property :expires, Time
|
11
|
+
property :cache_name, String
|
12
|
+
property :content, String
|
13
|
+
|
14
|
+
set_primary_key :unique_id, String
|
15
|
+
end
|
16
|
+
|
17
|
+
class OgCache
|
18
|
+
|
19
|
+
def initialize(cache_name, keepalive = nil)
|
20
|
+
@cache_name = cache_name
|
21
|
+
@keepalive = keepalive
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(k,v)
|
25
|
+
unless s = OgCached.find_by_unique_id_and_cache_name(k.to_s, @cache_name)
|
26
|
+
s = OgCached.new
|
27
|
+
s.cache_name = @cache_name
|
28
|
+
s.expires = Time.now + @keepalive if @keepalive
|
29
|
+
s.unique_id = k.to_s
|
30
|
+
end
|
31
|
+
#s.content = v.to_yaml
|
32
|
+
s.content = encode(v)
|
33
|
+
s.insert
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](k)
|
37
|
+
s = OgCached.find_by_unique_id_and_cache_name(k.to_s, @cache_name)
|
38
|
+
decode(s.content) if s
|
39
|
+
end
|
40
|
+
|
41
|
+
def gc!
|
42
|
+
OgCached.find(:condition => ["expires < ? AND cache_name = ?", Time.now, @cache_name]).each {|s| s.delete }
|
43
|
+
end
|
44
|
+
|
45
|
+
def all
|
46
|
+
OgCached.find_by_cache_name(@cache_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def encode(c)
|
52
|
+
Base64.encode64(Marshal.dump(c))
|
53
|
+
end
|
54
|
+
|
55
|
+
def decode(c)
|
56
|
+
Marshal::load(Base64.decode64(c))
|
57
|
+
#s.content = YAML::load(s.content)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/opod/pstore.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pstore'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
module Opod
|
6
|
+
|
7
|
+
class PStoreCache
|
8
|
+
|
9
|
+
setting :basedir, :default => File.join(Dir.tmpdir, 'nitro_file_cache'),
|
10
|
+
:doc => 'Base directory for cache files'
|
11
|
+
|
12
|
+
setting :max_tries, :default => 5, :doc => 'Maximum number of tries for cache operations'
|
13
|
+
|
14
|
+
setting :tokens, :default => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
15
|
+
:doc => 'Tokens used when generating cache file names'
|
16
|
+
|
17
|
+
def initialize(keepalive = nil)
|
18
|
+
@keepalive = keepalive
|
19
|
+
initialize_store
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the object stored under <i>key</i> from the cache.
|
23
|
+
# If the object isn't present in the cache <b>nil</b> is returned.
|
24
|
+
|
25
|
+
def [](key)
|
26
|
+
current_try = 0
|
27
|
+
begin
|
28
|
+
current_try += 1
|
29
|
+
@store.transaction(true) do
|
30
|
+
@store.fetch(key, nil)[1]
|
31
|
+
end
|
32
|
+
rescue PStore::Error
|
33
|
+
# Unable to return the requested object from the cache.
|
34
|
+
# Create a new store and retry.
|
35
|
+
initialize_store
|
36
|
+
unless current_try > PStoreCache.max_tries
|
37
|
+
retry
|
38
|
+
else
|
39
|
+
raise Exception.new("Unable to read from cache file #{@store.path}!")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Store <i>obj</i> under <i>key</i> in the cache.
|
45
|
+
|
46
|
+
def []=(key, obj)
|
47
|
+
current_try = 0
|
48
|
+
begin
|
49
|
+
current_try += 1
|
50
|
+
@store.transaction(false) do
|
51
|
+
if @keepalive
|
52
|
+
@store[key] = [Time.now + @keepalive, obj]
|
53
|
+
else
|
54
|
+
@store[key] = [nil, obj]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
rescue PStore::Error
|
58
|
+
# Unable to store object.
|
59
|
+
# Create a new store and retry.
|
60
|
+
initialize_store
|
61
|
+
unless current_try > PStoreCache.max_tries
|
62
|
+
retry
|
63
|
+
else
|
64
|
+
raise Exception.new("Unable to write to cache file #{@store.path}!")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return all objects stored in the cache.
|
70
|
+
|
71
|
+
def all
|
72
|
+
current_try = 0
|
73
|
+
begin
|
74
|
+
current_try += 1
|
75
|
+
@store.transaction(true) do
|
76
|
+
@store.roots.inject([]) do |result, current|
|
77
|
+
result << @store[current][1]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue PStore::Error
|
81
|
+
# Unable to return stored objects from cache.
|
82
|
+
# Create a new store and retry.
|
83
|
+
initialize_store
|
84
|
+
unless current_try > PStoreCache.max_tries
|
85
|
+
retry
|
86
|
+
else
|
87
|
+
raise Exception.new("Unable to read from cache file #{@store.path}!")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Delete the object stored under <i>key</i> in the cache.
|
93
|
+
|
94
|
+
def delete(key)
|
95
|
+
begin
|
96
|
+
@store.transaction(false) do
|
97
|
+
@store.delete(key)
|
98
|
+
end
|
99
|
+
rescue PStore::Error
|
100
|
+
# Unable to delete object from the cache.
|
101
|
+
# Create a new store.
|
102
|
+
initialize_store
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Remove all expired objects from the cache.
|
107
|
+
|
108
|
+
def gc!
|
109
|
+
return unless @keepalive
|
110
|
+
begin
|
111
|
+
now = Time.now
|
112
|
+
@store.transaction(false) do
|
113
|
+
@store.roots.each do |r|
|
114
|
+
@store.delete(r) if now > @store[r][0]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
rescue PStore::Error
|
118
|
+
# Unable to delete object from the cache.
|
119
|
+
# Create a new store.
|
120
|
+
initialize_store
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
alias get []
|
125
|
+
|
126
|
+
alias put []=
|
127
|
+
|
128
|
+
alias read []
|
129
|
+
|
130
|
+
alias values all
|
131
|
+
|
132
|
+
alias write []=
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Generate a random filename using <i>tokens</i> and <i>length</i> as constraints.
|
137
|
+
|
138
|
+
def generate_random_filename(tokens = PStoreCache.tokens, length = 16)
|
139
|
+
filename = ''
|
140
|
+
1.upto(length) do
|
141
|
+
filename << tokens[rand(tokens.length)]
|
142
|
+
end
|
143
|
+
filename
|
144
|
+
end
|
145
|
+
|
146
|
+
# Initialize store with a random filename.
|
147
|
+
# If no suitable filename can be found, raise an exception.
|
148
|
+
|
149
|
+
def initialize_store
|
150
|
+
# Create cache directory (if needed)
|
151
|
+
unless File.exists?(PStoreCache.basedir) && File.directory?(PStoreCache.basedir)
|
152
|
+
begin
|
153
|
+
FileUtils.mkdir_p(PStoreCache.basedir)
|
154
|
+
rescue Exception
|
155
|
+
raise Exception.new("Unable to create cache directory #{PStoreCache.basedir}!")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Check whether the current_process has sufficient privilieges for reading/writing files to the cache directory
|
160
|
+
|
161
|
+
unless File.readable?(PStoreCache.basedir) && File.writable?(PStoreCache.basedir)
|
162
|
+
raise Exception.new("Insuffient priviligies for cache directory #{PStoreCache.basedir}!")
|
163
|
+
end
|
164
|
+
|
165
|
+
# generate a new random filename for the cache file
|
166
|
+
|
167
|
+
current_try = 0
|
168
|
+
|
169
|
+
begin
|
170
|
+
current_try += 1
|
171
|
+
filename = generate_random_filename
|
172
|
+
end while File.exists?(File.join(PStoreCache.basedir, filename)) && current_try < PStoreCache.max_tries
|
173
|
+
|
174
|
+
if current_try <= PStoreCache.max_tries
|
175
|
+
begin
|
176
|
+
@store = PStore.new(File.join(PStoreCache.basedir, filename))
|
177
|
+
rescue Exception
|
178
|
+
raise Exception.new('Unable to create cache file!')
|
179
|
+
end
|
180
|
+
else
|
181
|
+
raise Exception.new('Unable to create cache file!')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
data/meta/MANIFEST
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -x doc -x dev -x pkg
|
2
|
+
COPYING
|
3
|
+
README
|
4
|
+
lib
|
5
|
+
lib/opod
|
6
|
+
lib/opod/cache.rb
|
7
|
+
lib/opod/drb.rb
|
8
|
+
lib/opod/file.rb
|
9
|
+
lib/opod/memcached.rb
|
10
|
+
lib/opod/memory.rb
|
11
|
+
lib/opod/og.rb
|
12
|
+
lib/opod/pstore.rb
|
13
|
+
log
|
14
|
+
meta
|
15
|
+
meta/MANIFEST
|
16
|
+
meta/opod-0.0.1.roll
|
17
|
+
task
|
18
|
+
task/config.yaml
|
19
|
+
task/publish
|
20
|
+
task/rdoc
|
21
|
+
test
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
title : Opod
|
3
|
+
author : George Moschovitis
|
4
|
+
contact : Thomas Sawyer
|
5
|
+
email : transfire@gmail.com
|
6
|
+
homepage : "http://opod.rubyforge.org"
|
7
|
+
status : beta
|
8
|
+
|
9
|
+
summary: Obejct Pods are places to keep digital stuff.
|
10
|
+
|
11
|
+
description: >
|
12
|
+
Object storage through various adapters.
|
13
|
+
|
14
|
+
dependency:
|
15
|
+
- [ facets, ">= 2.1.0" ]
|
16
|
+
|
17
|
+
formats: [ gem, zip ]
|
18
|
+
|
19
|
+
lib_path:
|
20
|
+
- lib/opod
|
21
|
+
|
data/task/config.yaml
ADDED
data/task/publish
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ratch
|
2
|
+
|
3
|
+
# publish website to rubyforge
|
4
|
+
|
5
|
+
# This task publishes the source dir (deafult doc/)
|
6
|
+
# to a rubyforge website.
|
7
|
+
|
8
|
+
config = configuration['publish']
|
9
|
+
|
10
|
+
project = config['project']
|
11
|
+
subdir = config['subdir']
|
12
|
+
source = config['source']
|
13
|
+
username = ENV['RUBYFORGE_USERNAME'] || config['username']
|
14
|
+
protect = %w{usage statcvs statsvn robot.txt wiki}
|
15
|
+
exclude = %w{.svn}
|
16
|
+
|
17
|
+
abort("no project name") unless project
|
18
|
+
abort("no username") unless username
|
19
|
+
abort("no source dir") unless source
|
20
|
+
|
21
|
+
if subdir
|
22
|
+
destination = File.join(project, subdir)
|
23
|
+
else
|
24
|
+
destination = project
|
25
|
+
end
|
26
|
+
|
27
|
+
dir = source.chomp('/') + '/'
|
28
|
+
url = "#{username}@rubyforge.org:/var/www/gforge-projects/#{destination}"
|
29
|
+
|
30
|
+
# maybe -p ?
|
31
|
+
op = ['-rLvz', '--delete']
|
32
|
+
if File.file?(File.join(source,'.rsync-filter'))
|
33
|
+
op << "--filter='dir-merge #{source}/.rsync-filter'"
|
34
|
+
else
|
35
|
+
op.concat exclude.map{|e| "--filter='- #{e}'"}
|
36
|
+
op.concat protect.map{|e| "--filter='P #{e}'"}
|
37
|
+
end
|
38
|
+
|
39
|
+
args = op + [dir, url]
|
40
|
+
|
41
|
+
#cd source do
|
42
|
+
rsync *args
|
43
|
+
#end
|
44
|
+
|
data/task/rdoc
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ratch
|
2
|
+
|
3
|
+
# generate rdocs
|
4
|
+
|
5
|
+
# Generate Rdoc documentation. Settings are
|
6
|
+
# the same as the rdoc command's options.
|
7
|
+
|
8
|
+
main :rdoc do
|
9
|
+
# Load rdoc configuration.
|
10
|
+
|
11
|
+
config = configuration['rdoc']
|
12
|
+
|
13
|
+
config = {
|
14
|
+
'template' => 'html',
|
15
|
+
'op' => 'doc/rdoc',
|
16
|
+
'merge' => true,
|
17
|
+
'inline-source' => true,
|
18
|
+
'exclude' => %w{InstalledFiles Manifest Project dev util},
|
19
|
+
'include' => %w{[A-Z]* lib}
|
20
|
+
}.update(config)
|
21
|
+
|
22
|
+
output = config['op']
|
23
|
+
|
24
|
+
# Check for 'doc' directory.
|
25
|
+
# (Helps to ensure we're in the right place.)
|
26
|
+
|
27
|
+
dir!(File.dirname(output))
|
28
|
+
|
29
|
+
# Prepare command arguments.
|
30
|
+
|
31
|
+
vector = config.command_vector('include')
|
32
|
+
|
33
|
+
# Remove old rdocs, if any.
|
34
|
+
|
35
|
+
rm_r(output) if File.exist?(output)
|
36
|
+
|
37
|
+
# Document.
|
38
|
+
|
39
|
+
rdoc(*vector)
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4.6
|
3
|
+
specification_version: 2
|
4
|
+
name: opod
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-11-14 00:00:00 -05:00
|
8
|
+
summary: Obejct Pods are places to keep digital stuff.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: transfire@gmail.com
|
12
|
+
homepage: http://opod.rubyforge.org
|
13
|
+
rubyforge_project:
|
14
|
+
description: Object storage through various adapters.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc:
|
19
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: "0"
|
30
|
+
version:
|
31
|
+
platform: ruby
|
32
|
+
signing_key:
|
33
|
+
cert_chain: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
extensions_fallback:
|
37
|
+
authors:
|
38
|
+
- George Moschovitis
|
39
|
+
files:
|
40
|
+
- COPYING
|
41
|
+
- README
|
42
|
+
- lib
|
43
|
+
- lib/opod
|
44
|
+
- lib/opod/cache.rb
|
45
|
+
- lib/opod/drb.rb
|
46
|
+
- lib/opod/file.rb
|
47
|
+
- lib/opod/memcached.rb
|
48
|
+
- lib/opod/memory.rb
|
49
|
+
- lib/opod/og.rb
|
50
|
+
- lib/opod/pstore.rb
|
51
|
+
- log
|
52
|
+
- meta
|
53
|
+
- meta/MANIFEST
|
54
|
+
- meta/opod-0.0.1.roll
|
55
|
+
- task
|
56
|
+
- task/config.yaml
|
57
|
+
- task/publish
|
58
|
+
- task/rdoc
|
59
|
+
- test
|
60
|
+
test_files: []
|
61
|
+
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
extra_rdoc_files: []
|
65
|
+
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
dependencies:
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: facets
|
75
|
+
version_requirement:
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 2.1.0
|
81
|
+
version:
|