tmuxinator 0.8.1 → 0.9.0

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