gitdocs 0.4.15 → 0.5.0.pre1
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.
- checksums.yaml +15 -0
- data/.gitignore +2 -1
- data/.travis.yml +8 -0
- data/CHANGELOG +13 -1
- data/README.md +17 -5
- data/Rakefile +2 -0
- data/bin/gitdocs +1 -1
- data/gitdocs.gemspec +8 -5
- data/lib/gitdocs.rb +2 -3
- data/lib/gitdocs/cli.rb +89 -45
- data/lib/gitdocs/configuration.rb +13 -12
- data/lib/gitdocs/docfile.rb +1 -1
- data/lib/gitdocs/manager.rb +35 -42
- data/lib/gitdocs/migration/001_create_shares.rb +3 -3
- data/lib/gitdocs/migration/002_add_remote_branch.rb +3 -3
- data/lib/gitdocs/migration/003_create_configs.rb +3 -3
- data/lib/gitdocs/migration/004_add_index_for_path.rb +3 -3
- data/lib/gitdocs/migration/005_add_start_web_frontend.rb +2 -2
- data/lib/gitdocs/migration/006_add_web_port_to_config.rb +2 -2
- data/lib/gitdocs/rendering.rb +1 -1
- data/lib/gitdocs/runner.rb +64 -50
- data/lib/gitdocs/server.rb +51 -27
- data/lib/gitdocs/version.rb +2 -2
- data/test/configuration_test.rb +13 -13
- data/test/runner_test.rb +13 -13
- data/test/test_helper.rb +24 -22
- metadata +34 -63
| @@ -2,12 +2,12 @@ class CreateShares < ActiveRecord::Migration | |
| 2 2 | 
             
              def self.up
         | 
| 3 3 | 
             
                create_table :shares do |t|
         | 
| 4 4 | 
             
                  t.column :path, :string
         | 
| 5 | 
            -
                  t.column :polling_interval, :double, : | 
| 6 | 
            -
                  t.column :notification, :boolean, : | 
| 5 | 
            +
                  t.column :polling_interval, :double, default: 15
         | 
| 6 | 
            +
                  t.column :notification, :boolean, default: true
         | 
| 7 7 | 
             
                end
         | 
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              def self.down
         | 
| 11 | 
            -
                 | 
| 11 | 
            +
                fail
         | 
| 12 12 | 
             
              end
         | 
| 13 13 | 
             
            end
         | 
| @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            class AddRemoteBranch < ActiveRecord::Migration
         | 
| 2 2 | 
             
              def self.up
         | 
| 3 | 
            -
                add_column :shares, :remote_name, :string, : | 
| 4 | 
            -
                add_column :shares, :branch_name, :string, : | 
| 3 | 
            +
                add_column :shares, :remote_name, :string, default: 'origin'
         | 
| 4 | 
            +
                add_column :shares, :branch_name, :string, default: 'master'
         | 
| 5 5 | 
             
              end
         | 
| 6 6 |  | 
| 7 7 | 
             
              def self.down
         | 
| 8 | 
            -
                 | 
| 8 | 
            +
                fail
         | 
| 9 9 | 
             
              end
         | 
| 10 10 | 
             
            end
         | 
| @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            class CreateConfigs < ActiveRecord::Migration
         | 
| 2 2 | 
             
              def self.up
         | 
| 3 3 | 
             
                create_table :configs do |t|
         | 
| 4 | 
            -
                  t.column :load_browser_on_startup, :boolean, : | 
| 4 | 
            +
                  t.column :load_browser_on_startup, :boolean, default: true
         | 
| 5 5 | 
             
                end
         | 
| 6 6 | 
             
              end
         | 
| 7 7 |  | 
| 8 8 | 
             
              def self.down
         | 
| 9 | 
            -
                 | 
| 9 | 
            +
                fail
         | 
| 10 10 | 
             
              end
         | 
| 11 | 
            -
            end
         | 
| 11 | 
            +
            end
         | 
| @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            class AddIndexForPath < ActiveRecord::Migration
         | 
| 2 2 | 
             
              def self.up
         | 
| 3 | 
            -
                shares = Gitdocs::Configuration::Share.all. | 
| 3 | 
            +
                shares = Gitdocs::Configuration::Share.all.reduce(Hash.new { |h, k| h[k] = [] }) { |h, s| h[s.path] << s; h }
         | 
| 4 4 | 
             
                shares.each do |path, shares|
         | 
| 5 5 | 
             
                  shares.shift
         | 
| 6 6 | 
             
                  shares.each(&:destroy) unless shares.empty?
         | 
| 7 7 | 
             
                end
         | 
| 8 | 
            -
                add_index :shares, :path, : | 
