lib/gitmodel.rb in gitmodel-0.0.4 vs lib/gitmodel.rb in gitmodel-0.0.5

- old
+ new

@@ -1,14 +1,17 @@ +# coding: UTF-8 + require 'rubygems' require 'bundler/setup' require 'active_model' require 'active_support/all' # TODO we don't really want all here, clean this up require 'grit' -require 'yajl' require 'lockfile' +require 'memcache' require 'pp' +require 'yajl' $:.unshift(File.dirname(__FILE__)) require 'gitmodel/errors' require 'gitmodel/index' require 'gitmodel/persistable' @@ -30,10 +33,13 @@ self.logger.level = ::Logger::WARN mattr_accessor :git_user_name mattr_accessor :git_user_email + mattr_accessor :memcache_servers + mattr_accessor :memcache_namespace + def self.repo @@repo = Grit::Repo.new(GitModel.db_root) end # Create the database defined in db_root. Raises an exception if it exists. @@ -54,30 +60,66 @@ logger.info "Deleting database #{db_root}!!" FileUtils.rm_rf db_root create_db! end - def self.last_commit(branch = nil) - branch ||= default_branch - # PERFORMANCE Cache this somewhere and update it on commit? - # (Need separate instance per branch) - - return nil unless repo.commits(branch).any? - - # We should be able to use just repo.commits(branch).first here but - # this is a workaround for this bug: - # http://github.com/mojombo/grit/issues/issue/38 - GitModel.repo.commits("#{branch}^..#{branch}").first || GitModel.repo.commits(branch).first + def self.last_commit(branch) + cache(branch, 'last-commit') do + unless repo.commits(branch).any? + nil + else + # We should be able to use just repo.commits(branch).first here but + # this is a workaround for this bug: + # http://github.com/mojombo/grit/issues/issue/38 + GitModel.repo.commits("#{branch}^..#{branch}").first || GitModel.repo.commits(branch).first + end + end end - def self.current_tree(branch = nil) + def self.current_tree(branch) c = last_commit(branch) c ? c.tree : nil end - def self.index! - dirs = (GitModel.current_tree).trees + def self.index!(branch) + dirs = (GitModel.current_tree(branch)).trees dirs.each do |dir| - dir.name.classify.constantize.index! + dir.name.classify.constantize.index!(branch) end + end + + # If we're using memcached (i.e. the memcache_servers setting is not nil) and + # the key exists in memcached, it's value will be returned and the block will + # not be run. If key does not exist in memcached, block will be executed, + # it's value stored in memcached under key, and value will be returned. + # + # There's no need to sweep the cache because the SHA of the latest Git commit + # is appended to the key, so any database change invalidates all cached + # objects. + def self.cache(branch, key, &block) + key = "#{key}-#{head_sha(branch)}" + value = nil + if memcache_servers + @@memcache ||= MemCache.new memcache_servers, :namespace => "#{File.basename(db_root)}-#{memcache_namespace}" + value = @@memcache.get(key) + if value.nil? + logger.info("✗ memcache MISS for key #{key}") + value = yield + @@memcache.set(key, value) + else + logger.info("✔ memcache HIT for key #{key}") + end + else + logger.debug("No memcache servers defined, not checking cache for key #{key}") + value = yield + end + value + end + + private + + # A more efficient way to get the SHA of the HEAD of the given branch + def self.head_sha(branch_name) + ref = File.join(repo.git.git_dir, "refs/heads/#{branch_name}") + File.exist?(ref) ? File.read(ref).chomp : nil end end