gitdocs 0.5.0.pre6 → 0.5.0.pre7

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 (61) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.haml-lint.yml +3 -0
  4. data/.jslint.yml +84 -0
  5. data/.rubocop.yml +13 -0
  6. data/CHANGELOG +11 -0
  7. data/README.md +6 -2
  8. data/Rakefile +22 -3
  9. data/gitdocs.gemspec +36 -29
  10. data/lib/gitdocs.rb +5 -2
  11. data/lib/gitdocs/cli.rb +31 -8
  12. data/lib/gitdocs/configuration.rb +95 -49
  13. data/lib/gitdocs/manager.rb +36 -28
  14. data/lib/gitdocs/migration/001_create_shares.rb +2 -0
  15. data/lib/gitdocs/migration/002_add_remote_branch.rb +2 -0
  16. data/lib/gitdocs/migration/003_create_configs.rb +2 -0
  17. data/lib/gitdocs/migration/004_add_index_for_path.rb +4 -0
  18. data/lib/gitdocs/migration/005_add_start_web_frontend.rb +2 -0
  19. data/lib/gitdocs/migration/006_add_web_port_to_config.rb +2 -0
  20. data/lib/gitdocs/migration/007_add_sync_type.rb +11 -0
  21. data/lib/gitdocs/notifier.rb +89 -6
  22. data/lib/gitdocs/public/img/file.png +0 -0
  23. data/lib/gitdocs/public/img/folder.png +0 -0
  24. data/lib/gitdocs/public/js/app.js +26 -11
  25. data/lib/gitdocs/public/js/edit.js +3 -3
  26. data/lib/gitdocs/public/js/settings.js +8 -5
  27. data/lib/gitdocs/public/js/util.js +21 -20
  28. data/lib/gitdocs/rendering.rb +14 -9
  29. data/lib/gitdocs/repository.rb +180 -216
  30. data/lib/gitdocs/repository/path.rb +166 -0
  31. data/lib/gitdocs/runner.rb +22 -65
  32. data/lib/gitdocs/search.rb +35 -0
  33. data/lib/gitdocs/server.rb +123 -86
  34. data/lib/gitdocs/version.rb +1 -1
  35. data/lib/gitdocs/views/_header.haml +6 -6
  36. data/lib/gitdocs/views/app.haml +17 -17
  37. data/lib/gitdocs/views/dir.haml +10 -10
  38. data/lib/gitdocs/views/edit.haml +8 -9
  39. data/lib/gitdocs/views/file.haml +1 -1
  40. data/lib/gitdocs/views/home.haml +4 -4
  41. data/lib/gitdocs/views/revisions.haml +6 -6
  42. data/lib/gitdocs/views/search.haml +6 -6
  43. data/lib/gitdocs/views/settings.haml +23 -16
  44. data/test/.rubocop.yml +13 -0
  45. data/test/integration/browse_test.rb +149 -0
  46. data/test/integration/full_sync_test.rb +3 -11
  47. data/test/integration/share_management_test.rb +59 -10
  48. data/test/integration/status_test.rb +2 -0
  49. data/test/integration/test_helper.rb +40 -7
  50. data/test/unit/configuration_test.rb +82 -0
  51. data/test/unit/notifier_test.rb +165 -0
  52. data/test/unit/repository_path_test.rb +368 -0
  53. data/test/{repository_test.rb → unit/repository_test.rb} +426 -245
  54. data/test/unit/runner_test.rb +122 -0
  55. data/test/unit/search_test.rb +52 -0
  56. data/test/{test_helper.rb → unit/test_helper.rb} +5 -0
  57. metadata +138 -41
  58. data/lib/gitdocs/docfile.rb +0 -23
  59. data/test/configuration_test.rb +0 -41
  60. data/test/notifier_test.rb +0 -68
  61. data/test/runner_test.rb +0 -123
