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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
require "fileutils"
require "pathname"
module Mhc
class DataStore
def initialize(basedir)
unless basedir and File.directory?(File.expand_path(basedir.to_s))
raise Mhc::ConfigurationError, "datastore directory '#{basedir}' not found"
end
@basedir = Pathname.new(File.expand_path(basedir))
@cache = Cache.new(File.expand_path("status/cache/events.pstore", @basedir))
end
def entries(date_range = nil)
if date_range
int_range = date_range.min.absolute_from_epoch .. date_range.max.absolute_from_epoch
end
Enumerator.new do |yielder|
["inbox", "spool", "presets"].each do |slot|
dir = File.expand_path(slot, @basedir)
next unless File.directory?(dir)
Dir.chdir(dir) do
Dir.foreach(".") do |ent|
parse_mhcc(ent).each {|ev| yielder << ev} if /\.mhcc$/ =~ ent
next unless /\.mhc$/ =~ ent
uid = $`
cache_entry = @cache.lookup(uid, ent)
if !date_range || cache_entry.involved?(int_range)
yielder << Event.parse_file(File.expand_path(ent))
end
end
end
end
@cache.save
end
end
def logger
@logger ||= Mhc::Logger.new(@logfile)
end
def find_by_uid(uid)
path = find_path(uid)
return nil unless path
return Event.parse_file(path)
end
def create(event)
if find_by_uid(event.uid)
raise "Already exist uid:#{uid} in #{@basedir}"
end
File.open(path, "w") do |f|
f.write(event.dump)
end
end
def update(event)
unless path = uid_to_path(event.uid)
raise "Not found uid:#{uid} in #{@basedir}"
end
File.open(path, "w") do |f|
f.write(event.dump)
end
end
def delete(uid_or_event)
uid = if uid_or_event.respond_to?(:uid)
uid_or_event.uid
else
uid_or_event
end
if path = find_path(uid)
File.delete(path)
else
raise "Not found uid:#{uid} in #{@basedir}"
end
end
################################################################
private
def parse_mhcc(filename)
string = File.open(filename).read.scrub.gsub(/^\s*#.*$/, "").strip
string.split(/\n\n\n*/).map do |header|
Event.parse(header)
end
end
def find_path(uid)
glob = @basedir + ('**/' + uid + '.mhc')
return Dir.glob(glob).first
end
def uid_to_path(uid)
return @basedir + ('spool/' + uid + '.mhc')
end
end # class DataStore
end # module Mhc
module Mhc
class DataStore
class Cache
require 'pstore'
def initialize(cache_filename)
@pstore = PStore.new(cache_filename)
load
end
def lookup(uid, filename)
unless c = get(uid) and File.mtime(filename).to_i <= c.mtime
c = CacheEntry.new(filename)
put(uid, c)
end
return c
end
def save
return self unless @dirty
@pstore.transaction do
@pstore["root"] = @db
end
@dirty = false
end
private
def get(uid)
@db[uid]
end
def put(uid, value)
@db[uid] = value
@dirty = true
end
def load
@pstore.transaction do
@db = @pstore["root"] || {}
end
@dirty = false
end
end # class Cache
class CacheEntry
attr_reader :mtime, :range
def initialize(filename)
@mtime, event = File.mtime(filename).to_i, Event.parse_file(filename)
@range = event.range.min.absolute_from_epoch ..
event.range.max.absolute_from_epoch
end
def involved?(range)
range.min <= @range.max && @range.min <= range.max
end
end # class CacheEntry
end # class DataStore
end # module Mhc
|