dolt 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +25 -5
- data/Readme.md +90 -16
- data/bin/dolt +22 -4
- data/dolt.gemspec +10 -2
- data/lib/dolt/disk_repo_resolver.rb +3 -5
- data/lib/dolt/git/blame.rb +112 -0
- data/lib/dolt/git/commit.rb +73 -0
- data/lib/dolt/git/repository.rb +26 -24
- data/lib/dolt/repo_actions.rb +32 -19
- data/lib/dolt/sinatra/actions.rb +51 -18
- data/lib/dolt/sinatra/multi_repo_browser.rb +30 -3
- data/lib/dolt/sinatra/single_repo_browser.rb +36 -2
- data/lib/dolt/template_renderer.rb +7 -8
- data/lib/dolt/version.rb +1 -1
- data/lib/dolt/view.rb +3 -61
- data/lib/dolt/view/blame.rb +57 -0
- data/lib/dolt/view/blob.rb +60 -0
- data/lib/dolt/view/breadcrumb.rb +10 -17
- data/lib/dolt/{git/blob.rb → view/commit.rb} +5 -11
- data/lib/dolt/view/gravatar.rb +29 -0
- data/lib/dolt/{git/shell.rb → view/markup.rb} +10 -16
- data/lib/dolt/view/multi_repository.rb +27 -0
- data/lib/dolt/{async/deferrable_child_process.rb → view/object.rb} +11 -24
- data/lib/dolt/view/single_repository.rb +27 -0
- data/lib/dolt/view/smart_blob_renderer.rb +33 -0
- data/lib/dolt/view/syntax_highlight.rb +86 -0
- data/lib/dolt/view/tree.rb +69 -0
- data/test/dolt/git/blame_test.rb +128 -0
- data/test/dolt/git/commit_test.rb +89 -0
- data/test/dolt/git/repository_test.rb +24 -73
- data/test/dolt/repo_actions_test.rb +77 -15
- data/test/dolt/sinatra/actions_test.rb +168 -8
- data/test/dolt/template_renderer_test.rb +44 -10
- data/test/dolt/templates/blame_test.rb +56 -0
- data/test/dolt/{views → templates}/blob_test.rb +44 -34
- data/test/dolt/templates/commits_test.rb +61 -0
- data/test/dolt/templates/raw_test.rb +41 -0
- data/test/dolt/templates/tree_test.rb +51 -0
- data/test/dolt/view/blame_test.rb +122 -0
- data/test/dolt/view/blob_test.rb +90 -0
- data/test/dolt/view/breadcrumb_test.rb +46 -0
- data/test/dolt/view/commit_test.rb +31 -0
- data/test/dolt/view/gravatar_test.rb +30 -0
- data/test/dolt/view/markup_test.rb +30 -0
- data/test/dolt/{git/blob_test.rb → view/multi_repository_test.rb} +10 -24
- data/test/dolt/view/object_test.rb +71 -0
- data/test/dolt/view/single_repository_test.rb +30 -0
- data/test/dolt/{view_test.rb → view/syntax_highlight_test.rb} +40 -27
- data/test/dolt/view/tree_test.rb +115 -0
- data/test/test_helper.rb +18 -13
- data/vendor/ui/css/gitorious.css +20 -8
- data/views/blame.erb +41 -0
- data/views/blob.erb +6 -9
- data/views/commits.erb +23 -0
- data/views/index.erb +25 -0
- data/views/raw.erb +19 -0
- data/views/tree.erb +28 -26
- metadata +156 -26
- data/lib/dolt/git/tree.rb +0 -65
- data/lib/dolt/view/highlighter.rb +0 -52
- data/test/dolt/git/shell_test.rb +0 -112
- data/test/dolt/git/tree_test.rb +0 -120
data/lib/dolt/git/repository.rb
CHANGED
@@ -15,44 +15,46 @@
|
|
15
15
|
# You should have received a copy of the GNU Affero General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
|
-
require "
|
19
|
-
require "
|
20
|
-
require "
|
18
|
+
require "em_rugged/repository"
|
19
|
+
require "em_pessimistic/deferrable_child_process"
|
20
|
+
require "em/deferrable"
|
21
|
+
require "dolt/git/blame"
|
22
|
+
require "dolt/git/commit"
|
21
23
|
|
22
24
|
module Dolt
|
23
25
|
module Git
|
24
|
-
class Repository
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@name = name
|
29
|
-
@git = git
|
30
|
-
end
|
31
|
-
|
32
|
-
def blob(path, ref = "HEAD")
|
33
|
-
async_git(git.show(path, ref)) do |data, status|
|
34
|
-
Dolt::Git::Blob.new(path, data)
|
26
|
+
class Repository < EMRugged::Repository
|
27
|
+
def blame(ref, path)
|
28
|
+
deferred_method("blame -l -t -p #{ref} #{path}") do |output, s|
|
29
|
+
Dolt::Git::Blame.parse_porcelain(output)
|
35
30
|
end
|
36
31
|
end
|
37
32
|
|
38
|
-
def
|
39
|
-
|
40
|
-
Dolt::Git::
|
33
|
+
def log(ref, path, limit)
|
34
|
+
deferred_method("log -n #{limit} #{ref} #{path}") do |out, s|
|
35
|
+
Dolt::Git::Commit.parse_log(out)
|
41
36
|
end
|
42
37
|
end
|
43
38
|
|
44
39
|
private
|
45
|
-
def
|
40
|
+
def deferred_method(cmd, &block)
|
41
|
+
d = EventMachine::DefaultDeferrable.new
|
42
|
+
cmd = git(cmd)
|
43
|
+
p = EMPessimistic::DeferrableChildProcess.open(cmd)
|
46
44
|
|
47
|
-
|
48
|
-
|
45
|
+
p.callback do |output, status|
|
46
|
+
d.succeed(block.call(output, status))
|
47
|
+
end
|
49
48
|
|
50
|
-
|
51
|
-
|
49
|
+
p.errback do |err|
|
50
|
+
d.fail(err)
|
52
51
|
end
|
53
52
|
|
54
|
-
|
55
|
-
|
53
|
+
d
|
54
|
+
end
|
55
|
+
|
56
|
+
def git(cmd)
|
57
|
+
"git --git-dir #{subject.path} #{cmd}"
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
data/lib/dolt/repo_actions.rb
CHANGED
@@ -23,31 +23,44 @@ module Dolt
|
|
23
23
|
@repo_resolver = repo_resolver
|
24
24
|
end
|
25
25
|
|
26
|
-
def blob(repo,
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
def blob(repo, ref, path, &block)
|
27
|
+
repo_action(repo, ref, path, :blob, :rev_parse, "#{ref}:#{path}", &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def tree(repo, ref, path, &block)
|
31
|
+
repo_action(repo, ref, path, :tree, :rev_parse, "#{ref}:#{path}", &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def blame(repo, ref, path, &block)
|
35
|
+
repo_action(repo, ref, path, :blame, :blame, ref, path, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def history(repo, ref, path, count, &block)
|
39
|
+
repo_action(repo, ref, path, :commits, :log, ref, path, count, &block)
|
36
40
|
end
|
37
41
|
|
38
|
-
def
|
42
|
+
def repositories
|
43
|
+
repo_resolver.all
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def repo_resolver; @repo_resolver; end
|
48
|
+
|
49
|
+
def repo_action(repo, ref, path, data, method, *args, &block)
|
39
50
|
repository = repo_resolver.resolve(repo)
|
40
|
-
d = repository.
|
41
|
-
d.callback do |
|
42
|
-
block.call(nil, {
|
43
|
-
:tree => tree,
|
44
|
-
:repository => repository,
|
45
|
-
:ref => ref })
|
51
|
+
d = repository.send(method, *args)
|
52
|
+
d.callback do |result|
|
53
|
+
block.call(nil, tpl_data(repo, ref, path, { data => result }))
|
46
54
|
end
|
47
55
|
d.errback { |err| block.call(err, nil) }
|
48
56
|
end
|
49
57
|
|
50
|
-
|
51
|
-
|
58
|
+
def tpl_data(repo, ref, path, locals = {})
|
59
|
+
{
|
60
|
+
:repository => repo,
|
61
|
+
:path => path,
|
62
|
+
:ref => ref
|
63
|
+
}.merge(locals)
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
data/lib/dolt/sinatra/actions.rb
CHANGED
@@ -15,33 +15,66 @@
|
|
15
15
|
# You should have received a copy of the GNU Affero General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
|
+
require "em_rugged"
|
19
|
+
|
18
20
|
module Dolt
|
19
21
|
module Sinatra
|
20
22
|
module Actions
|
21
|
-
|
23
|
+
# Built-in redirect seems to not work with Sinatra::Async, it throws
|
24
|
+
# an error.
|
25
|
+
def redirect(url)
|
26
|
+
response.status = 302
|
27
|
+
response["Location"] = url
|
28
|
+
body ""
|
29
|
+
end
|
30
|
+
|
31
|
+
def error(error)
|
22
32
|
response["Content-Type"] = "text/plain"
|
23
|
-
body("Process failed with exit code
|
33
|
+
body("Process failed with exit code #{error.exit_code}:\n#{error.message}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def raw(repo, ref, path)
|
37
|
+
blob(repo, ref, path, {
|
38
|
+
:template => :raw,
|
39
|
+
:content_type => "text/plain",
|
40
|
+
:template_options => { :layout => nil }
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
def blob(repo, ref, path, options = { :template => :blob, :content_type => "text/html" })
|
45
|
+
actions.blob(repo, ref, path) do |err, data|
|
46
|
+
return error(err) if !err.nil?
|
47
|
+
blob = data[:blob]
|
48
|
+
return redirect(tree_url(repo, ref, path)) if !blob.is_a?(Rugged::Blob)
|
49
|
+
response["Content-Type"] = options[:content_type]
|
50
|
+
tpl_options = options[:template_options] || {}
|
51
|
+
body(renderer.render(options[:template], data, tpl_options))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def tree(repo, ref, path)
|
56
|
+
actions.tree(repo, ref, path) do |err, data|
|
57
|
+
return error(err) if !err.nil?
|
58
|
+
tree = data[:tree]
|
59
|
+
return redirect(blob_url(repo, ref, path)) if !tree.is_a?(Rugged::Tree)
|
60
|
+
response["Content-Type"] = "text/html"
|
61
|
+
body(renderer.render(:tree, data))
|
62
|
+
end
|
24
63
|
end
|
25
64
|
|
26
|
-
def
|
27
|
-
actions.
|
28
|
-
if
|
29
|
-
|
30
|
-
|
31
|
-
else
|
32
|
-
error(status)
|
33
|
-
end
|
65
|
+
def blame(repo, ref, path)
|
66
|
+
actions.blame(repo, ref, path) do |err, data|
|
67
|
+
return error(err) if !err.nil?
|
68
|
+
response["Content-Type"] = "text/html"
|
69
|
+
body(renderer.render(:blame, data))
|
34
70
|
end
|
35
71
|
end
|
36
72
|
|
37
|
-
def
|
38
|
-
actions.
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
else
|
43
|
-
error(status)
|
44
|
-
end
|
73
|
+
def history(repo, ref, path, count)
|
74
|
+
actions.history(repo, ref, path, count) do |err, data|
|
75
|
+
return error(err) if !err.nil?
|
76
|
+
response["Content-Type"] = "text/html"
|
77
|
+
body(renderer.render(:commits, data))
|
45
78
|
end
|
46
79
|
end
|
47
80
|
end
|
@@ -16,17 +16,25 @@
|
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
18
|
require "dolt/sinatra/base"
|
19
|
+
require "dolt/view/single_repository"
|
20
|
+
require "dolt/view/blob"
|
21
|
+
require "dolt/view/tree"
|
19
22
|
|
20
23
|
module Dolt
|
21
24
|
module Sinatra
|
22
25
|
class MultiRepoBrowser < Dolt::Sinatra::Base
|
26
|
+
include Dolt::View::SingleRepository
|
27
|
+
include Dolt::View::Blob
|
28
|
+
include Dolt::View::Tree
|
29
|
+
|
23
30
|
aget "/" do
|
24
31
|
response["Content-Type"] = "text/html"
|
25
|
-
body(
|
32
|
+
body(renderer.render(:index, { :repositories => actions.repositories }))
|
26
33
|
end
|
27
34
|
|
28
35
|
aget "/*/tree/*:*" do
|
29
|
-
|
36
|
+
repo, ref, path = params[:splat]
|
37
|
+
tree(repo, ref, path)
|
30
38
|
end
|
31
39
|
|
32
40
|
aget "/*/tree/*" do
|
@@ -34,13 +42,32 @@ module Dolt
|
|
34
42
|
end
|
35
43
|
|
36
44
|
aget "/*/blob/*:*" do
|
37
|
-
|
45
|
+
repo, ref, path = params[:splat]
|
46
|
+
blob(repo, ref, path)
|
38
47
|
end
|
39
48
|
|
40
49
|
aget "/*/blob/*" do
|
41
50
|
force_ref(params[:splat], "blob", "master")
|
42
51
|
end
|
43
52
|
|
53
|
+
aget "/*/raw/*:*" do
|
54
|
+
repo, ref, path = params[:splat]
|
55
|
+
raw(repo, ref, path)
|
56
|
+
end
|
57
|
+
|
58
|
+
aget "/*/raw/*" do
|
59
|
+
force_ref(params[:splat], "raw", "master")
|
60
|
+
end
|
61
|
+
|
62
|
+
aget "/*/blame/*:*" do
|
63
|
+
repo, ref, path = params[:splat]
|
64
|
+
blame(repo, ref, path)
|
65
|
+
end
|
66
|
+
|
67
|
+
aget "/*/blame/*" do
|
68
|
+
force_ref(params[:splat], "blame", "master")
|
69
|
+
end
|
70
|
+
|
44
71
|
private
|
45
72
|
def force_ref(args, action, ref)
|
46
73
|
redirect(args.shift + "/#{action}/#{ref}:" + args.join)
|
@@ -16,10 +16,16 @@
|
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
18
|
require "dolt/sinatra/base"
|
19
|
+
require "dolt/view/single_repository"
|
20
|
+
require "dolt/view/blob"
|
21
|
+
require "dolt/view/tree"
|
19
22
|
|
20
23
|
module Dolt
|
21
24
|
module Sinatra
|
22
25
|
class SingleRepoBrowser < Dolt::Sinatra::Base
|
26
|
+
include Dolt::View::SingleRepository
|
27
|
+
include Dolt::View::Blob
|
28
|
+
include Dolt::View::Tree
|
23
29
|
attr_reader :repo
|
24
30
|
|
25
31
|
def initialize(repo, actions, renderer)
|
@@ -32,7 +38,8 @@ module Dolt
|
|
32
38
|
end
|
33
39
|
|
34
40
|
aget "/tree/*:*" do
|
35
|
-
|
41
|
+
ref, path = params[:splat]
|
42
|
+
tree(repo, ref, path)
|
36
43
|
end
|
37
44
|
|
38
45
|
aget "/tree/*" do
|
@@ -41,13 +48,40 @@ module Dolt
|
|
41
48
|
|
42
49
|
aget "/blob/*:*" do
|
43
50
|
ref, path = params[:splat]
|
44
|
-
blob(repo,
|
51
|
+
blob(repo, ref, path)
|
45
52
|
end
|
46
53
|
|
47
54
|
aget "/blob/*" do
|
48
55
|
force_ref(params[:splat], "blob", "master")
|
49
56
|
end
|
50
57
|
|
58
|
+
aget "/raw/*:*" do
|
59
|
+
ref, path = params[:splat]
|
60
|
+
raw(repo, ref, path)
|
61
|
+
end
|
62
|
+
|
63
|
+
aget "/raw/*" do
|
64
|
+
force_ref(params[:splat], "raw", "master")
|
65
|
+
end
|
66
|
+
|
67
|
+
aget "/blame/*:*" do
|
68
|
+
ref, path = params[:splat]
|
69
|
+
blame(repo, ref, path)
|
70
|
+
end
|
71
|
+
|
72
|
+
aget "/blame/*" do
|
73
|
+
force_ref(params[:splat], "blame", "master")
|
74
|
+
end
|
75
|
+
|
76
|
+
aget "/history/*:*" do
|
77
|
+
ref, path = params[:splat]
|
78
|
+
history(repo, ref, path, (params[:commit_count] || 20).to_i)
|
79
|
+
end
|
80
|
+
|
81
|
+
aget "/history/*" do
|
82
|
+
force_ref(params[:splat], "blame", "master")
|
83
|
+
end
|
84
|
+
|
51
85
|
private
|
52
86
|
def force_ref(args, action, ref)
|
53
87
|
redirect("/#{action}/#{ref}:" + args.join)
|
@@ -25,20 +25,20 @@ module Dolt
|
|
25
25
|
@layout = opt[:layout]
|
26
26
|
@type = opt[:type] || "erb"
|
27
27
|
@context_class = Class.new
|
28
|
-
@attributes = opt[:attributes] || {}
|
29
28
|
end
|
30
29
|
|
31
|
-
def helper(
|
32
|
-
|
30
|
+
def helper(helper)
|
31
|
+
helper = [helper] unless Array === helper
|
32
|
+
helper.each { |h| @context_class.send(:include, h) }
|
33
33
|
end
|
34
34
|
|
35
|
-
def render(template, locals = {})
|
36
|
-
locals = locals.merge(attributes)
|
35
|
+
def render(template, locals = {}, options = {})
|
37
36
|
context = context_class.new
|
38
37
|
content = load(template).render(context, locals)
|
38
|
+
layout_tpl = options.key?(:layout) ? options[:layout] : layout
|
39
39
|
|
40
|
-
if !
|
41
|
-
content = load(
|
40
|
+
if !layout_tpl.nil?
|
41
|
+
content = load(layout_tpl).render(context, locals) { content }
|
42
42
|
end
|
43
43
|
|
44
44
|
content
|
@@ -58,6 +58,5 @@ module Dolt
|
|
58
58
|
def layout; @layout; end
|
59
59
|
def type; @type; end
|
60
60
|
def context_class; @context_class; end
|
61
|
-
def attributes; @attributes; end
|
62
61
|
end
|
63
62
|
end
|
data/lib/dolt/version.rb
CHANGED
data/lib/dolt/view.rb
CHANGED
@@ -15,67 +15,9 @@
|
|
15
15
|
# You should have received a copy of the GNU Affero General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
|
-
require "dolt/view/breadcrumb"
|
19
|
-
require "dolt/view/highlighter"
|
20
18
|
|
21
|
-
|
22
|
-
module View
|
23
|
-
def breadcrumb(repository, path, ref, options = {})
|
24
|
-
bc = Dolt::View::Breadcrumb.new(:multi_repo_mode => options[:multi_repo_mode])
|
25
|
-
bc.render(repository, ref, path)
|
26
|
-
end
|
19
|
+
dir = File.join(File.dirname(__FILE__), "view")
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
class_names << "prettyprint" << "linenums"
|
31
|
-
|
32
|
-
num = 0
|
33
|
-
lines = blob.split("\n").inject("") do |html, line|
|
34
|
-
num += 1
|
35
|
-
"#{html}<li class=\"L#{num}\"><span class=\"line\">#{line}</span></li>"
|
36
|
-
end
|
37
|
-
|
38
|
-
"<pre class=\"#{class_names.join(' ')}\">" +
|
39
|
-
"<ol class=\"linenums\">#{lines}</ol></pre>"
|
40
|
-
end
|
41
|
-
|
42
|
-
def highlight(path, code, options = {})
|
43
|
-
lexer = lexer_for_file(path)
|
44
|
-
Dolt::View::Highlighter.new(options).highlight(code, lexer)
|
45
|
-
rescue RubyPython::PythonError
|
46
|
-
code
|
47
|
-
end
|
48
|
-
|
49
|
-
def highlight_lines(path, code, options = {})
|
50
|
-
lexer = lexer_for_file(path)
|
51
|
-
multiline(highlight(path, code, options), :class_names => [lexer])
|
52
|
-
end
|
53
|
-
|
54
|
-
def lexer_for_file(path)
|
55
|
-
suffix = path.split(".").pop
|
56
|
-
Dolt::View::Highlighter.lexer(suffix)
|
57
|
-
end
|
58
|
-
|
59
|
-
def tree_url(repository, entry, ref, multi_repo_mode)
|
60
|
-
action = entry.file? ? "blob" : "tree"
|
61
|
-
repo_url(repository, "/#{action}/#{ref}:#{entry.full_path}", multi_repo_mode)
|
62
|
-
end
|
63
|
-
|
64
|
-
def blame_url(repository, blob, ref, multi_repo_mode)
|
65
|
-
repo_url(repository, "/blame/#{ref}:#{blob.path}", multi_repo_mode)
|
66
|
-
end
|
67
|
-
|
68
|
-
def history_url(repository, blob, ref, multi_repo_mode)
|
69
|
-
repo_url(repository, "/history/#{ref}:#{blob.path}", multi_repo_mode)
|
70
|
-
end
|
71
|
-
|
72
|
-
def raw_url(repository, blob, ref, multi_repo_mode)
|
73
|
-
repo_url(repository, "/raw/#{ref}:#{blob.path}", multi_repo_mode)
|
74
|
-
end
|
75
|
-
|
76
|
-
def repo_url(repository, url, multi_repo_mode)
|
77
|
-
prefix = multi_repo_mode ? "/#{repository.name}" : ""
|
78
|
-
"#{prefix}#{url}"
|
79
|
-
end
|
80
|
-
end
|
21
|
+
Dir.entries(dir).select { |f| f =~ /\.rb$/ }.map do |file|
|
22
|
+
require(File.join(dir, file))
|
81
23
|
end
|