tmuxinator 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c09e2f806d313c2af1fbe0e9b4f14aca895b9ec0
4
- data.tar.gz: 26480bf9d165619207b7341fe42495bfb3c353a7
3
+ metadata.gz: d4701d5ffcbc531c162e257f79c3f3399e106ec4
4
+ data.tar.gz: 94774dea4bbad7cfbc17f16d9428cd85061e2b5d
5
5
  SHA512:
6
- metadata.gz: d45f97fc34f6ec6fc6b94e58dd0c8933fb41e3bfd3f39575f20d08cb076f1220e46585bb63a005b86a67002eea5a9a73f658e61d423f6436ef0039e981f8fded
7
- data.tar.gz: e7de0f6e7ff0c4e4a3ae60b93e00e21b44f3809a59d9bc13ee09b3cf576f6719a5c90c86678d14e52c1175f10327ddbaf093bfa9f0ada9a988028f3d527e1f88
6
+ metadata.gz: fac94b03868d51bfac62e4205b91b4695a97d686d46759a09df7e33144e415b100b9b72984f7c0fd7d0146a27ac21c630d97729769e137047d0253ecf16afaa7
7
+ data.tar.gz: d9833164fd80716d76f19269f0eb07d424b57e788a2fe70f6c31c2a7f74130285a1a332e2b1401c2026a9a696afaa76cb76db7fa05e4934a7391912170e00e42
@@ -1 +1 @@
1
- tmuxinator.fish
1
+ completion/tmuxinator.fish
@@ -22,3 +22,4 @@ _tmuxinator() {
22
22
  }
23
23
 
24
24
  complete -F _tmuxinator tmuxinator mux
25
+ alias mux="tmuxinator"
@@ -20,3 +20,5 @@ complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_comman
20
20
  complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command open' -a "(__fish_tmuxinator_program completions open)"
21
21
  complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command copy' -a "(__fish_tmuxinator_program completions copy)"
22
22
  complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command delete' -a "(__fish_tmuxinator_program completions delete)"
23
+
24
+ abbr --add mux "tmuxinator"
@@ -18,6 +18,7 @@ _tmuxinator() {
18
18
  }
19
19
 
20
20
  compdef _tmuxinator tmuxinator mux
21
+ alias mux="tmuxinator"
21
22
 
22
23
  # Local Variables:
23
24
  # mode: Shell-Script
@@ -19,7 +19,10 @@ root: ~/
19
19
  # tmux_command: byobu
20
20
 
21
21
  # Specifies (by name or index) which window will be selected on project startup. If not set, the first window is used.
22
- # startup_window: logs
22
+ # startup_window: editor
23
+
24
+ # Specitifes (by index) which pane of the specified window will be selected on project startup. If not set, the first pane is used.
25
+ # startup_pane: 1
23
26
 
24
27
  # Controls whether the tmux session should be attached to automatically. Defaults to true.
25
28
  # attach: false
@@ -30,6 +33,9 @@ root: ~/
30
33
  windows:
31
34
  - editor:
32
35
  layout: main-vertical
36
+ # Synchronize all panes of this window, can be enabled before or after the pane commands run.
37
+ # 'before' represents legacy functionality and will be deprecated in a future release, in favour of 'after'
38
+ # synchronize: after
33
39
  panes:
34
40
  - vim
35
41
  - guard
@@ -34,6 +34,9 @@ unset RBENV_DIR
34
34
  <% windows.each do |window| %>
35
35
 
36
36
  # Window "<%= window.name %>"
37
+ <% if window.synchronize_before? %>
38
+ <%= window.tmux_synchronize_panes %>
39
+ <% end %>
37
40
  <% unless window.panes? %>
38
41
  <% if window.project.pre_window %>
39
42
  <%= window.tmux_pre_window_command %>
@@ -49,12 +52,8 @@ unset RBENV_DIR
49
52
  <% if pane.tab.pre %>
50
53
  <%= pane.tmux_pre_command %>
51
54
  <% end %>
52
- <% if pane.multiple_commands? %>
53
- <% pane.commands.each do |command| %>
55
+ <% pane.commands.each do |command| %>
54
56
  <%= pane.tmux_main_command(command) %>
55
- <% end %>
56
- <% else %>
57
- <%= pane.tmux_main_command(commands.first) %>
58
57
  <% end %>
59
58
 
60
59
  <% unless pane.last? %>
@@ -66,9 +65,14 @@ unset RBENV_DIR
66
65
  <%= window.tmux_layout_command %>
67
66
  <%= window.tmux_select_first_pane %>
68
67
  <% end %>
68
+
69
+ <% if window.synchronize_after? %>
70
+ <%= window.tmux_synchronize_panes %>
71
+ <% end %>
69
72
  <% end %>
70
73
 
71
74
  <%= tmux %> select-window -t <%= startup_window %>
75
+ <%= tmux %> select-pane -t <%= startup_pane %>
72
76
  <%- end -%>
73
77
 
74
78
  <%- if attach? -%>
@@ -23,6 +23,9 @@ if [ "$?" -eq 127 ]; then
23
23
  <%- windows.each do |window| -%>
24
24
 
