Mercurial > hg > Members > atton > pmux
changeset 0:39b72942a4d2
Clone tmuxinator from github
https://github.com/tmuxinator/tmuxinator/tree/67140f4abfcf640f433f76310d6b927de06a58ac
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,19 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +.coveralls.yml +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +tmp +db +vendor/ +tags
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.rspec Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,1 @@ +--color --format=documentation
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.travis.yml Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,26 @@ +language: ruby +rvm: + - "1.9.3" + - "2.0.0" + - "2.1.2" +env: + - TMUX_VERSION=master + - TMUX_VERSION=1.9 + - TMUX_VERSION=1.8 + - TMUX_VERSION=1.7 + - TMUX_VERSION=1.6 + - TMUX_VERSION=1.5 +matrix: + allow_failures: + - env: TMUX_VERSION=1.7 +before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq libevent-dev libncurses-dev + - git clone git://git.code.sf.net/p/tmux/tmux-code tmux + - cd tmux + - git checkout $TMUX_VERSION + - sh autogen.sh + - ./configure && make && sudo make install + - cd .. + - tmux -V +script: bundle exec rspec spec
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHANGELOG.md Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,97 @@ +## 0.6.8 +- Remove some duplication #212 +- Add wemux support #88 - Thanks to Andrew Thal (@athal7) +- Fix typos in readme #217, #216 +- Fix encoding bug #229 +- Fix specs not running due to changes in thor + +## 0.6.7 +- Remove use of grep for base-index #171 +- Fix bugs in `Tmuxinator::Config.default?` #169 +- Fix path for Rails log in directory sample #177 +- Add completions for fish shell #179 +- Fix grammar in readme #184 +- Make commands take precedence over project names #182 +- Improve error messages when $EDITOR isn't set #186, #194 +- Add confirmation to deletion prompt #197 +- Fix broken badge references after organisation move +- Remove dependancy on ActiveSupport #199 +- Fix compatability with tmux 1.9 + +## 0.6.6 +- Fix a bug caused by not escaping the root path #145 +- Fix bash completion with a single argument #148 +- Fix regression where an array of commands for a window wasn't working #149 +- Add an option to call tmux wrappers or derivatives #154 +- Refactor build\_panes to always return an array #157 +- Clean up some branching code using `.presence` #163 +- Setup TravisCI test matrix for different tmux versions #164 +- Fix some grammar and spelling in readme #166 +- Make multiple commands use tmux's `send-keys` rather than just using `&&` for both panes and windows #100 + +## 0.6.5 +- Change deprecation continue message from any key to just the enter key +- Dramatically clean up the readme to be clearer for new users +- Update the contributing guide with references to the GitHub styleguide and add examples of how to leave good commit messages +- Use Erubis to render the project sample and fix a bad binding reference +- Update the sample project to be much simpler +- Fix not working delete command #142 +- Fix an error in the bash completion script +- Fix an issue where the wrong project path was being returned +- Fix an issue where command aliases were being ignored + +## 0.6.4 +- Fixes broken backwards compatibility of multiple pre commands #129 +- Fixes tmuxinator ignoring project root when started from within a tmux session #132 +- Add gem version badge + +## 0.6.3 +- Remove stray pry #128 +- Allow starting a tmuxinator project while inside a tmux session #130 +- Set the tmux layout after pane creation to avoid pane too small errors #131 +- Check for both pane-base-index and base-index #126 + +## 0.6.2 +- Also pass command line options to the `base_index` lookup. +- Fixed bug #116. + +## 0.6.1 +- Remove stray binding.pry +- Fix nil error when creating a new project. + +## 0.6.0 + +- Rewrote core functionality with proper abstractions and unit tests +- Fixed outstanding bugs #72 #89 #90 #93 #101 #102 #103 #104 #109 +- Switched to thor for command line argument parsing +- Switched to Erubis for more Rails like ERB +- Added simplecov for test coverage +- Added debug command line option to view generated shell commands +- Added commands and completion options for completion scripts +- Added `pre_window` option for running commands before all panes and windows +- Deprecated `rbenv` in favour of `pre_window` +- Deprecated `rvm` in favour of `pre_window` +- Deprecated `cli_args` in favour of `tmux_options` +- Deprecated `tabs` in favour of `windows` +- Dropped support for Ruby 1.9.2 + +## 0.5.0 +- Added optional socket name support (Thanks to Adam Walters) +- Added auto completion (Thanks to Jose Pablo Barrantes) + +## 0.4.0 +- Does not crash if given an invalid yaml file format. report it and exit gracefully. +- Removed clunky scripts & shell aliases (Thanks to Dane O'Connor) +- Config files are now rendered JIT (Thanks to Dane O'Connor) +- Can now start sessions from cli (Thanks to Dane O'Connor) + +## 0.3.0 +- Added pre command (Thanks to Ian Yang) +- Added multiple pre command (Thanks to Bjørn Arild Mæland) +- Using tmux set default-path for project root +- New aliases + +## 0.2.0 +- Added pane support (Thanks to Aaron Spiegel) +- RVM support (Thanks to Jay Adkisoon) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CONTRIBUTING.md Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,42 @@ +# Contributing + +## First + +* Check if the issue you're going to submit isn't already submitted in + the [Issues](https://github.com/aziz/tmuxinator/issues) page. + +## Issues + +* Submit a ticket for your issue, assuming one does not already exist. +* The issue must: + * Clearly describe the problem including steps to reproduce when it is a bug. + * Also include all the information you can to make it easier for us to reproduce it, + like OS version, gem versions, rbenv or rvm versions etc... + * Even better, provide a failing test case for it. + +## Pull Requests + +If you've gone the extra mile and have a patch that fixes the issue, you +should submit a Pull Request! + +* Please follow the [GitHub Styleguide](https://github.com/styleguide/ruby) for + Ruby in both implementation and tests! +* Fork the repo on Github. +* Create a topic branch from where you want to base your work. +* Add a test for your change. Only refactoring and documentation changes + require no new tests. If you are adding functionality or fixing a bug, + we need a test! +* Run _all_ the tests to assure nothine else was broken. We only take pull requests with passing tests. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Structure your commit messages like this: + +``` +Summarize clearly in one line what the commit is about + +Describe the problem the commit solves or the use +case for a new feature. Justify why you chose +the particular solution. +``` + +* Use "fix", "add", "change" instead of "fixed", "added", "changed" in your commit messages. +* Push to your fork and submit a pull request.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gemfile Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,5 @@ +source "https://rubygems.org" + + +# Specify your gem's dependencies in tmuxinator.gemspec +gemspec
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,20 @@ +Copyright (c) 2010-2013 Allen Bargi + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,251 @@ +# Tmuxinator + +[![Gem Version](https://badge.fury.io/rb/tmuxinator.png)](http://badge.fury.io/rb/tmuxinator) [![Build Status](https://secure.travis-ci.org/tmuxinator/tmuxinator.png)](http://travis-ci.org/tmuxinator/tmuxinator?branch=master) [![Coverage Status](https://coveralls.io/repos/tmuxinator/tmuxinator/badge.png)](https://coveralls.io/r/tmuxinator/tmuxinator) [![Code Climate](https://codeclimate.com/github/tmuxinator/tmuxinator.png)](https://codeclimate.com/github/tmuxinator/tmuxinator) [![Dependency Status](https://gemnasium.com/tmuxinator/tmuxinator.png)](https://gemnasium.com/tmuxinator/tmuxinator) + +Create and manage tmux sessions easily. + +![Screenshot](https://f.cloud.github.com/assets/141213/916084/065fef7c-fe82-11e2-9c23-a9622c7d83c3.png) + +## Installation + +``` +gem install tmuxinator +``` + +## Editor and Shell + +tmuxinator uses your shell's default editor for opening files. If you're not +sure what that is type: + +``` bash +echo $EDITOR +``` + +For me that produces "vim" If you want to change your default editor simply +put a line in ~/.bashrc that changes it. Mine looks like this: + +``` +export EDITOR='vim' +``` + +## tmux + +The recommended version of tmux to use is 1.8. Your mileage may vary for +earlier versions. Refer to the FAQ for any odd behaviour. + +### base-index + +If you use a `base-index` other than the default, please be sure to also set the `pane-base-index` + +``` +set-window-option -g pane-base-index 1 +``` + +## Completion + +Download the appropriate completion file from the repo and `source` the file. +The following are example where the completion file has been downloaded into +`~/.bin`. + +### bash + +Add the following to your `~/.bashrc`: + + source ~/.bin/tmuxinator.bash + +### zsh + +Add the following to your `~/.zshrc`: + + source ~/.bin/tmuxinator.zsh + +### fish + +Move `tmuxinator.fish` to your `completions` folder: + + cp ~/.bin/tmuxinator.fish ~/.config/completions/ + +## Usage + +A working knowledge of tmux is assumed. You should understand what windows and +panes are in tmux. If not please consult the [man pages](http://manpages.ubuntu.com/manpages/precise/en/man1/tmux.1.html#contenttoc6) for tmux. + +### Create a project + +Create or edit your projects with: + +``` +tmuxinator new [project] +``` + +For editing you can also use `tmuxinator open [project]`. `new` is aliased to +`o`,`open` and `n`. Your default editor (`$EDITOR`) is used to open the file. +If this is a new project you will see this default config: + +```yaml +# ~/.tmuxinator/sample.yml + +name: sample +root: ~/ + +# Optional. tmux socket +# socket_name: foo + +# Runs before everything. Use it to start daemons etc. +# pre: sudo /etc/rc.d/mysqld start + +# Runs in each window and pane before window/pane specific commands. Useful for setting up interpreter versions. +# pre_window: rbenv shell 2.0.0-p247 + +# Pass command line options to tmux. Useful for specifying a different tmux.conf. +# tmux_options: -f ~/.tmux.mac.conf + +# Change the command to call tmux. This can be used by derivatives/wrappers like byobu. +# tmux_command: byobu + +windows: + - editor: + layout: main-vertical + panes: + - vim + - guard + - server: bundle exec rails s + - logs: tail -f log/development.log +``` + +## Windows + +The windows option allows the specification of any number of tmux windows. Each window is denoted by a YAML array entry, followed by a name +and command to be run. + +``` +windows: + - editor: vim +``` + +## Panes + +**_Note that if you wish to use panes, make sure that you do not have `.` in your project name. tmux uses `.` to delimit between window and pane indices, +and tmuxinator uses the project name in combination with these indices to target the correct pane or window._** + +Panes are optional and are children of window entries, but unlike windows, they do not need a name. In the following example, the `editor` window has 2 panes, one running vim, the other guard. + +```yaml +windows: + - editor: + layout: main-vertical + panes: + - vim + - guard +``` + +The layout setting gets handed down to tmux directly, so you can choose from +one of [the five standard layouts](http://manpages.ubuntu.com/manpages/precise/en/man1/tmux.1.html#contenttoc6) +or [specify your own](http://stackoverflow.com/a/9976282/183537). + +## Interpreter Managers & Environment Variables + +To use tmuxinator with rbenv, RVM, NVM etc, use the `pre_window` option. + +``` +pre_window: rbenv shell 2.0.0-p247 +``` + +These command(s) will run before any subsequent commands in all panes and windows. + +## Passing directly to send-keys + +tmuxinator passes commands directly to send keys. This differs from simply chaining commands together using `&&` or `;`, in that +tmux will directly send the commands to a shell as if you typed them in. This allows commands to be executed on a remote server over +SSH for example. + +To support this both the window and pane options can take an array as an argument: + +``` +name: sample +root: ~/ + +windows: + - stats: + - ssh stats@example.com + - tail -f /var/log/stats.log + - logs: + layout: main-vertical + panes: + - logs: + - ssh logs@example.com + - cd /var/logs + - tail -f development.log +``` + +## Starting a session + +This will fire up tmux with all the tabs and panes you configured. + +``` +tmuxinator start [project] +``` + +## Shorthand + +A shorthand alias for tmuxinator can also be used. + +``` +mux [command] +``` + +## Other Commands + +Copy an existing project. Aliased to `c` and `cp` +``` +tmuxinator copy [existing] [new] +``` + +List all the projects you have configured. Aliased to `l` and `ls` +``` +tmuxinator list +``` + +Remove a project. Aliased to `rm` +``` +tmuxinator delete [project] +``` + +Remove all tmuxinator configs, aliases and scripts. Aliased to `i` +``` +tmuxinator implode +``` + +Examines your environment and identifies problems with your configuration +``` +tmuxinator doctor +``` + +Shows tmuxinator's help. Aliased to `h` +``` +tmuxinator help +``` + +Shows the shell commands that get executed for a project +``` +tmuxinator debug [project] +``` + +Shows tmuxinator's version. +``` +tmuxinator version +``` + +## FAQ + +### Window names are not displaying properly? + +Add `export DISABLE_AUTO_TITLE=true` to your `.zshrc` or `.bashrc` + +## Contributing + +To contribute, please read the [contributing guide](https://github.com/tmuxinator/tmuxinator/blob/master/CONTRIBUTING.md). + +## Copyright + +Copyright (c) 2010-2013 Allen Bargi. See LICENSE for further details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Rakefile Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,1 @@ +require "bundler/gem_tasks"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/mux Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,1 @@ +tmuxinator \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/tmuxinator Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby +$: << File.expand_path("../../lib/", __FILE__) + +require "thor" +require "tmuxinator" + +if ARGV.length == 1 + name = ARGV[0] + + if Tmuxinator::Cli.new.command_list.include?(name) + Tmuxinator::Cli.start + elsif Tmuxinator::Config.exists?(name) + Tmuxinator::Cli.new.start(name) + else + Tmuxinator::Cli.start + end +else + Tmuxinator::Cli.start +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/completion/tmuxinator.bash Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +_tmuxinator() { + COMPREPLY=() + local word="${COMP_WORDS[COMP_CWORD]}" + + if [ "$COMP_CWORD" -eq 1 ]; then + local commands="$(compgen -W "$(tmuxinator commands)" -- "$word")" + local projects="$(compgen -W "$(tmuxinator completions start)" -- "$word")" + + COMPREPLY=( $commands $projects ) + else + local words=("${COMP_WORDS[@]}") + unset words[0] + unset words[$COMP_CWORD] + local completions=$(tmuxinator completions "${words[@]}") + COMPREPLY=( $(compgen -W "$completions" -- "$word") ) + fi +} + +complete -F _tmuxinator tmuxinator mux
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/completion/tmuxinator.fish Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,16 @@ +function __fish_tmuxinator_using_command + set cmd (commandline -opc) + if [ (count $cmd) -gt 1 ] + if [ $argv[1] = $cmd[2] ] + return 0 + end + end + return 1 +end + +complete -f -c tmuxinator -a '(tmuxinator completions start)' +complete -f -c tmuxinator -a '(tmuxinator commands)' +complete -f -c tmuxinator -n '__fish_tmuxinator_using_command start' -a '(tmuxinator completions start)' +complete -f -c tmuxinator -n '__fish_tmuxinator_using_command open' -a '(tmuxinator completions open)' +complete -f -c tmuxinator -n '__fish_tmuxinator_using_command copy' -a '(tmuxinator completions copy)' +complete -f -c tmuxinator -n '__fish_tmuxinator_using_command delete' -a '(tmuxinator completions delete)'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/completion/tmuxinator.zsh Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,20 @@ +#!/usr/bin/env zsh + +if [[ ! -o interactive ]]; then + return +fi + +compctl -K _tmuxinator tmuxinator mux + +_tmuxinator() { + local words completions + read -cA words + + if [ "${#words}" -eq 2 ]; then + completions="$(tmuxinator commands)" + else + completions="$(tmuxinator completions ${words[2,-2]})" + fi + + reply=("${(ps:\n:)completions}") +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,24 @@ +require "yaml" +require "erubis" +require "shellwords" +require "thor" +require "thor/version" + +require "tmuxinator/util" +require "tmuxinator/deprecations" +require "tmuxinator/wemux_support" +require "tmuxinator/cli" +require "tmuxinator/config" +require "tmuxinator/pane" +require "tmuxinator/project" +require "tmuxinator/window" +require "tmuxinator/version" + +module Tmuxinator +end + +class Object + def blank? + respond_to?(:empty?) ? !!empty? : !self + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/assets/sample.yml Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,28 @@ +# ~/.tmuxinator/<%= name %>.yml + +name: <%= name %> +root: ~/ + +# Optional tmux socket +# socket_name: foo + +# Runs before everything. Use it to start daemons etc. +# pre: sudo /etc/rc.d/mysqld start + +# Runs in each window and pane before window/pane specific commands. Useful for setting up interpreter versions. +# pre_window: rbenv shell 2.0.0-p247 + +# Pass command line options to tmux. Useful for specifying a different tmux.conf. +# tmux_options: -f ~/.tmux.mac.conf + +# Change the command to call tmux. This can be used by derivatives/wrappers like byobu. +# tmux_command: byobu + +windows: + - editor: + layout: main-vertical + panes: + - vim + - guard + - server: bundle exec rails s + - logs: tail -f log/development.log
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/assets/template.erb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,67 @@ +#!<%= ENV["SHELL"] || "/bin/bash" %> + +# Clear rbenv variables before starting tmux +unset RBENV_VERSION +unset RBENV_DIR + +<%= tmux %> start-server\; has-session -t <%= name %> 2>/dev/null + +if [ "$?" -eq 1 ]; then + cd <%= root || "." %> + + # Run pre command. + <%= pre %> + + # Create the session and the first window. + TMUX= <%= tmux %> new-session -d -s <%= name %> -n <%= windows.first.name %> + + <%- if Tmuxinator::Config.version < 1.7 -%> + # Set the default path for versions prior to 1.7 + <%- if root? -%> + <%= tmux %> set-option -t <%= name %> <%= Tmuxinator::Config.default_path_option %> <%= root.shellescape -%> 1>/dev/null + <%- end -%> + <%- end -%> + + # Create other windows. + <%- windows.drop(1).each do |window| -%> + <%= window.tmux_new_window_command %> + <%- end -%> + + <%- windows.each do |window| -%> + + # Window "<%= window.name %>" + <%- unless window.panes? -%> + <%= window.tmux_pre_window_command %> + <%- window.commands.each do |command| -%> + <%= command %> + <%- end -%> + <%- else -%> + <%- window.panes.each do |pane| -%> + <%= pane.tmux_pre_window_command %> + <%= pane.tmux_pre_command %> + <%- if pane.multiple_commands? %> + <%- pane.commands.each do |command| -%> + <%= pane.tmux_main_command(command) %> + <%- end -%> + <%- else -%> + <%= pane.tmux_main_command(commands.first) %> + <%- end -%> + + <%- unless pane.last? -%> + <%= pane.tmux_split_command %> + <%- end -%> + <%= window.tmux_layout_command %> + <%- end -%> + + <%= window.tmux_select_first_pane %> + <%- end -%> + <%- end -%> + + <%= tmux %> select-window -t <%= base_index %> +fi + +if [ -z "$TMUX" ]; then + <%= tmux %> -u attach-session -t <%= name %> +else + <%= tmux %> -u switch-client -t <%= name %> +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/assets/wemux_template.erb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,56 @@ +#!<%= ENV["SHELL"] || "/bin/bash" %> +wemux ls 2>/dev/null + +if [ "$?" -eq 127 ]; then + cd <%= root || "." %> + + # Run pre command. + <%= pre %> + + # Create the session and the first window. + TMUX= <%= tmux %> new-session -d -s <%= name %> -n <%= windows.first.name %> + + # Set the default path. + <%- if root? -%> + <%= tmux %> set-option -t <%= name %> <%= Tmuxinator::Config.default_path_option %> <%= root.shellescape -%> 1>/dev/null + <%- end -%> + + # Create other windows. + <%- windows.drop(1).each do |window| -%> + <%= window.tmux_new_window_command %> + <%- end -%> + + <%- windows.each do |window| -%> + + # Window "<%= window.name %>" + <%- unless window.panes? -%> + <%= window.tmux_pre_window_command %> + <%- window.commands.each do |command| -%> + <%= command %> + <%- end -%> + <%- else -%> + <%- window.panes.each do |pane| -%> + <%= pane.tmux_pre_window_command %> + <%= pane.tmux_pre_command %> + <%- if pane.multiple_commands? %> + <%- pane.commands.each do |command| -%> + <%= pane.tmux_main_command(command) %> + <%- end -%> + <%- else -%> + <%= pane.tmux_main_command(commands.first) %> + <%- end -%> + + <%- unless pane.last? -%> + <%= pane.tmux_split_command %> + <%- end -%> + <%= window.tmux_layout_command %> + <%- end -%> + + <%= window.tmux_select_first_pane %> + <%- end -%> + <%- end -%> + + <%= tmux %> select-window -t <%= base_index %> +fi + +wemux attach
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/cli.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,147 @@ +module Tmuxinator + class Cli < Thor + include Tmuxinator::Util + + attr_reader :command_list + + def initialize(*args) + super + @command_list = %w(commands copy debug delete doctor help implode list start version) + end + + package_name "tmuxinator" unless Gem::Version.create(Thor::VERSION) < Gem::Version.create("0.18") + + desc "commands", "Lists commands available in tmuxinator" + + def commands + puts command_list.join("\n") + end + + desc "completions [arg1 arg2]", "Used for shell completion" + + def completions(arg) + if %w(start open copy delete).include?(arg) + configs = Tmuxinator::Config.configs + puts configs + end + end + + desc "new [PROJECT]", "Create a new project file and open it in your editor" + map "open" => :new + map "o" => :new + map "n" => :new + + def new(name) + config = Tmuxinator::Config.project(name) + + unless Tmuxinator::Config.exists?(name) + template = Tmuxinator::Config.default? ? Tmuxinator::Config.default : Tmuxinator::Config.sample + erb = Erubis::Eruby.new(File.read(template)).result(binding) + File.open(config, "w") { |f| f.write(erb) } + end + + Kernel.system("$EDITOR #{config}") || doctor + end + + desc "start [PROJECT]", "Start a tmux session using a project's tmuxinator config" + map "s" => :start + + def start(name) + project = Tmuxinator::Config.validate(name) + + if project.deprecations.any? + project.deprecations.each { |deprecation| say deprecation, :red } + puts + print "Press ENTER to continue." + STDIN.getc + end + + Kernel.exec(project.render) + end + + desc "debug [PROJECT]", "Output the shell commands that are generated by tmuxinator" + + def debug(name) + project = Tmuxinator::Config.validate(name) + puts project.render + end + + desc "copy [EXISTING] [NEW]", "Copy an existing project to a new project and open it in your editor" + map "c" => :copy + map "cp" => :copy + + def copy(existing, new) + existing_config_path = Tmuxinator::Config.project(existing) + new_config_path = Tmuxinator::Config.project(new) + + exit!("Project #{existing} doesn't exist!") unless Tmuxinator::Config.exists?(existing) + + if Tmuxinator::Config.exists?(new) + if yes?("#{new} already exists, would you like to overwrite it?", :red) + FileUtils.rm(new_config_path) + say "Overwriting #{new}" + end + end + + FileUtils.copy_file(existing_config_path, new_config_path) + Kernel.system("$EDITOR #{new_config_path}") + end + + desc "delete [PROJECT]", "Deletes given project" + map "d" => :delete + map "rm" => :delete + + def delete(project) + if Tmuxinator::Config.exists?(project) + config = "#{Tmuxinator::Config.root}/#{project}.yml" + + if yes?("Are you sure you want to delete #{project}?(y/n)", :red) + FileUtils.rm(config) + say "Deleted #{project}" + end + else + exit! "That file doesn't exist." + end + end + + desc "implode", "Deletes all tmuxinator projects" + map "i" => :implode + + def implode + if yes?("Are you sure you want to delete all tmuxinator configs?", :red) + FileUtils.remove_dir(Tmuxinator::Config.root) + say "Deleted all tmuxinator projects." + end + end + + desc "list", "Lists all tmuxinator projects" + map "l" => :list + map "ls" => :list + + def list + say "tmuxinator projects:" + + print_in_columns Tmuxinator::Config.configs + end + + desc "version", "Display installed tmuxinator version" + map "-v" => :version + + def version + say "tmuxinator #{Tmuxinator::VERSION}" + end + + desc "doctor", "Look for problems in your configuration" + + def doctor + say "Checking if tmux is installed ==> " + yes_no Tmuxinator::Config.installed? + + say "Checking if $EDITOR is set ==> " + yes_no Tmuxinator::Config.editor? + + say "Checking if $SHELL is set ==> " + yes_no Tmuxinator::Config.shell? + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/config.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,96 @@ +module Tmuxinator + class Config + class << self + def root + Dir.mkdir("#{ENV["HOME"]}/.tmuxinator") unless File.directory?(File.expand_path("~/.tmuxinator")) + "#{ENV["HOME"]}/.tmuxinator" + end + + def sample + "#{File.dirname(__FILE__)}/assets/sample.yml" + end + + def default + "#{ENV["HOME"]}/.tmuxinator/default.yml" + end + + def default? + exists?("default") + end + + def installed? + Kernel.system("type tmux > /dev/null") + end + + def version + `tmux -V`.split(" ")[1].to_f if installed? + end + + def default_path_option + version && version < 1.7 ? "default-path" : "-c" + end + + def editor? + !ENV["EDITOR"].nil? && !ENV["EDITOR"].empty? + end + + def shell? + !ENV["SHELL"].nil? && !ENV["SHELL"].empty? + end + + def exists?(name) + File.exists?(project(name)) + end + + def project(name) + projects = Dir.glob("#{root}/**/*.yml") + project_file = projects.detect { |project| project =~ /^#{name}.yml$/ } + project_file || "#{root}/#{name}.yml" + end + + def template + "#{File.dirname(__FILE__)}/assets/template.erb" + end + + def wemux_template + "#{File.dirname(__FILE__)}/assets/wemux_template.erb" + end + + def configs + Dir["#{Tmuxinator::Config.root}/*.yml"].sort.map do |path| + File.basename(path, ".yml") + end + end + + def validate(name) + unless Tmuxinator::Config.exists?(name) + puts "Project #{name} doesn't exist." + exit! + end + + config_path = Tmuxinator::Config.project(name) + + yaml = begin + YAML.load(File.read(config_path)) + rescue SyntaxError, StandardError + puts "Failed to parse config file. Please check your formatting." + exit! + end + + project = Tmuxinator::Project.new(yaml) + + unless project.windows? + puts "Your project file should include some windows." + exit! + end + + unless project.name? + puts "Your project file didn't specify a 'project_name'" + exit! + end + + project + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/deprecations.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,19 @@ +module Tmuxinator + module Deprecations + def rvm? + yaml["rvm"] + end + + def rbenv? + yaml["rbenv"] + end + + def pre_tab? + yaml["pre_tab"] + end + + def cli_args? + yaml["cli_args"] + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/pane.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,40 @@ +module Tmuxinator + class Pane + attr_reader :commands, :project, :index, :project, :tab + + def initialize(index, project, tab, *commands) + @commands = commands + @index = index + @project = project + @tab = tab + end + + def tmux_window_and_pane_target + "#{project.name}:#{tab.index + project.base_index}.#{index + project.base_index}" + end + + def tmux_pre_command + tab.pre ? "#{project.tmux} send-keys -t #{tmux_window_and_pane_target} #{tab.pre.shellescape} C-m" : "" + end + + def tmux_pre_window_command + project.pre_window ? "#{project.tmux} send-keys -t #{tmux_window_and_pane_target} #{project.pre_window.shellescape} C-m" : "" + end + + def tmux_main_command(command) + command ? "#{project.tmux} send-keys -t #{project.name}:#{tab.index + project.base_index}.#{index + tab.project.base_index} #{command.shellescape} C-m" : "" + end + + def tmux_split_command + "#{project.tmux} splitw -t #{tab.tmux_window_target}" + end + + def last? + index == tab.panes.length - 1 + end + + def multiple_commands? + commands && commands.length > 0 + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/project.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,174 @@ +module Tmuxinator + class Project + include Tmuxinator::Util + include Tmuxinator::Deprecations + include Tmuxinator::WemuxSupport + + attr_reader :yaml + + def initialize(yaml) + @yaml = yaml + load_wemux_overrides if wemux? + end + + def render + template = File.read(Tmuxinator::Config.template) + Erubis::Eruby.new(template).result(binding) + end + + def windows + windows_yml = yaml["tabs"] || yaml["windows"] + + @windows ||= windows_yml.map.with_index do |window_yml, index| + Tmuxinator::Window.new(window_yml, index, self) + end + end + + def root + root = yaml["project_root"] || yaml["root"] + root.blank? ? nil : File.expand_path(root) + end + + def name + name = yaml["project_name"] || yaml["name"] + name.shellescape + end + + def pre + pre_config = yaml["pre"] + if pre_config.is_a?(Array) + pre_config.join("; ") + else + pre_config + end + end + + def pre_window + if rbenv? + "rbenv shell #{yaml["rbenv"]}" + elsif rvm? + "rvm use #{yaml["rvm"]}" + elsif pre_tab? + yaml["pre_tab"] + else + yaml["pre_window"] + end + end + + def tmux + "#{tmux_command}#{tmux_options}#{socket}" + end + + def tmux_command + yaml["tmux_command"] || "tmux" + end + + def socket + if socket_path + " -S #{socket_path}" + elsif socket_name + " -L #{socket_name}" + else + nil + end + end + + def socket_name + yaml["socket_name"] + end + + def socket_path + yaml["socket_path"] + end + + def tmux_options + if cli_args? + " #{yaml["cli_args"].to_s.strip}" + elsif tmux_options? + " #{yaml["tmux_options"].to_s.strip}" + else + "" + end + end + + def base_index + get_pane_base_index ? get_pane_base_index.to_i : get_base_index.to_i + end + + def tmux_options? + yaml["tmux_options"] + end + + def windows? + windows.any? + end + + def root? + !root.nil? + end + + def name? + !name.nil? + end + + def window(i) + "#{name}:#{i}" + end + + def send_pane_command(cmd, window_index, pane_index) + if cmd.empty? + "" + else + "#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m" + end + end + + def send_keys(cmd, window_index) + if cmd.empty? + "" + else + "#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m" + end + end + + def deprecations + deprecations = [] + deprecations << "DEPRECATION: rbenv/rvm specific options have been replaced by the pre_tab option and will not be supported in 0.8.0." if yaml["rbenv"] || yaml["rvm"] + deprecations << "DEPRECATION: The tabs option has been replaced by the windows option and will not be supported in 0.8.0." if yaml["tabs"] + deprecations << "DEPRECATION: The cli_args option has been replaced by the tmux_options option and will not be supported in 0.8.0." if yaml["cli_args"] + deprecations + end + + def get_pane_base_index + tmux_config["pane-base-index"] + end + + def get_base_index + tmux_config["base-index"] + end + + def show_tmux_options + "#{tmux} start-server\\; show-option -g" + end + + private + + def tmux_config + @tmux_config ||= extract_tmux_config + end + + def extract_tmux_config + options_hash = {} + + options_string = `#{show_tmux_options}` + options_string.encode!("UTF-8", :invalid => :replace) + options_string.split("\n").map do |entry| + key, value = entry.split("\s") + options_hash[key] = value + options_hash + end + + options_hash + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/util.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,14 @@ +module Tmuxinator + module Util + include Thor::Actions + + def exit!(msg) + puts msg + Kernel.exit(1) + end + + def yes_no(condition) + condition ? say("Yes", :green) : say("No", :red) + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/version.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,3 @@ +module Tmuxinator + VERSION = "0.6.8" +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/wemux_support.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,24 @@ +module Tmuxinator + module WemuxSupport + def wemux? + yaml["tmux_command"] == "wemux" + end + + def load_wemux_overrides + self.instance_eval do + def render + template = File.read(Tmuxinator::Config.wemux_template) + Erubis::Eruby.new(template).result(binding) + end + + def name + "wemux" + end + + def tmux + "wemux" + end + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/tmuxinator/window.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,90 @@ +module Tmuxinator + class Window + include Tmuxinator::Util + + attr_reader :name, :panes, :layout, :commands, :index, :project + + def initialize(window_yaml, index, project) + @name = !window_yaml.keys.first.nil? ? window_yaml.keys.first.shellescape : nil + @panes = [] + @layout = nil + @pre = nil + @project = project + @index = index + + value = window_yaml.values.first + + if value.is_a?(Hash) + @layout = value["layout"] ? value["layout"].shellescape : nil + @pre = value["pre"] if value["pre"] + + @panes = build_panes(value["panes"]) + else + @commands = build_commands(tmux_window_command_prefix, value) + end + end + + def build_panes(panes_yml) + Array(panes_yml).map.with_index do |pane_yml, index| + if pane_yml.is_a?(Hash) + pane_yml.map do |name, commands| + Tmuxinator::Pane.new(index, project, self, *commands) + end + else + Tmuxinator::Pane.new(index, project, self, pane_yml) + end + end.flatten + end + + def build_commands(prefix, command_yml) + if command_yml.is_a?(Array) + command_yml.map do |command| + "#{tmux_window_command_prefix} #{command.shellescape} C-m" if command + end.compact + elsif command_yml.is_a?(String) && !command_yml.empty? + ["#{tmux_window_command_prefix} #{command_yml.shellescape} C-m"] + else + [] + end + end + + def pre + if @pre.is_a?(Array) + @pre.join(" && ") + elsif @pre.is_a?(String) + @pre + else + "" + end + end + + def panes? + panes.any? + end + + def tmux_window_target + "#{project.name}:#{index + project.base_index}" + end + + def tmux_pre_window_command + project.pre_window ? "#{project.tmux} send-keys -t #{tmux_window_target} #{project.pre_window.shellescape} C-m" : "" + end + + def tmux_window_command_prefix + "#{project.tmux} send-keys -t #{project.name}:#{index + project.base_index}" + end + + def tmux_new_window_command + path = project.root? ? "#{Tmuxinator::Config.default_path_option} #{File.expand_path(project.root).shellescape}" : nil + "#{project.tmux} new-window #{path} -t #{tmux_window_target} -n #{name}" + end + + def tmux_layout_command + "#{project.tmux} select-layout -t #{tmux_window_target} #{layout}" + end + + def tmux_select_first_pane + "#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.base_index}" + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/factories/projects.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,25 @@ +FactoryGirl.define do + factory :project, :class => Tmuxinator::Project do + ignore do + file { YAML.load(File.read("#{File.expand_path("spec/fixtures/sample.yml")}")) } + end + + initialize_with { Tmuxinator::Project.new(file) } + end + + factory :project_with_deprecations, :class => Tmuxinator::Project do + ignore do + file { YAML.load(File.read("#{File.expand_path("spec/fixtures/sample.deprecations.yml")}")) } + end + + initialize_with { Tmuxinator::Project.new(file) } + end + + factory :wemux_project, :class => Tmuxinator::Project do + ignore do + file { YAML.load(File.read("#{File.expand_path("spec/fixtures/sample_wemux.yml")}")) } + end + + initialize_with { Tmuxinator::Project.new(file) } + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/fixtures/sample.deprecations.yml Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,34 @@ +# ~/.tmuxinator/sample.deprecations.yml + +project_name: sample +project_root: ~/test +socket_name: foo # Remove to use default socket +pre: sudo /etc/rc.d/mysqld start # Runs before everything +rbenv: 2.0.0-p247 +cli_args: -f ~/.tmux.mac.conf # Pass arguments to tmux +tabs: + - editor: + pre: + - echo "I get run in each pane, before each pane command!" + - + layout: main-vertical + panes: + - vim + - #empty, will just run plain bash + - top + - shell: git pull + - guard: + layout: tiled + pre: + - echo "I get run in each pane." + - echo "Before each pane command!" + panes: + - + - #empty, will just run plain bash + - + - database: bundle exec rails db + - server: bundle exec rails s + - logs: tail -f log/development.log + - console: bundle exec rails c + - capistrano: + - server: ssh user@example.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/fixtures/sample.yml Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,40 @@ +# ~/.tmuxinator/sample.yml +# you can make as many tabs as you wish... + +name: sample +root: ~/test +socket_name: foo # Remove to use default socket +pre: sudo /etc/rc.d/mysqld start # Runs before everything +pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane +tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux +windows: + - editor: + pre: + - echo "I get run in each pane, before each pane command!" + - + layout: main-vertical + panes: + - vim + - #empty, will just run plain bash + - top + - pane_with_multiple_commands: + - ssh server + - echo "Hello" + - shell: + - git pull + - git merge + - guard: + layout: tiled + pre: + - echo "I get run in each pane." + - echo "Before each pane command!" + panes: + - + - #empty, will just run plain bash + - + - database: bundle exec rails db + - server: bundle exec rails s + - logs: tail -f log/development.log + - console: bundle exec rails c + - capistrano: + - server: ssh user@example.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/fixtures/sample_wemux.yml Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,37 @@ +# ~/.tmuxinator/sample_wemux.yml +# you can make as many tabs as you wish... + +name: sample +root: ~/test +pre: sudo /etc/rc.d/mysqld start # Runs before everything +pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane +tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux +tmux_command: wemux +windows: + - editor: + pre: + - echo "I get run in each pane, before each pane command!" + - + layout: main-vertical + panes: + - vim + - #empty, will just run plain bash + - top + - shell: + - git pull + - git merge + - guard: + layout: tiled + pre: + - echo "I get run in each pane." + - echo "Before each pane command!" + panes: + - + - #empty, will just run plain bash + - + - database: bundle exec rails db + - server: bundle exec rails s + - logs: tail -f log/development.log + - console: bundle exec rails c + - capistrano: + - server: ssh user@example.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/cli_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,231 @@ +require "spec_helper" +describe Tmuxinator::Cli do + let(:cli) { Tmuxinator::Cli } + + before do + ARGV.clear + allow(Kernel).to receive(:system) + allow(FileUtils).to receive(:copy_file) + allow(FileUtils).to receive(:rm) + allow(FileUtils).to receive(:remove_dir) + end + + context "no arguments" do + it "runs without error" do + _, err = capture_io { cli.start } + expect(err).to be_empty + end + end + + describe "#completions" do + before do + ARGV.replace(["completions", "start"]) + allow(Tmuxinator::Config).to receive_messages(:configs => ["test.yml"]) + end + + it "gets completions" do + out, _ = capture_io { cli.start } + expect(out).to include("test.yml") + end + end + + describe "#commands" do + before do + ARGV.replace(["commands"]) + end + + it "lists the commands" do + out, _ = capture_io { cli.start } + expect(out).to eq "#{%w(commands copy debug delete doctor help implode list start version).join("\n")}\n" + end + end + + describe "#start" do + before do + ARGV.replace(["start", "foo"]) + allow(Tmuxinator::Config).to receive_messages(:validate => project) + allow(Tmuxinator::Config).to receive_messages(:version => 1.9) + allow(Kernel).to receive(:exec) + end + + context "no deprecations" do + let(:project) { FactoryGirl.build(:project) } + + it "starts the project" do + expect(Kernel).to receive(:exec) + capture_io { cli.start } + end + end + + context "deprecations" do + before do + allow($stdin).to receive_messages(:getc => "y") + end + + let(:project) { FactoryGirl.build(:project_with_deprecations) } + + it "prints the deprecations" do + out, _ = capture_io { cli.start } + expect(out).to include "DEPRECATION" + end + end + end + + describe "#new" do + let(:file) { StringIO.new } + + before do + ARGV.replace(["new", "test"]) + allow(File).to receive(:open) { |&block| block.yield file } + end + + context "existing project doesn't exist" do + before do + allow(Tmuxinator::Config).to receive_messages(:exists? => false) + end + + it "creates a new tmuxinator project file" do + capture_io { cli.start } + expect(file.string).to_not be_empty + end + end + + context "files exists" do + before do + allow(File).to receive_messages(:exists? => true) + end + + it "just opens the file" do + expect(Kernel).to receive(:system) + capture_io { cli.start } + end + end + end + + describe "#copy" do + before do + ARGV.replace(["copy", "foo", "bar"]) + allow(Tmuxinator::Config).to receive(:exists?) { true } + end + + context "new project already exists" do + before do + allow(Thor::LineEditor).to receive_messages(:readline => "y") + end + + it "prompts user to confirm overwrite" do + expect(FileUtils).to receive(:rm) + capture_io { cli.start } + end + + it "copies the config" do + expect(FileUtils).to receive(:copy_file) + capture_io { cli.start } + end + end + + context "existing project doens't exist" do + before do + allow(Tmuxinator::Config).to receive(:exists?) { false } + end + + it "exit with error code" do + expect { capture_io { cli.start } }.to raise_error SystemExit + end + end + end + + describe "#debug" do + let(:project) { FactoryGirl.build(:project) } + + before do + ARGV.replace(["debug", "foo"]) + allow(Tmuxinator::Config).to receive_messages(:validate => project) + end + + it "renders the project" do + expect(project).to receive(:render) + capture_io { cli.start } + end + end + + describe "#delete" do + before do + ARGV.replace(["delete", "foo"]) + allow(Thor::LineEditor).to receive_messages(:readline => "y") + end + + context "project exists" do + before do + allow(Tmuxinator::Config).to receive(:exists?) { true } + end + + it "deletes the project" do + expect(FileUtils).to receive(:rm) + capture_io { cli.start } + end + end + + context "project doesn't exist" do + before do + allow(Thor::LineEditor).to receive_messages(:readline => "y") + end + + it "exits with error message" do + expect { capture_io { cli.start } }.to raise_error SystemExit + end + end + end + + describe "#implode" do + before do + ARGV.replace(["implode"]) + allow(Thor::LineEditor).to receive_messages(:readline => "y") + end + + it "confirms deletion of all projects" do + expect(Thor::LineEditor).to receive(:readline).and_return("y") + capture_io { cli.start } + end + + it "deletes all projects" do + expect(FileUtils).to receive(:remove_dir) + capture_io { cli.start } + end + end + + describe "#list" do + before do + ARGV.replace(["list"]) + allow(Dir).to receive_messages(:[] => ["/path/to/project.yml"]) + end + + it "lists all projects" do + expect { capture_io { cli.start } }.to_not raise_error + end + end + + describe "#version" do + before do + ARGV.replace(["version"]) + end + + it "prints the current version" do + out, _ = capture_io { cli.start } + expect(out).to eq "tmuxinator #{Tmuxinator::VERSION}\n" + end + end + + describe "#doctor" do + before do + ARGV.replace(["doctor"]) + end + + it "checks requirements" do + expect(Tmuxinator::Config).to receive(:installed?) + expect(Tmuxinator::Config).to receive(:editor?) + expect(Tmuxinator::Config).to receive(:shell?) + capture_io { cli.start } + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/config_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,153 @@ +require "spec_helper" + +describe Tmuxinator::Config do + describe "#root" do + it "is ~/.tmuxintaor" do + expect(Tmuxinator::Config.root).to eq "#{ENV["HOME"]}/.tmuxinator" + end + end + + describe "#sample" do + it "gets the path of the sample project" do + expect(Tmuxinator::Config.sample).to include("sample.yml") + end + end + + describe "#default" do + it "gets the path of the default config" do + expect(Tmuxinator::Config.default).to include("default.yml") + end + end + + describe "#default?" do + let(:root) { Tmuxinator::Config.root } + + context "when the file exists" do + before do + allow(File).to receive(:exists?).with(Tmuxinator::Config.default) { true } + end + + it "returns true" do + expect(Tmuxinator::Config.default?).to be_truthy + end + end + + context "when the file doesn't exist" do + before do + allow(File).to receive(:exists?).with(Tmuxinator::Config.default) { false } + end + + it "returns true" do + expect(Tmuxinator::Config.default?).to be_falsey + end + end + end + + describe "#configs" do + before do + allow(Dir).to receive_messages(:[] => ["test.yml"]) + end + + it "gets a list of all projects" do + expect(Tmuxinator::Config.configs).to include("test") + end + end + + describe "#installed?" do + context "tmux is installed" do + before do + allow(Kernel).to receive(:system) { true } + end + + it "returns true" do + expect(Tmuxinator::Config.installed?).to be_truthy + end + end + + context "tmux is not installed" do + before do + allow(Kernel).to receive(:system) { false } + end + + it "returns true" do + expect(Tmuxinator::Config.installed?).to be_falsey + end + end + end + + describe "#editor?" do + context "$EDITOR is set" do + before do + allow(ENV).to receive(:[]).with("EDITOR") { "vim" } + end + + it "returns true" do + expect(Tmuxinator::Config.editor?).to be_truthy + end + end + + context "$EDITOR is not set" do + before do + allow(ENV).to receive(:[]).with("EDITOR") { nil } + end + + it "returns false" do + expect(Tmuxinator::Config.editor?).to be_falsey + end + end + end + + describe "#shell?" do + context "$SHELL is set" do + before do + allow(ENV).to receive(:[]).with("SHELL") { "vim" } + end + + it "returns true" do + expect(Tmuxinator::Config.shell?).to be_truthy + end + end + + context "$SHELL is not set" do + before do + allow(ENV).to receive(:[]).with("SHELL") { nil } + end + + it "returns false" do + expect(Tmuxinator::Config.shell?).to be_falsey + end + end + end + + describe "#exists?" do + before do + allow(File).to receive_messages(:exists? => true) + allow(Tmuxinator::Config).to receive_messages(:project => "") + end + + it "checks if the given project exists" do + expect(Tmuxinator::Config.exists?("test")).to be_truthy + end + end + + describe "#project" do + let(:root) { Tmuxinator::Config.root } + + before do + path = File.expand_path("../../../fixtures/", __FILE__) + allow(Tmuxinator::Config).to receive_messages(:root => path) + end + + context "with project yml" do + it "gets the project as path to the yml file" do + expect(Tmuxinator::Config.project("sample")).to eq "#{root}/sample.yml" + end + end + + context "without project yml" do + it "gets the project as path to the yml file" do + expect(Tmuxinator::Config.project("new-project")).to eq "#{root}/new-project.yml" + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/pane_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,7 @@ +require "spec_helper" + +describe Tmuxinator::Pane do + it "creates an instance" do + expect(Tmuxinator::Pane.new("vim", 0, nil, nil)).to be_a(Tmuxinator::Pane) + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/project_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,313 @@ +require "spec_helper" + +describe Tmuxinator::Project do + let(:project) { FactoryGirl.build(:project) } + let(:project_with_deprecations) { FactoryGirl.build(:project_with_deprecations) } + let(:wemux_project) { FactoryGirl.build(:wemux_project) } + + describe "#initialize" do + context "valid yaml" do + it "creates an instance" do + expect(project).to be_a(Tmuxinator::Project) + end + end + end + + describe "#render" do + it "renders the tmux config" do + expect(project.render).to_not be_empty + end + + context "wemux" do + it "renders the wemux config" do + expect(wemux_project.render).to_not be_empty + end + end + end + + describe "#windows" do + context "without deprecations" do + it "gets the list of windows" do + expect(project.windows).to_not be_empty + end + end + + context "with deprecations" do + it "still gets the list of windows" do + expect(project_with_deprecations.windows).to_not be_empty + end + end + end + + describe "#root" do + context "without deprecations" do + it "gets the root" do + expect(project.root).to include("test") + end + end + + context "with deprecations" do + it "still gets the root" do + expect(project_with_deprecations.root).to include("test") + end + end + end + + describe "#name" do + context "without deprecations" do + it "gets the name" do + expect(project.name).to eq "sample" + end + end + + context "with deprecations" do + it "still gets the name" do + expect(project_with_deprecations.name).to eq "sample" + end + end + + context "wemux" do + it "is wemux" do + expect(wemux_project.name).to eq "wemux" + end + end + end + + describe "#pre_window" do + it "gets the pre_window command" do + expect(project.pre_window).to eq "rbenv shell 2.0.0-p247" + end + + context "with deprecations" do + context "rbenv option is present" do + before do + allow(project).to receive_messages(:rbenv? => true) + allow(project).to receive_message_chain(:yaml, :[]).and_return("2.0.0-p247") + end + + it "still gets the correct pre_window command" do + expect(project.pre_window).to eq "rbenv shell 2.0.0-p247" + end + end + + context "rvm option is present" do + before do + allow(project).to receive_messages(:rbenv? => false) + allow(project).to receive_message_chain(:yaml, :[]).and_return("ruby-2.0.0-p247") + end + + it "still gets the correct pre_window command" do + expect(project.pre_window).to eq "rvm use ruby-2.0.0-p247" + end + end + + context "pre_tab is present" do + before do + allow(project).to receive_messages(:rbenv? => false) + allow(project).to receive_messages(:pre_tab? => true) + end + + it "still gets the correct pre_window command" do + expect(project.pre_window).to be_nil + end + end + end + end + + describe "#socket" do + context "socket path is present" do + before do + allow(project).to receive_messages(:socket_path => "/tmp") + end + + it "gets the socket path" do + expect(project.socket).to eq " -S /tmp" + end + end + end + + describe "#tmux_command" do + context "tmux_command specified" do + before do + project.yaml["tmux_command"] = "byobu" + end + + it "gets the custom tmux command" do + expect(project.tmux_command).to eq "byobu" + end + end + + context "tmux_command is not specified" do + it "returns the default" do + expect(project.tmux_command).to eq "tmux" + end + end + end + + describe "#tmux_options" do + context "no tmux options" do + before do + allow(project).to receive_messages(:tmux_options? => false) + end + + it "returns nothing" do + expect(project.tmux_options).to eq "" + end + end + + context "with deprecations" do + before do + allow(project_with_deprecations).to receive_messages(:cli_args? => true) + end + + it "still gets the tmux options" do + expect(project_with_deprecations.tmux_options).to eq " -f ~/.tmux.mac.conf" + end + end + end + + describe "#get_pane_base_index" do + it "extracts the pane_base_index from tmux_options" do + allow(project).to receive_messages(show_tmux_options: tmux_config(pane_base_index: 3)) + + expect(project.get_pane_base_index).to eq("3") + end + end + + describe "#get_base_index" do + it "extracts the base index from options" do + allow(project).to receive_messages(show_tmux_options: tmux_config(base_index: 1)) + + expect(project.get_base_index).to eq("1") + end + end + + describe "#base_index" do + context "pane base index present" do + before do + allow(project).to receive_messages(:get_pane_base_index => "1") + allow(project).to receive_messages(:get_base_index => "1") + end + + it "gets the pane base index" do + expect(project.base_index).to eq 1 + end + end + + context "pane base index no present" do + before do + allow(project).to receive_messages(:get_pane_base_index => nil) + allow(project).to receive_messages(:get_base_index => "0") + end + + it "gets the base index" do + expect(project.base_index).to eq 0 + end + end + end + + describe "#window" do + it "gets the window and index for tmux" do + expect(project.window(1)).to eq "sample:1" + end + end + + describe "#name?" do + context "name is present" do + it "returns true" do + expect(project.name?).to be_truthy + end + end + end + + describe "#windows?" do + context "windows are present" do + it "returns true" do + expect(project.windows?).to be_truthy + end + end + end + + describe "#root?" do + context "root are present" do + it "returns true" do + expect(project.root?).to be_truthy + end + end + end + + describe "#send_keys" do + context "no command for window" do + it "returns empty string" do + expect(project.send_keys("", 1)).to be_empty + end + end + + context "command for window is not empty" do + it "returns the tmux command" do + expect(project.send_keys("vim", 1)).to eq "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 vim C-m" + end + end + end + + describe "#send_pane_command" do + context "no command for pane" do + it "returns empty string" do + expect(project.send_pane_command("", 0, 0)).to be_empty + end + end + + context "command for pane is not empty" do + it "returns the tmux command" do + expect(project.send_pane_command("vim", 1, 0)).to eq "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 vim C-m" + end + end + end + + describe "#deprecations" do + context "without deprecations" do + it "is empty" do + expect(project.deprecations).to be_empty + end + end + + context "with deprecations" do + it "is not empty" do + expect(project_with_deprecations.deprecations).to_not be_empty + end + end + end + + describe "#commands" do + let(:window) { project.windows.keep_if { |w| w.name == "shell" }.first } + + it "splits commands into an array" do + expect(window.commands).to eq(["tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 git\\ pull C-m", "tmux -f ~/.tmux.mac.conf -L foo send-keys -t sample:1 git\\ merge C-m"]) + end + end + + describe "#pre" do + subject(:pre) { project.pre } + + context "pre in yaml is string" do + before { project.yaml["pre"] = "mysql.server start" } + + it "returns the string" do + expect(pre).to eq("mysql.server start") + end + end + + context "pre in yaml is Array" do + before { + project.yaml["pre"] = [ + "mysql.server start", + "memcached -d" + ] + } + + it "joins array using ;" do + expect(pre).to eq("mysql.server start; memcached -d") + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/util_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,6 @@ +require "spec_helper" + +describe Tmuxinator::Util do + let(:util) { Object.new.extend(Tmuxinator::Util) } + +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/lib/tmuxinator/window_spec.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,154 @@ +require "spec_helper" + +describe Tmuxinator::Window do + let(:project) { double } + let(:panes) { ["vim", nil, "top"] } + let(:yaml) do + { + "editor" => { + "pre" => ["echo 'I get run in each pane. Before each pane command!'", nil], + "layout" => "main-vertical", + "panes" => panes + } + } + end + + let(:window) { Tmuxinator::Window.new(yaml, 0, project) } + + before do + allow(project).to receive_messages(:tmux => "tmux", :name => "test", :base_index => 1) + end + + describe "#initialize" do + it "creates an instance" do + expect(window).to be_a(Tmuxinator::Window) + end + end + + describe "#panes" do + let(:pane) { double(:pane) } + + before do + allow(Tmuxinator::Pane).to receive_messages :new => pane + end + + context "with a three element Array" do + let(:panes) { ["vim", nil, "top"] } + + it "creates three panes" do + expect(Tmuxinator::Pane).to receive(:new).exactly(3).times + window.panes + end + + it "returns three panes" do + expect(window.panes).to eql [pane, pane, pane] + end + end + + context "with a String" do + let(:panes) { "vim" } + + it "creates one pane" do + expect(Tmuxinator::Pane).to receive(:new).once + window.panes + end + + it "returns one pane in an Array" do + expect(window.panes).to eql [pane] + end + end + + context "with nil" do + let(:panes) { nil } + + it "doesn't create any panes" do + expect(Tmuxinator::Pane).to_not receive(:new) + window.panes + end + + it "returns an empty Array" do + expect(window.panes).to be_empty + end + end + end + + describe "#pre" do + context "pre is a string" do + before do + yaml["editor"]["pre"] = "vim" + end + + it "returns the pre command" do + expect(window.pre).to eq "vim" + end + end + + + context "pre is not present" do + before do + yaml["editor"].delete("pre") + end + + it "returns an empty string" do + expect(window.pre).to eq "" + end + end + end + + describe "#build_commands" do + context "command is an array" do + before do + yaml["editor"] = ["git fetch", "git status"] + end + + it "returns the flattened command" do + expect(window.commands).to eq ["tmux send-keys -t test:1 git\\ fetch C-m", "tmux send-keys -t test:1 git\\ status C-m"] + end + end + + context "command is a string" do + before do + yaml["editor"] = "vim" + end + + it "returns the command" do + expect(window.commands).to eq ["tmux send-keys -t test:1 vim C-m"] + end + end + + context "command is empty" do + before do + yaml["editor"] = "" + end + + it "returns an empty array" do + expect(window.commands).to be_empty + end + end + end + + describe "#tmux_new_window_command" do + let(:project) { double(:project) } + let(:window) { Tmuxinator::Window.new(yaml, 0, project) } + + before do + allow(project).to receive_messages( + :name => "", + :tmux => "tmux", + :root => "/project/tmuxinator", + :root? => true, + :base_index => 1 + ) + end + + context "tmux 1.6 and below" do + before do + allow(Tmuxinator::Config).to receive_messages(:version => 1.6) + end + + it "specifies root path by passing default-path to tmux" do + expect(window.tmux_new_window_command).to include("default-path /project/tmuxinator") + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/spec_helper.rb Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,58 @@ +require "coveralls" +require "simplecov" +require "pry" + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ + SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter +] +SimpleCov.start do + add_filter 'vendor/cache' +end + +require "tmuxinator" +require "factory_girl" + + +FactoryGirl.find_definitions + +RSpec.configure do |config| + config.order = "random" +end + +# Copied from minitest. +def capture_io + begin + require 'stringio' + + captured_stdout, captured_stderr = StringIO.new, StringIO.new + + orig_stdout, orig_stderr = $stdout, $stderr + $stdout, $stderr = captured_stdout, captured_stderr + + yield + + return captured_stdout.string, captured_stderr.string + ensure + $stdout = orig_stdout + $stderr = orig_stderr + end +end + +def tmux_config(options = {}) + standard_options = [ + "assume-paste-time 1", + "bell-action any", + "bell-on-alert off", + ] + + if base_index = options.fetch(:base_index) {1} + standard_options << "base-index #{base_index}" + end + + if pane_base_index = options.fetch(:pane_base_index) {1} + standard_options << "pane-base-index #{pane_base_index}" + end + + "echo '#{standard_options.join("\n")}'" +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tmuxinator.gemspec Mon Jul 21 08:04:55 2014 +0900 @@ -0,0 +1,53 @@ +# coding: utf-8 +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "tmuxinator/version" + +Gem::Specification.new do |s| + s.name = "tmuxinator" + s.version = Tmuxinator::VERSION + s.authors = ["Allen Bargi"] + s.email = ["allen.bargi@gmail.com"] + s.description = %q{Create and manage complex tmux sessions easily.} + s.summary = %q{Create and manage complex tmux sessions easily.} + s.homepage = "https://github.com/aziz/tmuxinator" + s.license = "MIT" + + s.files = Dir["lib/**/*", "spec/**/*", "bin/*"] + s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } + s.test_files = s.files.grep(%r{^(test|spec|features)/}) + s.require_paths = ["lib"] + + s.post_install_message = %q{ + __________________________________________________________ + .......................................................... + + Thank you for installing tmuxinator. + + Make sure that you've set these variables in your ENV: + + $EDITOR, $SHELL + + You can run `tmuxinator doctor` to make sure everything is set. + Happy tmuxing with tmuxinator! + + .......................................................... + __________________________________________________________ + } + + s.required_rubygems_version = ">= 1.8.23" + + s.add_dependency "thor", "~> 0.19", ">= 0.15.0" + s.add_dependency "erubis" + + s.add_development_dependency "bundler", "~> 1.3" + s.add_development_dependency "rspec", "~> 3.0.0" + s.add_development_dependency "simplecov" + s.add_development_dependency "coveralls" + s.add_development_dependency "awesome_print" + s.add_development_dependency "pry" + s.add_development_dependency "pry-nav" + s.add_development_dependency "factory_girl" + s.add_development_dependency "transpec" +end +