From 62c4b795be45bc6c7e3e47deec69af44d8015973 Mon Sep 17 00:00:00 2001 From: rick Date: Fri, 8 Oct 2010 13:36:18 -0700 Subject: [PATCH 01/19] add basic GitAccess class --- lib/gollum.rb | 1 + lib/gollum/git_access.rb | 19 +++++++++++++++++++ lib/gollum/wiki.rb | 7 ++++--- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 lib/gollum/git_access.rb diff --git a/lib/gollum.rb b/lib/gollum.rb index 36bcab92..fd2a4ab1 100644 --- a/lib/gollum.rb +++ b/lib/gollum.rb @@ -11,6 +11,7 @@ require 'sanitize' require 'gollum/ruby1.8' # internal +require 'gollum/git_access' require 'gollum/pagination' require 'gollum/blob_entry' require 'gollum/wiki' diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb new file mode 100644 index 00000000..7f9c299d --- /dev/null +++ b/lib/gollum/git_access.rb @@ -0,0 +1,19 @@ +module Gollum + class GitAccess + attr_reader :path + attr_reader :repo + attr_reader :ref_map + attr_reader :tree_map + + def initialize(path) + @path = path + @repo = Grit::Repo.new(path) + @ref_map = {} + @tree_map = {} + end + + def exist? + @repo.git.exist? + end + end +end \ No newline at end of file diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index 172aa9aa..d7f68556 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -59,7 +59,8 @@ module Gollum # Returns a fresh Gollum::Repo. def initialize(path, options = {}) @path = path - @repo = Grit::Repo.new(path) + @access = options[:access] || GitAccess.new(path) + @repo = @access.repo @base_path = options[:base_path] || "/" @page_class = options[:page_class] || self.class.page_class @file_class = options[:file_class] || self.class.file_class @@ -70,7 +71,7 @@ module Gollum # # Returns true if the repo exists, and false if it does not. def exist? - @repo.git.exist? + @access.exist? end # Public: Get the formatted page for a given page name. @@ -520,7 +521,7 @@ module Gollum data, name = line.split("\t") mode, type, sha = data.split(' ') name = decode_git_path(name) - BlobEntry.new sha, name + BlobEntry.new(sha, name) end # Decode octal sequences (\NNN) in tree path names. From ebf7855c090f26e9a0c0f35c55c792494794a0e1 Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 12:06:35 -0700 Subject: [PATCH 02/19] integrate Gollum::GitAccess --- lib/gollum/blob_entry.rb | 10 ++-- lib/gollum/file.rb | 5 +- lib/gollum/git_access.rb | 98 ++++++++++++++++++++++++++++++++++++++-- lib/gollum/page.rb | 5 +- lib/gollum/wiki.rb | 98 +++++++++------------------------------- test/test_wiki.rb | 49 ++++++++++---------- 6 files changed, 151 insertions(+), 114 deletions(-) diff --git a/lib/gollum/blob_entry.rb b/lib/gollum/blob_entry.rb index b224fc4a..1c3f8f2e 100644 --- a/lib/gollum/blob_entry.rb +++ b/lib/gollum/blob_entry.rb @@ -6,10 +6,13 @@ module Gollum # Gets the String full path for this blob. attr_reader :path - def initialize(sha, path) + attr_reader :size + + def initialize(sha, path, size = nil) @sha = sha @path = path - @dir = @name = @blob = nil + @size = size + @dir = @name = @blob = nil end # Gets the normalized directory path for this blob. @@ -28,7 +31,8 @@ module Gollum # # Returns an unbaked Grit::Blob instance. def blob(repo) - @blob ||= Grit::Blob.create(repo, :id => @sha, :name => @name) + @blob ||= Grit::Blob.create(repo, + :id => @sha, :name => name, :size => @size) end # Gets a Page instance for this blob. diff --git a/lib/gollum/file.rb b/lib/gollum/file.rb index 8e7c33d3..295a5e52 100644 --- a/lib/gollum/file.rb +++ b/lib/gollum/file.rb @@ -53,11 +53,10 @@ module Gollum def find(name, version) checked = name.downcase map = @wiki.tree_map_for(version) - sha = @wiki.ref_map[version] || version if entry = map.detect { |entry| entry.path.downcase == checked } @path = name - @blob = Grit::Blob.create(@wiki.repo, :id => entry.sha, :name => entry.name) - @version = Grit::Commit.create(@wiki.repo, :id => sha) + @blob = entry.blob(@wiki.repo) + @version = @wiki.commit_for(version) self end end diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 7f9c299d..9887f70c 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -2,18 +2,108 @@ module Gollum class GitAccess attr_reader :path attr_reader :repo + + # Gets a Hash cache of refs to commit SHAs. + # + # {"master" => "abc123", ...} + # + # Returns the Hash cache. attr_reader :ref_map + + # Gets a Hash cache of commit SHAs to a recursive tree of blobs. + # + # {"abc123" => [["lib/foo.rb", "blob-sha"], [file, sha], ...], ...} + # + # Returns the Hash cache. attr_reader :tree_map + attr_reader :commit_map def initialize(path) - @path = path - @repo = Grit::Repo.new(path) - @ref_map = {} - @tree_map = {} + @path = path + @repo = Grit::Repo.new(path) + clear + end + + def clear + @ref_map = {} + @tree_map = {} + @commit_map = {} + end + + def refresh + @ref_map.clear end def exist? @repo.git.exist? end + + def ref_to_sha(ref) + @ref_map[ref] ||= ref_to_sha!(ref) + end + + def tree(ref) + sha = ref_to_sha(ref) + @tree_map[sha] ||= tree!(sha) + end + + def blob(sha) + cat_file!(sha) + end + + def commit(ref) + if sha = @ref_map[ref] + @commit_map[sha] ||= commit!(sha) + else + cm = commit!(ref) + @ref_map[ref] = cm.id + @commit_map[cm.id] = cm + end + end + + def ref_to_sha!(ref) + @repo.git.rev_list({:max_count=>1}, ref) + end + + def tree!(sha) + tree = @repo.git.native(:ls_tree, + {:r => true, :l => true, :z => true}, sha) + tree.split("\0").inject([]) do |items, line| + items << parse_tree_line(line) + end + end + + def cat_file!(sha) + @repo.git.cat_file({:p => true}, sha) + end + + def commit!(sha) + @repo.commit(sha) + end + + # Parses a line of output from the `ls-tree` command. + # + # line - A String line of output: + # "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md" + # + # Returns an Array of BlobEntry instances. + def parse_tree_line(line) + mode, type, sha, size, *name = line.split(/\s+/) + BlobEntry.new(sha, name.to_s, size.to_i) + end + + # Decode octal sequences (\NNN) in tree path names. + # + # path - String path name. + # + # Returns a decoded String. + def decode_git_path(path) + if path[0] == ?" && path[-1] == ?" + path = path[1...-1] + path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr } + end + path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) } + path + end end end \ No newline at end of file diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index ba091458..21a9cbb4 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -272,9 +272,8 @@ module Gollum def find(name, version) map = @wiki.tree_map_for(version) if page = find_page_in_tree(map, name) - sha = @wiki.ref_map[version] || version - page.version = Grit::Commit.create(@wiki.repo, :id => sha) - page.historical = sha == version + page.version = @wiki.commit_for(version) + page.historical = page.version.id == version page end rescue Grit::GitRuby::Repository::NoSuchShaFound diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index d7f68556..9b13d6a6 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -58,13 +58,16 @@ module Gollum # # Returns a fresh Gollum::Repo. def initialize(path, options = {}) + if path.is_a?(GitAccess) + options[:access] = path + path = path.path + end @path = path - @access = options[:access] || GitAccess.new(path) - @repo = @access.repo + @access = options[:access] || GitAccess.new(path) @base_path = options[:base_path] || "/" @page_class = options[:page_class] || self.class.page_class @file_class = options[:file_class] || self.class.file_class - clear_cache + @repo = @access.repo end # Public: check whether the wiki's git repo exists on the filesystem. @@ -109,7 +112,7 @@ module Gollum path = @page_class.cname(name) + '.' + ext blob = OpenStruct.new(:name => path, :data => data) page.populate(blob, path) - page.version = self.repo.commit("HEAD") + page.version = @access.commit('HEAD') page end @@ -138,7 +141,7 @@ module Gollum actor = Grit::Actor.new(commit[:name], commit[:email]) sha1 = index.commit(commit[:message], parents, actor) - @ref_map.clear + @access.refresh update_working_dir(index, '', name, format) sha1 @@ -181,7 +184,7 @@ module Gollum actor = Grit::Actor.new(commit[:name], commit[:email]) sha1 = index.commit(commit[:message], [pcommit], actor) - @ref_map.clear + @access.refresh update_working_dir(index, dir, page.name, page.format) update_working_dir(index, dir, name, format) @@ -210,7 +213,7 @@ module Gollum actor = Grit::Actor.new(commit[:name], commit[:email]) sha1 = index.commit(commit[:message], [pcommit], actor) - @ref_map.clear + @access.refresh update_working_dir(index, dir, page.name, page.format) sha1 @@ -267,6 +270,10 @@ module Gollum @repo.log('master', nil, log_pagination_options(options)) end + def clear_cache + @access.refresh + end + ######################################################################### # # Internal Methods @@ -283,20 +290,6 @@ module Gollum # Returns the String path. attr_reader :path - # Gets a Hash cache of refs to commit SHAs. - # - # {"master" => "abc123", ...} - # - # Returns the Hash cache. - attr_reader :ref_map - - # Gets a Hash cache of commit SHAs to a recursive tree of blobs. - # - # {"abc123" => [["lib/foo.rb", "blob-sha"], [file, sha], ...], ...} - # - # Returns the Hash cache. - attr_reader :tree_map - # Gets the page class used by all instances of this Wiki. attr_reader :page_class @@ -359,8 +352,7 @@ module Gollum def tree_list(ref) tree_map_for(ref).inject([]) do |list, entry| next list unless @page_class.valid_page_name?(entry.name) - sha = ref_map[ref] - list << entry.page(self, @repo.commit(sha)) + list << entry.page(self, @access.commit(ref)) end end @@ -479,6 +471,11 @@ module Gollum @repo.config['user.email'] || self.class.default_committer_email end + def commit_for(ref) + @access.commit(ref) + rescue Grit::GitRuby::Repository::NoSuchShaFound + end + # Finds a full listing of files and their blob SHA for a given ref. Each # listing is cached based on its actual commit SHA. # @@ -486,62 +483,9 @@ module Gollum # # Returns an Array of BlobEntry instances. def tree_map_for(ref) - sha = @ref_map[ref] || ref - @tree_map[sha] || begin - real_sha = @repo.git.rev_list({:max_count=>1}, ref) - @ref_map[ref] = real_sha if real_sha != ref - @tree_map[real_sha] ||= parse_tree_for(real_sha) - end + @access.tree(ref) rescue Grit::GitRuby::Repository::NoSuchShaFound [] end - - # Finds the full listing of files and their blob SHA for a given commit - # SHA. No caching or ref lookups are performed. - # - # sha - String commit SHA. - # - # Returns an Array of BlobEntry instances. - def parse_tree_for(sha) - tree = @repo.git.native(:ls_tree, {:r => true, :z => true}, sha) - items = [] - tree.split("\0").each do |line| - items << parse_tree_line(line) - end - items - end - - # Parses a line of output from the `ls-tree` command. - # - # line - A String line of output: - # "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md" - # - # Returns an Array of BlobEntry instances. - def parse_tree_line(line) - data, name = line.split("\t") - mode, type, sha = data.split(' ') - name = decode_git_path(name) - BlobEntry.new(sha, name) - end - - # Decode octal sequences (\NNN) in tree path names. - # - # path - String path name. - # - # Returns a decoded String. - def decode_git_path(path) - if path[0] == ?" && path[-1] == ?" - path = path[1...-1] - path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr } - end - path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) } - path - end - - # Resets the ref and tree caches for this wiki. - def clear_cache - @ref_map = {} - @tree_map = {} - end end end diff --git a/test/test_wiki.rb b/test/test_wiki.rb index 51cd287b..36642161 100644 --- a/test/test_wiki.rb +++ b/test/test_wiki.rb @@ -2,7 +2,8 @@ require File.join(File.dirname(__FILE__), *%w[helper]) context "Wiki" do setup do - @wiki = Gollum::Wiki.new(testpath("examples/lotr.git")) + @access = Gollum::GitAccess.new(testpath("examples/lotr.git")) + @wiki = Gollum::Wiki.new(@access) end test "repo path" do @@ -60,29 +61,29 @@ context "Wiki" do @wiki.normalize_commit(commit.dup)) end - test "#tree_map_for caches ref and tree" do - assert @wiki.ref_map.empty? - assert @wiki.tree_map.empty? - @wiki.tree_map_for 'master' - assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @wiki.ref_map) - - map = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'] - assert_equal 'Bilbo-Baggins.md', map[0].path - assert_equal '', map[0].dir - assert_equal map[0].path, map[0].name - assert_equal 'Mordor/Eye-Of-Sauron.md', map[3].path - assert_equal '/Mordor', map[3].dir - assert_equal 'Eye-Of-Sauron.md', map[3].name - end - - test "#tree_map_for only caches tree for commit" do - assert @wiki.tree_map.empty? - @wiki.tree_map_for '60f12f4254f58801b9ee7db7bca5fa8aeefaa56b' - assert @wiki.ref_map.empty? - - entry = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0] - assert_equal 'Bilbo-Baggins.md', entry.path - end + #test "#tree_map_for caches ref and tree" do + # assert @wiki.ref_map.empty? + # assert @wiki.tree_map.empty? + # @wiki.tree_map_for 'master' + # assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @wiki.ref_map) + # + # map = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'] + # assert_equal 'Bilbo-Baggins.md', map[0].path + # assert_equal '', map[0].dir + # assert_equal map[0].path, map[0].name + # assert_equal 'Mordor/Eye-Of-Sauron.md', map[3].path + # assert_equal '/Mordor', map[3].dir + # assert_equal 'Eye-Of-Sauron.md', map[3].name + #end + # + #test "#tree_map_for only caches tree for commit" do + # assert @access.tree_map.empty? + # @access.tree '60f12f4254f58801b9ee7db7bca5fa8aeefaa56b' + # assert @access.ref_map.empty? + # + # entry = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0] + # assert_equal 'Bilbo-Baggins.md', entry.path + #end end context "Wiki page previewing" do From a92a882021e4b889687f7279cf0f36de5cb3685c Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 13:32:14 -0700 Subject: [PATCH 03/19] restore ref_map and tree_map tests --- lib/gollum/git_access.rb | 6 +++++- test/test_git_access.rb | 31 +++++++++++++++++++++++++++++++ test/test_wiki.rb | 27 +-------------------------- 3 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 test/test_git_access.rb diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 9887f70c..a27225b4 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -39,7 +39,11 @@ module Gollum end def ref_to_sha(ref) - @ref_map[ref] ||= ref_to_sha!(ref) + if ref =~ /^[0-9a-f]{40}$/ + ref + else + @ref_map[ref] ||= ref_to_sha!(ref) + end end def tree(ref) diff --git a/test/test_git_access.rb b/test/test_git_access.rb new file mode 100644 index 00000000..8d991fea --- /dev/null +++ b/test/test_git_access.rb @@ -0,0 +1,31 @@ +require File.join(File.dirname(__FILE__), *%w[helper]) + +context "GitAccess" do + setup do + @access = Gollum::GitAccess.new(testpath("examples/lotr.git")) + end + + test "#tree_map_for caches ref and tree" do + assert @access.ref_map.empty? + assert @access.tree_map.empty? + @access.tree 'master' + assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @access.ref_map) + + map = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'] + assert_equal 'Bilbo-Baggins.md', map[0].path + assert_equal '', map[0].dir + assert_equal map[0].path, map[0].name + assert_equal 'Mordor/Eye-Of-Sauron.md', map[3].path + assert_equal '/Mordor', map[3].dir + assert_equal 'Eye-Of-Sauron.md', map[3].name + end + + test "#tree_map_for only caches tree for commit" do + assert @access.tree_map.empty? + @access.tree '60f12f4254f58801b9ee7db7bca5fa8aeefaa56b' + assert @access.ref_map.empty? + + entry = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0] + assert_equal 'Bilbo-Baggins.md', entry.path + end +end \ No newline at end of file diff --git a/test/test_wiki.rb b/test/test_wiki.rb index 36642161..7d9ac0b3 100644 --- a/test/test_wiki.rb +++ b/test/test_wiki.rb @@ -2,8 +2,7 @@ require File.join(File.dirname(__FILE__), *%w[helper]) context "Wiki" do setup do - @access = Gollum::GitAccess.new(testpath("examples/lotr.git")) - @wiki = Gollum::Wiki.new(@access) + @wiki = Gollum::Wiki.new(testpath("examples/lotr.git")) end test "repo path" do @@ -60,30 +59,6 @@ context "Wiki" do assert_equal({:message => 'abc', :name => 'bob', :email => 'foo@bar.com'}, @wiki.normalize_commit(commit.dup)) end - - #test "#tree_map_for caches ref and tree" do - # assert @wiki.ref_map.empty? - # assert @wiki.tree_map.empty? - # @wiki.tree_map_for 'master' - # assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @wiki.ref_map) - # - # map = @wiki.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'] - # assert_equal 'Bilbo-Baggins.md', map[0].path - # assert_equal '', map[0].dir - # assert_equal map[0].path, map[0].name - # assert_equal 'Mordor/Eye-Of-Sauron.md', map[3].path - # assert_equal '/Mordor', map[3].dir - # assert_equal 'Eye-Of-Sauron.md', map[3].name - #end - # - #test "#tree_map_for only caches tree for commit" do - # assert @access.tree_map.empty? - # @access.tree '60f12f4254f58801b9ee7db7bca5fa8aeefaa56b' - # assert @access.ref_map.empty? - # - # entry = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0] - # assert_equal 'Bilbo-Baggins.md', entry.path - #end end context "Wiki page previewing" do From 6552323797bc8c97f69002394a89feb8dce17e0e Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 16:01:06 -0700 Subject: [PATCH 04/19] implement GitAccess#commits using grit head --- lib/gollum/git_access.rb | 37 ++++++++++++++++++++++++++++++++++--- test/test_git_access.rb | 17 +++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index a27225b4..9f259bee 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -39,7 +39,7 @@ module Gollum end def ref_to_sha(ref) - if ref =~ /^[0-9a-f]{40}$/ + if sha?(ref) ref else @ref_map[ref] ||= ref_to_sha!(ref) @@ -56,15 +56,46 @@ module Gollum end def commit(ref) - if sha = @ref_map[ref] + ref_is_sha = sha?(ref) + if sha = (!ref_is_sha && @ref_map[ref]) @commit_map[sha] ||= commit!(sha) else cm = commit!(ref) - @ref_map[ref] = cm.id + @ref_map[ref] = cm.id if !ref_is_sha @commit_map[cm.id] = cm end end + def commits(*shas) + shas.flatten! + cached_commits = multi_get(:commit_map, shas) + missing_shas = shas.select do |sha| + !cached_commits.key?(sha) + end + if !missing_shas.empty? + missing_shas.each_slice(500) do |slice| + @repo.batch(slice).each do |commit| + cached_commits[commit.id] = commit + end + end + end + shas.map { |sha| cached_commits[sha] } + end + + def multi_get(name, keys) + value = instance_variable_get("@#{name}") + keys.inject({}) do |memo, key| + if v = value[key] + memo[key] = v + end + memo + end + end + + def sha?(str) + str =~ /^[0-9a-f]{40}$/ + end + def ref_to_sha!(ref) @repo.git.rev_list({:max_count=>1}, ref) end diff --git a/test/test_git_access.rb b/test/test_git_access.rb index 8d991fea..7e62c689 100644 --- a/test/test_git_access.rb +++ b/test/test_git_access.rb @@ -5,6 +5,23 @@ context "GitAccess" do @access = Gollum::GitAccess.new(testpath("examples/lotr.git")) end + test "#commit fills commit_map cache" do + assert @access.commit_map.empty? + actual = @access.repo.commits.first + expected = @access.commit(actual.id) + assert_equal actual.message, expected.message + assert_equal actual.message, @access.commit_map[actual.id].message + end + + test "#commits uses commit_map" do + actual = @access.repo.commits.first + #@access.commit actual.id + @access.commit_map['abc'] = 1 + commits = @access.commits('abc', actual.id) + assert_equal 1, commits[0] + assert_equal actual.message, commits[1].message + end + test "#tree_map_for caches ref and tree" do assert @access.ref_map.empty? assert @access.tree_map.empty? From 300d8eacc57ed002a9beaf451550c2097ad4020b Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 16:15:18 -0700 Subject: [PATCH 05/19] optimize Gollum::Wiki#tree_list --- lib/gollum/file.rb | 2 +- lib/gollum/git_access.rb | 15 +++++++++------ lib/gollum/page.rb | 7 ++++--- lib/gollum/wiki.rb | 6 ++++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/gollum/file.rb b/lib/gollum/file.rb index 295a5e52..acea37a6 100644 --- a/lib/gollum/file.rb +++ b/lib/gollum/file.rb @@ -56,7 +56,7 @@ module Gollum if entry = map.detect { |entry| entry.path.downcase == checked } @path = name @blob = entry.blob(@wiki.repo) - @version = @wiki.commit_for(version) + @version = version.is_a?(Grit::Commit) ? version : @wiki.commit_for(version) self end end diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 9f259bee..5fadef6f 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -56,13 +56,16 @@ module Gollum end def commit(ref) - ref_is_sha = sha?(ref) - if sha = (!ref_is_sha && @ref_map[ref]) - @commit_map[sha] ||= commit!(sha) + if sha?(ref) + @commit_map[ref] ||= commit!(ref) else - cm = commit!(ref) - @ref_map[ref] = cm.id if !ref_is_sha - @commit_map[cm.id] = cm + if sha = @ref_map[ref] + commit(sha) + else + cm = commit!(ref) + @ref_map[ref] = cm.id + @commit_map[cm.id] = cm + end end end diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 21a9cbb4..32bdd6ae 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -270,10 +270,11 @@ module Gollum # # Returns a Gollum::Page or nil if the page could not be found. def find(name, version) - map = @wiki.tree_map_for(version) + map = @wiki.tree_map_for(version.to_s) if page = find_page_in_tree(map, name) - page.version = @wiki.commit_for(version) - page.historical = page.version.id == version + page.version = version.is_a?(Grit::Commit) ? + version : @wiki.commit_for(version) + page.historical = page.version.to_s == version.to_s page end rescue Grit::GitRuby::Repository::NoSuchShaFound diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index 9b13d6a6..c8a4ee2c 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -350,9 +350,11 @@ module Gollum # # Returns a flat Array of Gollum::Page instances. def tree_list(ref) - tree_map_for(ref).inject([]) do |list, entry| + sha = @access.ref_to_sha(ref) + commit = @access.commit(sha) + tree_map_for(sha).inject([]) do |list, entry| next list unless @page_class.valid_page_name?(entry.name) - list << entry.page(self, @access.commit(ref)) + list << entry.page(self, commit) end end From b8a50c0ccc139433119c5e18bb0bf1e7907e6788 Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 17:03:44 -0700 Subject: [PATCH 06/19] updated experimental gemspec --- gollum.gemspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gollum.gemspec b/gollum.gemspec index 521f8bbf..2b13efe3 100644 --- a/gollum.gemspec +++ b/gollum.gemspec @@ -460,6 +460,7 @@ Gem::Specification.new do |s| lib/gollum/frontend/views/layout.rb lib/gollum/frontend/views/page.rb lib/gollum/frontend/views/search.rb + lib/gollum/git_access.rb lib/gollum/markup.rb lib/gollum/page.rb lib/gollum/pagination.rb @@ -504,6 +505,7 @@ Gem::Specification.new do |s| test/examples/lotr.git/refs/heads/master test/helper.rb test/test_file.rb + test/test_git_access.rb test/test_markup.rb test/test_page.rb test/test_wiki.rb From c6dc4acc0298ebaf30fff8a577ae3f0d91716469 Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 11 Oct 2010 18:05:02 -0700 Subject: [PATCH 07/19] add extension points for GitAccess caching --- lib/gollum/git_access.rb | 48 ++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 5fadef6f..ed5a7b59 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -42,13 +42,13 @@ module Gollum if sha?(ref) ref else - @ref_map[ref] ||= ref_to_sha!(ref) + get_cache(:ref, ref) { ref_to_sha!(ref) } end end def tree(ref) sha = ref_to_sha(ref) - @tree_map[sha] ||= tree!(sha) + get_cache(:tree, sha) { tree!(sha) } end def blob(sha) @@ -57,21 +57,21 @@ module Gollum def commit(ref) if sha?(ref) - @commit_map[ref] ||= commit!(ref) + get_cache(:commit, ref) { commit!(ref) } else - if sha = @ref_map[ref] + if sha = get_cache(:ref, ref) commit(sha) else cm = commit!(ref) - @ref_map[ref] = cm.id - @commit_map[cm.id] = cm + set_cache(:ref, ref, cm.id) + set_cache(:commit, cm.id, cm) end end end def commits(*shas) shas.flatten! - cached_commits = multi_get(:commit_map, shas) + cached_commits = multi_get(:commit, shas) missing_shas = shas.select do |sha| !cached_commits.key?(sha) end @@ -85,16 +85,6 @@ module Gollum shas.map { |sha| cached_commits[sha] } end - def multi_get(name, keys) - value = instance_variable_get("@#{name}") - keys.inject({}) do |memo, key| - if v = value[key] - memo[key] = v - end - memo - end - end - def sha?(str) str =~ /^[0-9a-f]{40}$/ end @@ -119,6 +109,30 @@ module Gollum @repo.commit(sha) end + def get_cache(name, key) + cache = instance_variable_get("@#{name}_map") + value = cache[key] + if value.nil? && block_given? + set_cache(name, key, value = yield) + end + value + end + + def set_cache(name, key, value) + cache = instance_variable_get("@#{name}_map") + cache[key] = value + end + + def multi_get(name, keys) + value = instance_variable_get("@#{name}_map") + keys.inject({}) do |memo, key| + if v = value[key] + memo[key] = v + end + memo + end + end + # Parses a line of output from the `ls-tree` command. # # line - A String line of output: From f2ed24ef8cbdb23153c551b5ec013571352c21e4 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 12 Oct 2010 10:02:48 -0700 Subject: [PATCH 08/19] extract multi_commit! method from GitAccess --- lib/gollum/git_access.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index ed5a7b59..25bace47 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -75,14 +75,18 @@ module Gollum missing_shas = shas.select do |sha| !cached_commits.key?(sha) end - if !missing_shas.empty? - missing_shas.each_slice(500) do |slice| - @repo.batch(slice).each do |commit| - cached_commits[commit.id] = commit - end + + multi_commit!(missing_shas, cached_commits) if !missing_shas.empty? + + shas.map { |sha| cached_commits[sha] } + end + + def multi_commit!(shas, hash) + shas.each_slice(500) do |slice| + @repo.batch(slice).each do |commit| + hash[commit.id] = commit end end - shas.map { |sha| cached_commits[sha] } end def sha?(str) From e7f2da2d4a5dd42f0174ca03cd69dfc94bec1c44 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 12 Oct 2010 15:36:36 -0700 Subject: [PATCH 09/19] add caching hooks for Gollum::Markup --- lib/gollum/markup.rb | 39 ++++++++++++++++++++++++++++++++------- lib/gollum/page.rb | 2 +- lib/gollum/wiki.rb | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/lib/gollum/markup.rb b/lib/gollum/markup.rb index 2a78bbbf..b9a3bef3 100644 --- a/lib/gollum/markup.rb +++ b/lib/gollum/markup.rb @@ -348,8 +348,11 @@ module Gollum # Returns the placeholder'd String data. def extract_code(data) data.gsub(/^``` ?(.+?)\r?\n(.+?)\r?\n```\r?$/m) do - id = Digest::SHA1.hexdigest($2) - @codemap[id] = { :lang => $1, :code => $2 } + id = Digest::SHA1.hexdigest($2) + cached = check_cache(id) + @codemap[id] = cached ? + { :output => cached } : + { :lang => $1, :code => $2 } id end end @@ -362,14 +365,36 @@ module Gollum # Returns the marked up String data. def process_code(data) @codemap.each do |id, spec| - lang = spec[:lang] - code = spec[:code] - if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ } - code.gsub!(/^( |\t)/m, '') + formatted = spec[:output] || begin + lang = spec[:lang] + code = spec[:code] + if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ } + code.gsub!(/^( |\t)/m, '') + end + formatted = Gollum::Albino.new(code, lang).colorize + update_cache(id, formatted) + formatted end - data.gsub!(id, Gollum::Albino.new(code, lang).colorize) + data.gsub!(id, formatted) end data end + + # Hook for getting the formatted value of extracted tag data. + # + # id - String SHA1 hash of original extracted tag data. + # + # Returns the String cached formatted data, or nil. + def check_cache(id) + end + + # Hook for caching the formatted value of extracted tag data. + # + # id - String SHA1 hash of original extracted tag data. + # data - The String formatted value to be cached. + # + # Returns nothing. + def update_cache(id, data) + end end end diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 32bdd6ae..32f0ad51 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -120,7 +120,7 @@ module Gollum # # Returns the String data. def formatted_data - @blob && Gollum::Markup.new(self).render(historical?) + @blob && @wiki.markup_class.new(self).render(historical?) end # Public: The format of the page. diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index e1d265cd..9e529e17 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -9,6 +9,9 @@ module Gollum # Sets the file class used by all instances of this Wiki. attr_writer :file_class + # Sets the markup class used by all instances of this Wiki. + attr_writer :markup_class + # Sets the default name for commits. attr_accessor :default_committer_name @@ -36,6 +39,17 @@ module Gollum ::Gollum::File end end + + # Gets the markup class used by all instances of this Wiki. + # Default: Gollum::Markup + def markup_class + @markup_class || + if superclass.respond_to?(:markup_class) + superclass.markup_class + else + ::Gollum::Markup + end + end end self.default_committer_name = 'Anonymous' @@ -51,10 +65,11 @@ module Gollum # repo - The String path to the Git repository that holds the Gollum # site. # options - Optional Hash: - # :base_path - String base path for all Wiki links. - # Default: "/" - # :page_class - The page Class. Default: Gollum::Page - # :file_class - The file Class. Default: Gollum::File + # :base_path - String base path for all Wiki links. + # Default: "/" + # :page_class - The page Class. Default: Gollum::Page + # :file_class - The file Class. Default: Gollum::File + # :markup_class - The markup Class. Default: Gollum::Markup # # Returns a fresh Gollum::Repo. def initialize(path, options = {}) @@ -62,12 +77,13 @@ module Gollum options[:access] = path path = path.path end - @path = path - @access = options[:access] || GitAccess.new(path) - @base_path = options[:base_path] || "/" - @page_class = options[:page_class] || self.class.page_class - @file_class = options[:file_class] || self.class.file_class - @repo = @access.repo + @path = path + @access = options[:access] || GitAccess.new(path) + @base_path = options[:base_path] || "/" + @page_class = options[:page_class] || self.class.page_class + @file_class = options[:file_class] || self.class.file_class + @markup_class = options[:markup_class] || self.class.markup_class + @repo = @access.repo end # Public: check whether the wiki's git repo exists on the filesystem. @@ -298,6 +314,9 @@ module Gollum # Gets the file class used by all instances of this Wiki. attr_reader :file_class + # Gets the markup class used by all instances of this Wiki. + attr_reader :markup_class + # Normalize the data. # # data - The String data to be normalized. From 497b36ad2b49605025f70d3f0075b5fb72c43841 Mon Sep 17 00:00:00 2001 From: rick Date: Tue, 12 Oct 2010 15:46:55 -0700 Subject: [PATCH 10/19] give the markup caching methods a bit more info --- lib/gollum/markup.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/gollum/markup.rb b/lib/gollum/markup.rb index b9a3bef3..bf418b5b 100644 --- a/lib/gollum/markup.rb +++ b/lib/gollum/markup.rb @@ -349,7 +349,7 @@ module Gollum def extract_code(data) data.gsub(/^``` ?(.+?)\r?\n(.+?)\r?\n```\r?$/m) do id = Digest::SHA1.hexdigest($2) - cached = check_cache(id) + cached = check_cache(:code, id) @codemap[id] = cached ? { :output => cached } : { :lang => $1, :code => $2 } @@ -372,7 +372,7 @@ module Gollum code.gsub!(/^( |\t)/m, '') end formatted = Gollum::Albino.new(code, lang).colorize - update_cache(id, formatted) + update_cache(:code, id, formatted) formatted end data.gsub!(id, formatted) @@ -382,19 +382,21 @@ module Gollum # Hook for getting the formatted value of extracted tag data. # - # id - String SHA1 hash of original extracted tag data. + # type - Symbol value identifying what type of data is being extracted. + # id - String SHA1 hash of original extracted tag data. # # Returns the String cached formatted data, or nil. - def check_cache(id) + def check_cache(type, id) end # Hook for caching the formatted value of extracted tag data. # + # type - Symbol value identifying what type of data is being extracted. # id - String SHA1 hash of original extracted tag data. # data - The String formatted value to be cached. # # Returns nothing. - def update_cache(id, data) + def update_cache(type, id, data) end end end From 54917bbdb9f2df527c6a14f06cb9c2bb8415dd9d Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 13 Oct 2010 09:52:04 -0700 Subject: [PATCH 11/19] recover from bad shas --- lib/gollum/git_access.rb | 17 ++++++++++------- lib/gollum/page.rb | 2 +- test/test_git_access.rb | 15 +++++++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 25bace47..00ae88f0 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -47,8 +47,9 @@ module Gollum end def tree(ref) - sha = ref_to_sha(ref) - get_cache(:tree, sha) { tree!(sha) } + if sha = ref_to_sha(ref) + get_cache(:tree, sha) { tree!(sha) } + end end def blob(sha) @@ -62,9 +63,10 @@ module Gollum if sha = get_cache(:ref, ref) commit(sha) else - cm = commit!(ref) - set_cache(:ref, ref, cm.id) - set_cache(:commit, cm.id, cm) + if cm = commit!(ref) + set_cache(:ref, ref, cm.id) + set_cache(:commit, cm.id, cm) + end end end end @@ -95,6 +97,7 @@ module Gollum def ref_to_sha!(ref) @repo.git.rev_list({:max_count=>1}, ref) + rescue Grit::GitRuby::Repository::NoSuchShaFound end def tree!(sha) @@ -119,12 +122,12 @@ module Gollum if value.nil? && block_given? set_cache(name, key, value = yield) end - value + value == :_nil ? nil : value end def set_cache(name, key, value) cache = instance_variable_get("@#{name}_map") - cache[key] = value + cache[key] = value || :_nil end def multi_get(name, keys) diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 32f0ad51..a534371c 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -289,7 +289,7 @@ module Gollum # # Returns a Gollum::Page or nil if the page could not be found. def find_page_in_tree(map, name, checked_dir = nil) - return nil if name.to_s.empty? + return nil if !map || name.to_s.empty? if checked_dir = BlobEntry.normalize_dir(checked_dir) checked_dir.downcase! end diff --git a/test/test_git_access.rb b/test/test_git_access.rb index 7e62c689..2d9ac653 100644 --- a/test/test_git_access.rb +++ b/test/test_git_access.rb @@ -15,7 +15,6 @@ context "GitAccess" do test "#commits uses commit_map" do actual = @access.repo.commits.first - #@access.commit actual.id @access.commit_map['abc'] = 1 commits = @access.commits('abc', actual.id) assert_equal 1, commits[0] @@ -27,7 +26,7 @@ context "GitAccess" do assert @access.tree_map.empty? @access.tree 'master' assert_equal({"master"=>"60f12f4254f58801b9ee7db7bca5fa8aeefaa56b"}, @access.ref_map) - + map = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'] assert_equal 'Bilbo-Baggins.md', map[0].path assert_equal '', map[0].dir @@ -45,4 +44,16 @@ context "GitAccess" do entry = @access.tree_map['60f12f4254f58801b9ee7db7bca5fa8aeefaa56b'][0] assert_equal 'Bilbo-Baggins.md', entry.path end + + test "cannot access commit from invalid ref" do + assert_nil @access.commit('foo') + end + + test "cannot access sha from invalid ref" do + assert_nil @access.ref_to_sha('foo') + end + + test "cannot access tree from invalid ref" do + assert_nil @access.tree('foo') + end end \ No newline at end of file From 1a19278fc71ad0e03965aae8ffdf3b9731ed71ef Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 13 Oct 2010 12:06:23 -0700 Subject: [PATCH 12/19] GitAccess#tree should always return an array --- lib/gollum/git_access.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 00ae88f0..409a0197 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -49,6 +49,8 @@ module Gollum def tree(ref) if sha = ref_to_sha(ref) get_cache(:tree, sha) { tree!(sha) } + else + [] end end From 4dda9fb2ae73793c1065dbc914a9d19839b965f9 Mon Sep 17 00:00:00 2001 From: rick Date: Sat, 20 Nov 2010 13:08:39 -0800 Subject: [PATCH 13/19] tomdoc'd --- lib/gollum/blob_entry.rb | 7 +- lib/gollum/git_access.rb | 129 ++++++++++++++++++++++++++++++++----- lib/gollum/sanitization.rb | 1 - 3 files changed, 118 insertions(+), 19 deletions(-) diff --git a/lib/gollum/blob_entry.rb b/lib/gollum/blob_entry.rb index 1c3f8f2e..cbadc01c 100644 --- a/lib/gollum/blob_entry.rb +++ b/lib/gollum/blob_entry.rb @@ -3,9 +3,10 @@ module Gollum # Gets the String SHA for this blob. attr_reader :sha - # Gets the String full path for this blob. + # Gets the full path String for this blob. attr_reader :path + # Gets the Fixnum size of this blob. attr_reader :size def initialize(sha, path, size = nil) @@ -15,12 +16,12 @@ module Gollum @dir = @name = @blob = nil end - # Gets the normalized directory path for this blob. + # Gets the normalized directory path String for this blob. def dir @dir ||= self.class.normalize_dir(::File.dirname(@path)) end - # Gets the String file base name for this blob. + # Gets the file base name String for this blob. def name @name ||= ::File.basename(@path) end diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 409a0197..76568f73 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -1,21 +1,29 @@ module Gollum + # Controls all access to the Git objects from Gollum. Extend this class to + # add custom caching for special cases. class GitAccess + # Gets the String path to the Git repository. attr_reader :path + + # Gets the Grit::Repo instance for the Git repository. attr_reader :repo # Gets a Hash cache of refs to commit SHAs. # # {"master" => "abc123", ...} # - # Returns the Hash cache. attr_reader :ref_map # Gets a Hash cache of commit SHAs to a recursive tree of blobs. # - # {"abc123" => [["lib/foo.rb", "blob-sha"], [file, sha], ...], ...} + # {"abc123" => [, ]} # - # Returns the Hash cache. attr_reader :tree_map + + # Gets a Hash cache of commit SHAs to the Grit::Commit instance. + # + # {"abcd123" => } + # attr_reader :commit_map def initialize(path) @@ -24,20 +32,19 @@ module Gollum clear end - def clear - @ref_map = {} - @tree_map = {} - @commit_map = {} - end - - def refresh - @ref_map.clear - end - + # Public: Determines whether the Git repository exists on disk. + # + # Returns true if it exists, or false. def exist? @repo.git.exist? end + # Public: Converts a given Git reference to a SHA, using the cache if + # available. + # + # ref - a String Git reference (ex: "master") + # + # Returns a String. def ref_to_sha(ref) if sha?(ref) ref @@ -46,6 +53,12 @@ module Gollum end end + # Public: Gets a recursive list of Git blobs for the whole tree at the + # given commit. + # + # ref - A String Git reference or Git SHA to a commit. + # + # Returns an Array of BlobEntry instances. def tree(ref) if sha = ref_to_sha(ref) get_cache(:tree, sha) { tree!(sha) } @@ -54,10 +67,20 @@ module Gollum end end + # Public: Fetches the contents of the Git blob at the given SHA. + # + # sha - A String Git SHA. + # + # Returns the String content of the blob. def blob(sha) cat_file!(sha) end + # Public: Looks up the Git commit using the given Git SHA or ref. + # + # ref - A String Git SHA or ref. + # + # Returns a Grit::Commit. def commit(ref) if sha?(ref) get_cache(:commit, ref) { commit!(ref) } @@ -73,6 +96,11 @@ module Gollum end end + # Public: Gets a list of Git commits. + # + # *shas - An Array of String SHAs. + # + # Returns an Array of Grit::Commit instances. def commits(*shas) shas.flatten! cached_commits = multi_get(:commit, shas) @@ -85,23 +113,62 @@ module Gollum shas.map { |sha| cached_commits[sha] } end - def multi_commit!(shas, hash) + # Public: Clears all of the cached data that this GitAccess is tracking. + # + # Returns nothing. + def clear + @ref_map = {} + @tree_map = {} + @commit_map = {} + end + + # Public: Refreshes just the cached Git reference data. This should + # be called after every Gollum update. + # + # Returns nothing. + def refresh + @ref_map.clear + end + + # Raw method for fetching a list of Git commits. + # + # shas - An Array of String SHAs. + # hash - Optional Hash to store the found commits, indexed by their SHA. + # + # Returns the same Hash instance. + def multi_commit!(shas, hash = {}) shas.each_slice(500) do |slice| @repo.batch(slice).each do |commit| hash[commit.id] = commit end end + hash end + # Checks to see if the given String is a 40 character hex SHA. + # + # str - Possible String SHA. + # + # Returns true if the String is a SHA, or false. def sha?(str) - str =~ /^[0-9a-f]{40}$/ + !!(str =~ /^[0-9a-f]{40}$/) end + # Looks up the Git SHA for the given Git ref. + # + # ref - String Git ref. + # + # Returns a String SHA. def ref_to_sha!(ref) @repo.git.rev_list({:max_count=>1}, ref) rescue Grit::GitRuby::Repository::NoSuchShaFound end + # Looks up the Git blobs for a given commit. + # + # sha - String commit SHA. + # + # Returns an Array of BlobEntry instances. def tree!(sha) tree = @repo.git.native(:ls_tree, {:r => true, :l => true, :z => true}, sha) @@ -110,14 +177,32 @@ module Gollum end end + # Reads the content from the Git db at the given SHA. + # + # sha - The String SHA. + # + # Returns the String content of the Git object. def cat_file!(sha) @repo.git.cat_file({:p => true}, sha) end + # Reads a Git commit. + # + # sha - The string SHA of the Git commit. + # + # Returns a Grit::Commit. def commit!(sha) @repo.commit(sha) end + # Attempts to get the given data from a cache. If it doesn't exist, it'll + # pass the results of the yielded block to the cache for future accesses. + # + # name - The cache prefix used in building the full cache key. + # key - The unique cache key suffix, usually a String Git SHA. + # + # Yields a block to pass to the cache. + # Returns the cached result. def get_cache(name, key) cache = instance_variable_get("@#{name}_map") value = cache[key] @@ -127,11 +212,25 @@ module Gollum value == :_nil ? nil : value end + # Writes some data to the internal cache. + # + # name - The cache prefix used in building the full cache key. + # key - The unique cache key suffix, usually a String Git SHA. + # value - The value to write to the cache. + # + # Returns nothing. def set_cache(name, key, value) cache = instance_variable_get("@#{name}_map") cache[key] = value || :_nil end + # Gets multiple values from the cache in a single call. + # + # name - The cache prefix used in building the full cache key. + # keys - Array of cache key names to fetch. + # + # Returns a Hash of the objects that were found in the cache, indexed by + # the cache key. def multi_get(name, keys) value = instance_variable_get("@#{name}_map") keys.inject({}) do |memo, key| diff --git a/lib/gollum/sanitization.rb b/lib/gollum/sanitization.rb index 72ba2ec9..b9f76aac 100644 --- a/lib/gollum/sanitization.rb +++ b/lib/gollum/sanitization.rb @@ -1,5 +1,4 @@ module Gollum - # Encapsulate sanitization options. # # This class does not yet support all options of Sanitize library. From db2a6deca95c7b412eb7537157784bbd4ae2d94e Mon Sep 17 00:00:00 2001 From: rick Date: Sat, 20 Nov 2010 14:14:41 -0800 Subject: [PATCH 14/19] tomdoc reorg for git_access.rb --- lib/gollum/git_access.rb | 54 ++++++++++++++++++++++------------------ lib/gollum/wiki.rb | 4 +++ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/gollum/git_access.rb b/lib/gollum/git_access.rb index 76568f73..9306c893 100644 --- a/lib/gollum/git_access.rb +++ b/lib/gollum/git_access.rb @@ -2,30 +2,6 @@ module Gollum # Controls all access to the Git objects from Gollum. Extend this class to # add custom caching for special cases. class GitAccess - # Gets the String path to the Git repository. - attr_reader :path - - # Gets the Grit::Repo instance for the Git repository. - attr_reader :repo - - # Gets a Hash cache of refs to commit SHAs. - # - # {"master" => "abc123", ...} - # - attr_reader :ref_map - - # Gets a Hash cache of commit SHAs to a recursive tree of blobs. - # - # {"abc123" => [, ]} - # - attr_reader :tree_map - - # Gets a Hash cache of commit SHAs to the Grit::Commit instance. - # - # {"abcd123" => } - # - attr_reader :commit_map - def initialize(path) @path = path @repo = Grit::Repo.new(path) @@ -130,6 +106,36 @@ module Gollum @ref_map.clear end + ######################################################################### + # + # Internal Methods + # + ######################################################################### + + # Gets the String path to the Git repository. + attr_reader :path + + # Gets the Grit::Repo instance for the Git repository. + attr_reader :repo + + # Gets a Hash cache of refs to commit SHAs. + # + # {"master" => "abc123", ...} + # + attr_reader :ref_map + + # Gets a Hash cache of commit SHAs to a recursive tree of blobs. + # + # {"abc123" => [, ]} + # + attr_reader :tree_map + + # Gets a Hash cache of commit SHAs to the Grit::Commit instance. + # + # {"abcd123" => } + # + attr_reader :commit_map + # Raw method for fetching a list of Git commits. # # shas - An Array of String SHAs. diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index 249dee12..e94c5087 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -326,6 +326,10 @@ module Gollum @repo.log('master', nil, log_pagination_options(options)) end + # Public: Refreshes just the cached Git reference data. This should + # be called after every Gollum update. + # + # Returns nothing. def clear_cache @access.refresh end From 76064da1317ffe4286cc6881d87dc4a8973f7ca9 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 2 Dec 2010 14:35:31 -0800 Subject: [PATCH 15/19] A Gollum::Wiki saves Sanitize instances --- lib/gollum/sanitization.rb | 7 +++++++ lib/gollum/wiki.rb | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/gollum/sanitization.rb b/lib/gollum/sanitization.rb index b9f76aac..153cd570 100644 --- a/lib/gollum/sanitization.rb +++ b/lib/gollum/sanitization.rb @@ -103,6 +103,13 @@ module Gollum :allow_comments => allow_comments? } end + + # Builds a Sanitize instance from the current options. + # + # Returns a Sanitize instance. + def to_sanitize + Sanitize.new(to_hash) + end end end diff --git a/lib/gollum/wiki.rb b/lib/gollum/wiki.rb index e94c5087..9d9b7b10 100644 --- a/lib/gollum/wiki.rb +++ b/lib/gollum/wiki.rb @@ -334,6 +334,26 @@ module Gollum @access.refresh end + # Public: Creates a Sanitize instance using the Wiki's sanitization + # options. + # + # Returns a Sanitize instance. + def sanitizer + if options = sanitization + @sanitizer ||= options.to_sanitize + end + end + + # Public: Creates a Sanitize instance using the Wiki's history sanitization + # options. + # + # Returns a Sanitize instance. + def history_sanitizer + if options = history_sanitization + @history_sanitizer ||= options.to_sanitize + end + end + ######################################################################### # # Internal Methods From bffa4be78f2768f2cc4c8be1025885a95d09710b Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 2 Dec 2010 14:40:24 -0800 Subject: [PATCH 16/19] re-use Sanitize instances in Gollum::Markup --- lib/gollum/markup.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/gollum/markup.rb b/lib/gollum/markup.rb index 95d3de76..73886a66 100644 --- a/lib/gollum/markup.rb +++ b/lib/gollum/markup.rb @@ -27,9 +27,9 @@ module Gollum # # Returns the formatted String content. def render(no_follow = false) - sanitize_options = no_follow ? - @wiki.history_sanitization : - @wiki.sanitization + sanitize = no_follow ? + @wiki.history_sanitizer : + @wiki.sanitizer data = extract_tex(@data) data = extract_code(data) @@ -44,12 +44,16 @@ module Gollum end data = process_tags(data) data = process_code(data) - data = Sanitize.clean(data, sanitize_options.to_hash) if sanitize_options + data = sanitize.clean!(data) if sanitize data = process_tex(data) data.gsub!(/