25
25
  # Window "<%= window.name %>"
26
+ <% if window.synchronize_before? %>
27
+ <%= window.tmux_synchronize_panes %>
28
+ <% end %>
26
29
  <%- unless window.panes? -%>
27
30
  <%= window.tmux_pre_window_command %>
28
31
  <%- window.commands.each do |command| -%>
@@ -32,12 +35,8 @@ if [ "$?" -eq 127 ]; then
32
35
  <%- window.panes.each do |pane| -%>
33
36
  <%= pane.tmux_pre_window_command %>
34
37
  <%= pane.tmux_pre_command %>
35
- <%- if pane.multiple_commands? %>
36
- <%- pane.commands.each do |command| -%>
38
+ <%- pane.commands.each do |command| -%>
37
39
  <%= pane.tmux_main_command(command) %>
38
- <%- end -%>
39
- <%- else -%>
40
- <%= pane.tmux_main_command(commands.first) %>
41
40
  <%- end -%>
42
41
 
43
42
  <%- unless pane.last? -%>
@@ -48,9 +47,14 @@ if [ "$?" -eq 127 ]; then
48
47
 
49
48
  <%= window.tmux_select_first_pane %>
50
49
  <%- end -%>
50
+
51
+ <%- if window.synchronize_after? -%>
52
+ <%= window.tmux_synchronize_panes %>
53
+ <%- end %>
51
54
  <%- end -%>
52
55
 
53
56
  <%= tmux %> select-window -t <%= startup_window %>
57
+ <%= tmux %> select-pane -t <%= startup_pane %>
54
58
  fi
55
59
 
56
60
  wemux attach
@@ -12,20 +12,19 @@ module Tmuxinator
12
12
  commands: "Lists commands available in tmuxinator",
13
13
  completions: "Used for shell completion",
14
14
  new: "Create a new project file and open it in your editor",
15
+ edit: "Alias of new",
15
16
  open: "Alias of new",
16
- start: <<-DESC,
17
+ start: %w{
17
18
  Start a tmux session using a project's tmuxinator config,
18
19
  with an optional [ALIAS] for project reuse
19
- DESC
20
- stop: <<-DESC,
21
- Stop a tmux session using a project's tmuxinator config.
22
- DESC
20
+ }.join(" "),
21
+ stop: "Stop a tmux session using a project's tmuxinator config",
23
22
  local: "Start a tmux session using ./.tmuxinator.yml",
24
23
  debug: "Output the shell commands that are generated by tmuxinator",
25
- copy: <<-DESC,
26
- Copy an existing project to a new project and open it in
27
- your editor
28
- DESC
24
+ copy: %w{
25
+ Copy an existing project to a new project and
26
+ open it in your editor
27
+ }.join(" "),
29
28
  delete: "Deletes given project",
30
29
  implode: "Deletes all tmuxinator projects",
31
30
  version: "Display installed tmuxinator version",
@@ -53,7 +52,7 @@ module Tmuxinator
53
52
  desc "completions [arg1 arg2]", COMMANDS[:completions]
54
53
 
55
54
  def completions(arg)
56
- if %w(start stop open copy delete).include?(arg)
55
+ if %w(start stop edit open copy delete).include?(arg)
57
56
  configs = Tmuxinator::Config.configs
58
57
  say configs.join("\n")
59
58
  end
@@ -81,7 +80,7 @@ module Tmuxinator
81
80
  else
82
81
  Tmuxinator::Config.default_project(name)
83
82
  end
84
- if File.exists?(path)
83
+ if File.exist?(path)
85
84
  path
86
85
  else
87
86
  generate_project_file(name, path)
@@ -5,9 +5,9 @@ module Tmuxinator
5
5
 
6
6
  class << self
7
7
  def root
8
- root_dir = File.expand_path("~/.tmuxinator")
9
- Dir.mkdir("#{ENV['HOME']}/.tmuxinator") unless File.directory?(root_dir)
10
- "#{ENV['HOME']}/.tmuxinator"
8
+ root_dir = File.expand_path("#{ENV['HOME']}/.tmuxinator")
9
+ Dir.mkdir(root_dir) unless File.directory?(root_dir)
10
+ root_dir
11
11
  end
12
12
 
13
13
  def sample
@@ -43,7 +43,7 @@ module Tmuxinator
43
43
  end
44
44
 
45
45
  def exists?(name)
46
- File.exists?(project(name))
46
+ File.exist?(project(name))
47
47
  end
48
48
 
49
49
  def project_in_root(name)
@@ -56,7 +56,7 @@ module Tmuxinator
56
56
  end
57
57
 
58
58
  def project_in_local
59
- [LOCAL_DEFAULT].detect { |f| File.exists?(f) }
59
+ [LOCAL_DEFAULT].detect { |f| File.exist?(f) }
60
60
  end
61
61
 
62
62
  def default_project(name)
@@ -53,9 +53,5 @@ module Tmuxinator
53
53
  def last?
54
54
  index == tab.panes.length - 1
55
55
  end
56
-
57
- def multiple_commands?
58
- commands && commands.length > 0
59
- end
60
56
  end
61
57
  end