@@ -0,0 +1,166 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ # Class for executing File and Git operations on a specific path in the
4
+ # repository.
5
+ class Gitdocs::Repository::Path
6
+ attr_reader :relative_path
7
+
8
+ # @param [Gitdocs::Repository] repository
9
+ # @param [String] relative_path
10
+ def initialize(repository, relative_path)
11
+ @repository = repository
12
+ @relative_path = relative_path.gsub(/^\//, '')
13
+ @absolute_path = File.join(
14
+ File.absolute_path(@repository.root), @relative_path
15
+ )
16
+ end
17
+
18
+ # Write the content to the path and create any necessary directories.
19
+ #
20
+ # @param [String] content
21
+ # @param [String] commit_message
22
+ def write(content, commit_message)
23
+ FileUtils.mkdir_p(File.dirname(@absolute_path))
24
+ File.open(@absolute_path, 'w') { |f| f.puts(content) }
25
+
26
+ @repository.write_commit_message(commit_message)
27
+ end
28
+
29
+ # Touch and path and create any necessary directories.
30
+ def touch
31
+ FileUtils.mkdir_p(File.dirname(@absolute_path))
32
+ FileUtils.touch(@absolute_path)
33
+ end
34
+
35
+ # Create the path as a directory.
36
+ def mkdir
37
+ FileUtils.mkdir_p(@absolute_path)
38
+ end
39
+
40
+ # Remove the path, but only if it is a file.
41
+ def remove
42
+ return nil unless File.file?(@absolute_path)
43
+ FileUtils.rm(@absolute_path)
44
+ end
45
+
46
+ # @return [Boolean]
47
+ def text?
48
+ return false unless File.file?(@absolute_path)
49
+ mime_type = File.mime_type?(File.open(@absolute_path))
50
+ !!(mime_type =~ /text\/|x-empty/) # rubocop:disable DoubleNegation
51
+ end
52
+
53
+ # Returns file meta data based on relative file path
54
+ #
55
+ # @example
56
+ # meta
57
+ # => { :author => "Nick", :size => 1000, :modified => ... }
58
+ #
59
+ # @raise [RuntimeError] if the file is not found in any commits
60
+ #
61
+ # @return [Hash<:author => String, :size => Integer, modified => Time>]
62
+ def meta
63
+ commit = @repository.last_commit_for(@relative_path)
64
+
65
+ # FIXME: This should actually just return an empty hash
66
+ fail("File #{@relative_path} not found") unless commit
67
+
68
+ {
69
+ author: commit.author[:name],
70
+ size: total_size,
71
+ modified: commit.author[:time]
72
+ }
73
+ end
74
+
75
+ def exist?
76
+ File.exist?(@absolute_path)
77
+ end
78
+
79
+ def directory?
80
+ File.directory?(@absolute_path)
81
+ end
82
+
83
+ def absolute_path(ref = nil)
84
+ return @absolute_path unless ref
85
+
86
+ blob = @repository.blob_at(@relative_path, ref)
87
+ content = blob ? blob.text : ''
88
+ tmp_path = File.expand_path(File.basename(@relative_path), Dir.tmpdir)
89
+ File.open(tmp_path, 'w') { |f| f.puts content }
90
+ tmp_path
91
+ end
92
+
93
+ def readme_path
94
+ return nil unless directory?
95
+ Dir.glob(File.join(@absolute_path, 'README.{md}')).first
96
+ end
97
+
98
+ DirEntry = Struct.new(:name, :is_directory)
99
+
100
+ # @return [Array<DirEntry>] entries in the directory
101
+ # * excluding any git related directories
102
+ # * sorted by filename, ignoring any leading '.'s
103
+ def file_listing
104
+ return nil unless directory?
105
+
106
+ Dir.glob(File.join(@absolute_path, '{*,.*}'))
107
+ .reject { |x| x.match(/\/\.(\.|git|gitignore|gitmessage~)?$/) }
108
+ .sort_by { |x| File.basename(x).sub(/^\./, '') }
109
+ .map { |x| DirEntry.new(File.basename(x), File.directory?(x)) }
110
+ end
111
+
112
+ def content
113
+ return nil unless File.file?(@absolute_path)
114
+ File.read(@absolute_path)
115
+ end
116
+
117
+ # Returns the revisions available for a particular file
118
+ #
119
+ # @param [String] file
120
+ #
121
+ # @return [Array<Hash>]
122
+ def revisions
123
+ @repository.commits_for(@relative_path, 100).map do |commit|
124
+ {
125
+ commit: commit.oid[0, 7],
126
+ subject: commit.message.split("\n")[0],
127
+ author: commit.author[:name],
128
+ date: commit.author[:time]
129
+ }
130
+ end
131
+ end
132
+
133
+ # Revert file to the specified ref
134
+ #
135
+ # @param [String] ref
136
+ def revert(ref)
137
+ return unless ref
138
+
139
+ blob = @repository.blob_at(@relative_path, ref)
140
+ # Silently fail if the file/ref do not existing in the repository.
141
+ # Which is consistent with the original behaviour.
142
+ # TODO: should consider throwing an exception on this condition
143
+ return unless blob
144
+
145
+ write(blob.text, "Reverting '#{@relative_path}' to #{ref}")
146
+ end
147
+
148
+ #############################################################################
149
+
150
+ private
151
+
152
+ def total_size
153
+ result =
154
+ if File.directory?(@absolute_path)
155
+ Dir[File.join(@absolute_path, '**', '*')].reduce(0) do |size, filename|
156
+ File.symlink?(filename) ? size : size + File.size(filename)
157
+ end
158
+ else
159
+ File.symlink?(@absolute_path) ? 0 : File.size(@absolute_path)
160
+ end
161
+
162
+ # HACK: A value of 0 breaks the table sort for some reason
163
+ return -1 if result == 0
164
+ result
165
+ end
166
+ end
@@ -1,3 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  module Gitdocs
2
4
  class Runner
3
5
  def self.start_all(shares)
@@ -40,14 +42,16 @@ module Gitdocs
40
42
  # Listen for changes in local repository
41
43
 
42
44
  EM.defer(proc do
43
- listener = Guard::Listener.select_and_init(root, watch_all_modifications: true)
45
+ listener = Guard::Listener.select_and_init(
46
+ root, watch_all_modifications: true
47
+ )
44
48
  listener.on_change do |directories|
45
49
  directories.uniq!
46
50
  directories.delete_if { |d| d =~ /\/\.git/ }
47
51
  unless directories.empty?
48
52
  EM.next_tick do
49
53
  EM.defer(proc do
50
- mutex.synchronize { push_changes }
54
+ mutex.synchronize { sync_changes }
51
55
  end, proc {})
52
56
  end
53
57
  end
@@ -61,70 +65,33 @@ module Gitdocs
61
65
  end
62
66
 
63
67
  def sync_changes
64
- result = @repository.pull
65
-
66
- return if result.nil? || result == :no_remote
67
-
68
- if result.kind_of?(String)
69
- @notifier.error(
70
- 'There was a problem synchronizing this gitdoc',
71
- "A problem occurred in #{root}:\n#{result}"
72
- )
73
- return
74
- end
68
+ # Commit #################################################################
69
+ @repository.commit if @share.sync_type == 'full'
75
70
 
76
- if result == :ok
77
- author_change_count = latest_author_count
78
- unless author_change_count.empty?
79
- author_list = author_change_count.map { |author, count| "* #{author} (#{change_count(count)})" }.join("\n")
80
- @notifier.info(
81
- "Updated with #{change_count(author_change_count)}",
82
- "In '#{root}':\n#{author_list}"
83
- )
84
- end
85
- else
86
- #assert result.kind_of?(Array)
87
- @notifier.warn(
88
- 'There were some conflicts',
89
- result.map { |f| "* #{f}" }.join("\n")
90
- )
91
- end
71
+ # Fetch ##################################################################
72
+ fetch_result = @repository.fetch
73
+ return unless fetch_result == :ok
74
+ return if @share.sync_type == 'fetch'
92
75
 
93
- push_changes
94
- end
76
+ # Merge ##################################################################
77
+ merge_result = @repository.merge
78
+ merge_result = latest_author_count if merge_result == :ok
79
+ @notifier.merge_notification(merge_result, root)
80
+ return if merge_result.is_a?(String)
95
81
 
96
- def push_changes
97
- message_file = File.expand_path('.gitmessage~', root)
98
- if File.exist?(message_file)
99
- message = File.read(message_file)
100
- File.delete(message_file)
101
- else
102
- message = 'Auto-commit from gitdocs'
103
- end
104
-
105
- @repository.commit(message)
82
+ # Push ###################################################################
106
83
  result = @repository.push
107
-
108
- return if result.nil? || result == :no_remote || result == :nothing
109
- level, title, message = case result
110
- when :ok then [:info, "Pushed #{change_count(latest_author_count)}", "'#{root}' has been pushed"]
111
- when :conflict then [:warn, "There was a conflict in #{root}, retrying", '']
112
- else
113
- # assert result.kind_of?(String)
114
- [:error, "BAD Could not push changes in #{root}", result]
115
- # TODO: need to add a status on shares so that the push problem can be
116
- # displayed.
117
- end
118
- @notifier.send(level, title, message)
84
+ result = latest_author_count if result == :ok
85
+ @notifier.push_notification(result, root)
119
86
  rescue => e
120
87
  # Rescue any standard exceptions which come from the push related
121
88
  # commands. This will prevent problems on a single share from killing
122
89
  # the entire daemon.
123
- @notifier.error("Unexpected error pushing changes in #{root}", "#{e}")
124
- # TODO: get logging and/or put the error message into a status field in the database
90
+ @notifier.error("Unexpected error syncing changes in #{root}", "#{e}")
125
91
  end
126
92
 
127
93
  ############################################################################
94
+
128
95
  private
129
96
 
130
97
  # Update the author count for the last synced changes, and then update the
@@ -137,15 +104,5 @@ module Gitdocs
137
104
 
138
105
  @repository.author_count(last_oid)
139
106
  end
140
-
141
- def change_count(count_or_hash)
142
- count = if count_or_hash.respond_to?(:values)
143
- count_or_hash .values.reduce(:+)
144
- else
145
- count_or_hash
146
- end
147
-
148
- "#{count} change#{count == 1 ? '' : 's'}"
149
- end
150
107
  end
151
108
  end
@@ -0,0 +1,35 @@
1
+ class Gitdocs::Search
2
+ RepoDescriptor = Struct.new(:name, :index)
3
+ SearchResult = Struct.new(:file, :context)
4
+
5
+ # @param [Array<Gitdocs::Repository>] repositories
6
+ def initialize(repositories)
7
+ @repositories = repositories
8
+ end
9
+
10
+ def search(term)
11
+ results = {}
12
+ @repositories.each_with_index do |repository, index|
13
+ descriptor = RepoDescriptor.new(repository.root, index)
14
+ results[descriptor] = search_repository(repository, term)
15
+ end
16
+ results.delete_if { |_key, value| value.empty? }
17
+ end
18
+
19
+ private
20
+
21
+ def search_repository(repository, term)
22
+ return [] if term.empty?
23
+
24
+ results = []
25
+ repository.grep(term) do |file, context|
26
+ result = results.find { |s| s.file == file }
27
+ if result
28
+ result.context += ' ... ' + context
29
+ else
30
+ results << SearchResult.new(file, context)
31
+ end
32
+ end
33
+ results
34
+ end
35
+ end
@@ -1,3 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ # Disable style checks that are invalid for Renee
4
+ # rubocop:disable Blocks, MultilineBlockChain
5
+ #
6
+ # TODO: extract the WebApp into its own class but until then...
7
+ # rubocop:disable LineLength, ClassLength, CyclomaticComplexity, BlockNesting
8
+
1
9
  require 'thin'
2
10
  require 'renee'
3
11
  require 'coderay'
@@ -12,6 +20,17 @@ module Gitdocs
12
20
  @manager = manager
13
21
  @port = port.to_i
14
22
  @repositories = repositories
23
+ @search = Gitdocs::Search.new(repositories)
24
+ end
25
+
26
+ def self.start_and_wait(manager, override_port, repositories)
27
+ return false unless manager.start_web_frontend
28
+
29
+ web_port = override_port || manager.web_frontend_port
30
+ server = Server.new(manager, web_port, repositories)
31
+ server.start
32
+ server.wait_for_start
33
+ true
15
34
  end
16
35
 
17
36
  def start
@@ -19,134 +38,152 @@ module Gitdocs
19
38
  manager = @manager
20
39
  Thin::Logging.debug = @manager.debug
21
40
  Thin::Server.start('127.0.0.1', @port) do
22
- use Rack::Static, urls: ['/css', '/js', '/img', '/doc'], root: File.expand_path('../public', __FILE__)
41
+ use Rack::Static, urls: %w(/css /js /img /doc), root: File.expand_path('../public', __FILE__)
23
42
  use Rack::MethodOverride
24
43
  run Renee {
25
44
  if request.path_info == '/'
26
- if manager.config.shares.size == 1
45
+ if manager.shares.size == 1
27
46
  redirect! '/0'
28
47
  else
29
- render! 'home', layout: 'app', locals: { conf: manager.config, nav_state: 'home' }
48
+ render!(
49
+ 'home',
50
+ layout: 'app',
51
+ locals: { shares: manager.shares, nav_state: 'home' }
52
+ )
30
53
  end
31
54
  else
32
55
  path 'settings' do
33
- get.render! 'settings', layout: 'app', locals: { conf: manager.config, nav_state: 'settings' }
56
+ get.render!(
57
+ 'settings',
58
+ layout: 'app',
59
+ locals: { conf: manager, nav_state: 'settings' }
60
+ )
34
61
  post do
35
- shares = manager.config.shares
36
- manager.config.global.update_attributes(request.POST['config'])
37
- request.POST['share'].each do |idx, share|
38
- if remote_branch = share.delete('remote_branch')
39
- share['remote_name'], share['branch_name'] = remote_branch.split('/', 2)
40
- end
41
- # Update paths
42
- if share['path'] && !share['path'].empty?
43
- shares[Integer(idx)].update_attributes(share)
44
- end
45
- end
46
- EM.add_timer(0.1) { manager.restart }
62
+ manager.update_all(request.POST)
47
63
  redirect! '/settings'
48
64
  end
49
65
  end
50
66
 
51
67
  path('search').get do
52
- render! 'search', layout: 'app', locals: { conf: manager.config, results: Gitdocs::Repository.search(request.GET['q'], repositories), nav_state: nil }
68
+ render!(
69
+ 'search',
70
+ layout: 'app',
71
+ locals: { results: @search.search(request.GET['q']), nav_state: nil }
72
+ )
53
73
  end
54
74
 
55
75
  path('shares') do
56
- post do
57
- Configuration::Share.create
58
- redirect! '/settings'
59
- end
60
-
61
76
  var(:int) do |id|
62
77
  delete do
63
- share = manager.config.shares.find { |s| s.id == id }
64
- halt 404 if share.nil?
65
- share.destroy
66
- redirect! '/settings'
78
+ halt(404) unless manager.remove_by_id(id)
79
+ redirect!('/settings')
67
80
  end
68
81
  end
69
82
  end
70
83
 
71
84
  var :int do |idx|
72
- repository = repositories[idx]
85
+ halt(404) unless repositories[idx]
86
+ path = Gitdocs::Repository::Path.new(
87
+ repositories[idx], URI.unescape(request.path_info)
88
+ )
89
+
90
+ mode = request.params['mode']
91
+ default_locals = {
92
+ idx: idx,
93
+ root: repositories[idx].root,
94
+ nav_state: nil
95
+ }
73
96
 
74
- halt 404 if repository.nil?
75
- file_path = URI.unescape(request.path_info)
76
- expanded_path = File.expand_path(".#{file_path}", repository.root)
77
- message_file = File.expand_path('.gitmessage~', repository.root)
78
- halt 400 unless expanded_path[/^#{Regexp.quote(repository.root)}/]
79
- parent = File.dirname(file_path)
80
- parent = '' if parent == '/'
81
- parent = nil if parent == '.'
82
- locals = { idx: idx, parent: parent, root: repository.root, file_path: expanded_path, nav_state: nil }
83
- mime = File.mime_type?(File.open(expanded_path)) if File.file?(expanded_path)
84
- mode = request.params['mode']
85
97
  if mode == 'meta' # Meta
86
- halt 200, { 'Content-Type' => 'application/json' }, [repository.file_meta(file_path).to_json]
98
+ halt 200, { 'Content-Type' => 'application/json' }, [path.meta.to_json]
87
99
  elsif mode == 'save' # Saving
88
- File.open(expanded_path, 'w') { |f| f.print request.params['data'] }
89
- File.open(message_file, 'w') { |f| f.print request.params['message'] } unless request.params['message'] == ''
90
- redirect! '/' + idx.to_s + file_path
100
+ path.write(request.params['data'], request.params['message'])
101
+ redirect!("/#{idx}/#{path.relative_path}")
91
102
  elsif mode == 'upload' # Uploading
92
- halt 404 unless file = request.params['file']
93
- tempfile, filename = file[:tempfile], file[:filename]
94
- FileUtils.mv(tempfile.path, File.expand_path(filename, expanded_path))
95
- redirect! '/' + idx.to_s + file_path + '/' + filename
96
- elsif !File.exist?(expanded_path) && !request.params['dir'] # edit for non-existent file
97
- FileUtils.mkdir_p(File.dirname(expanded_path))
98
- FileUtils.touch(expanded_path)
99
- redirect! '/' + idx.to_s + file_path + '?mode=edit'
100
- elsif !File.exist?(expanded_path) && request.params['dir'] # create directory
101
- FileUtils.mkdir_p(expanded_path)
102
- redirect! '/' + idx.to_s + file_path
103
- elsif File.directory?(expanded_path) # list directory
104
- contents = Dir[File.join(expanded_path, '*')].map { |x| Docfile.new(x) }
105
- rendered_readme = nil
106
- if readme = Dir[File.expand_path('README.{md}', expanded_path)].first
107
- rendered_readme = '<h3>' + File.basename(readme) + '</h3><div class="tilt">' + render(readme) + '</div>'
108
- end
109
- render! 'dir', layout: 'app', locals: locals.merge(contents: contents, rendered_readme: rendered_readme)
103
+ file = request.params['file']
104
+ halt 404 unless file
105
+ tempfile = file[:tempfile]
106
+ filename = file[:filename]
107
+ FileUtils.mv(tempfile.path, path.absolute_path)
108
+ redirect!("/#{idx}/#{path.relative_path}/#{filename}")
109
+ elsif !path.exist? && !request.params['dir'] # edit for non-existent file
110
+ path.touch
111
+ redirect!("/#{idx}/#{path.relative_path}?mode=edit")
112
+ elsif !path.exist? && request.params['dir'] # create directory
113
+ path.mkdir
114
+ redirect!("/#{idx}/#{path.relative_path}")
115
+ elsif path.directory? # list directory
116
+ rendered_readme =
117
+ if path.readme_path
118
+ <<-EOS.gusb(/^\s+/, '')
119
+ <h3>#{File.basename(path.readme_path)}</h3>
120
+ <div class="tilt">#{render(path.readme_path)}</div>
121
+ EOS
122
+ else
123
+ nil
124
+ end
125
+ render!(
126
+ 'dir',
127
+ layout: 'app',
128
+ locals: default_locals.merge(
129
+ contents: path.file_listing,
130
+ rendered_readme: rendered_readme
131
+ )
132
+ )
110
133
  elsif mode == 'revisions' # list revisions
111
- revisions = repository.file_revisions(file_path)
112
- render! 'revisions', layout: 'app', locals: locals.merge(revisions: revisions)
134
+ render!(
135
+ 'revisions',
136
+ layout: 'app',
137
+ locals: default_locals.merge(revisions: path.revisions)
138
+ )
113
139
  elsif mode == 'revert' # revert file
114
- if revision = request.params['revision']
115
- File.open(message_file, 'w') { |f| f.print "Reverting '#{file_path}' to #{revision}" }
116
- repository.file_revert(file_path, revision)
117
- end
118
- redirect! '/' + idx.to_s + file_path
140
+ path.revert(request.params['revision'])
141
+ redirect!("/#{idx}/#{path.relative_path}")
119
142
  elsif mode == 'delete' # delete file
120
- FileUtils.rm(expanded_path)
121
- redirect! '/' + idx.to_s + parent
122
- elsif mode == 'edit' && (mime.match(%r{text/}) || mime.match(%r{x-empty})) # edit file
123
- contents = File.read(expanded_path)
124
- render! 'edit', layout: 'app', locals: locals.merge(contents: contents)
143
+ path.remove
144
+ parent = File.dirname(path.relative_path)
145
+ parent = '' if parent == '/'
146
+ parent = nil if parent == '.'
147
+ redirect!("/#{idx}#{parent}")
148
+ elsif mode == 'edit' && path.text? # edit file
149
+ render!(
150
+ 'edit',
151
+ layout: 'app',
152
+ locals: default_locals.merge(contents: path.content)
153
+ )
125
154
  elsif mode != 'raw' # render file
126
- revision = request.params['revision']
127
- expanded_path = repository.file_revision_at(file_path, revision) if revision
128
- begin # attempting to render file
129
- contents = '<div class="tilt">' + render(expanded_path) + '</div>'
130
- rescue RuntimeError # not tilt supported
131
- contents = if mime.match(%r{text/})
132
- '<pre class="CodeRay">' + CodeRay.scan_file(expanded_path).encode(:html) + '</pre>'
133
- else
134
- %|<embed class="inline-file" src="/#{idx}#{request.path_info}?mode=raw"></embed>|
155
+ revision_path = path.absolute_path(request.params['revision'])
156
+ contents =
157
+ begin # attempting to render file
158
+ %(<div class="tilt">#{render(revision_path)}</div>)
159
+ rescue RuntimeError # not tilt supported
160
+ if path.text?
161
+ <<-EOS.gsub(/^\s+/, '')
162
+ <pre class="CodeRay">
163
+ #{CodeRay.scan_file(revision_path).encode(:html)}
164
+ </pre>
165
+ EOS
166
+ else
167
+ %(<embed class="inline-file" src="/#{idx}#{request.path_info}?mode=raw"></embed>)
168
+ end
135
169
  end
136
- end
137
- render! 'file', layout: 'app', locals: locals.merge(contents: contents)
170
+ render!(
171
+ 'file',
172
+ layout: 'app',
173
+ locals: default_locals.merge(contents: contents)
174
+ )
138
175
  else # other file
139
- run! Rack::File.new(repository.root)
176
+ run! Rack::File.new(repositories[idx].root)
140
177
  end
141
178
  end
142
179
  end
143
180
  }.setup {
144
- views_path File.expand_path('../views', __FILE__)
181
+ views_path(File.expand_path('../views', __FILE__))
145
182
  }
146
183
  end
147
184
  end
148
185
 
149
- def wait_for_start_and_open(restarting)
186
+ def wait_for_start
150
187
  wait_for_web_server = proc do
151
188
  i = 0
152
189
  begin