-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial framework to allow KV object to have different data backends
- Loading branch information
Showing
2 changed files
with
171 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,166 @@ | |||
require "rubygems" | |||
require "fileutils" | |||
require "json" | |||
require "kv/exception" | |||
require "kv/node" | |||
require "kv/util" | |||
require "uuidtools" | |||
|
|||
class KV | |||
class Backend | |||
class File | |||
public | |||
def self.create_kvdb(kvdb_path) | |||
kvdb_metadata_path = File.join(kvdb_path, ".kvdb") | |||
kvdb_metadata = { | |||
"version" => "1", | |||
"mapping" => {}, | |||
} | |||
|
|||
if File.exists?(kvdb_path) | |||
raise KV::Error.new("#{kvdb_path} exists, cannot create a kvdb there") | |||
end | |||
|
|||
FileUtils.mkdir_p(kvdb_path) | |||
File.open(kvdb_metadata_path, "w+") do |f| | |||
f.puts kvdb_metadata.to_json | |||
end | |||
end # def self.create_kvdb | |||
|
|||
public | |||
def initialize(opts) | |||
@opts = { | |||
:path => nil, | |||
}.merge(opts) | |||
|
|||
if @opts[:path].nil? | |||
raise KV::Error.new("missing :path argument to constructor") | |||
end | |||
|
|||
@kvdb_metadata_path = ::File.join(@opts[:path], ".kvdb") | |||
if !::File.exists?(@kvdb_metadata_path) | |||
raise KV::Error.new("can't see #{@kvdb_metadata_path}") | |||
end | |||
|
|||
@nodes = {} | |||
|
|||
load_metadata | |||
end # def initialize | |||
|
|||
|
|||
private | |||
def load_metadata | |||
begin | |||
@kvdb_metadata = JSON.parse(::File.read(@kvdb_metadata_path)) | |||
rescue | |||
raise KV::Error.new("error parsing #{@kvdb_metadata_path}: #{$!}") | |||
end | |||
|
|||
if ! @kvdb_metadata["mapping"].is_a?(Hash) | |||
raise KV::Error.new("corrupt metadata, mapping is not a hash") | |||
end | |||
|
|||
if ! @kvdb_metadata.member?("version") | |||
raise KV::Error.new("corrupt metadata, version is missing") | |||
end | |||
|
|||
# TODO(petef): some day handle the ability to have multiple versions. | |||
if @kvdb_metadata["version"] != "1" | |||
raise KV::Error.new("unknown metadata version #{@kvdb_metadata["version"].inspect}") | |||
end | |||
end # def load_metadata | |||
|
|||
private | |||
def write_metadata | |||
::File.open(@kvdb_metadata_path, "w+") { |f| f.puts @kvdb_metadata.to_json } | |||
end # def write_metadata | |||
|
|||
public | |||
def node_path(node_name) | |||
if ! node_name.is_a?(String) | |||
raise "node_path takes a String, not: #{node_name.inspect}" | |||
end | |||
if @kvdb_metadata["mapping"][node_name] | |||
return @kvdb_metadata["mapping"][node_name] | |||
end | |||
|
|||
# get a UUID, build a path | |||
uuid = UUIDTools::UUID.sha1_create(UUIDTools::UUID_OID_NAMESPACE, | |||
node_name).to_s | |||
path = ::File.join(@opts[:path], | |||
uuid[0..0], | |||
uuid[1..1], | |||
uuid[2..2], | |||
uuid) | |||
@kvdb_metadata["mapping"][node_name] = path | |||
|
|||
# re-write metadata | |||
write_metadata | |||
|
|||
return path | |||
end # def node_path | |||
|
|||
public | |||
def node(node_name) | |||
@nodes[node_name] ||= KV::Node.new(node_name, node_path(node_name)) | |||
return @nodes[node_name] | |||
end | |||
|
|||
public | |||
def node?(node_name) | |||
return @kvdb_metadata["mapping"].keys.member?(node_name) | |||
end | |||
|
|||
public | |||
def nodes | |||
return @kvdb_metadata["mapping"].keys.sort | |||
end # def nodes | |||
|
|||
public | |||
def expand(key_path, verbose = false, raise_on_bad_node_name = true) | |||
res = [] | |||
# key_path must be a full key_path or just a node name | |||
node_name, key, index = KV::Util.expand_key_path(key_path) | |||
|
|||
if ! node?(node_name) | |||
if raise_on_bad_node_name | |||
raise KV::Error, "node #{node_name} does not exist" | |||
else | |||
return res | |||
end | |||
end | |||
|
|||
node = self.node(node_name) | |||
if key.nil? | |||
node.attrs.to_hash.each do |key, values| | |||
res.push(*expand_values(node, key, values, true)) | |||
end | |||
elsif node[key] | |||
res.push(*expand_values(node, key, node[key], verbose)) | |||
end | |||
|
|||
return res.sort | |||
end # def expand | |||
|
|||
private | |||
def expand_values(node, key, values, verbose) | |||
res = [] | |||
values = [values] unless values.is_a?(Array) | |||
|
|||
index = values.length > 1 ? 0 : nil | |||
|
|||
values.each do |value| | |||
if verbose | |||
index_suffix = index ? "##{index}" : "" | |||
res << "#{node.name}##{key}#{index_suffix}: #{value}" | |||
index += 1 if index | |||
else | |||
res << value | |||
end | |||
end | |||
|
|||
return res | |||
end # def expand_values | |||
end # class File | |||
end # class Backend | |||
end # class KV |