dolt 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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