dolt 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Gemfile.lock +25 -5
  2. data/Readme.md +90 -16
  3. data/bin/dolt +22 -4
  4. data/dolt.gemspec +10 -2
  5. data/lib/dolt/disk_repo_resolver.rb +3 -5
  6. data/lib/dolt/git/blame.rb +112 -0
  7. data/lib/dolt/git/commit.rb +73 -0
  8. data/lib/dolt/git/repository.rb +26 -24
  9. data/lib/dolt/repo_actions.rb +32 -19
  10. data/lib/dolt/sinatra/actions.rb +51 -18
  11. data/lib/dolt/sinatra/multi_repo_browser.rb +30 -3
  12. data/lib/dolt/sinatra/single_repo_browser.rb +36 -2
  13. data/lib/dolt/template_renderer.rb +7 -8
  14. data/lib/dolt/version.rb +1 -1
  15. data/lib/dolt/view.rb +3 -61
  16. data/lib/dolt/view/blame.rb +57 -0
  17. data/lib/dolt/view/blob.rb +60 -0
  18. data/lib/dolt/view/breadcrumb.rb +10 -17
  19. data/lib/dolt/{git/blob.rb → view/commit.rb} +5 -11
  20. data/lib/dolt/view/gravatar.rb +29 -0
  21. data/lib/dolt/{git/shell.rb → view/markup.rb} +10 -16
  22. data/lib/dolt/view/multi_repository.rb +27 -0
  23. data/lib/dolt/{async/deferrable_child_process.rb → view/object.rb} +11 -24
  24. data/lib/dolt/view/single_repository.rb +27 -0
  25. data/lib/dolt/view/smart_blob_renderer.rb +33 -0
  26. data/lib/dolt/view/syntax_highlight.rb +86 -0
  27. data/lib/dolt/view/tree.rb +69 -0
  28. data/test/dolt/git/blame_test.rb +128 -0
  29. data/test/dolt/git/commit_test.rb +89 -0
  30. data/test/dolt/git/repository_test.rb +24 -73
  31. data/test/dolt/repo_actions_test.rb +77 -15
  32. data/test/dolt/sinatra/actions_test.rb +168 -8
  33. data/test/dolt/template_renderer_test.rb +44 -10
  34. data/test/dolt/templates/blame_test.rb +56 -0
  35. data/test/dolt/{views → templates}/blob_test.rb +44 -34
  36. data/test/dolt/templates/commits_test.rb +61 -0
  37. data/test/dolt/templates/raw_test.rb +41 -0
  38. data/test/dolt/templates/tree_test.rb +51 -0
  39. data/test/dolt/view/blame_test.rb +122 -0
  40. data/test/dolt/view/blob_test.rb +90 -0
  41. data/test/dolt/view/breadcrumb_test.rb +46 -0
  42. data/test/dolt/view/commit_test.rb +31 -0
  43. data/test/dolt/view/gravatar_test.rb +30 -0
  44. data/test/dolt/view/markup_test.rb +30 -0
  45. data/test/dolt/{git/blob_test.rb → view/multi_repository_test.rb} +10 -24
  46. data/test/dolt/view/object_test.rb +71 -0
  47. data/test/dolt/view/single_repository_test.rb +30 -0
  48. data/test/dolt/{view_test.rb → view/syntax_highlight_test.rb} +40 -27
  49. data/test/dolt/view/tree_test.rb +115 -0
  50. data/test/test_helper.rb +18 -13
  51. data/vendor/ui/css/gitorious.css +20 -8
  52. data/views/blame.erb +41 -0
  53. data/views/blob.erb +6 -9
  54. data/views/commits.erb +23 -0
  55. data/views/index.erb +25 -0
  56. data/views/raw.erb +19 -0
  57. data/views/tree.erb +28 -26
  58. metadata +156 -26
  59. data/lib/dolt/git/tree.rb +0 -65
  60. data/lib/dolt/view/highlighter.rb +0 -52
  61. data/test/dolt/git/shell_test.rb +0 -112
  62. data/test/dolt/git/tree_test.rb +0 -120
@@ -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 "dolt/async/when"
19
- require "dolt/git/blob"
20
- require "dolt/git/tree"
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
- attr_reader :name
26
-
27
- def initialize(name, git = nil)
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 tree(path, ref = "HEAD")
39
- async_git(git.ls_tree(path, ref)) do |data, status|
40
- Dolt::Git::Tree.parse(path, data)
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 git; @git; end
40
+ def deferred_method(cmd, &block)
41
+ d = EventMachine::DefaultDeferrable.new
42
+ cmd = git(cmd)
43
+ p = EMPessimistic::DeferrableChildProcess.open(cmd)
46
44
 
47
- def async_git(gitop, &block)
48
- deferred = When::Deferred.new
45
+ p.callback do |output, status|
46
+ d.succeed(block.call(output, status))
47
+ end
49
48
 
50
- gitop.callback do |data, status|
51
- deferred.resolve(block.call(data, status))
49
+ p.errback do |err|
50
+ d.fail(err)
52
51
  end
53
52
 
54
- gitop.errback { |err| deferred.reject(err) }
55
- deferred.promise
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
@@ -23,31 +23,44 @@ module Dolt
23
23
  @repo_resolver = repo_resolver
24
24
  end
25
25
 
26
- def blob(repo, path, ref, &block)
27
- repository = repo_resolver.resolve(repo)
28
- d = repository.blob(path, ref)
29
- d.callback do |blob, status|
30
- block.call(nil, {
31
- :blob => blob,
32
- :repository => repository,
33
- :ref => ref })
34
- end
35
- d.errback { |err| block.call(err, nil) }
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 tree(repo, path, ref, &block)
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.tree(path, ref)
41
- d.callback do |tree, status|
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
- private
51
- def repo_resolver; @repo_resolver; end
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
@@ -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
- def error(status)
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 \n#{status.exitstatus}")
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 blob(repo, path, ref)
27
- actions.blob(repo, path, ref) do |status, data|
28
- if status.nil?
29
- response["Content-Type"] = "text/html"
30
- body(renderer.render(:blob, data))
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 tree(repo, path, ref)
38
- actions.tree(repo, path, ref) do |status, data|
39
- if status.nil?
40
- response["Content-Type"] = "text/html"
41
- body(renderer.render(:tree, data))
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("<h1>Welcome to Dolt</h1>")
32
+ body(renderer.render(:index, { :repositories => actions.repositories }))
26
33
  end
27
34
 
28
35
  aget "/*/tree/*:*" do
29
- tree(params[:splat][0], params[:splat][2], params[:splat][1])
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
- blob(params[:splat][0], params[:splat][2], params[:splat][1])
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
- tree(repo, params[:splat][1], params[:splat][0])
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, path, ref)
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(helper_module)
32
- @context_class.send(:include, helper_module)
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 !layout.nil?
41
- content = load(layout).render(context, locals) { content }
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
@@ -17,5 +17,5 @@
17
17
  #++
18
18
 
19
19
  module Dolt
20
- VERSION = "0.1.1"
20
+ VERSION = "0.2.0"
21
21
  end
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
- module Dolt
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
- def multiline(blob, options = {})
29
- class_names = options[:class_names] || []
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