@@ -16,6 +16,15 @@ module Tmuxinator
16
16
  DEPRECATION: The cli_args option has been replaced by the tmux_options
17
17
  option and will not be supported in 0.8.0.
18
18
  M
19
+ SYNC_DEP_MSG = <<-M
20
+ DEPRECATION: The synchronize option's current default behaviour is to
21
+ enable pane synchronization before running commands. In a future release,
22
+ the default syncronization option will be `after`, i.e. synchronization of
23
+ panes will occur after the commands described in each of the panes
24
+ have run. At that time, the current behavior will need to be explicitly
25
+ enabled, using the `synchronize: before` option. To use this behaviour
26
+ now, use the 'synchronize: after' option.
27
+ M
19
28
 
20
29
  attr_reader :yaml
21
30
  attr_reader :force_attach
@@ -148,8 +157,11 @@ module Tmuxinator
148
157
  end
149
158
 
150
159
  def tmux_has_session?(name)
151
- sessions = `#{tmux_command} ls`
152
-
160
+ # Redirect stderr to /dev/null in order to prevent "failed to connect
161
+ # to server: Connection refused" error message and non-zero exit status
162
+ # if no tmux sessions exist.
163
+ # Please see issues #402 and #414.
164
+ sessions = `#{tmux_command} ls 2> /dev/null`
153
165
  !!sessions.match("^#{name}:")
154
166
  end
155
167
 
@@ -183,10 +195,18 @@ module Tmuxinator
183
195
  get_pane_base_index ? get_pane_base_index.to_i : get_base_index.to_i
184
196
  end
185
197
 
198
+ def pane_base_index
199
+ get_pane_base_index.to_i
200
+ end
201
+
186
202
  def startup_window
187
203
  yaml["startup_window"] || base_index
188
204
  end
189
205
 
206
+ def startup_pane
207
+ yaml["startup_pane"] || pane_base_index
208
+ end
209
+
190
210
  def tmux_options?
191
211
  yaml["tmux_options"]
192
212
  end
@@ -228,6 +248,7 @@ module Tmuxinator
228
248
  deprecations << RBENVRVM_DEP_MSG if yaml["rbenv"] || yaml["rvm"]
229
249
  deprecations << TABS_DEP_MSG if yaml["tabs"]
230
250
  deprecations << CLIARGS_DEP_MSG if yaml["cli_args"]
251
+ deprecations << SYNC_DEP_MSG if legacy_synchronize?
231
252
  deprecations
232
253
  end
233
254
 
@@ -271,5 +292,19 @@ module Tmuxinator
271
292
 
272
293
  options_hash
273
294
  end
295
+
296
+ def legacy_synchronize?
297
+ (synchronize_options & [true, "before"]).any?
298
+ end
299
+
300
+ def synchronize_options
301
+ window_options.map do |option|
302
+ option["synchronize"] if option.is_a?(Hash)
303
+ end
304
+ end
305
+
306
+ def window_options
307
+ yaml["windows"].map(&:values).flatten
308
+ end
274
309
  end
275
310
  end
@@ -1,3 +1,3 @@
1
1
  module Tmuxinator
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -2,7 +2,7 @@ module Tmuxinator
2
2
  class Window
3
3
  include Tmuxinator::Util
4
4
 
5
- attr_reader :name, :root, :panes, :layout, :commands, :index, :project
5
+ attr_reader :name, :root, :panes, :layout, :commands, :index, :project, :synchronize
6
6
 
7
7
  def initialize(window_yaml, index, project)
8
8
  @name = if !window_yaml.keys.first.nil?
@@ -10,6 +10,7 @@ module Tmuxinator
10
10
  end
11
11
  @root = nil
12
12
  @panes = []
13
+ @synchronize = false
13
14
  @layout = nil
14
15
  @pre = nil
15
16
  @project = project
@@ -18,6 +19,7 @@ module Tmuxinator
18
19
  value = window_yaml.values.first
19
20
 
20
21
  if value.is_a?(Hash)
22
+ @synchronize = value["synchronize"] || false
21
23
  @layout = value["layout"] ? value["layout"].shellescape : nil
22
24
  @pre = value["pre"] if value["pre"]
23
25
  @root = if value["root"]
@@ -27,20 +29,23 @@ module Tmuxinator
27
29
  end
28
30
 
29
31
  @panes = build_panes(value["panes"])
30
- else
31
- @commands = build_commands(tmux_window_command_prefix, value)
32
32
  end
33
+
34
+ @commands = build_commands(tmux_window_command_prefix, value)
33
35
  end
34
36
 
35
37
  def build_panes(panes_yml)
36
38
  Array(panes_yml).map.with_index do |pane_yml, index|
37
- if pane_yml.is_a?(Hash)
38
- pane_yml.map do |_name, commands|
39
- Tmuxinator::Pane.new(index, project, self, *commands)
40
- end
41
- else
42
- Tmuxinator::Pane.new(index, project, self, pane_yml)
43
- end
39
+ commands = case pane_yml
40
+ when Hash
41
+ pane_yml.values.first
42
+ when Array
43
+ pane_yml
44
+ else
45
+ pane_yml
46
+ end
47
+
48
+ Tmuxinator::Pane.new(index, project, self, *commands)
44
49
  end.flatten
