forked from skopp/hurl
/
db.rb
118 lines (96 loc) · 2.93 KB
/
db.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
require 'fileutils'
require 'pg'
module Hurl
class AbstractDB
def self.encode(object)
Zlib::Deflate.deflate Yajl::Encoder.encode(object)
end
def self.decode(object)
Yajl::Parser.parse(Zlib::Inflate.inflate(object)) rescue nil
end
end
class PostgresDB < AbstractDB
def self.connection
@@connection ||= PG::Connection::new(
ENV.fetch("POSTGRES_HOST", "localhost"),
ENV.fetch("POSTGRES_PORT", 5432),
:dbname => ENV.fetch("POSTGRES_DATABASE", "hurls"),
:user => ENV.fetch("POSTGRES_USER", "postgres"),
:password => ENV.fetch("POSTGRES_PASSWORD", "postgres")
)
end
def self.select_query(scope)
"SELECT content::bytea FROM %s WHERE id = $1 LIMIT 1" % connection.escape_string(scope.to_s)
end
def self.find(scope, id)
connection.exec(select_query(scope), [id], 1) do |result|
decode(result.getvalue(0, 0)) if result.num_tuples >= 1
end
end
def self.insert_query(scope)
"INSERT INTO %s VALUES ($1::varchar, $2::bytea)" % connection.escape_string(scope.to_s)
end
def self.save(scope, id, content)
connection.exec(insert_query(scope), [id, {:value => encode(content), :format => 1}])
end
def self.count(scope)
connection.exec("SELECT COUNT(*) FROM %s" % connection.escape_string(scope.to_s)) do |result|
result.getvalue(0, 0)
end
end
end
class RedisDB < AbstractDB
def self.connection
@@uri = URI.parse(ENV.fetch("REDISTOGO_URL", "redis://127.0.0.1:6379"))
@@connection ||= Redis.new(
:host => @@uri.host,
:port => @@uri.port,
:password => @@uri.password)
end
def self.find(scope, id)
decode(connection.get("hurl/#{scope}/#{id}"))
end
def self.save(scope, id, content)
connection.set("hurl/#{scope}/#{id}", encode(content))
end
def self.count(scope)
connection.keys("hurl/#{scope}/*").size
end
end
class FileDB < AbstractDB
DIR = File.expand_path(ENV['HURL_DB_DIR'] || "db")
def self.find(scope, id)
decode File.read(dir(scope, id) + id) if id && id.is_a?(String)
rescue Errno::ENOENT
nil
end
def self.save(scope, id, content)
File.open(dir(scope, id) + id, 'w') do |f|
f.puts encode(content)
end
true
end
def self.count(scope)
files = Dir["#{DIR}/#{scope}/**/**"].reject do |file|
File.directory?(file)
end
files.size
end
def self.dir(scope, id)
path = FileUtils.mkdir_p "#{DIR}/#{scope}/#{id[0...2]}/#{id[2...4]}/"
# In Ruby 1.9, mkdir_p always returns Array,
# while in 1.8 it returns String when it has only one item to return.
if path.is_a? Array
path[0]
else
path
end
end
end
db_backend = ENV.fetch("DB_BACKEND", "postgres")
DB = {
"file" => FileDB,
"redis" => RedisDB,
"postgres" => PostgresDB
}.fetch(db_backend)
end