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.
@@ -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, :default => 15
6
- t.column :notification, :boolean, :default => true
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
- raise
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, :default => 'origin'
4
- add_column :shares, :branch_name, :string, :default => 'master'
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
- raise
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, :default => true
4
+ t.column :load_browser_on_startup, :boolean, default: true
5
5
  end
6
6
  end
7
7
 
8
8
  def self.down
9
- raise
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.inject(Hash.new{|h,k| h[k] = []}) {|h, s| h[s.path] << s; h}
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, :unique => true
8
+ add_index :shares, :path, unique: true
9
9
  end
10
- end
10
+ end
@@ -1,9 +1,9 @@
1
1
  class AddStartWebFrontend < ActiveRecord::Migration
2
2
  def self.up
3
- add_column :configs, :start_web_frontend, :boolean, :default => true
3
+ add_column :configs, :start_web_frontend, :boolean, default: true
4
4
  end
5
5
 
6
6
  def self.down
7
- raise
7
+ fail
8
8
  end
9
9
  end
@@ -1,9 +1,9 @@
1
1
  class AddWebPortToConfig < ActiveRecord::Migration
2
2
  def self.up
3
- add_column :configs, :web_frontend_port, :integer, :default => 8888
3
+ add_column :configs, :web_frontend_port, :integer, default: 8888
4
4
  end
5
5
 
6
6
  def self.down
7
- raise
7
+ fail
8
8
  end
9
9
  end
@@ -20,4 +20,4 @@ class RedcarpetCompat
20
20
  @text = @text.force_encoding('utf-8') if @text.respond_to?(:force_encoding)
21
21
  @markdown.render(@text)
22
22
  end
23
- end
23
+ end
@@ -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("../../img/icon.png", __FILE__)
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("git rev-parse HEAD")
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("Running gitdocs!", "Running gitdocs in `#{@root}'")
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, :watch_all_modifications => true)
59
- listener.on_change { |directories|
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
- }, proc {} )
72
+ end, proc {})
67
73
  end
68
74
  end
69
- }
75
+ end
70
76
  listener.start
71
- }, proc{EM.stop_reactor})
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.inject(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")
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("git ls-files -u --full-name -z").split("\0").
89
- inject(Hash.new{|h, k| h[k] = []}) {|h, line|
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("There were some conflicts", "#{conflicted_files.keys.map{|f| "* #{f}"}.join("\n")}")
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 = " original" if id == "1"
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}") or raise
107
+ system("cd #{@root} && git rm #{conflict}") || fail
102
108
  end
103
109
  push_changes
104
- elsif sh_string("git remote").nil? # no remote to pull from
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("There was a problem synchronizing this gitdoc", "A problem occurred in #{@root}:\n#{out}")
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(".gitmessage~", @root)
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("git status -s").strip.empty?
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
- exit
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("git rev-parse HEAD").strip
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, "*")].to_a.map { |path| Docfile.new(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
- result = {} unless File.exist?(full_path) && log_result
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
- Dir[File.join(full_path, '**', '*')].inject(0) do |size, file|
176
- File.symlink?(file) ? size : size += File.size(file)
177
- end
178
- else
179
- File.symlink?(full_path) ? 0 : File.size(full_path)
180
- end
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
- result = { :author => author, :size => size, :modified => modified }
183
- result
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
- { :commit => commit, :subject => subject, :author => author, :date => date }
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 "git status"
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, :title => title)
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, :title => title, :image => @icon)
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, :title => title, :image => :failure)
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
- (val.nil? || val.empty?) ? default : val
264
+ val.nil? || val.empty? ? default : val
251
265
  end
252
266
 
253
267
  # Run in shell, return both status and output
@@ -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(port = 8888)
16
- gds = @gitdocs
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, :urls => ['/css', '/js', '/img', '/doc'], :root => File.expand_path("../public", __FILE__)
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! "/0"
27
+ redirect! '/0'
26
28
  else
27
- render! "home", :layout => 'app', :locals => {:conf => manager.config, :nav_state => "home" }
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', :layout => 'app', :locals => {:conf => manager.config, :nav_state => "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! "search", :layout => 'app', :locals => {:conf => manager.config, :results => manager.search(request.GET['q']), :nav_state => nil}
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(".gitmessage~", gd.root)
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 = {:idx => idx, :parent => parent, :root => gd.root, :file_path => expanded_path, :nav_state => nil }
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! "/" + idx.to_s + file_path
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! "/" + idx.to_s + file_path + "/" + filename
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! "/" + idx.to_s + file_path + "?mode=edit"
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! "/" + idx.to_s + file_path
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("README.{md,txt}", expanded_path)].first
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! "dir", :layout => 'app', :locals => locals.merge(:contents => contents, :rendered_readme => rendered_readme)
108
- elsif mode == "revisions" # list revisions
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! "revisions", :layout => 'app', :locals => locals.merge(:revisions => revisions)
111
- elsif mode == "revert" # revert file
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! "/" + idx.to_s + file_path
117
+ redirect! '/' + idx.to_s + file_path
117
118
  elsif mode == 'delete' # delete file
118
119
  FileUtils.rm(expanded_path)
119
- redirect! "/" + idx.to_s + parent
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! "edit", :layout => 'app', :locals => locals.merge(:contents => contents)
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 => e # not tilt supported
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! "file", :layout => 'app', :locals => locals.merge(:contents => contents)
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("../views", __FILE__)
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