This commit is contained in:
rick
2010-09-30 14:22:07 -07:00
26 changed files with 643 additions and 221 deletions
+9
View File
@@ -3,6 +3,14 @@
* Major Enhancements
* Optimize page write/update/delete to use Grit::Index::read_tree instead
of manually recreating entire index contents.
* Added --irb option for the gollum command.
* Update working dir (if present) when edited via the API (#6)
* Minor Enhancements
* Support a `:gollum_path` Sinatra setting for `Precious::App`
* Add Wiki#size to efficiently count pages without loading them.
* Add the correct content type when serving files from the frontend.
* Wiki#pages come back sorted by Page#title.
* Add --host option and default it to 127.0.0.1.
* Bug Fixes
* Increase minimum Sanitize version requirement to 1.1.0.
1.0.x versions of Sanitize require Hpricot instead of Nokogiri
@@ -13,6 +21,7 @@
anonymous info.
* Prevent `Gollum::Wiki#write_page` from clobbering existing pages.
* Handle duplicate page errors in frontend.
* Fix bugs trying to retrieve pages with invalid names.
# 1.0.1 / 2010-08-12
+1 -1
View File
@@ -326,7 +326,7 @@ Initialize the Gollum::Repo object:
wiki = Gollum::Wiki.new("my-gollum-repo.git")
# => <Gollum::Wiki>
By default, internal wiki links are all absolute from the root. To specify a different base path, you can send specify the `:base_path` option:
By default, internal wiki links are all absolute from the root. To specify a different base path, you can specify the `:base_path` option:
wiki = Gollum::Wiki.new("my-gollum-repo.git", :base_path => "/wiki")
+69 -7
View File
@@ -8,18 +8,17 @@ Gollum is a multi-format Wiki Engine/API/Frontend.
Basic Command Line Usage:
gollum [OPTIONS] [PATH]
PATH The path to the Gollum repository.
PATH The path to the Gollum repository (default .).
Options:
HELP
require 'optparse'
require 'rubygems'
require 'gollum/frontend/app'
require 'gollum'
exec = {}
options = {}
options = { 'port' => 4567, 'bind' => '127.0.0.1' }
opts = OptionParser.new do |opts|
opts.banner = help
@@ -27,15 +26,78 @@ opts = OptionParser.new do |opts|
options['port'] = port.to_i
end
opts.on("--host [HOST]", "Hostname or IP address to listen on (default 0.0.0.0).") do |host|
options['bind'] = host
end
opts.on("--version", "Display current version.") do
puts "Gollum " + Gollum::VERSION
exit 0
end
opts.on("--irb", "Start an irb process with gollum loaded for the current wiki.") do
options['irb'] = true
end
end
# Read command line options into `options` hash
opts.parse!
begin
opts.parse!
rescue OptionParser::InvalidOption
puts "gollum: #{$!.message}"
puts "gollum: try 'gollum --help' for more information"
exit
end
$path = ARGV[0] || Dir.pwd
gollum_path = ARGV[0] || Dir.pwd
Precious::App.run!(options)
if options['irb']
require 'irb'
# http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application/
module IRB # :nodoc:
def self.start_session(binding)
unless @__initialized
args = ARGV
ARGV.replace(ARGV.dup)
IRB.setup(nil)
ARGV.replace(args)
@__initialized = true
end
ws = WorkSpace.new(binding)
irb = Irb.new(ws)
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
@CONF[:MAIN_CONTEXT] = irb.context
catch(:IRB_EXIT) do
irb.eval_input
end
end
end
begin
wiki = Gollum::Wiki.new(gollum_path)
if !wiki.exist? then raise Grit::InvalidGitRepositoryError end
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
puts
puts %( page = wiki.page('page-name'))
puts %( # => <Gollum::Page>)
puts
puts %( page.raw_data)
puts %( # => "# My wiki page")
puts
puts %( page.formatted_data)
puts %( # => "<h1>My wiki page</h1>")
puts
puts "Check out the Gollum README for more."
IRB.start_session(binding)
rescue Grit::InvalidGitRepositoryError, Grit::NoSuchPathError
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
exit 0
end
else
require 'gollum/frontend/app'
Precious::App.set(:gollum_path, gollum_path)
Precious::App.run!(options)
end
+4 -2
View File
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.rdoc_options = ["--charset=UTF-8"]
s.extra_rdoc_files = %w[README.md LICENSE]
s.add_dependency('grit', "~> 2.2")
s.add_dependency('grit', "~> 2.3")
s.add_dependency('github-markup', [">= 0.4.0", "< 1.0.0"])
s.add_dependency('albino', "~> 1.0")
s.add_dependency('sinatra', "~> 1.0")
@@ -31,9 +31,11 @@ Gem::Specification.new do |s|
s.add_dependency('sanitize', "~> 1.1")
s.add_dependency('nokogiri', "~> 1.4")
s.add_development_dependency('shoulda')
s.add_development_dependency('RedCloth')
s.add_development_dependency('mocha')
s.add_development_dependency('org-ruby')
s.add_development_dependency('rdiscount')
s.add_development_dependency('shoulda')
# = MANIFEST =
s.files = %w[
+1
View File
@@ -12,6 +12,7 @@ require 'gollum/ruby1.8'
# internal
require 'gollum/pagination'
require 'gollum/blob_entry'
require 'gollum/wiki'
require 'gollum/page'
require 'gollum/file'
+71
View File
@@ -0,0 +1,71 @@
module Gollum
class BlobEntry
# Gets the String SHA for this blob.
attr_reader :sha
# Gets the String full path for this blob.
attr_reader :path
def initialize(sha, path)
@sha = sha
@path = path
@dir = @name = @blob = nil
end
# Gets the normalized directory path for this blob.
def dir
@dir ||= self.class.normalize_dir(::File.dirname(@path))
end
# Gets the String file base name for this blob.
def name
@name ||= ::File.basename(@path)
end
# Gets a Grit::Blob instance for this blob.
#
# repo - Grit::Repo instance for the Grit::Blob.
#
# Returns an unbaked Grit::Blob instance.
def blob(repo)
@blob ||= Grit::Blob.create(repo, :id => @sha, :name => @name)
end
# Gets a Page instance for this blob.
#
# wiki - Gollum::Wiki instance for the Gollum::Page
#
# Returns a Gollum::Page instance.
def page(wiki, commit)
blob = self.blob(wiki.repo)
page = wiki.page_class.new(wiki).populate(blob, self.dir)
page.version = commit
page
end
def inspect
%(#<Gollum::BlobEntry #{@sha} #{@path}>)
end
# Normalizes a given directory name for searching through tree paths.
# Ensures that a directory begins with a slash, or
#
# normalize_dir("") # => ""
# normalize_dir(".") # => ""
# normalize_dir("foo") # => "/foo"
# normalize_dir("/foo/") # => "/foo"
# normalize_dir("/") # => ""
#
# dir - String directory name.
#
# Returns a normalized String directory name, or nil if no directory
# is given.
def self.normalize_dir(dir)
if dir
dir = ::File.expand_path(dir, '/')
dir = '' if dir == '/'
end
dir
end
end
end
+13 -7
View File
@@ -33,6 +33,11 @@ module Gollum
# Public: The String path of the file.
attr_reader :path
# Public: The String mime type of the file.
def mime_type
@blob.mime_type
end
#########################################################################
#
# Internal Methods
@@ -46,13 +51,14 @@ module Gollum
#
# Returns a Gollum::File or nil if the file could not be found.
def find(name, version)
if commit = @wiki.repo.commit(version)
if (blob = commit.tree / name) && blob.is_a?(Grit::Blob)
@blob = blob
@path = name
@version = commit
self
end
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)
self
end
end
end
+22 -21
View File
@@ -1,3 +1,4 @@
require 'cgi'
require 'sinatra'
require 'gollum'
require 'mustache/sinatra'
@@ -12,6 +13,7 @@ module Precious
dir = File.dirname(File.expand_path(__FILE__))
# We want to serve public assets for now
set :public, "#{dir}/public"
set :static, true
@@ -38,9 +40,9 @@ module Precious
show_page_or_file('Home')
end
get '/edit/:name' do
@name = params[:name]
wiki = Gollum::Wiki.new($path)
get '/edit/*' do
@name = params[:splat].first
wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(@name)
@page = page
@content = page.raw_data
@@ -50,27 +52,27 @@ module Precious
end
end
post '/edit/:name' do
name = params[:name]
wiki = Gollum::Wiki.new($path)
post '/edit/*' do
name = params[:splat].first
wiki = Gollum::Wiki.new(settings.gollum_path)
page = wiki.page(name)
format = params[:format].intern
name = params[:rename] if params[:rename]
wiki.update_page(page, name, format, params[:content], commit_message)
redirect "/#{Gollum::Page.cname name}"
redirect "/#{CGI.escape(Gollum::Page.cname(name))}"
end
post '/create/:name' do
post '/create/*' do
name = params[:page]
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
format = params[:format].intern
begin
wiki.write_page(name, format, params[:content], commit_message)
redirect "/#{name}"
redirect "/#{CGI.escape(name)}"
rescue Gollum::DuplicatePageError => e
@message = "Duplicate page: #{e.message}"
mustache :error
@@ -80,13 +82,13 @@ module Precious
post '/preview' do
format = params['wiki_format']
data = params['text']
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
wiki.preview_page("Preview", data, format).formatted_data
end
get '/history/:name' do
@name = params[:name]
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
@page = wiki.page(@name)
@page_num = [params[:page].to_i, 1].max
@versions = @page.versions :page => @page_num
@@ -96,10 +98,10 @@ module Precious
post '/compare/:name' do
@versions = params[:versions] || []
if @versions.size < 2
redirect "/history/#{params[:name]}"
redirect "/history/#{CGI.escape(params[:name])}"
else
redirect "/compare/%s/%s...%s" % [
params[:name],
CGI.escape(params[:name]),
@versions.last,
@versions.first]
end
@@ -108,7 +110,7 @@ module Precious
get '/compare/:name/:version_list' do
@name = params[:name]
@versions = params[:version_list].split(/\.{2,3}/)
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
@page = wiki.page(@name)
diffs = wiki.repo.diff(@versions.first, @versions.last, @page.path)
@diff = diffs.first
@@ -117,7 +119,7 @@ module Precious
get %r{/(.+?)/([0-9a-f]{40})} do
name = params[:captures][0]
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(name, params[:captures][1])
@page = page
@name = name
@@ -130,7 +132,7 @@ module Precious
get '/search' do
@query = params[:q]
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
@results = wiki.search @query
mustache :search
end
@@ -140,13 +142,14 @@ module Precious
end
def show_page_or_file(name)
wiki = Gollum::Wiki.new($path)
wiki = Gollum::Wiki.new(settings.gollum_path)
if page = wiki.page(name)
@page = page
@name = name
@content = page.formatted_data
mustache :page
elsif file = wiki.file(name)
content_type file.mime_type
file.raw_data
else
@name = name
@@ -155,9 +158,7 @@ module Precious
end
def commit_message
{ :message => params[:message],
:name => `git config --get user.name `.strip,
:email => `git config --get user.email`.strip }
{ :message => params[:message] }
end
end
end
@@ -1,7 +1,7 @@
<div class="guide">
<div class="main">
<div class="actions">
<a href="/{{name}}">&laquo; Back</a>
<a href="/{{escaped_name}}">&laquo; Back</a>
</div>
<h1>{{title}}: {{before}} &rarr; {{after}}</h1>
<div id="files">
@@ -2,7 +2,7 @@
<a href="/">&laquo; Home</a>
<h1>{{title}}</h1>
<form class="new_wiki" method="post" action="/create/{{name}}">
<form class="new_wiki" method="post" action="/create/{{escaped_name}}">
<div>
<label>
Title<br />
+2 -2
View File
@@ -1,8 +1,8 @@
<div class="write">
<a href="/{{name}}">&laquo; Back</a>
<a href="/{{escaped_name}}">&laquo; Back</a>
<h1>{{title}}</h1>
<form class="edit_wiki" method="post" action="/edit/{{name}}">
<form class="edit_wiki" method="post" action="/edit/{{escaped_name}}">
<div>
<label>
Title<br />
@@ -1,10 +1,10 @@
<div class="guide">
<div class="main">
<div class="actions">
<a href="/{{name}}">&laquo; Back</a>
<a href="/{{escaped_name}}">&laquo; Back</a>
</div>
<h1>{{title}}</h1>
<form id="history" method="post" action="/compare/{{name}}">
<form id="history" method="post" action="/compare/{{escaped_name}}">
<table class="commits" cellpadding="0" cellspacing="0">
<tr>
<th colspan="5">
@@ -17,7 +17,7 @@
<input name="versions[]" type="checkbox" value="{{id}}" />
</td>
<td class="sha">
<a href="/{{name}}/{{id}}">{{id7}}</a>
<a href="/{{escaped_name}}/{{id}}">{{id7}}</a>
</td>
<td nowrap class="author">
<img src="http://www.gravatar.com/avatar/{{gravatar}}?s=16" alt="Gravatar" />
+7 -3
View File
@@ -2,7 +2,11 @@
<div class="main">
<div class="actions">
<form action="/search" method="get">
<a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a> | <input type="search" name="q" size="10" /> <input type="submit" value="search" />
<div>
<a href="/">Home</a> |
<a href="/edit/{{escaped_name}}">Edit</a> |
<input type="search" name="q" size="10" /> <input type="submit" value="search" />
</div>
</form>
</div>
<h1>{{title}}</h1>
@@ -20,10 +24,10 @@
<div style="float: left;">
<small>Last edited by <b>{{author}}</b>, {{date}}</small>
<div class="actions">
<a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a>
<a href="/">Home</a> | <a href="/edit/{{escaped_name}}">Edit</a>
</div>
</div>
<div style="float: right;">
<a href="/history/{{name}}">View Revision History</a>
<a href="/history/{{escaped_name}}">View Revision History</a>
</div>
</div>
+6
View File
@@ -1,3 +1,5 @@
require 'cgi'
module Precious
module Views
class Layout < Mustache
@@ -6,6 +8,10 @@ module Precious
attr_reader :name
def escaped_name
CGI.escape(@name)
end
def title
"Home"
end
+3 -2
View File
@@ -1,4 +1,5 @@
require 'digest/sha1'
require 'cgi'
module Gollum
class Markup
@@ -267,10 +268,10 @@ module Gollum
%{<a href="#{name}">#{name}</a>}
else
if page = @wiki.page(cname)
link = ::File.join(@wiki.base_path, Page.cname(page.name))
link = ::File.join(@wiki.base_path, CGI.escape(Page.cname(page.name)))
presence = "present"
else
link = ::File.join(@wiki.base_path, cname)
link = ::File.join(@wiki.base_path, CGI.escape(cname))
presence = "absent"
end
%{<a class="internal #{presence}" href="#{link}">#{name}</a>}
+25 -55
View File
@@ -182,20 +182,15 @@ module Gollum
dirs = self.path.split('/')
dirs.pop
map = @wiki.tree_map_for(self.version.id)
while !dirs.empty?
tree = self.version.tree / dirs.join('/')
if page = find_page_in_this_tree(tree, dirs.join('/'), '_Footer')
if page = find_page_in_tree(map, '_Footer', dirs.join('/'))
return page
end
dirs.pop
end
tree = self.version.tree
if page = find_page_in_this_tree(tree, '', '_Footer')
return page
end
return nil
find_page_in_tree(map, '_Footer', '')
end
#########################################################################
@@ -215,7 +210,9 @@ module Gollum
#
# Returns the String canonical name.
def self.cname(name)
name.gsub(%r{[ /<>]}, '-')
name.respond_to?(:gsub) ?
name.gsub(%r{[ /<>]}, '-') :
''
end
# Convert a format Symbol into an extension String.
@@ -259,61 +256,34 @@ module Gollum
#
# Returns a Gollum::Page or nil if the page could not be found.
def find(name, version)
if commit = @wiki.repo.commit(version)
if page = find_page_in_tree(commit.tree, name)
page.version = commit
page
end
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
end
rescue Grit::GitRuby::Repository::NoSuchShaFound
end
# Find a page in a given tree.
#
# tree - The Grit::Tree in which to look.
# name - The canonical String page name.
# map - The Array tree map from Wiki#tree_map.
# name - The canonical String page name.
# checked_dir - Optional String of the directory a matching page needs
# to be in. The string should
#
# Returns a Gollum::Page or nil if the page could not be found.
def find_page_in_tree(tree, name)
treemap = {}
trees = [tree]
while !trees.empty?
ptree = trees.shift
ptree.contents.each do |item|
case item
when Grit::Blob
if page_match(name, item.name)
return self.class.new(@wiki).populate(item, tree_path(treemap, ptree))
end
when Grit::Tree
treemap[item] = ptree
trees << item
end
end
def find_page_in_tree(map, name, checked_dir = nil)
return nil if name.to_s.empty?
if checked_dir = BlobEntry.normalize_dir(checked_dir)
checked_dir.downcase!
end
return nil # nothing was found
end
# Find a page in a given tree without recursing into subtrees.
#
# tree - The Grit::Tree in which to look.
# dir - The String path of the given Grit::Tree.
# name - The canonical String page name.
#
# Returns a Gollum::Page or nil if the page could not be found.
def find_page_in_this_tree(tree, dir, name)
treemap = {}
tree.contents.each do |item|
case item
when Grit::Blob
if page_match(name, item.name)
path = dir == '' ? '' : ::File.join('/', dir)
page = self.class.new(@wiki).populate(item, path)
page.version = self.version
return page
end
end
map.each do |entry|
next if entry.name.to_s.empty?
next unless checked_dir.nil? || entry.dir.downcase == checked_dir
next unless page_match(name, entry.name)
return entry.page(@wiki, @version)
end
return nil # nothing was found
+214 -38
View File
@@ -63,6 +63,7 @@ module Gollum
@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
end
# Public: check whether the wiki's git repo exists on the filesystem.
@@ -134,7 +135,12 @@ module Gollum
parents = pcommit ? [pcommit] : []
actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], parents, actor)
sha1 = index.commit(commit[:message], parents, actor)
@ref_map.clear
update_working_dir(index, '', name, format)
sha1
end
# Public: Update an existing page with new content. The location of the
@@ -159,19 +165,26 @@ module Gollum
format ||= page.format
index = self.repo.index
dir = ::File.dirname(page.path)
dir = '' if dir == '.'
index.read_tree(pcommit.tree.id)
if page.name == name && page.format == format
index.add(page.path, normalize(data))
else
index.delete(page.path)
dir = ::File.dirname(page.path)
dir = '' if dir == '.'
add_to_index(index, dir, name, format, data, :allow_same_ext)
end
actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], [pcommit], actor)
sha1 = index.commit(commit[:message], [pcommit], actor)
@ref_map.clear
update_working_dir(index, dir, page.name, page.format)
update_working_dir(index, dir, name, format)
sha1
end
# Public: Delete a page.
@@ -190,8 +203,16 @@ module Gollum
index.read_tree(pcommit.tree.id)
index.delete(page.path)
dir = ::File.dirname(page.path)
dir = '' if dir == '.'
actor = Grit::Actor.new(commit[:name], commit[:email])
index.commit(commit[:message], [pcommit], actor)
sha1 = index.commit(commit[:message], [pcommit], actor)
@ref_map.clear
update_working_dir(index, dir, page.name, page.format)
sha1
end
# Public: Lists all pages for this wiki.
@@ -200,11 +221,19 @@ module Gollum
#
# Returns an Array of Gollum::Page instances.
def pages(treeish = nil)
treeish ||= 'master'
if commit = @repo.commit(treeish)
tree_list(commit)
else
[]
tree_list(treeish || 'master').sort! do |x, y|
x.title.downcase <=> y.title.downcase
end
end
# Public: Returns the number of pages accessible from a commit
#
# ref - A String ref that is either a commit SHA or references one.
#
# Returns a Fixnum
def size(ref = nil)
tree_map_for(ref || 'master').inject(0) do |num, entry|
num + (@page_class.valid_page_name?(entry.name) ? 1 : 0)
end
end
@@ -255,6 +284,26 @@ 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
# Gets the file class used by all instances of this Wiki.
attr_reader :file_class
# Normalize the data.
#
# data - The String data to be normalized.
@@ -264,33 +313,84 @@ module Gollum
data.gsub(/\r/, '')
end
# Fill an array with a list of pages.
# Assemble a Page's filename from its name and format.
#
# commit - The Grit::Commit
# tree - The Grit::Tree to start with.
# sub_tree - Optional String specifying the parent path of the Page.
# name - The String name of the page (may be in human format).
# format - The Symbol format of the page.
#
# Returns a flat Array of Gollum::Page instances.
def tree_list(commit, tree = commit.tree, sub_tree = nil)
list = []
path = tree.name ? "#{sub_tree}/#{tree.name}" : ''
tree.contents.each do |item|
case item
when Grit::Blob
if @page_class.valid_page_name?(item.name)
page = @page_class.new(self).populate(item, path)
page.version = commit
list << page
end
when Grit::Tree
list.push *tree_list(commit, item, path)
end
end
list
# Returns the String filename.
def page_file_name(name, format)
ext = @page_class.format_to_ext(format)
@page_class.cname(name) + '.' + ext
end
# Determine if a given page path is scheduled to be deleted in the next
# commit for the given Index.
# Update the given file in the repository's working directory if there
# is a working directory present.
#
# index - The Grit::Index with which to sync.
# dir - The String directory in which the file lives.
# name - The String name of the page (may be in human format).
# format - The Symbol format of the page.
#
# Returns nothing.
def update_working_dir(index, dir, name, format)
unless @repo.bare
path =
if dir == ''
page_file_name(name, format)
else
::File.join(dir, page_file_name(name, format))
end
Dir.chdir(::File.join(@repo.path, '..')) do
if file_path_scheduled_for_deletion?(index.tree, path)
@repo.git.rm({'f' => true}, '--', path)
else
@repo.git.checkout({}, 'HEAD', '--', path)
end
end
end
end
# Fill an array with a list of pages.
#
# ref - A String ref that is either a commit SHA or references one.
#
# Returns a flat Array of Gollum::Page instances.
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))
end
end
# Determine if a given file is scheduled to be deleted in the next commit
# for the given Index.
#
# map - The Hash map:
# key - The String directory or filename.
# val - The Hash submap or the String contents of the file.
# path - The String path of the file including extension.
#
# Returns the Boolean response.
def file_path_scheduled_for_deletion?(map, path)
parts = path.split('/')
if parts.size == 1
deletions = map.keys.select { |k| !map[k] }
deletions.any? { |d| d == parts.first }
else
part = parts.shift
if rest = map[part]
file_path_scheduled_for_deletion?(rest, parts.join('/'))
else
false
end
end
end
# Determine if a given page (regardless of format) is scheduled to be
# deleted in the next commit for the given Index.
#
# map - The Hash map:
# key - The String directory or filename.
@@ -310,7 +410,7 @@ module Gollum
if rest = map[part]
page_path_scheduled_for_deletion?(rest, parts.join('/'))
else
nil
false
end
end
end
@@ -331,8 +431,7 @@ module Gollum
#
# Returns nothing (modifies the Index in place).
def add_to_index(index, dir, name, format, data, allow_same_ext = false)
ext = @page_class.format_to_ext(format)
path = @page_class.cname(name) + '.' + ext
path = page_file_name(name, format)
dir = '/' if dir.strip.empty?
@@ -364,9 +463,86 @@ module Gollum
#
# Returns the commit Hash
def normalize_commit(commit = {})
commit[:name] = self.class.default_committer_name if commit[:name].to_s.empty?
commit[:email] = self.class.default_committer_email if commit[:email].to_s.empty?
commit[:name] = default_committer_name if commit[:name].to_s.empty?
commit[:email] = default_committer_email if commit[:email].to_s.empty?
commit
end
# Gets the default name for commits.
def default_committer_name
@default_committer_name ||= \
@repo.config['user.name'] || self.class.default_committer_name
end
# Gets the default email for commits.
def default_committer_email
@default_committer_email ||= \
@repo.config['user.email'] || self.class.default_committer_email
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.
#
# ref - A String ref that is either a commit SHA or references one.
#
# 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
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
@@ -0,0 +1,2 @@
x∙н[
б0@Q©Ё┼ы─▓╓yDэй$≥╘AшH:БЖМЭ=pА√╬╝MюзИ$┐(Ш≤≤UJ(ег╧tяq╤yv.EvVsUoмфДШ■╢и3Q╛9Ф┌·1!#З░~Дя▄V·p*▐╜┴╤FВeеЖ╨■╬чюьД┌÷ЭdА╛ёжЙпcOХОPa╜╟В!m[@hУrH┐
+1 -1
View File
@@ -1 +1 @@
60f12f4254f58801b9ee7db7bca5fa8aeefaa56b
308fdf72d89351bf53fa6eeb00884273047e07fa
+7 -1
View File
@@ -18,6 +18,12 @@ def testpath(path)
File.join(TEST_DIR, path)
end
def commit_details
{ :message => "Did something at #{Time.now}",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
end
# test/spec/mini 3
# http://gist.github.com/25455
# chris@ozmm.org
@@ -26,7 +32,7 @@ def context(*args, &block)
return super unless (name = args.first) && block
require 'test/unit'
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
def self.test(name, &block)
def self.test(name, &block)
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
end
def self.xtest(*args) end
+5 -2
View File
@@ -11,9 +11,12 @@ context "File" do
end
test "existing file" do
file = @wiki.file("Mordor/todo.txt")
commit = @wiki.repo.commits.first
file = @wiki.file("Mordor/todo.txt")
assert_equal "[ ] Write section on Ents\n", file.raw_data
assert_equal @wiki.repo.commits.first.id, file.version.id
assert_equal 'todo.txt', file.name
assert_equal commit.id, file.version.id
assert_equal commit.author.name, file.version.author.name
end
test "accessing tree" do
+56 -17
View File
@@ -6,10 +6,6 @@ context "Markup" do
FileUtils.rm_rf(@path)
Grit::Repo.init_bare(@path)
@wiki = Gollum::Wiki.new(@path)
@commit = { :message => "Add stuff",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
end
teardown do
@@ -17,12 +13,18 @@ context "Markup" do
end
test "formats page from Wiki#pages" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", commit_details)
assert @wiki.pages[0].formatted_data
end
#########################################################################
#
# Links
#
#########################################################################
test "double page links no space" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]][[Bar]] b", commit_details)
# "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a><a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>"
page = @wiki.page("Bilbo Baggins")
@@ -41,7 +43,7 @@ context "Markup" do
end
test "double page links with space" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]] [[Bar]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Foo]] [[Bar]] b", commit_details)
# "<p>a <a class=\"internal absent\" href=\"/Foo\">Foo</a> <a class=\"internal absent\" href=\"/Bar\">Bar</a> b</p>"
page = @wiki.page("Bilbo Baggins")
@@ -60,7 +62,7 @@ context "Markup" do
end
test "page link" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Bilbo Baggins]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Bilbo Baggins]] b", commit_details)
page = @wiki.page("Bilbo Baggins")
output = page.formatted_data
@@ -70,7 +72,7 @@ context "Markup" do
end
test "absent page link" do
@wiki.write_page("Tolkien", :markdown, "a [[J. R. R. Tolkien]]'s b", @commit)
@wiki.write_page("Tolkien", :markdown, "a [[J. R. R. Tolkien]]'s b", commit_details)
page = @wiki.page("Tolkien")
output = page.formatted_data
@@ -83,7 +85,7 @@ context "Markup" do
["/wiki", "/wiki/"].each_with_index do |path, i|
name = "Bilbo Baggins #{i}"
@wiki = Gollum::Wiki.new(@path, :base_path => path)
@wiki.write_page(name, :markdown, "a [[#{name}]] b", @commit)
@wiki.write_page(name, :markdown, "a [[#{name}]] b", commit_details)
page = @wiki.page(name)
output = page.formatted_data
@@ -94,16 +96,22 @@ context "Markup" do
end
test "external page link" do
@wiki.write_page("Bilbo Baggins", :markdown, "a [[http://example.com]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[http://example.com]] b", commit_details)
page = @wiki.page("Bilbo Baggins")
assert_equal "<p>a <a href=\"http://example.com\">http://example.com</a> b</p>", page.formatted_data
end
#########################################################################
#
# Images
#
#########################################################################
test "image with http url" do
['http', 'https'].each do |scheme|
name = "Bilbo Baggins #{scheme}"
@wiki.write_page(name, :markdown, "a [[#{scheme}://example.com/bilbo.jpg]] b", @commit)
@wiki.write_page(name, :markdown, "a [[#{scheme}://example.com/bilbo.jpg]] b", commit_details)
page = @wiki.page(name)
output = page.formatted_data
@@ -116,7 +124,7 @@ context "Markup" do
index = @wiki.repo.index
index.add("alpha.jpg", "hi")
index.commit("Add alpha.jpg")
@wiki.write_page("Bilbo Baggins", :markdown, "a [[/alpha.jpg]] [[a | /alpha.jpg]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[/alpha.jpg]] [[a | /alpha.jpg]] b", commit_details)
page = @wiki.page("Bilbo Baggins")
assert_equal %{<p>a <img src="/wiki/alpha.jpg" /><a href="/wiki/alpha.jpg">a</a> b</p>}, page.formatted_data
@@ -217,11 +225,17 @@ context "Markup" do
relative_image(content, output)
end
#########################################################################
#
# File links
#
#########################################################################
test "file link with absolute path" do
index = @wiki.repo.index
index.add("alpha.jpg", "hi")
index.commit("Add alpha.jpg")
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Alpha|/alpha.jpg]] b", @commit)
@wiki.write_page("Bilbo Baggins", :markdown, "a [[Alpha|/alpha.jpg]] b", commit_details)
page = @wiki.page("Bilbo Baggins")
output = Gollum::Markup.new(page).render
@@ -248,6 +262,12 @@ context "Markup" do
assert_equal %{<p>a <a href="http://example.com/alpha.jpg">Alpha</a> b</p>}, page.formatted_data
end
#########################################################################
#
# Code
#
#########################################################################
test "code blocks" do
content = "a\n\n```ruby\nx = 1\n```\n\nb"
output = "<p>a</p>\n\n<div class=\"highlight\"><pre>" +
@@ -296,6 +316,12 @@ context "Markup" do
compare(content, output)
end
#########################################################################
#
# Various
#
#########################################################################
test "escaped wiki link" do
content = "a '[[Foo]], b"
output = "<p>a [[Foo]], b</p>"
@@ -317,18 +343,30 @@ context "Markup" do
compare(content, output, 'org')
end
test "tex block syntax" do
#########################################################################
#
# TeX
#
#########################################################################
test "TeX block syntax" do
content = 'a \[ a^2 \] b'
output = "<p>a <script type=\"math/tex; mode=display\">a^2</script> b</p>"
compare(content, output, 'md')
end
test "tex inline syntax" do
test "TeX inline syntax" do
content = 'a \( a^2 \) b'
output = "<p>a <script type=\"math/tex\">a^2</script> b</p>"
compare(content, output, 'md')
end
#########################################################################
#
# Helpers
#
#########################################################################
def compare(content, output, ext = "md", regexes = [])
index = @wiki.repo.index
index.add("Bilbo-Baggins.#{ext}", content)
@@ -349,8 +387,9 @@ context "Markup" do
index.add("greek/Bilbo-Baggins.md", content)
index.commit("Add alpha.jpg")
@wiki.clear_cache
page = @wiki.page("Bilbo Baggins")
rendered = Gollum::Markup.new(page).render
assert_equal output, rendered
end
end
end
+5
View File
@@ -110,4 +110,9 @@ context "Page" do
footer = @wiki.page("_Footer")
assert_nil footer.footer
end
test "cannot convert non string to human readable page title" do
assert_equal '', Gollum::Page.cname(nil)
assert_equal '', Gollum::Page.cname(3)
end
end
+115 -57
View File
@@ -32,27 +32,57 @@ context "Wiki" do
assert_equal commits, @wiki.log(:page => 2).map { |c| c.id }
end
test "list pages" do
test "list pages, sorted by title" do
pages = @wiki.pages
assert_equal \
%w(Bilbo-Baggins.md Eye-Of-Sauron.md Home.textile My-Precious.md),
pages.map { |p| p.filename }.sort
%w(bilbo.md Bilbo-Baggins.md Eye-Of-Sauron.md My-Precious.md Home.textile),
pages.map { |p| p.filename }
end
test "counts pages" do
assert_equal 5, @wiki.size
end
test "normalizes commit hash" do
commit = {:message => 'abc'}
assert_equal({:message => 'abc', :name => 'Anonymous', :email => 'anon@anon.com'},
name = @wiki.repo.config['user.name']
email = @wiki.repo.config['user.email']
assert_equal({:message => 'abc', :name => name, :email => email},
@wiki.normalize_commit(commit.dup))
commit[:name] = 'bob'
commit[:email] = ''
assert_equal({:message => 'abc', :name => 'bob', :email => 'anon@anon.com'},
assert_equal({:message => 'abc', :name => 'bob', :email => email},
@wiki.normalize_commit(commit.dup))
commit[:email] = 'foo@bar.com'
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"=>"308fdf72d89351bf53fa6eeb00884273047e07fa"}, @wiki.ref_map)
map = @wiki.tree_map['308fdf72d89351bf53fa6eeb00884273047e07fa']
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 '308fdf72d89351bf53fa6eeb00884273047e07fa'
assert @wiki.ref_map.empty?
entry = @wiki.tree_map['308fdf72d89351bf53fa6eeb00884273047e07fa'][0]
assert_equal 'Bilbo-Baggins.md', entry.path
end
end
context "Wiki page previewing" do
@@ -79,58 +109,48 @@ context "Wiki page writing" do
end
test "write_page" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
cd = commit_details
@wiki.write_page("Gollum", :markdown, "# Gollum", cd)
assert_equal 1, @wiki.repo.commits.size
assert_equal "Gollum page", @wiki.repo.commits.first.message
assert_equal "Tom Preston-Werner", @wiki.repo.commits.first.author.name
assert_equal "tom@github.com", @wiki.repo.commits.first.author.email
assert_equal cd[:message], @wiki.repo.commits.first.message
assert_equal cd[:name], @wiki.repo.commits.first.author.name
assert_equal cd[:email], @wiki.repo.commits.first.author.email
assert @wiki.page("Gollum")
@wiki.write_page("Bilbo", :markdown, "# Bilbo", commit)
@wiki.write_page("Bilbo", :markdown, "# Bilbo", commit_details)
assert_equal 2, @wiki.repo.commits.size
assert @wiki.page("Bilbo")
assert @wiki.page("Gollum")
end
test "is not allowed to overwrite file" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Abc-Def", :markdown, "# Gollum", commit)
@wiki.write_page("Abc-Def", :markdown, "# Gollum", commit_details)
assert_raises Gollum::DuplicatePageError do
@wiki.write_page("ABC DEF", :textile, "# Gollum", commit)
@wiki.write_page("ABC DEF", :textile, "# Gollum", commit_details)
end
end
test "update_page" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
@wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
page = @wiki.page("Gollum")
@wiki.update_page(page, page.name, :markdown, "# Gollum2", commit)
cd = commit_details
@wiki.update_page(page, page.name, :markdown, "# Gollum2", cd)
assert_equal 2, @wiki.repo.commits.size
assert_equal "# Gollum2", @wiki.page("Gollum").raw_data
assert_equal "Gollum page", @wiki.repo.commits.first.message
assert_equal "Tom Preston-Werner", @wiki.repo.commits.first.author.name
assert_equal "tom@github.com", @wiki.repo.commits.first.author.email
assert_equal cd[:message], @wiki.repo.commits.first.message
assert_equal cd[:name], @wiki.repo.commits.first.author.name
assert_equal cd[:email], @wiki.repo.commits.first.author.email
end
test "update page with format change" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
@wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum")
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit)
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size
assert_equal :textile, @wiki.page("Gollum").format
@@ -138,30 +158,24 @@ context "Wiki page writing" do
end
test "update page with name change" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
@wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum")
@wiki.update_page(page, 'Smeagol', :markdown, "h1. Gollum", commit)
@wiki.update_page(page, 'Smeagol', :markdown, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size
assert_equal "h1. Gollum", @wiki.page("Smeagol").raw_data
end
test "update page with name and format change" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
@wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
assert_equal :markdown, @wiki.page("Gollum").format
page = @wiki.page("Gollum")
@wiki.update_page(page, 'Smeagol', :textile, "h1. Gollum", commit)
@wiki.update_page(page, 'Smeagol', :textile, "h1. Gollum", commit_details)
assert_equal 2, @wiki.repo.commits.size
assert_equal :textile, @wiki.page("Smeagol").format
@@ -169,17 +183,13 @@ context "Wiki page writing" do
end
test "update nested page with format change" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
index = @wiki.repo.index
index.add("lotr/Gollum.md", "# Gollum")
index.commit("Add nested page")
page = @wiki.page("Gollum")
assert_equal :markdown, @wiki.page("Gollum").format
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit)
@wiki.update_page(page, page.name, :textile, "h1. Gollum", commit_details)
page = @wiki.page("Gollum")
assert_equal "lotr/Gollum.textile", page.path
@@ -188,23 +198,16 @@ context "Wiki page writing" do
end
test "delete root page" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
@wiki.write_page("Gollum", :markdown, "# Gollum", commit)
@wiki.write_page("Gollum", :markdown, "# Gollum", commit_details)
page = @wiki.page("Gollum")
@wiki.delete_page(page, commit)
@wiki.delete_page(page, commit_details)
assert_equal 2, @wiki.repo.commits.size
assert_nil @wiki.page("Gollum")
end
test "delete nested page" do
commit = { :message => "Gollum page",
:name => "Tom Preston-Werner",
:email => "tom@github.com" }
index = @wiki.repo.index
index.add("greek/Bilbo-Baggins.md", "hi")
index.add("Gollum.md", "hi")
@@ -212,7 +215,7 @@ context "Wiki page writing" do
page = @wiki.page("Bilbo-Baggins")
assert page
@wiki.delete_page(page, commit)
@wiki.delete_page(page, commit_details)
assert_equal 2, @wiki.repo.commits.size
assert_nil @wiki.page("Bilbo-Baggins")
@@ -223,4 +226,59 @@ context "Wiki page writing" do
teardown do
FileUtils.rm_r(File.join(File.dirname(__FILE__), *%w[examples test.git]))
end
end
end
context "Wiki sync with working directory" do
setup do
@path = testpath('examples/wdtest')
Grit::Repo.init(@path)
@wiki = Gollum::Wiki.new(@path)
end
test "write a page" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
assert_equal "Hi", File.read(File.join(@path, "New-Page.md"))
end
test "update a page with same name and format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, page.name, page.format, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page.md"))
end
test "update a page with different name and same format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, "New Page 2", page.format, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page-2.md"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "update a page with same name and different format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, page.name, :textile, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page.textile"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "update a page with different name and different format" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.update_page(page, "New Page 2", :textile, "Bye", commit_details)
assert_equal "Bye", File.read(File.join(@path, "New-Page-2.textile"))
assert !File.exist?(File.join(@path, "New-Page.md"))
end
test "delete a page" do
@wiki.write_page("New Page", :markdown, "Hi", commit_details)
page = @wiki.page("New Page")
@wiki.delete_page(page, commit_details)
assert !File.exist?(File.join(@path, "New-Page.md"))
end
teardown do
FileUtils.rm_r(@path)
end
end