| 8 | 
            +
                add_index :shares, :path, unique: true
         | 
| 9 9 | 
             
              end
         | 
| 10 | 
            -
            end
         | 
| 10 | 
            +
            end
         | 
    
        data/lib/gitdocs/rendering.rb
    CHANGED
    
    
    
        data/lib/gitdocs/runner.rb
    CHANGED
    
    | @@ -4,11 +4,17 @@ module Gitdocs | |
| 4 4 |  | 
| 5 5 | 
             
                attr_reader :root, :listener
         | 
| 6 6 |  | 
| 7 | 
            +
                def self.start_all(shares)
         | 
| 8 | 
            +
                  runners = shares.map { |share| Runner.new(share) }
         | 
| 9 | 
            +
                  runners.each(&:run)
         | 
| 10 | 
            +
                  runners
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 7 13 | 
             
                def initialize(share)
         | 
| 8 14 | 
             
                  @share = share
         | 
| 9 | 
            -
                  @root  = share.path.sub(%r{/+$},'') if share.path
         | 
| 15 | 
            +
                  @root  = share.path.sub(%r{/+$}, '') if share.path
         | 
| 10 16 | 
             
                  @polling_interval = share.polling_interval
         | 
| 11 | 
            -
                  @icon = File.expand_path( | 
| 17 | 
            +
                  @icon = File.expand_path('../../img/icon.png', __FILE__)
         | 
| 12 18 | 
             
                end
         | 
| 13 19 |  | 
| 14 20 | 
             
                SearchResult = Struct.new(:file, :context)
         | 
| @@ -17,7 +23,7 @@ module Gitdocs | |
| 17 23 |  | 
| 18 24 | 
             
                  results = []
         | 
| 19 25 | 
             
                  if result_test = sh_string("git grep -i #{ShellTools.escape(term)}")
         | 
| 20 | 
            -
                    result_test.scan(/(.*?):([^\n]*)/) do |(file, context)| | 
| 26 | 
            +
                    result_test.scan(/(.*?):([^\n]*)/) do |(file, context)|
         | 
| 21 27 | 
             
                      if result = results.find { |s| s.file == file }
         | 
| 22 28 | 
             
                        result.context += ' ... ' + context
         | 
| 23 29 | 
             
                      else
         | 
| @@ -34,41 +40,41 @@ module Gitdocs | |
| 34 40 | 
             
                  @show_notifications = @share.notification
         | 
| 35 41 | 
             
                  @current_remote     = @share.remote_name
         | 
| 36 42 | 
             
                  @current_branch     = @share.branch_name
         | 
| 37 | 
            -
                  @current_revision   = sh_string( | 
| 43 | 
            +
                  @current_revision   = sh_string('git rev-parse HEAD')
         | 
| 38 44 | 
             
                  Guard::Notifier.turn_on if @show_notifications
         | 
| 39 45 |  | 
| 40 46 | 
             
                  mutex = Mutex.new
         | 
| 41 47 |  | 
| 42 | 
            -
                  info( | 
| 48 | 
            +
                  info('Running gitdocs!', "Running gitdocs in `#{@root}'")
         | 
| 43 49 |  | 
| 44 50 | 
             
                  # Pull changes from remote repository
         | 
| 45 51 | 
             
                  syncer = proc do
         | 
| 46 52 | 
             
                    EM.defer(proc do
         | 
| 47 53 | 
             
                      mutex.synchronize { sync_changes }
         | 
| 48 54 | 
             
                    end, proc do
         | 
| 49 | 
            -
                      EM.add_timer(@polling_interval)  | 
| 55 | 
            +
                      EM.add_timer(@polling_interval) do
         | 
| 50 56 | 
             
                        syncer.call
         | 
| 51 | 
            -
                       | 
| 57 | 
            +
                      end
         | 
| 52 58 | 
             
                    end)
         | 
| 53 59 | 
             
                  end
         | 
| 54 60 | 
             
                  syncer.call
         | 
| 55 61 | 
             
                  # Listen for changes in local repository
         | 
| 56 62 |  | 
| 57 | 
            -
                  EM.defer(proc | 
| 58 | 
            -
                    listener = Guard::Listener.select_and_init(@root, : | 
| 59 | 
            -
                    listener.on_change  | 
| 63 | 
            +
                  EM.defer(proc do
         | 
| 64 | 
            +
                    listener = Guard::Listener.select_and_init(@root, watch_all_modifications: true)
         | 
| 65 | 
            +
                    listener.on_change do |directories|
         | 
| 60 66 | 
             
                      directories.uniq!
         | 
| 61 | 
            -
                      directories.delete_if {|d| d =~ /\/\.git/}
         | 
| 67 | 
            +
                      directories.delete_if { |d| d =~ /\/\.git/ }
         | 
| 62 68 | 
             
                      unless directories.empty?
         | 
| 63 69 | 
             
                        EM.next_tick do
         | 
| 64 | 
            -
                          EM.defer(proc  | 
| 70 | 
            +
                          EM.defer(proc do
         | 
| 65 71 | 
             
                            mutex.synchronize { push_changes }
         | 
| 66 | 
            -
                           | 
| 72 | 
            +
                          end, proc {})
         | 
| 67 73 | 
             
                        end
         | 
| 68 74 | 
             
                      end
         | 
| 69 | 
            -
                     | 
| 75 | 
            +
                    end
         | 
| 70 76 | 
             
                    listener.start
         | 
| 71 | 
            -
                   | 
| 77 | 
            +
                  end, proc { EM.stop_reactor })
         | 
| 72 78 | 
             
                end
         | 
| 73 79 |  | 
| 74 80 | 
             
                def clear_state
         | 
| @@ -80,36 +86,36 @@ module Gitdocs | |
| 80 86 | 
             
                  if status.success?
         | 
| 81 87 | 
             
                    changes = get_latest_changes
         | 
| 82 88 | 
             
                    unless changes.empty?
         | 
| 83 | 
            -
                      author_list = changes. | 
| 89 | 
            +
                      author_list = changes.reduce(Hash.new { |h, k| h[k] = 0 }) { |h, c| h[c['author']] += 1; h }.to_a.sort { |a, b| b[1] <=> a[1] }.map { |(name, count)| "* #{name} (#{count} change#{count == 1 ? '' : 's'})" }.join("\n")
         | 
| 84 90 | 
             
                      info("Updated with #{changes.size} change#{changes.size == 1 ? '' : 's'}", "In `#{@root}':\n#{author_list}")
         | 
| 85 91 | 
             
                    end
         | 
| 86 92 | 
             
                    push_changes
         | 
| 87 93 | 
             
                  elsif out[/CONFLICT/]
         | 
| 88 | 
            -
                    conflicted_files = sh( | 
| 89 | 
            -
                       | 
| 94 | 
            +
                    conflicted_files = sh('git ls-files -u --full-name -z').split("\0")
         | 
| 95 | 
            +
                      .reduce(Hash.new { |h, k| h[k] = [] }) do|h, line|
         | 
| 90 96 | 
             
                        parts = line.split(/\t/)
         | 
| 91 97 | 
             
                        h[parts.last] << parts.first.split(/ /)
         | 
| 92 98 | 
             
                        h
         | 
| 93 | 
            -
                       | 
| 94 | 
            -
                    warn( | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
                    warn('There were some conflicts', "#{conflicted_files.keys.map { |f| "* #{f}" }.join("\n")}")
         | 
| 95 101 | 
             
                    conflicted_files.each do |conflict, ids|
         | 
| 96 102 | 
             
                      conflict_start, conflict_end = conflict.scan(/(.*?)(|\.[^\.]+)$/).first
         | 
| 97 103 | 
             
                      ids.each do |(mode, sha, id)|
         | 
| 98 | 
            -
                        author =   | 
| 104 | 
            +
                        author =  ' original' if id == '1'
         | 
| 99 105 | 
             
                        system("cd #{@root} && git show :#{id}:#{conflict} > '#{conflict_start} (#{sha[0..6]}#{author})#{conflict_end}'")
         | 
| 100 106 | 
             
                      end
         | 
| 101 | 
            -
                      system("cd #{@root} && git rm #{conflict}")  | 
| 107 | 
            +
                      system("cd #{@root} && git rm #{conflict}") || fail
         | 
| 102 108 | 
             
                    end
         | 
| 103 109 | 
             
                    push_changes
         | 
| 104 | 
            -
                  elsif sh_string( | 
| 110 | 
            +
                  elsif sh_string('git remote').nil? # no remote to pull from
         | 
| 105 111 | 
             
                    # Do nothing, no remote repo yet
         | 
| 106 112 | 
             
                  else
         | 
| 107 | 
            -
                    error( | 
| 113 | 
            +
                    error('There was a problem synchronizing this gitdoc', "A problem occurred in #{@root}:\n#{out}")
         | 
| 108 114 | 
             
                  end
         | 
| 109 115 | 
             
                end
         | 
| 110 116 |  | 
| 111 117 | 
             
                def push_changes
         | 
| 112 | 
            -
                  message_file = File.expand_path( | 
| 118 | 
            +
                  message_file = File.expand_path('.gitmessage~', @root)
         | 
| 113 119 | 
             
                  if File.exist? message_file
         | 
| 114 120 | 
             
                    message = File.read message_file
         | 
| 115 121 | 
             
                    File.delete message_file
         | 
| @@ -118,7 +124,7 @@ module Gitdocs | |
| 118 124 | 
             
                  end
         | 
| 119 125 | 
             
                  sh 'find . -type d -regex ``./[^.].*'' -empty -exec touch \'{}/.gitignore\' \;'
         | 
| 120 126 | 
             
                  sh 'git add .'
         | 
| 121 | 
            -
                  sh "git commit -a -m #{ShellTools.escape(message)}" unless sh( | 
| 127 | 
            +
                  sh "git commit -a -m #{ShellTools.escape(message)}" unless sh('git status -s').strip.empty?
         | 
| 122 128 | 
             
                  if @current_revision.nil? || sh('git status')[/branch is ahead/]
         | 
| 123 129 | 
             
                    out, code = sh_with_code("git push #{@current_remote} #{@current_branch}")
         | 
| 124 130 | 
             
                    if code.success?
         | 
| @@ -127,12 +133,19 @@ module Gitdocs | |
| 127 133 | 
             
                    elsif @current_revision.nil?
         | 
| 128 134 | 
             
                      # ignorable
         | 
| 129 135 | 
             
                    elsif out[/\[rejected\]/]
         | 
| 130 | 
            -
                      warn("There was a conflict in #{@root}, retrying",  | 
| 136 | 
            +
                      warn("There was a conflict in #{@root}, retrying", '')
         | 
| 131 137 | 
             
                    else
         | 
| 132 138 | 
             
                      error("BAD Could not push changes in #{@root}", out)
         | 
| 133 | 
            -
                       | 
| 139 | 
            +
                      # TODO: need to add a status on shares so that the push problem can be
         | 
| 140 | 
            +
                      # displayed.
         | 
| 134 141 | 
             
                    end
         | 
| 135 142 | 
             
                  end
         | 
| 143 | 
            +
                rescue
         | 
| 144 | 
            +
                  # Rescue any standard exceptions which come from the push related
         | 
| 145 | 
            +
                  # commands. This will prevent problems on a single share from killing
         | 
| 146 | 
            +
                  # the entire daemon.
         | 
| 147 | 
            +
                  error("Unexpected error pushing changes in #{@root}")
         | 
| 148 | 
            +
                  # TODO: get logging and/or put the error message into a status field in the database
         | 
| 136 149 | 
             
                end
         | 
| 137 150 |  | 
| 138 151 | 
             
                def get_latest_changes
         | 
| @@ -145,7 +158,7 @@ module Gitdocs | |
| 145 158 | 
             
                      Yajl::Parser.new.parse(out) do |obj|
         | 
| 146 159 | 
             
                        lines << obj
         | 
| 147 160 | 
             
                      end
         | 
| 148 | 
            -
                      @current_revision = sh( | 
| 161 | 
            +
                      @current_revision = sh('git rev-parse HEAD').strip
         | 
| 149 162 | 
             
                      lines
         | 
| 150 163 | 
             
                    end
         | 
| 151 164 | 
             
                  else
         | 
| @@ -157,30 +170,28 @@ module Gitdocs | |
| 157 170 | 
             
                # Returns the list of files in a given directory
         | 
| 158 171 | 
             
                # dir_files("some/dir") => [<Docfile>, <Docfile>]
         | 
| 159 172 | 
             
                def dir_files(dir_path)
         | 
| 160 | 
            -
                  Dir[File.join(dir_path,  | 
| 173 | 
            +
                  Dir[File.join(dir_path, '*')].to_a.map { |path| Docfile.new(path) }
         | 
| 161 174 | 
             
                end
         | 
| 162 175 |  | 
| 163 176 | 
             
                # Returns file meta data based on relative file path
         | 
| 164 177 | 
             
                # file_meta("path/to/file")
         | 
| 165 178 | 
             
                #  => { :author => "Nick", :size => 1000, :modified => ... }
         | 
| 166 179 | 
             
                def file_meta(file)
         | 
| 167 | 
            -
                  result = {}
         | 
| 168 180 | 
             
                  file = file.gsub(%r{^/}, '')
         | 
| 169 181 | 
             
                  full_path = File.expand_path(file, @root)
         | 
| 170 182 | 
             
                  log_result = sh_string("git log --format='%aN|%ai' -n1 #{ShellTools.escape(file)}")
         | 
| 171 | 
            -
                   | 
| 172 | 
            -
                  author, modified = log_result.split("|")
         | 
| 183 | 
            +
                  author, modified = log_result.split('|')
         | 
| 173 184 | 
             
                  modified = Time.parse(modified.sub(' ', 'T')).utc.iso8601
         | 
| 174 185 | 
             
                  size = if File.directory?(full_path)
         | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 186 | 
            +
                    Dir[File.join(full_path, '**', '*')].reduce(0) do |size, file|
         | 
| 187 | 
            +
                      File.symlink?(file) ? size : size += File.size(file)
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                  else
         | 
| 190 | 
            +
                    File.symlink?(full_path) ? 0 : File.size(full_path)
         | 
| 191 | 
            +
                  end
         | 
| 181 192 | 
             
                  size = -1 if size == 0 # A value of 0 breaks the table sort for some reason
         | 
| 182 | 
            -
             | 
| 183 | 
            -
                   | 
| 193 | 
            +
             | 
| 194 | 
            +
                  { author: author, size: size, modified: modified }
         | 
| 184 195 | 
             
                end
         | 
| 185 196 |  | 
| 186 197 | 
             
                # Returns the revisions available for a particular file
         | 
| @@ -189,9 +200,9 @@ module Gitdocs | |
| 189 200 | 
             
                  file = file.gsub(%r{^/}, '')
         | 
| 190 201 | 
             
                  output = sh_string("git log --format='%h|%s|%aN|%ai' -n100 #{ShellTools.escape(file)}")
         | 
| 191 202 | 
             
                  output.to_s.split("\n").map do |log_result|
         | 
| 192 | 
            -
                    commit, subject, author, date = log_result.split( | 
| 203 | 
            +
                    commit, subject, author, date = log_result.split('|')
         | 
| 193 204 | 
             
                    date = Time.parse(date.sub(' ', 'T')).utc.iso8601
         | 
| 194 | 
            -
                    { : | 
| 205 | 
            +
                    { commit: commit, subject: subject, author: author, date: date }
         | 
| 195 206 | 
             
                  end
         | 
| 196 207 | 
             
                end
         | 
| 197 208 |  | 
| @@ -207,7 +218,7 @@ module Gitdocs | |
| 207 218 |  | 
| 208 219 | 
             
                # Revert a file to a particular revision
         | 
| 209 220 | 
             
                def file_revert(file, ref)
         | 
| 210 | 
            -
                  if file_revisions(file).map {|r| r[:commit]}.include? ref
         | 
| 221 | 
            +
                  if file_revisions(file).map { |r| r[:commit] }.include? ref
         | 
| 211 222 | 
             
                    file = file.gsub(%r{^/}, '')
         | 
| 212 223 | 
             
                    full_path = File.expand_path(file, @root)
         | 
| 213 224 | 
             
                    content = File.read(file_revision_at(file, ref))
         | 
| @@ -216,38 +227,41 @@ module Gitdocs | |
| 216 227 | 
             
                end
         | 
| 217 228 |  | 
| 218 229 | 
             
                def valid?
         | 
| 219 | 
            -
                  out, status = sh_with_code  | 
| 230 | 
            +
                  out, status = sh_with_code 'git status'
         | 
| 220 231 | 
             
                  @root.present? && status.success?
         | 
| 221 232 | 
             
                end
         | 
| 222 233 |  | 
| 223 234 | 
             
                def warn(title, msg)
         | 
| 224 235 | 
             
                  if @show_notifications
         | 
| 225 | 
            -
                    Guard::Notifier.notify(msg, : | 
| 236 | 
            +
                    Guard::Notifier.notify(msg, title: title)
         | 
| 226 237 | 
             
                  else
         | 
| 227 238 | 
             
                    Kernel.warn("#{title}: #{msg}")
         | 
| 228 239 | 
             
                  end
         | 
| 240 | 
            +
                rescue # Prevent StandardErrors from stopping the daemon.
         | 
| 229 241 | 
             
                end
         | 
| 230 242 |  | 
| 231 243 | 
             
                def info(title, msg)
         | 
| 232 244 | 
             
                  if @show_notifications
         | 
| 233 | 
            -
                    Guard::Notifier.notify(msg, : | 
| 245 | 
            +
                    Guard::Notifier.notify(msg, title: title, image: @icon)
         | 
| 234 246 | 
             
                  else
         | 
| 235 247 | 
             
                    puts("#{title}: #{msg}")
         | 
| 236 248 | 
             
                  end
         | 
| 249 | 
            +
                rescue # Prevent StandardErrors from stopping the daemon.
         | 
| 237 250 | 
             
                end
         | 
| 238 251 |  | 
| 239 252 | 
             
                def error(title, msg)
         | 
| 240 253 | 
             
                  if @show_notifications
         | 
| 241 | 
            -
                    Guard::Notifier.notify(msg, : | 
| 254 | 
            +
                    Guard::Notifier.notify(msg, title: title, image: :failure)
         | 
| 242 255 | 
             
                  else
         | 
| 243 256 | 
             
                    Kernel.warn("#{title}: #{msg}")
         | 
| 244 257 | 
             
                  end
         | 
| 258 | 
            +
                rescue # Prevent StandardErrors from stopping the daemon.
         | 
| 245 259 | 
             
                end
         | 
| 246 260 |  | 
| 247 261 | 
             
                # sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", "origin")
         | 
| 248 | 
            -
                def sh_string(cmd, default=nil)
         | 
| 262 | 
            +
                def sh_string(cmd, default = nil)
         | 
| 249 263 | 
             
                  val = sh(cmd).strip rescue nil
         | 
| 250 | 
            -
                   | 
| 264 | 
            +
                  val.nil? || val.empty? ? default : val
         | 
| 251 265 | 
             
                end
         | 
| 252 266 |  | 
| 253 267 | 
             
                # Run in shell, return both status and output
         | 
    
        data/lib/gitdocs/server.rb
    CHANGED
    
    | @@ -4,31 +4,33 @@ require 'coderay' | |
| 4 4 | 
             
            require 'uri'
         | 
| 5 5 | 
             
            require 'haml'
         | 
| 6 6 | 
             
            require 'mimetype_fu'
         | 
| 7 | 
            +
            require 'launchy'
         | 
| 7 8 |  | 
| 8 9 | 
             
            module Gitdocs
         | 
| 9 10 | 
             
              class Server
         | 
| 10 | 
            -
                def initialize(manager, *gitdocs)
         | 
| 11 | 
            +
                def initialize(manager, port = 8888, *gitdocs)
         | 
| 11 12 | 
             
                  @manager = manager
         | 
| 13 | 
            +
                  @port    = port.to_i
         | 
| 12 14 | 
             
                  @gitdocs = gitdocs
         | 
| 13 15 | 
             
                end
         | 
| 14 16 |  | 
| 15 | 
            -
                def start | 
| 16 | 
            -
                  gds | 
| 17 | 
            +
                def start
         | 
| 18 | 
            +
                  gds     = @gitdocs
         | 
| 17 19 | 
             
                  manager = @manager
         | 
| 18 20 | 
             
                  Thin::Logging.debug = @manager.debug
         | 
| 19 | 
            -
                  Thin::Server.start('127.0.0.1', port) do
         | 
| 20 | 
            -
                    use Rack::Static, : | 
| 21 | 
            +
                  Thin::Server.start('127.0.0.1', @port) do
         | 
| 22 | 
            +
                    use Rack::Static, urls: ['/css', '/js', '/img', '/doc'], root: File.expand_path('../public', __FILE__)
         | 
| 21 23 | 
             
                    use Rack::MethodOverride
         | 
| 22 24 | 
             
                    run Renee {
         | 
| 23 25 | 
             
                      if request.path_info == '/'
         | 
| 24 26 | 
             
                        if manager.config.shares.size == 1
         | 
| 25 | 
            -
                          redirect!  | 
| 27 | 
            +
                          redirect! '/0'
         | 
| 26 28 | 
             
                        else
         | 
| 27 | 
            -
                          render!  | 
| 29 | 
            +
                          render! 'home', layout: 'app', locals: { conf: manager.config, nav_state: 'home' }
         | 
| 28 30 | 
             
                        end
         | 
| 29 31 | 
             
                      else
         | 
| 30 32 | 
             
                        path 'settings' do
         | 
| 31 | 
            -
                          get.render! 'settings', : | 
| 33 | 
            +
                          get.render! 'settings', layout: 'app', locals: { conf: manager.config, nav_state: 'settings' }
         | 
| 32 34 | 
             
                          post do
         | 
| 33 35 | 
             
                            shares = manager.config.shares
         | 
| 34 36 | 
             
                            manager.config.global.update_attributes(request.POST['config'])
         | 
| @@ -47,7 +49,7 @@ module Gitdocs | |
| 47 49 | 
             
                        end
         | 
| 48 50 |  | 
| 49 51 | 
             
                        path('search').get do
         | 
| 50 | 
            -
                          render!  | 
| 52 | 
            +
                          render! 'search', layout: 'app', locals: { conf: manager.config, results: manager.search(request.GET['q']), nav_state: nil }
         | 
| 51 53 | 
             
                        end
         | 
| 52 54 |  | 
| 53 55 | 
             
                        path('shares') do
         | 
| @@ -70,14 +72,13 @@ module Gitdocs | |
| 70 72 | 
             
                          gd = gds[idx]
         | 
| 71 73 | 
             
                          halt 404 if gd.nil?
         | 
| 72 74 | 
             
                          file_path = URI.unescape(request.path_info)
         | 
| 73 | 
            -
                          file_ext  = File.extname(file_path)
         | 
| 74 75 | 
             
                          expanded_path = File.expand_path(".#{file_path}", gd.root)
         | 
| 75 | 
            -
                          message_file = File.expand_path( | 
| 76 | 
            +
                          message_file = File.expand_path('.gitmessage~', gd.root)
         | 
| 76 77 | 
             
                          halt 400 unless expanded_path[/^#{Regexp.quote(gd.root)}/]
         | 
| 77 78 | 
             
                          parent = File.dirname(file_path)
         | 
| 78 79 | 
             
                          parent = '' if parent == '/'
         | 
| 79 80 | 
             
                          parent = nil if parent == '.'
         | 
| 80 | 
            -
                          locals = {: | 
| 81 | 
            +
                          locals = { idx: idx, parent: parent, root: gd.root, file_path: expanded_path, nav_state: nil }
         | 
| 81 82 | 
             
                          mime = File.mime_type?(File.open(expanded_path)) if File.file?(expanded_path)
         | 
| 82 83 | 
             
                          mode = request.params['mode']
         | 
| 83 84 | 
             
                          if mode == 'meta' # Meta
         | 
| @@ -85,63 +86,86 @@ module Gitdocs | |
| 85 86 | 
             
                          elsif mode == 'save' # Saving
         | 
| 86 87 | 
             
                            File.open(expanded_path, 'w') { |f| f.print request.params['data'] }
         | 
| 87 88 | 
             
                            File.open(message_file, 'w') { |f| f.print request.params['message'] } unless request.params['message'] == ''
         | 
| 88 | 
            -
                            redirect!  | 
| 89 | 
            +
                            redirect! '/' + idx.to_s + file_path
         | 
| 89 90 | 
             
                          elsif mode == 'upload'  # Uploading
         | 
| 90 91 | 
             
                            halt 404 unless file = request.params['file']
         | 
| 91 92 | 
             
                            tempfile, filename = file[:tempfile], file[:filename]
         | 
| 92 93 | 
             
                            FileUtils.mv(tempfile.path, File.expand_path(filename, expanded_path))
         | 
| 93 | 
            -
                            redirect!  | 
| 94 | 
            +
                            redirect! '/' + idx.to_s + file_path + '/' + filename
         | 
| 94 95 | 
             
                          elsif !File.exist?(expanded_path) && !request.params['dir'] # edit for non-existent file
         | 
| 95 96 | 
             
                            FileUtils.mkdir_p(File.dirname(expanded_path))
         | 
| 96 97 | 
             
                            FileUtils.touch(expanded_path)
         | 
| 97 | 
            -
                            redirect!   | 
| 98 | 
            +
                            redirect!  '/' + idx.to_s + file_path + '?mode=edit'
         | 
| 98 99 | 
             
                          elsif !File.exist?(expanded_path) && request.params['dir'] # create directory
         | 
| 99 100 | 
             
                            FileUtils.mkdir_p(expanded_path)
         | 
| 100 | 
            -
                            redirect!   | 
| 101 | 
            +
                            redirect!  '/' + idx.to_s + file_path
         | 
| 101 102 | 
             
                          elsif File.directory?(expanded_path) # list directory
         | 
| 102 103 | 
             
                            contents =  gd.dir_files(expanded_path)
         | 
| 103 104 | 
             
                            rendered_readme = nil
         | 
| 104 | 
            -
                            if readme = Dir[File.expand_path( | 
| 105 | 
            +
                            if readme = Dir[File.expand_path('README.{md}', expanded_path)].first
         | 
| 105 106 | 
             
                              rendered_readme = '<h3>' + File.basename(readme) + '</h3><div class="tilt">' + render(readme) + '</div>'
         | 
| 106 107 | 
             
                            end
         | 
| 107 | 
            -
                            render!  | 
| 108 | 
            -
                          elsif mode ==  | 
| 108 | 
            +
                            render! 'dir', layout: 'app', locals: locals.merge(contents: contents, rendered_readme: rendered_readme)
         | 
| 109 | 
            +
                          elsif mode == 'revisions' # list revisions
         | 
| 109 110 | 
             
                            revisions = gd.file_revisions(file_path)
         | 
| 110 | 
            -
                            render!  | 
| 111 | 
            -
                          elsif mode ==  | 
| 111 | 
            +
                            render! 'revisions', layout: 'app', locals: locals.merge(revisions: revisions)
         | 
| 112 | 
            +
                          elsif mode == 'revert' # revert file
         | 
| 112 113 | 
             
                            if revision = request.params['revision']
         | 
| 113 114 | 
             
                              File.open(message_file, 'w') { |f| f.print "Reverting '#{file_path}' to #{revision}" }
         | 
| 114 115 | 
             
                              gd.file_revert(file_path, revision)
         | 
| 115 116 | 
             
                            end
         | 
| 116 | 
            -
                            redirect!  | 
| 117 | 
            +
                            redirect! '/' + idx.to_s + file_path
         | 
| 117 118 | 
             
                          elsif mode == 'delete' # delete file
         | 
| 118 119 | 
             
                            FileUtils.rm(expanded_path)
         | 
| 119 | 
            -
                            redirect!  | 
| 120 | 
            +
                            redirect! '/' + idx.to_s + parent
         | 
| 120 121 | 
             
                          elsif mode == 'edit' && (mime.match(%r{text/}) || mime.match(%r{x-empty})) # edit file
         | 
| 121 122 | 
             
                            contents = File.read(expanded_path)
         | 
| 122 | 
            -
                            render!  | 
| 123 | 
            +
                            render! 'edit', layout: 'app', locals: locals.merge(contents: contents)
         | 
| 123 124 | 
             
                          elsif mode != 'raw' # render file
         | 
| 124 125 | 
             
                            revision = request.params['revision']
         | 
| 125 126 | 
             
                            expanded_path = gd.file_revision_at(file_path, revision) if revision
         | 
| 126 127 | 
             
                            begin # attempting to render file
         | 
| 127 128 | 
             
                              contents = '<div class="tilt">' + render(expanded_path) + '</div>'
         | 
| 128 | 
            -
                            rescue RuntimeError  | 
| 129 | 
            +
                            rescue RuntimeError # not tilt supported
         | 
| 129 130 | 
             
                              contents = if mime.match(%r{text/})
         | 
| 130 131 | 
             
                                '<pre class="CodeRay">' + CodeRay.scan_file(expanded_path).encode(:html) + '</pre>'
         | 
| 131 132 | 
             
                              else
         | 
| 132 133 | 
             
                                %|<embed class="inline-file" src="/#{idx}#{request.path_info}?mode=raw"></embed>|
         | 
| 133 134 | 
             
                              end
         | 
| 134 135 | 
             
                            end
         | 
| 135 | 
            -
                            render!  | 
| 136 | 
            +
                            render! 'file', layout: 'app', locals: locals.merge(contents: contents)
         | 
| 136 137 | 
             
                          else # other file
         | 
| 137 138 | 
             
                            run! Rack::File.new(gd.root)
         | 
| 138 139 | 
             
                          end
         | 
| 139 140 | 
             
                        end
         | 
| 140 141 | 
             
                      end
         | 
| 141 142 | 
             
                    }.setup {
         | 
| 142 | 
            -
                      views_path File.expand_path( | 
| 143 | 
            +
                      views_path File.expand_path('../views', __FILE__)
         | 
| 143 144 | 
             
                    }
         | 
| 144 145 | 
             
                  end
         | 
| 145 146 | 
             
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def wait_for_start_and_open(restarting)
         | 
| 149 | 
            +
                  wait_for_web_server = proc do
         | 
| 150 | 
            +
                    i = 0
         | 
| 151 | 
            +
                    begin
         | 
| 152 | 
            +
                      TCPSocket.open('127.0.0.1', @port).close
         | 
| 153 | 
            +
                      @manager.log('Web server running!')
         | 
| 154 | 
            +
                      if !restarting && @manager.config.global.load_browser_on_startup
         | 
| 155 | 
            +
                        Launchy.open("http://localhost:#{@port}/")
         | 
| 156 | 
            +
                      end
         | 
| 157 | 
            +
                    rescue Errno::ECONNREFUSED
         | 
| 158 | 
            +
                      sleep 0.2
         | 
| 159 | 
            +
                      i += 1
         | 
| 160 | 
            +
                      if i <= 20
         | 
| 161 | 
            +
                        @manager.log('Retrying web server loop...')
         | 
| 162 | 
            +
                        retry
         | 
| 163 | 
            +
                      else
         | 
| 164 | 
            +
                        @manager.log('Web server failed to start')
         | 
| 165 | 
            +
                      end
         | 
| 166 | 
            +
                    end
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                  EM.defer(wait_for_web_server)
         | 
| 169 | 
            +
                end
         | 
| 146 170 | 
             
              end
         | 
| 147 171 | 
             
            end
         |