45
50
  end
46
51
 
@@ -99,6 +104,10 @@ module Tmuxinator
99
104
  "#{project.tmux} select-layout -t #{tmux_window_target} tiled"
100
105
  end
101
106
 
107
+ def tmux_synchronize_panes
108
+ "#{project.tmux} set-window-option -t #{tmux_window_target} synchronize-panes on"
109
+ end
110
+
102
111
  def tmux_layout_command
103
112
  "#{project.tmux} select-layout -t #{tmux_window_target} #{layout}"
104
113
  end
@@ -106,5 +115,13 @@ module Tmuxinator
106
115
  def tmux_select_first_pane
107
116
  "#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.base_index}"
108
117
  end
118
+
119
+ def synchronize_before?
120
+ synchronize == true || synchronize == "before"
121
+ end
122
+
123
+ def synchronize_after?
124
+ synchronize == "after"
125
+ end
109
126
  end
110
127
  end
@@ -73,6 +73,14 @@ FactoryGirl.define do
73
73
  initialize_with { Tmuxinator::Project.new(file) }
74
74
  end
75
75
 
76
+ factory :noroot_project, class: Tmuxinator::Project do
77
+ transient do
78
+ file { yaml_load("spec/fixtures/noroot.yml") }
79
+ end
80
+
81
+ initialize_with { Tmuxinator::Project.new(file) }
82
+ end
83
+
76
84
  factory :nowindows_project, class: Tmuxinator::Project do
77
85
  transient do
78
86
  file { yaml_load("spec/fixtures/nowindows.yml") }
@@ -0,0 +1,5 @@
1
+ # ~/.tmuxinator/noroot.yml
2
+ # you can make as many tabs as you wish...
3
+
4
+ windows:
5
+ - test: echo foo
@@ -32,3 +32,6 @@ tabs:
32
32
  - console: bundle exec rails c
33
33
  - capistrano:
34
34
  - server: ssh user@example.com