<\/p>/, '') data end + def doc_to_html(doc) + doc.to_xhtml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XHTML) + end + ######################################################################### # # TeX From 2fe6b7b7f1b4edab1f939f02ec4e957046e53a53 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 2 Dec 2010 14:47:20 -0800 Subject: [PATCH 17/19] provide access to the parsed Nokogiri document fragment when rendering marked up content. --- lib/gollum/markup.rb | 7 ++++++- lib/gollum/page.rb | 4 ++-- test/test_markup.rb | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/gollum/markup.rb b/lib/gollum/markup.rb index 73886a66..e63e96b1 100644 --- a/lib/gollum/markup.rb +++ b/lib/gollum/markup.rb @@ -44,7 +44,12 @@ module Gollum end data = process_tags(data) data = process_code(data) - data = sanitize.clean!(data) if sanitize + if sanitize || block_given? + doc = Nokogiri::HTML::DocumentFragment.parse(data) + doc = sanitize.clean_node!(doc) if sanitize + yield doc if block_given? + data = doc_to_html(doc) + end data = process_tex(data) data.gsub!(/

<\/p>/, '') data diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index cf1c9f59..2b9ad5ad 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -132,8 +132,8 @@ module Gollum # Public: The formatted contents of the page. # # Returns the String data. - def formatted_data - @blob && @wiki.markup_class.new(self).render(historical?) + def formatted_data(&block) + @blob && @wiki.markup_class.new(self).render(historical?, &block) end # Public: The format of the page. diff --git a/test/test_markup.rb b/test/test_markup.rb index c75ad1a6..892685af 100644 --- a/test/test_markup.rb +++ b/test/test_markup.rb @@ -25,6 +25,31 @@ context "Markup" do end end + test "Gollum::Markup#render yields a DocumentFragment" do + yielded = false + @wiki.write_page("Yielded", :markdown, "abc", commit_details) + + page = @wiki.page("Yielded") + markup = Gollum::Markup.new(page) + markup.render do |doc| + assert_kind_of Nokogiri::HTML::DocumentFragment, doc + yielded = true + end + assert yielded + end + + test "Gollum::Page#formatted_data yields a DocumentFragment" do + yielded = false + @wiki.write_page("Yielded", :markdown, "abc", commit_details) + + page = @wiki.page("Yielded") + page.formatted_data do |doc| + assert_kind_of Nokogiri::HTML::DocumentFragment, doc + yielded = true + end + assert yielded + end + ######################################################################### # # Links From 09d62fa327ea11d8b34675f89057eba673affad1 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 2 Dec 2010 14:57:38 -0800 Subject: [PATCH 18/19] history lesson --- HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index e2620f58..6938de58 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,8 @@ how `Sanitize.clean` modifies formatted wiki content. * Add `--config` option for the command line, to specify a ruby file that is run during startup. + * Provide access to a parsed Nokogiri::DocumentFragment during markup + rendering for added customization. * Bug Fixes * Use `@wiki.page_class` in Gollum::Markup where appropriate (#63). From 280afb42ea2c6c9562545214706419c913c69b26 Mon Sep 17 00:00:00 2001 From: rick Date: Thu, 2 Dec 2010 17:53:16 -0800 Subject: [PATCH 19/19] extract sub-page logic from Gollum::Page#footer --- lib/gollum/page.rb | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/gollum/page.rb b/lib/gollum/page.rb index 2b9ad5ad..5ec7cfb5 100644 --- a/lib/gollum/page.rb +++ b/lib/gollum/page.rb @@ -196,19 +196,7 @@ module Gollum # # Returns the footer Page or nil if none exists. def footer - return nil if page_match('_Footer', self.filename) - - dirs = self.path.split('/') - dirs.pop - map = @wiki.tree_map_for(self.version.id) - while !dirs.empty? - if page = find_page_in_tree(map, '_Footer', dirs.join('/')) - return page - end - dirs.pop - end - - find_page_in_tree(map, '_Footer', '') + find_sub_page :footer end # Gets a Boolean determining whether this page is a historical version. @@ -356,5 +344,28 @@ module Gollum false end end + + # Loads a sub page. Sub page nanes (footers) are prefixed with + # an underscore to distinguish them from other Pages. + # + # name - String page name. + # + # Returns the Page or nil if none exists. + def find_sub_page(name) + name = "_#{name.to_s.capitalize}" + return nil if page_match(name, self.filename) + + dirs = self.path.split('/') + dirs.pop + map = @wiki.tree_map_for(self.version.id) + while !dirs.empty? + if page = find_page_in_tree(map, name, dirs.join('/')) + return page + end + dirs.pop + end + + find_page_in_tree(map, name, '') + end end end