Object
A cloche is a local JSON hashes store.
Warning : cloches are process-safe but not thread-safe.
Creates a new 'cloche'.
There are 2 options :
:dir : to specify the directory into which the cloche data is store
:nolock : when set to true, no flock is used
On the Windows platform, :nolock is set to true automatically.
With JRuby 1.4.0, :nolock=true seems necessary on Ubuntu. While flock seems to work on MacOSX Snow Leopard.
# File lib/rufus/cloche.rb, line 64 def initialize (opts={}) @dir = File.expand_path(opts[:dir] || 'cloche') @mutex = Mutex.new @nolock = WIN || opts[:nolock] end
Attempts at deleting a document. You have to pass the current version or at least the { '_id' => i, 'type' => t, '_rev' => r }.
Will return nil if the deletion is successful.
If the deletion failed because the given doc has an older revision number that the one currently stored, the doc in its freshest version will be returned.
Returns true if the deletion failed.
# File lib/rufus/cloche.rb, line 137 def delete (doc) drev = doc['_rev'] raise ArgumentError.new('cannot delete doc without _rev') unless drev type, key = doc['type'], doc['_id'] r = lock(false, type, key) do |f| cur = do_get(f) return nil unless cur return cur if cur['_rev'] != drev begin f.close File.delete(f.path) nil rescue Exception => e #p e false end end r == false ? true : nil end
Gets a document (or nil if not found (or corrupted)).
# File lib/rufus/cloche.rb, line 119 def get (type, key) r = lock(false, type, key) { |f| do_get(f) } r == false ? nil : r end
Given a type, this method will return an array of all the documents for that type.
A optional second parameter may be used to select, based on a regular expression, which documents to include (match on the key '_id').
Will return an empty Hash if there is no documents for a given type.
The only option know for now is :limit, which limits the number of documents returned.
# File lib/rufus/cloche.rb, line 178 def get_many (type, key_match=nil, opts={}) opts = opts.inject({}) { |h, (k, v)| h[k.to_s] = v; h } d = dir_for(type) return [] unless File.exist?(d) docs = [] limit = opts['limit'] files = Dir[File.join(d, '**', '*.json')].sort { |p0, p1| File.basename(p0) <=> File.basename(p1) } files.each do |fn| key = File.basename(fn, '.json') if (not key_match) || key.match(key_match) doc = get(type, key) docs << doc if doc break if limit && (docs.size >= limit) end end # WARNING : there is a twist here, the filenames may have a different # sort order from actual _ids... #docs.sort { |doc0, doc1| doc0['_id'] <=> doc1['_id'] } # let's trust filename order docs end
Returns a sorted list of all the ids for a given type of documents.
Warning : trusts the ids to be identical to the filenames
# File lib/rufus/cloche.rb, line 226 def ids (type) Dir[File.join(dir_for(type), '**', '*.json')].collect { |p| File.basename(p, '.json') }.sort end
Removes entirely documents of a given type.
# File lib/rufus/cloche.rb, line 217 def purge_type! (type) FileUtils.rm_rf(dir_for(type)) end
Puts a document (Hash) under the cloche.
If the document is brand new, it will be given a revision number '_rev' of 0.
If the document already exists in the cloche and the version to put has an older (different) revision number than the one currently stored, put will fail and return the current version of the doc.
If the put is successful, nil is returned.
# File lib/rufus/cloche.rb, line 83 def put (doc, opts={}) opts = opts.inject({}) { |h, (k, v)| h[k.to_s] = v; h } doc = Rufus::Json.dup(doc) unless opts['update_rev'] # work with a copy, don't touch original type, key = doc['type'], doc['_id'] raise( ArgumentError.new("missing values for keys 'type' and/or '_id'") ) if type.nil? || key.nil? rev = (doc['_rev'] ||= -1) raise( ArgumentError.new("values for '_rev' must be positive integers") ) if rev.class != Fixnum && rev.class != Bignum lock(true, type, key) do |file| cur = do_get(file) return cur if cur && cur['_rev'] != doc['_rev'] return true if cur.nil? && doc['_rev'] != -1 doc['_rev'] = doc['_rev'] + 1 File.open(file.path, 'wb') { |io| io.write(Rufus::Json.encode(doc)) } end nil end
Returns a sorted list of all the ids for a given type of documents.
Actually reads each file and returns the real _id list
# File lib/rufus/cloche.rb, line 237 def real_ids (type) Dir[File.join(dir_for(type), '**', '*.json')].inject([]) { |a, p| doc = do_get(p) a << doc['_id'] if doc a }.sort end
# File lib/rufus/cloche.rb, line 259 def dir_for (type) File.join(@dir, Cloche.neutralize(type || 'no_type')) end
# File lib/rufus/cloche.rb, line 253 def do_get (file) s = file.is_a?(File) ? file.read : File.read(file) Rufus::Json.decode(s) rescue nil end
# File lib/rufus/cloche.rb, line 273 def lock (create, type, key, &block) @mutex.synchronize do begin d, f = path_for(type, key) fn = File.join(d, f) if create && ( ! File.exist?(fn)) FileUtils.mkdir_p(d) unless File.exist?(d) FileUtils.touch(fn) unless File.exist?(fn) end file = File.new(fn) rescue nil return false if file.nil? file.flock(File::LOCK_EX) unless @nolock block.call(file) ensure begin file.flock(File::LOCK_UN) unless @nolock rescue Exception => e #p [ :lock, @fpath, e ] #e.backtrace.each { |l| puts l } end begin file.close if file rescue Exception => e #p [ :close_fail, e ] end end end end
Generated with the Darkfish Rdoc Generator 2.