35
+ windows:
36
+ - sample:
37
+ - synchronize: true
@@ -40,6 +40,7 @@ describe Tmuxinator::Cli do
40
40
  expected = %w(commands
41
41
  completions
42
42
  new
43
+ edit
43
44
  open
44
45
  start
45
46
  stop
@@ -181,7 +182,7 @@ describe Tmuxinator::Cli do
181
182
 
182
183
  before do
183
184
  # make sure that no project file exists initially
184
- FileUtils.remove_file(path) if File.exists?(path)
185
+ FileUtils.remove_file(path) if File.exist?(path)
185
186
  expect(File).not_to exist(path)
186
187
 
187
188
  # now generate a project file
@@ -221,7 +222,7 @@ describe Tmuxinator::Cli do
221
222
 
222
223
  context "existing project doesn't exist" do
223
224
  before do
224
- expect(File).to receive_messages(exists?: false)
225
+ expect(File).to receive_messages(exist?: false)
225
226
  end
226
227
 
227
228
  it "creates a new tmuxinator project file" do
@@ -234,8 +235,8 @@ describe Tmuxinator::Cli do
234
235
  let(:root_path) { "#{ENV['HOME']}\/\.tmuxinator\/#{name}\.yml" }
235
236
 
236
237
  before do
237
- allow(File).to receive(:exists?).with(anything).and_return(false)
238
- expect(File).to receive(:exists?).with(root_path).and_return(true)
238
+ allow(File).to receive(:exist?).with(anything).and_return(false)
239
+ expect(File).to receive(:exist?).with(root_path).and_return(true)
239
240
  end
240
241
 
241
242
  it "just opens the file" do
@@ -252,7 +253,7 @@ describe Tmuxinator::Cli do
252
253
 
253
254
  context "existing project doesn't exist" do
254
255
  before do
255
- allow(File).to receive(:exists?).at_least(:once) do
256
+ allow(File).to receive(:exist?).at_least(:once) do
256
257
  false
257
258
  end
258
259
  end
@@ -266,7 +267,7 @@ describe Tmuxinator::Cli do
266
267
  context "files exists" do
267
268
  let(:path) { Tmuxinator::Config::LOCAL_DEFAULT }
268
269
  before do
269
- expect(File).to receive(:exists?).with(path) { true }
270
+ expect(File).to receive(:exist?).with(path) { true }
270
271
  end
271
272
 
272
273
  it "just opens the file" do
@@ -489,7 +490,7 @@ describe Tmuxinator::Cli do
489
490
  let(:path) { Tmuxinator::Config.default_project(name) }
490
491
 
491
492
  after(:each) do
492
- FileUtils.remove_file(path) if File.exists?(path)
493
+ FileUtils.remove_file(path) if File.exist?(path)
493
494
  end
494
495
 
495
496
  context "when the project file does not already exist" do
@@ -537,7 +538,7 @@ describe Tmuxinator::Cli do
537
538
  end
538
539
 
539
540
  after(:each) do
540
- FileUtils.remove_file(path) if File.exists?(path)
541
+ FileUtils.remove_file(path) if File.exist?(path)
541
542
  end
542
543
 
543
544
  it "should always generate a project file" do
@@ -48,8 +48,8 @@ describe Tmuxinator::Config do
48
48
 
49
49
  context "when the file exists" do
50
50
  before do
51
- allow(File).to receive(:exists?).with(local_default) { false }
52
- allow(File).to receive(:exists?).with(proj_default) { true }
51
+ allow(File).to receive(:exist?).with(local_default) { false }
52
+ allow(File).to receive(:exist?).with(proj_default) { true }
53
53
  end
54
54
 
55
55
  it "returns true" do
@@ -59,8 +59,8 @@ describe Tmuxinator::Config do
59
59
 
60
60
  context "when the file doesn't exist" do
61
61
  before do
62
- allow(File).to receive(:exists?).with(local_default) { false }
63
- allow(File).to receive(:exists?).with(proj_default) { false }
62
+ allow(File).to receive(:exist?).with(local_default) { false }
63
+ allow(File).to receive(:exist?).with(proj_default) { false }
64
64
  end
65
65
 
66
66
  it "returns true" do
@@ -147,7 +147,7 @@ describe Tmuxinator::Config do
147
147
 
148
148
  describe "#exists?" do
149
149
  before do
150
- allow(File).to receive_messages(exists?: true)
150
+ allow(File).to receive_messages(exist?: true)
151
151
  allow(Tmuxinator::Config).to receive_messages(project: "")
152
152
  end
153
153
 
@@ -181,7 +181,7 @@ describe Tmuxinator::Config do
181
181
  describe "#local?" do
182
182
  it "checks if the given project exists" do
183
183
  path = Tmuxinator::Config::LOCAL_DEFAULT
184
- expect(File).to receive(:exists?).with(path) { true }
184
+ expect(File).to receive(:exist?).with(path) { true }
185
185
  expect(Tmuxinator::Config.local?).to be_truthy
186
186
  end
187
187
  end
@@ -191,7 +191,7 @@ describe Tmuxinator::Config do
191
191
 
192
192
  context "with a project yml" do
193
193
  it "gets the project as path to the yml file" do
194
- expect(File).to receive(:exists?).with(default) { true }
194
+ expect(File).to receive(:exist?).with(default) { true }
195
195
  expect(Tmuxinator::Config.project_in_local).to eq default
196
196
  end
197
197
  end
@@ -220,7 +220,7 @@ describe Tmuxinator::Config do
220
220
 
221
221
  context "with a local project, but no project in root" do
222
222
  it "gets the project as path to the yml file" do
223
- expect(File).to receive(:exists?).with(default) { true }
223
+ expect(File).to receive(:exist?).with(default) { true }
224
224
  expect(Tmuxinator::Config.project("sample")).to eq "./.tmuxinator.yml"
225
225
  end
226
226
  end
@@ -253,7 +253,7 @@ describe Tmuxinator::Config do
253
253
 
254
254
  context "when no project name is provided" do
255
255
  it "should raise if the local project file doesn't exist" do
256
- expect(File).to receive(:exists?).with(default) { false }
256
+ expect(File).to receive(:exist?).with(default) { false }
257
257
  expect do
258
258
  Tmuxinator::Config.validate
259
259
  end.to raise_error RuntimeError, %r{Project.+doesn't.exist}
@@ -262,7 +262,7 @@ describe Tmuxinator::Config do
262
262
  it "should load and validate the project" do
263
263
  content = File.read(File.join(path, "sample.yml"))
264
264
 
265
- expect(File).to receive(:exists?).with(default).at_least(:once) { true }
265
+ expect(File).to receive(:exist?).with(default).at_least(:once) { true }
266
266
  expect(File).to receive(:read).with(default).and_return(content)
267
267
 
268
268
  expect(Tmuxinator::Config.validate).to be_a Tmuxinator::Project
@@ -23,6 +23,7 @@ describe Tmuxinator::Project do
23
23
 
24
24
  let(:wemux_project) { FactoryGirl.build(:wemux_project) }
25
25
  let(:noname_project) { FactoryGirl.build(:noname_project) }
26
+ let(:noroot_project) { FactoryGirl.build(:noroot_project) }
26
27
  let(:nameless_window_project) do
27
28
  FactoryGirl.build(:nameless_window_project)
28
29
  end
@@ -57,24 +58,40 @@ describe Tmuxinator::Project do
57
58
  end
58
59
 
59
60
  describe "#tmux_has_session?" do
60
- before do
61
- cmd = "#{project.tmux_command} ls"
62
- resp = ""\
63
- "foo: 1 window (created Sun May 25 10:12:00 1986) [50x50] (detached)\n"\
64
- "bar: 1 window (created Sat Sept 01 00:00:00 1990) [50x50] (detached)"
65
- call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
61
+ context "no active sessions" do
62
+ before do
63
+ cmd = "#{project.tmux_command} ls 2> /dev/null"
64
+ resp = ""
65
+ call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
66
66
 
67
- expect(project).to call_tmux_ls
68
- end
67
+ expect(project).to call_tmux_ls
68
+ end
69
69
 
70
- it "should return true only when `tmux ls` has the named session" do
71
- expect(project.tmux_has_session?("foo")).to be true
72
- expect(project.tmux_has_session?("bar")).to be true
73
- expect(project.tmux_has_session?("quux")).to be false
70
+ it "should return false if no sessions are found" do
71
+ expect(project.tmux_has_session?("no server running")).to be false
72
+ end
74
73
  end
75
74
 
76
- it "should return false if a partial (prefix) match is found" do
77
- expect(project.tmux_has_session?("foobar")).to be false
75
+ context "active sessions" do
76
+ before do
77
+ cmd = "#{project.tmux_command} ls 2> /dev/null"
78
+ resp = ""\
79
+ "foo: 1 window (created Sun May 25 10:12:00 1986) [0x0] (detached)\n"\
80
+ "bar: 1 window (created Sat Sept 01 00:00:00 1990) [0x0] (detached)"
81
+ call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
82
+
83
+ expect(project).to call_tmux_ls
84
+ end
85
+
86
+ it "should return true only when `tmux ls` has the named session" do
87
+ expect(project.tmux_has_session?("foo")).to be true
88
+ expect(project.tmux_has_session?("bar")).to be true
89
+ expect(project.tmux_has_session?("quux")).to be false
90
+ end
91
+
92
+ it "should return false if a partial (prefix) match is found" do
93
+ expect(project.tmux_has_session?("foobar")).to be false
94
+ end
78
95
  end
79
96
  end
80
97
 
@@ -107,7 +124,7 @@ describe Tmuxinator::Project do
107
124
 
108
125
  context "without root" do
109
126
  it "doesn't throw an error" do
110
- expect { noname_project.root }.to_not raise_error
127
+ expect { noroot_project.root }.to_not raise_error
111
128
  end
112
129
  end
113
130
  end
@@ -310,6 +327,24 @@ describe Tmuxinator::Project do
310
327
  end
311
328
  end
312
329
 
330
+ describe "#startup_pane" do
331
+ context "startup pane specified" do
332
+ it "get the startup pane index from project config" do
333
+ project.yaml["startup_pane"] = 1
334
+
335
+ expect(project.startup_pane).to eq(1)
336
+ end
337
+ end
338
+
339
+ context "startup pane not specified" do
340
+ it "returns the base pane instead" do
341
+ allow(project).to receive_messages(pane_base_index: 4)
342
+
343
+ expect(project.startup_pane).to eq(4)
344
+ end
345
+ end
346
+ end
347
+
313
348
  describe "#window" do
314
349
  it "gets the window and index for tmux" do
315
350
  expect(project.window(1)).to eq "sample:1"
@@ -4,6 +4,7 @@ describe Tmuxinator::Window do
4
4
  let(:project) { double }
5
5
  let(:panes) { ["vim", nil, "top"] }
6
6
  let(:window_name) { "editor" }
7
+ let(:synchronize) { false }
7
8
  let(:yaml) do
8
9
  {
9
10
  window_name => {
@@ -11,6 +12,7 @@ describe Tmuxinator::Window do
11
12
  "echo 'I get run in each pane. Before each pane command!'",
12
13
  nil
13
14
  ],
15
+ "synchronize" => synchronize,
14
16
  "layout" => "main-vertical",
15
17
  "panes" => panes
16
18
  }
@@ -34,6 +36,25 @@ describe Tmuxinator::Window do
34
36
  let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
35
37
  let(:window_root) { Tmuxinator::Window.new(yaml_root, 0, project) }
36
38
 
39
+ shared_context "window command context" do
40
+ let(:project) { double(:project) }
41
+ let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
42
+ let(:root?) { true }
43
+ let(:root) { "/project/tmuxinator" }
44
+
45
+ before do
46
+ allow(project).to receive_messages(
47
+ name: "test",
48
+ tmux: "tmux",
49
+ root: root,
50
+ root?: root?,
51
+ base_index: 1
52
+ )
53
+ end
54
+
55
+ let(:tmux_part) { project.tmux }
56
+ end
57
+
37
58
  before do
38
59
  allow(project).to receive_messages(
39
60
  tmux: "tmux",
@@ -65,14 +86,8 @@ describe Tmuxinator::Window do
65
86
  end
66
87
 
67
88
  describe "#panes" do
68
- let(:pane) { double(:pane) }
69
-
70
- before do
71
- allow(Tmuxinator::Pane).to receive_messages new: pane
72
- end
73
-
74
89
  context "with a three element Array" do
75
- let(:panes) { ["vim", nil, "top"] }
90
+ let(:panes) { ["vim", "ls", "top"] }
76
91
 
77
92
  it "creates three panes" do
78
93
  expect(Tmuxinator::Pane).to receive(:new).exactly(3).times
@@ -80,35 +95,63 @@ describe Tmuxinator::Window do
80
95
  end
81
96
 
82
97
  it "returns three panes" do
83
- expect(window.panes).to eql [pane, pane, pane]
98
+ expect(window.panes).to all be_a_pane.with(
99
+ project: project, tab: window
100
+ )
101
+
102
+ expect(window.panes).to match([
103
+ a_pane.with(index: 0).and_commands("vim"),
104
+ a_pane.with(index: 1).and_commands("ls"),
105
+ a_pane.with(index: 2).and_commands("top")
106
+ ])
84
107
  end
85
108
  end
86
109
 
87
110
  context "with a String" do
88
111
  let(:panes) { "vim" }
89
112
 
90
- it "creates one pane" do
91
- expect(Tmuxinator::Pane).to receive(:new).once
92
- window.panes
93
- end
94
-
95
113
  it "returns one pane in an Array" do
96
- expect(window.panes).to eql [pane]
114
+ expect(window.panes.first).to be_a_pane.
115
+ with(index: 0).and_commands("vim")
97
116
  end
98
117
  end
99
118
 
100
119
  context "with nil" do
101
120
  let(:panes) { nil }
102
121
 
103
- it "doesn't create any panes" do
104
- expect(Tmuxinator::Pane).to_not receive(:new)
105
- window.panes
106
- end
107
-
108
122
  it "returns an empty Array" do
109
123
  expect(window.panes).to be_empty
110
124
  end
111
125
  end
126
+
127
+ context "nested collections" do
128
+ let(:command1) { "cd /tmp/" }
129
+ let(:command2) { "ls" }
130
+
131
+ let(:panes) { ["vim", nested_collection] }
132
+
133
+ context "with nested hash" do
134
+ let(:nested_collection) { { pane2: [command1, command2] } }
135
+
136
+ it "returns two panes in an Array" do
137
+ expect(window.panes).to match [
138
+ a_pane.with(index: 0).and_commands("vim"),
139
+ a_pane.with(index: 1).and_commands(command1, command2)
140
+ ]
141
+ end
142
+ end
143
+
144
+ context "with nested array" do
145
+ let(:nested_collection) { [command1, command2] }
146
+
147
+ it "returns two panes in an Array" do
148
+ expect(window.panes).to match [
149
+ a_pane.with(index: 0).and_commands("vim"),
150
+ a_pane.with(index: 1).and_commands(command1, command2)
151
+ ]
152
+ end
153
+ end
154
+ end
112
155
  end
113
156
 
114
157
  describe "#pre" do
@@ -166,6 +209,16 @@ describe Tmuxinator::Window do
166
209
  expect(window.commands).to be_empty
167
210
  end
168
211
  end
212
+
213
+ context "command is a hash" do
214
+ before do
215
+ yaml["editor"] = { "layout" => "main-horizontal", "panes" => [nil] }
216
+ end
217
+
218
+ it "returns an empty array" do
219
+ expect(window.commands).to be_empty
220
+ end
221
+ end
169
222
  end
170
223
 
171
224
  describe "#name_options" do
@@ -186,23 +239,94 @@ describe Tmuxinator::Window do
186
239
  end
187
240
  end
188
241
 
189
- describe "#tmux_new_window_command" do
190
- let(:project) { double(:project) }
191
- let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
192
- let(:root?) { true }
193
- let(:root) { "/project/tmuxinator" }
242
+ describe "#synchronize_before?" do
243
+ subject { window.synchronize_before? }
194
244
 
195
- before do
196
- allow(project).to receive_messages(
197
- name: "test",
198
- tmux: "tmux",
199
- root: root,
200
- root?: root?,
201
- base_index: 1
202
- )
245
+ context "synchronize is 'before'" do
246
+ let(:synchronize) { "before" }
247
+
248
+ it { is_expected.to be true }
203
249
  end
204
250
 
205
- let(:tmux_part) { project.tmux }
251
+ context "synchronize is true" do
252
+ let(:synchronize) { true }
253
+
254
+ it { is_expected.to be true }
255
+ end
256
+
257
+ context "synchronize is 'after'" do
258
+ let(:synchronize) { "after" }
259
+
260
+ it { is_expected.to be false }
261
+ end
262
+
263
+ context "synchronization disabled" do
264
+ let(:synchronize) { false }
265
+
266
+ it { is_expected.to be false }
267
+ end
268
+
269
+ context "synchronization not specified" do
270
+ it { is_expected.to be false }
271
+ end
272
+ end
273
+
274
+ describe "#synchronize_after?" do
275
+ subject { window.synchronize_after? }
276
+
277
+ context "synchronization is 'after'" do
278
+ let(:synchronize) { "after" }
279
+
280
+ it { is_expected.to be true }
281
+ end
282
+
283
+ context "synchronization is true" do
284
+ let(:synchronize) { true }
285
+
286
+ it { is_expected.to be false }
287
+ end
288
+
289
+ context "synchronization is 'before'" do
290
+ let(:synchronize) { "before" }
291
+
292
+ it { is_expected.to be false }
293
+ end
294
+
295
+ context "synchronization disabled" do
296
+ let(:synchronize) { false }
297
+
298
+ it { is_expected.to be false }
299
+ end
300
+
301
+ context "synchronization not specified" do
302
+ it { is_expected.to be false }
303
+ end
304
+ end
305
+
306
+ describe "#tmux_synchronize_panes" do
307
+ include_context "window command context"
308
+
309
+ let(:window_option_set_part) { "set-window-option" }
310
+ let(:target_part) { "-t #{window.tmux_window_target}" }
311
+ let(:synchronize_panes_part) { "synchronize-panes" }
312
+
313
+ context "synchronization enabled" do
314
+ let(:synchronize) { true }
315
+ let(:enabled) { "on" }
316
+
317
+ let(:full_command) do
318
+ "#{tmux_part} #{window_option_set_part} #{target_part} #{synchronize_panes_part} #{enabled}"
319
+ end
320
+
321
+ it "should set the synchronize-panes window option on" do
322
+ expect(window.tmux_synchronize_panes).to eq full_command
323
+ end
324
+ end
325
+ end
326
+
327
+ describe "#tmux_new_window_command" do
328
+ include_context "window command context"
329
+
206
330
  let(:window_part) { "new-window" }
207
331
  let(:name_part) { window.tmux_window_name_option }
208
332
  let(:target_part) { "-t #{window.tmux_window_target}" }
@@ -0,0 +1,44 @@
1
+ RSpec::Matchers.alias_matcher :be_a_pane, :a_pane
2
+ RSpec::Matchers.define :a_pane do
3
+ attr_reader :commands
4
+
5
+ match do
6
+ result = is_pane
7
+
8
+ result && attributes_match if @expected_attrs
9
+ result &&= commands_match if commands
10
+
11
+ result
12
+ end
13
+
14
+ failure_message do |actual|
15
+ return "Expected #{actual} to be a Tmuxinator::Pane" unless is_pane
16
+
17
+ msg = "Actual pane does not match expected"
18
+ msg << "\n Expected #{@commands} but has #{actual.commands}" if @commands
19
+ msg << "\n Expected pane to have #{@expected_attrs}" if @expected_attrs
20
+ end
21
+
22
+ chain :with do |attrs|
23
+ @expected_attrs = attrs
24
+ end
25
+
26
+ chain :with_commands do |*expected|
27
+ @commands = expected
28
+ end
29
+ alias_method :and_commands, :with_commands
30
+
31
+ private
32
+
33
+ def attributes_match
34
+ expect(@actual).to have_attributes(@expected_attrs)
35
+ end
36
+
37
+ def commands_match
38
+ @actual.commands == commands
39
+ end
40
+
41
+ def is_pane
42
+ @actual.is_a? Tmuxinator::Pane
43
+ end
44
+ end
@@ -16,6 +16,9 @@ require "factory_girl"
16
16
 
17
17
  FactoryGirl.find_definitions
18
18
 
19
+ # Custom Matchers
20
+ require_relative "matchers/pane_matcher"
21
+
19
22
  RSpec.configure do |config|
20
23
  config.order = "random"
21
24
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmuxinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Bargi
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-19 00:00:00.000000000 Z
12
+ date: 2016-10-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -64,12 +64,10 @@ email:
64
64
  - allen.bargi@gmail.com
65
65
  - chris@chowie.net
66
66
  executables:
67
- - mux
68
67
  - tmuxinator
69
68
  extensions: []
70
69
  extra_rdoc_files: []
71
70
  files:
72
- - bin/mux
73
71
  - bin/tmuxinator
74
72
  - completion/mux.fish
75
73
  - completion/tmuxinator.bash
@@ -92,6 +90,7 @@ files:
92
90
  - spec/fixtures/detach.yml
93
91
  - spec/fixtures/nameless_window.yml
94
92
  - spec/fixtures/noname.yml
93
+ - spec/fixtures/noroot.yml
95
94
  - spec/fixtures/nowindows.yml
96
95
  - spec/fixtures/sample.deprecations.yml
97
96
  - spec/fixtures/sample.yml
@@ -105,6 +104,7 @@ files:
105
104
  - spec/lib/tmuxinator/util_spec.rb
106
105
  - spec/lib/tmuxinator/wemux_support_spec.rb
107
106
  - spec/lib/tmuxinator/window_spec.rb
107
+ - spec/matchers/pane_matcher.rb
108
108
  - spec/spec_helper.rb
109
109
  homepage: https://github.com/tmuxinator/tmuxinator
110
110
  licenses:
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
131
  version: 1.8.23
132
132
  requirements: []
133
133
  rubyforge_project:
134
- rubygems_version: 2.5.1
134
+ rubygems_version: 2.6.6
135
135
  signing_key:
136
136
  specification_version: 4
137
137
  summary: Create and manage complex tmux sessions easily.
@@ -140,6 +140,7 @@ test_files:
140
140
  - spec/fixtures/detach.yml
141
141
  - spec/fixtures/nameless_window.yml
142
142
  - spec/fixtures/noname.yml
143
+ - spec/fixtures/noroot.yml
143
144
  - spec/fixtures/nowindows.yml
144
145
  - spec/fixtures/sample.deprecations.yml
145
146
  - spec/fixtures/sample.yml
@@ -153,4 +154,5 @@ test_files:
153
154
  - spec/lib/tmuxinator/util_spec.rb
154
155
  - spec/lib/tmuxinator/wemux_support_spec.rb
155
156
  - spec/lib/tmuxinator/window_spec.rb
157
+ - spec/matchers/pane_matcher.rb
156
158
  - spec/spec_helper.rb
data/bin/mux DELETED
@@ -1 +0,0 @@
1
- tmuxinator