class Treet::Repo
Attributes
root[R]
Public Class Methods
filefor(keyname)
click to toggle source
patch keys can look like
name.first emails[]
(address syntax has been eliminated, we recognize array elements by matching the entire content)
# File lib/treet/repo.rb, line 24 def self.filefor(keyname) if keyname =~ /\[/ keyname = keyname.match(/^(.*)\[\]$/).captures.first [keyname, '', nil] elsif keyname =~ /\./ # subelement # negative lookbehind; don't split on a dot at beginning of string filename,field = keyname.split(/(?<!^)\./) ['.', filename, field] else ['.', keyname] end end
new(path, opts = {})
click to toggle source
# File lib/treet/repo.rb, line 6 def initialize(path, opts = {}) raise Errno::ENOENT, "Missing or invalid source path #{path}" unless File.directory?(path) @root = path end
Public Instance Methods
compare(target)
click to toggle source
# File lib/treet/repo.rb, line 16 def compare(target) Treet::Hash.diff(to_hash, target.to_hash) end
patch(diffs)
click to toggle source
Patching a repo is not the same as patching a hash. Make the changes directly to the data files.
# File lib/treet/repo.rb, line 40 def patch(diffs) @hash = nil # invalidate any cached image Dir.chdir(root) do diffs.each do |diff| flag, key, v1, _ = diff # if key =~ /\[/ # keyname = key.match(/^(.*)\[\]$/).captures # elsif key =~ /\./ # keyname, subkey = key.match(/^(.*)\.(.*)$/).captures # else # keyname = key # end dirname, filename, fieldname = Treet::Repo.filefor(key) filepath = "#{dirname}/#{filename}" case flag when '~' # change a value in place # load the current data & overwrite with the new value # idempotent: this will overwrite the file with the same contents if fieldname # hash entry data = File.exists?(filepath) ? JSON.load(File.open(filepath)) : {} data[fieldname] = v1 File.open(filepath, "w") {|f| f << JSON.pretty_generate(data)} else # string entry File.open(filepath, "w") {|f| f << v1} end when '+' # add something if fieldname # writing a value into a hash # idempotent: this will overwrite the file with the same contents data = File.exists?(filepath) ? JSON.load(File.open(filepath)) : {} data[fieldname] = v1 Dir.mkdir(dirname) unless Dir.exists?(dirname) File.open(filepath, "w") {|f| f << JSON.pretty_generate(data)} else # writing an entire hash into an array entry # idempotent: this will overwrite the file with the same contents subfile = "#{dirname}/#{Treet::Hash.digestify(v1)}" Dir.mkdir(dirname) unless Dir.exists?(dirname) case v1 when Hash # hash entry File.open(subfile, "w") {|f| f << JSON.pretty_generate(v1)} else # string entry - create empty file with this name FileUtils.touch(subfile) end end when '-' # remove something if fieldname # this is a key in a subhash if File.exists?(filepath) # if the subhash is missing, there's nothing to remove, so do nothing (for idempotence) data = JSON.load(File.open(filepath)) data.delete(fieldname) if data.empty? # all keys have been removed, clean up the file File.delete(filename) else File.open(filepath, "w") {|f| f << JSON.pretty_generate(data)} end end elsif dirname == "." # this is a top-level string File.delete(filename) if File.exists?(filename) # need the existence check for idempotence else # this is an array, we look for a match on the entire contents via digest subfile = "#{dirname}/#{Treet::Hash.digestify(v1)}" File.delete(subfile) if File.exists?(subfile) # need the existence check for idempotence # TODO: if dirname is now empty, should it be removed? is that worthwhile? end end end end to_hash # ?? return the patched data? or no return value? true/false for success? end
to_hash()
click to toggle source
# File lib/treet/repo.rb, line 12 def to_hash expand(root) end
Private Instance Methods
expand(path)
click to toggle source
# File lib/treet/repo.rb, line 149 def expand(path) raw = filenames(path).each_with_object({}) {|f,h| h[f] = expand_json("#{path}/#{f}")} Treet::Hash.new(raw) end
expand_json(path)
click to toggle source
# File lib/treet/repo.rb, line 129 def expand_json(path) if File.file?(path) if File.zero?(path) # empty files are just keys or string elements in an array File.basename(path) else # if file contents is JSON, then parse it # otherwise treat it as a raw string value s = File.read(path) JSON.load(s) rescue s end else # should be a subdirectory containing files named with numbers, each containing JSON files = Dir.entries(path).select {|f| f !~ /^\./} files.sort_by(&:to_i).each_with_object([]) do |f, ary| ary << expand_json("#{path}/#{f}") end end end
filenames(path)
click to toggle source
# File lib/treet/repo.rb, line 154 def filenames(path) Dir.entries(path).select {|f| f !~ /^\./} end