Compare commits
31 Commits
feature-re
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
bb85ff7641 | ||
|
9ad83268ee | ||
|
b083bddeeb | ||
|
ab51679e33 | ||
|
22e4bc1d43 | ||
|
b6acecae20 | ||
|
50f3580b96 | ||
|
30ac196713 | ||
|
60ccf7f035 | ||
|
6390d0d7ed | ||
|
048daa65a9 | ||
|
7deae7eff7 | ||
|
124bdb1961 | ||
|
490df3bd99 | ||
|
a43064e439 | ||
|
192281392b | ||
|
d053706ce6 | ||
|
b44303b80d | ||
|
4a6abd75a8 | ||
|
11af9bfe85 | ||
|
dbc1fc55d7 | ||
|
51f79bcc1e | ||
|
0068baf3cb | ||
|
2a2fc58063 | ||
|
7288d0ab57 | ||
|
75283ac21f | ||
|
459b971a6a | ||
|
68d07cd897 | ||
|
f2926471bd | ||
|
cbed2f3e5b | ||
|
e4e9f2d1cd |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
.local*
|
||||
.yardoc
|
||||
doc
|
||||
pkg
|
||||
|
2
.travis.yml
Normal file
2
.travis.yml
Normal file
@ -0,0 +1,2 @@
|
||||
rvm: 1.9.2
|
||||
script: "bundle exec rake spec"
|
13
.yardopts
13
.yardopts
@ -1,13 +0,0 @@
|
||||
--title "Teamocil"
|
||||
|
||||
--no-cache
|
||||
--protected
|
||||
--no-private
|
||||
|
||||
--markup "markdown"
|
||||
--markup-provider "maruku"
|
||||
|
||||
--format html
|
||||
|
||||
"README.mkd"
|
||||
"lib/**/*.rb"
|
@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
teamocil (0.1.11)
|
||||
teamocil (0.3.2)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
@ -19,7 +19,7 @@ GEM
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.6.0)
|
||||
syntax (1.0.0)
|
||||
yard (0.7.2)
|
||||
yard (0.7.5)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
2
LICENSE
2
LICENSE
@ -1,3 +1,3 @@
|
||||
Copyright 2011 Rémi Prévost.
|
||||
Copyright 2011-2012 Rémi Prévost.
|
||||
You may use this work without restrictions, as long as this notice is included.
|
||||
The work is provided "as is" without warranty of any kind, neither express nor implied.
|
||||
|
157
README.md
157
README.md
@ -2,19 +2,24 @@
|
||||
|
||||
Teamocil is a simple tool used to automatically create sessions, windows and splits in [tmux](http://tmux.sourceforge.net/) with YAML files.
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/remiprev/teamocil.png)](http://travis-ci.org/remiprev/teamocil)
|
||||
|
||||
## Usage
|
||||
|
||||
$ gem install teamocil
|
||||
$ mkdir ~/.teamocil
|
||||
$ teamocil --edit sample
|
||||
$ tmux
|
||||
$ teamocil sample
|
||||
```bash
|
||||
$ gem install teamocil
|
||||
$ mkdir ~/.teamocil
|
||||
$ teamocil --edit sample
|
||||
$ tmux
|
||||
$ teamocil sample
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
* `--here` opens the session in the current window, it doesn’t create an empty first window.
|
||||
* `--layout` takes a custom file path to a YAML layout file.
|
||||
* `--edit` opens the layout file (whether or not `--layout` is used) with `$EDITOR`.
|
||||
* `--list` lists all available layouts.
|
||||
|
||||
## Layout file structure
|
||||
|
||||
@ -30,10 +35,12 @@ You can wrap your entire layout file in a `session` and Teamocil will rename the
|
||||
|
||||
#### Example
|
||||
|
||||
session:
|
||||
name: my-awesome-session
|
||||
windows:
|
||||
[windows list]
|
||||
```yaml
|
||||
session:
|
||||
name: "my-awesome-session"
|
||||
windows:
|
||||
[windows list]
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
@ -49,27 +56,27 @@ If you are not using a top-level `session` key, then the first key of your layou
|
||||
|
||||
#### Example
|
||||
|
||||
windows:
|
||||
- name: my-first-window
|
||||
options:
|
||||
synchronize-panes: true
|
||||
root: ~/Projects/foo-www
|
||||
filters:
|
||||
before:
|
||||
- "echo 'Let’s use ruby-1.9.2 for each split in this window.'"
|
||||
- "rvm use 1.9.2"
|
||||
splits:
|
||||
[splits list]
|
||||
- name: my-second-window
|
||||
root: ~/Projects/foo-api
|
||||
filters:
|
||||
after: "rvm use 1.9.2"
|
||||
splits:
|
||||
[splits list]
|
||||
- name: my-third-window
|
||||
root: ~/Projects/foo-daemons
|
||||
splits:
|
||||
[splits list]
|
||||
```yaml
|
||||
windows:
|
||||
- name: "my-first-window"
|
||||
options:
|
||||
synchronize-panes: true
|
||||
root: "~/Projects/foo-www"
|
||||
filters:
|
||||
before:
|
||||
- "echo 'Let’s use ruby-1.9.2 for each split in this window.'"
|
||||
- "rvm use 1.9.2"
|
||||
splits:
|
||||
[splits list]
|
||||
- name: "my-second-window"
|
||||
root: "~/Projects/foo-api"
|
||||
splits:
|
||||
[splits list]
|
||||
- name: "my-third-window"
|
||||
root: "~/Projects/foo-daemons"
|
||||
splits:
|
||||
[splits list]
|
||||
```
|
||||
|
||||
### Splits
|
||||
|
||||
@ -84,20 +91,22 @@ Every window must define an array of splits that will be created within it. A ve
|
||||
|
||||
#### Example
|
||||
|
||||
windows:
|
||||
- name: my-first-window
|
||||
root: ~/Projects/foo-www
|
||||
filters:
|
||||
before: "rvm use 1.9.2"
|
||||
after: "echo 'I am done initializing this split.'"
|
||||
splits:
|
||||
- cmd: "git status"
|
||||
- cmd: "bundle exec rails server --port 4000"
|
||||
width: 50
|
||||
- cmd:
|
||||
- sudo service memcached start
|
||||
- sudo service mongodb start
|
||||
height: 50
|
||||
```yaml
|
||||
windows:
|
||||
- name: "my-first-window"
|
||||
root: "~/Projects/foo-www"
|
||||
filters:
|
||||
before: "rvm use 1.9.2"
|
||||
after: "echo 'I am done initializing this split.'"
|
||||
splits:
|
||||
- cmd: "git status"
|
||||
- cmd: "bundle exec rails server --port 4000"
|
||||
width: 50
|
||||
- cmd:
|
||||
- "sudo service memcached start"
|
||||
- "sudo service mongodb start"
|
||||
height: 50
|
||||
```
|
||||
|
||||
## Layout examples
|
||||
|
||||
@ -107,15 +116,16 @@ See more example files in the `examples` directory.
|
||||
|
||||
#### Content of `~/.teamocil/sample-1.yml`
|
||||
|
||||
windows:
|
||||
- name: sample-two-splits
|
||||
root: ~/Code/sample/www
|
||||
splits:
|
||||
- cmd:
|
||||
- pwd
|
||||
- ls -la
|
||||
- cmd: rails server --port 3000
|
||||
width: 50
|
||||
```yaml
|
||||
windows:
|
||||
- name: "sample-two-splits"
|
||||
root: "~/Code/sample/www"
|
||||
splits:
|
||||
- cmd: ["pwd", "ls -la"]
|
||||
- cmd: "rails server --port 3000"
|
||||
width: 50
|
||||
```
|
||||
|
||||
|
||||
#### Result of `$ teamocil sample-1`
|
||||
|
||||
@ -135,19 +145,21 @@ See more example files in the `examples` directory.
|
||||
|
||||
#### Content of `~/.teamocil/sample-2.yml`
|
||||
|
||||
windows:
|
||||
- name: sample-four-splits
|
||||
root: ~/Code/sample/www
|
||||
splits:
|
||||
- cmd: pwd
|
||||
- cmd: pwd
|
||||
width: 50
|
||||
- cmd: pwd
|
||||
height: 50
|
||||
target: bottom-right
|
||||
- cmd: pwd
|
||||
height: 50
|
||||
target: bottom-left
|
||||
```yaml
|
||||
windows:
|
||||
- name: "sample-four-splits"
|
||||
root: "~/Code/sample/www"
|
||||
splits:
|
||||
- cmd: "pwd"
|
||||
- cmd: "pwd"
|
||||
width: 50
|
||||
- cmd: "pwd"
|
||||
height: 50
|
||||
target: "bottom-right"
|
||||
- cmd: "pwd"
|
||||
height: 50
|
||||
target: "bottom-left"
|
||||
```
|
||||
|
||||
#### Result of `$ teamocil sample-2`
|
||||
|
||||
@ -169,13 +181,14 @@ See more example files in the `examples` directory.
|
||||
|
||||
To get autocompletion when typing `teamocil <Tab>` in a zsh session, add this line to your `~/.zshrc` file:
|
||||
|
||||
compctl -g '~/.teamocil/*(:t:r)' teamocil
|
||||
```zsh
|
||||
compctl -g '~/.teamocil/*(:t:r)' teamocil
|
||||
```
|
||||
|
||||
## Todo list
|
||||
|
||||
* Support tmux options for windows and splits (eg. `synchronize-panes`)
|
||||
* Making sure the layout is valid before executing it
|
||||
* Refactor the `Layout` class to make it “test-friendly”
|
||||
* Making sure the layout is valid before executing it (ie. throw exceptions).
|
||||
* Add more specs.
|
||||
|
||||
## Contributors
|
||||
|
||||
@ -184,6 +197,8 @@ Feel free to contribute and submit issues/pull requests [on GitHub](https://gith
|
||||
* Samuel Garneau ([garno](https://github.com/garno))
|
||||
* Jimmy Bourassa ([jbourassa](https://github.com/jbourassa))
|
||||
|
||||
Take a look at the `spec` folder before you do, and make sure `bundle exec rake spec` passes after your modifications :)
|
||||
|
||||
## License
|
||||
|
||||
Teamocil is © 2011 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [LITL license](http://litl.info/). See the `LICENSE` file.
|
||||
Teamocil is © 2011-2012 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [LITL license](http://litl.info/). See the `LICENSE` file.
|
||||
|
29
Rakefile
29
Rakefile
@ -1,11 +1,28 @@
|
||||
require "bundler"
|
||||
Bundler.require(:development)
|
||||
|
||||
require "bundler/gem_tasks"
|
||||
require "rspec/core/rake_task"
|
||||
|
||||
desc "Run specs"
|
||||
task :spec do # {{{
|
||||
sh "bundle exec rspec --color --format=nested #{Dir.glob(File.join(File.dirname(__FILE__), "spec/**/*_spec.rb")).join(" ")}"
|
||||
task :default => :spec
|
||||
|
||||
desc "Run all specs"
|
||||
RSpec::Core::RakeTask.new(:spec) do |task| # {{{
|
||||
task.pattern = "spec/**/*_spec.rb"
|
||||
task.rspec_opts = "--colour --format=documentation"
|
||||
end # }}}
|
||||
|
||||
desc "Generate documentation"
|
||||
task :doc do # {{{
|
||||
sh "bundle exec yard doc"
|
||||
desc "Generate YARD Documentation"
|
||||
YARD::Rake::YardocTask.new do |task| # {{{
|
||||
task.options = [
|
||||
"-o", File.expand_path("../doc", __FILE__),
|
||||
"--readme=README.md",
|
||||
"--markup=markdown",
|
||||
"--markup-provider=maruku",
|
||||
"--no-private",
|
||||
"--no-cache",
|
||||
"--protected",
|
||||
"--title=Teamocil",
|
||||
]
|
||||
task.files = ["lib/**/*.rb"]
|
||||
end # }}}
|
||||
|
@ -1,5 +1,5 @@
|
||||
module Teamocil
|
||||
VERSION = "0.2"
|
||||
VERSION = "0.3.2"
|
||||
autoload :Layout, "teamocil/layout"
|
||||
autoload :CLI, "teamocil/cli"
|
||||
end
|
||||
|
@ -5,34 +5,44 @@ module Teamocil
|
||||
# This class handles interaction with the `tmux` utility.
|
||||
class CLI
|
||||
|
||||
attr_accessor :layout, :layouts
|
||||
|
||||
# Initialize a new run of `tmux`
|
||||
#
|
||||
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
||||
# @param env [Hash] the environment variables hash (usually `ENV`).
|
||||
def initialize(argv, env) # {{{
|
||||
bail "You must be in a tmux session to use teamocil" unless env["TMUX"]
|
||||
parse_options! argv
|
||||
layout_path = File.join("#{env["HOME"]}", ".teamocil")
|
||||
|
||||
if @options.include?(:list)
|
||||
@layouts = get_layouts(layout_path)
|
||||
return print_layouts
|
||||
end
|
||||
|
||||
parse_options!
|
||||
if @options.include?(:layout)
|
||||
file = options[:layout]
|
||||
file = @options[:layout]
|
||||
else
|
||||
file = ::File.join("#{env["HOME"]}/.teamocil", "#{argv[0]}.yml")
|
||||
file = ::File.join(layout_path, "#{argv[0]}.yml")
|
||||
end
|
||||
|
||||
if @options[:edit]
|
||||
::FileUtils.touch file unless File.exists?(file)
|
||||
system("$EDITOR \"#{file}\"")
|
||||
|
||||
Kernel.system("$EDITOR \"#{file}\"")
|
||||
else
|
||||
bail "There is no file \"#{file}\"" unless File.exists?(file)
|
||||
bail "You must be in a tmux session to use teamocil" unless env["TMUX"]
|
||||
|
||||
parsed_layout = YAML.load_file(file)
|
||||
layout = Teamocil::Layout.new(parsed_layout, @options)
|
||||
layout.compile!
|
||||
layout.execute_commands(layout.generate_commands)
|
||||
@layout = Teamocil::Layout.new(parsed_layout, @options)
|
||||
@layout.compile!
|
||||
@layout.execute_commands(@layout.generate_commands)
|
||||
end
|
||||
end # }}}
|
||||
|
||||
# Parse the command line options
|
||||
def parse_options! # {{{
|
||||
def parse_options!(args) # {{{
|
||||
@options = {}
|
||||
opts = ::OptionParser.new do |opts|
|
||||
opts.banner = "Usage: teamocil [options] <layout>
|
||||
@ -47,19 +57,36 @@ module Teamocil
|
||||
@options[:edit] = true
|
||||
end
|
||||
|
||||
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of ~/.teamocil/<layout>.yml") do |layout|
|
||||
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of `~/.teamocil/<layout>.yml`") do |layout|
|
||||
@options[:layout] = layout
|
||||
end
|
||||
|
||||
opts.on("--list", "List all available layouts in `~/.teamocil/`") do
|
||||
@options[:list] = true
|
||||
end
|
||||
|
||||
end
|
||||
opts.parse!
|
||||
opts.parse! args
|
||||
end # }}}
|
||||
|
||||
# Return an array of available layouts
|
||||
#
|
||||
# @param path [String] the path used to look for layouts
|
||||
def get_layouts(path) # {{{
|
||||
Dir.glob(File.join(path, "*.yml")).map { |file| File.basename(file).gsub(/\..+$/, "") }.sort
|
||||
end # }}}
|
||||
|
||||
# Print each layout on a single line
|
||||
def print_layouts # {{{
|
||||
STDOUT.puts @layouts.join("\n")
|
||||
exit 0
|
||||
end # }}}
|
||||
|
||||
# Print an error message and exit the utility
|
||||
#
|
||||
# @param msg [Mixed] something to print before exiting.
|
||||
def bail(msg) # {{{
|
||||
puts "[teamocil] #{msg}"
|
||||
STDERR.puts "[teamocil] #{msg}"
|
||||
exit 1
|
||||
end # }}}
|
||||
|
||||
|
@ -2,135 +2,11 @@ module Teamocil
|
||||
|
||||
# This class act as a wrapper around a tmux YAML layout file
|
||||
class Layout
|
||||
autoload :Session, "teamocil/layout/session"
|
||||
autoload :Window, "teamocil/layout/window"
|
||||
autoload :Split, "teamocil/layout/split"
|
||||
|
||||
# This class represents a session within tmux
|
||||
class Session
|
||||
attr_reader :options, :windows, :name
|
||||
|
||||
# Initialize a new tmux session
|
||||
#
|
||||
# @param options [Hash] the options, mostly passed by the CLI
|
||||
# @param attrs [Hash] the session data from the layout file
|
||||
def initialize(options, attrs={}) # {{{
|
||||
@name = attrs["name"]
|
||||
@windows = attrs["windows"].each_with_index.map { |window, index| Window.new(self, index, window) }
|
||||
@options = options
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
commands << "tmux rename-session \"#{@name}\"" unless @name.nil?
|
||||
commands << @windows.map(&:generate_commands)
|
||||
commands << "tmux select-pane -t 0"
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
# This class represents a window within tmux
|
||||
class Window
|
||||
attr_reader :filters, :root, :splits, :options, :index, :name
|
||||
|
||||
# Initialize a new tmux window
|
||||
#
|
||||
# @param session [Session] the session where the window is initialized
|
||||
# @param index [Fixnnum] the window index
|
||||
# @param attrs [Hash] the window data from the layout file
|
||||
def initialize(session, index, attrs={}) # {{{
|
||||
@name = attrs["name"]
|
||||
@root = attrs["root"]
|
||||
@options = attrs["options"]
|
||||
@filters = attrs["filters"]
|
||||
@splits = attrs["splits"].each_with_index.map { |split, index| Split.new(self, index, split) }
|
||||
@index = index
|
||||
@session = session
|
||||
|
||||
@options ||= {}
|
||||
@filters ||= {}
|
||||
@filters["before"] ||= []
|
||||
@filters["after"] ||= []
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
|
||||
if @session.options.include?(:here) and @index == 0
|
||||
commands << "tmux rename-window \"#{@name}\""
|
||||
else
|
||||
commands << "tmux new-window -n \"#{@name}\""
|
||||
end
|
||||
|
||||
commands << @splits.map(&:generate_commands)
|
||||
|
||||
@options.each_pair do |option, value|
|
||||
value = "on" if value === true
|
||||
value = "off" if value === false
|
||||
commands << "tmux set-window-option #{option} #{value}"
|
||||
end
|
||||
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
# This class represents a split within a tmux window
|
||||
class Split
|
||||
attr_reader :width, :height, :cmd, :index, :target
|
||||
|
||||
# Initialize a new tmux split
|
||||
#
|
||||
# @param session [Session] the window where the split is initialized
|
||||
# @param index [Fixnnum] the split index
|
||||
# @param attrs [Hash] the split data from the layout file
|
||||
def initialize(window, index, attrs={}) # {{{
|
||||
@height = attrs["height"]
|
||||
@width = attrs["width"]
|
||||
@cmd = attrs["cmd"]
|
||||
@target = attrs["target"]
|
||||
@window = window
|
||||
@index = index
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
|
||||
# Is it a vertical or horizontal split?
|
||||
unless @index == 0
|
||||
if !@width.nil?
|
||||
commands << "tmux split-window -h -p #{@width}"
|
||||
elsif !@height.nil?
|
||||
commands << "tmux split-window -p #{@height}"
|
||||
else
|
||||
commands << "tmux split-window"
|
||||
end
|
||||
commands << " -t #{@target}" unless @target.nil?
|
||||
end
|
||||
|
||||
# Wrap all commands around filters
|
||||
@cmd = [@window.filters["before"]] + [@cmd] + [@window.filters["after"]]
|
||||
|
||||
# If a `root` key exist, start each split in this directory
|
||||
@cmd.unshift "cd \"#{@window.root}\"" unless @window.root.nil?
|
||||
|
||||
# Execute each split command
|
||||
@cmd.flatten.compact.each do |command|
|
||||
commands << "tmux send-keys -t #{@index} \"#{command}\""
|
||||
commands << "tmux send-keys -t #{@index} Enter"
|
||||
end
|
||||
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
attr_reader :session
|
||||
|
||||
# Initialize a new layout from a hash
|
||||
#
|
||||
|
31
lib/teamocil/layout/session.rb
Normal file
31
lib/teamocil/layout/session.rb
Normal file
@ -0,0 +1,31 @@
|
||||
module Teamocil
|
||||
class Layout
|
||||
|
||||
# This class represents a session within tmux
|
||||
class Session
|
||||
attr_reader :options, :windows, :name
|
||||
|
||||
# Initialize a new tmux session
|
||||
#
|
||||
# @param options [Hash] the options, mostly passed by the CLI
|
||||
# @param attrs [Hash] the session data from the layout file
|
||||
def initialize(options, attrs={}) # {{{
|
||||
@name = attrs["name"]
|
||||
@windows = attrs["windows"].each_with_index.map { |window, window_index| Window.new(self, window_index, window) }
|
||||
@options = options
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
commands << "tmux rename-session \"#{@name}\"" unless @name.nil?
|
||||
commands << @windows.map(&:generate_commands)
|
||||
commands << "tmux select-pane -t 0"
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
end
|
||||
end
|
64
lib/teamocil/layout/split.rb
Normal file
64
lib/teamocil/layout/split.rb
Normal file
@ -0,0 +1,64 @@
|
||||
module Teamocil
|
||||
class Layout
|
||||
|
||||
# This class represents a split within a tmux window
|
||||
class Split
|
||||
attr_reader :width, :height, :cmd, :index, :target
|
||||
|
||||
# Initialize a new tmux split
|
||||
#
|
||||
# @param session [Session] the window where the split is initialized
|
||||
# @param index [Fixnnum] the split index
|
||||
# @param attrs [Hash] the split data from the layout file
|
||||
def initialize(window, index, attrs={}) # {{{
|
||||
@height = attrs["height"]
|
||||
@width = attrs["width"]
|
||||
@cmd = attrs["cmd"]
|
||||
@target = attrs["target"]
|
||||
|
||||
@window = window
|
||||
@index = index
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
|
||||
# Is it a vertical or horizontal split?
|
||||
init_command = ""
|
||||
unless @index == 0
|
||||
if !@width.nil?
|
||||
init_command = "tmux split-window -h -p #{@width}"
|
||||
elsif !@height.nil?
|
||||
init_command = "tmux split-window -p #{@height}"
|
||||
else
|
||||
init_command = "tmux split-window"
|
||||
end
|
||||
init_command << " -t #{@target}" unless @target.nil?
|
||||
commands << init_command
|
||||
end
|
||||
|
||||
# Wrap all commands around filters
|
||||
@cmd = [@window.filters["before"]] + [@cmd] + [@window.filters["after"]]
|
||||
|
||||
# If a `root` key exist, start each split in this directory
|
||||
@cmd.unshift "cd \"#{@window.root}\"" unless @window.root.nil?
|
||||
|
||||
# Set the TEAMOCIL environment variable
|
||||
@cmd.unshift "export TEAMOCIL=1"
|
||||
|
||||
# Execute each split command
|
||||
@cmd.flatten.compact.each do |command|
|
||||
commands << "tmux send-keys -t #{@index} \"#{command}\""
|
||||
commands << "tmux send-keys -t #{@index} Enter"
|
||||
end
|
||||
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
55
lib/teamocil/layout/window.rb
Normal file
55
lib/teamocil/layout/window.rb
Normal file
@ -0,0 +1,55 @@
|
||||
module Teamocil
|
||||
class Layout
|
||||
|
||||
# This class represents a window within tmux
|
||||
class Window
|
||||
attr_reader :filters, :root, :splits, :options, :index, :name
|
||||
|
||||
# Initialize a new tmux window
|
||||
#
|
||||
# @param session [Session] the session where the window is initialized
|
||||
# @param index [Fixnnum] the window index
|
||||
# @param attrs [Hash] the window data from the layout file
|
||||
def initialize(session, index, attrs={}) # {{{
|
||||
@name = attrs["name"]
|
||||
@root = attrs["root"]
|
||||
@options = attrs["options"] || {}
|
||||
|
||||
@splits = attrs["splits"] || []
|
||||
@splits = @splits.each_with_index.map { |split, split_index| Split.new(self, split_index, split) }
|
||||
|
||||
@filters = attrs["filters"] || {}
|
||||
@filters["before"] ||= []
|
||||
@filters["after"] ||= []
|
||||
|
||||
@index = index
|
||||
@session = session
|
||||
end # }}}
|
||||
|
||||
# Generate commands to send to tmux
|
||||
#
|
||||
# @return [Array]
|
||||
def generate_commands # {{{
|
||||
commands = []
|
||||
|
||||
if @session.options.include?(:here) and @index == 0
|
||||
commands << "tmux rename-window \"#{@name}\""
|
||||
else
|
||||
commands << "tmux new-window -n \"#{@name}\""
|
||||
end
|
||||
|
||||
commands << @splits.map(&:generate_commands)
|
||||
|
||||
@options.each_pair do |option, value|
|
||||
value = "on" if value === true
|
||||
value = "off" if value === false
|
||||
commands << "tmux set-window-option #{option} #{value}"
|
||||
end
|
||||
|
||||
commands
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
43
spec/cli_spec.rb
Normal file
43
spec/cli_spec.rb
Normal file
@ -0,0 +1,43 @@
|
||||
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
||||
|
||||
describe Teamocil::CLI do
|
||||
|
||||
context "executing" do
|
||||
|
||||
context "not in tmux" do
|
||||
|
||||
before do # {{{
|
||||
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures") }
|
||||
end # }}}
|
||||
|
||||
it "should allow editing" do # {{{
|
||||
FileUtils.stub(:touch)
|
||||
Kernel.should_receive(:system).with(any_args())
|
||||
|
||||
@cli = Teamocil::CLI.new(["--edit", "my-layout"], @fake_env)
|
||||
end # }}}
|
||||
end
|
||||
|
||||
context "in tmux" do
|
||||
|
||||
before do # {{{
|
||||
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures") }
|
||||
end # }}}
|
||||
|
||||
it "creates a layout" do # {{{
|
||||
@cli = Teamocil::CLI.new(["sample"], @fake_env)
|
||||
@cli.layout.session.name.should == "sample"
|
||||
@cli.layout.session.windows.length.should == 2
|
||||
@cli.layout.session.windows.first.name.should == "foo"
|
||||
@cli.layout.session.windows.last.name.should == "bar"
|
||||
end # }}}
|
||||
|
||||
it "lists available layouts" do # {{{
|
||||
@cli = Teamocil::CLI.new(["--list"], @fake_env)
|
||||
@cli.layouts.should == ["sample", "sample-2"]
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
10
spec/fixtures/.teamocil/sample-2.yml
vendored
Normal file
10
spec/fixtures/.teamocil/sample-2.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
session:
|
||||
name: sample-2
|
||||
root: ~
|
||||
windows:
|
||||
- name: "foo"
|
||||
splits:
|
||||
- cmd: "pwd"
|
||||
- name: "bar"
|
||||
splits:
|
||||
- cmd: "pwd"
|
10
spec/fixtures/.teamocil/sample.yml
vendored
Normal file
10
spec/fixtures/.teamocil/sample.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
session:
|
||||
name: sample
|
||||
root: ~
|
||||
windows:
|
||||
- name: "foo"
|
||||
splits:
|
||||
- cmd: "pwd"
|
||||
- name: "bar"
|
||||
splits:
|
||||
- cmd: "pwd"
|
9
spec/fixtures/layouts.yml
vendored
9
spec/fixtures/layouts.yml
vendored
@ -13,6 +13,7 @@ two-windows:
|
||||
- cmd:
|
||||
- "echo 'bar'"
|
||||
- "echo 'bar in an array'"
|
||||
target: bottom-right
|
||||
- cmd: "echo 'bar again'"
|
||||
width: 50
|
||||
|
||||
@ -32,3 +33,11 @@ two-windows-with-filters:
|
||||
- cmd: "echo 'foo'"
|
||||
- cmd: "echo 'foo again'"
|
||||
width: 50
|
||||
|
||||
three-windows-within-a-session:
|
||||
session:
|
||||
name: "my awesome session"
|
||||
windows:
|
||||
- name: "first window"
|
||||
- name: "second window"
|
||||
- name: "third window"
|
||||
|
@ -2,15 +2,19 @@ require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
||||
|
||||
describe Teamocil::Layout do
|
||||
|
||||
context "initializing" do
|
||||
end
|
||||
|
||||
context "compiling" do
|
||||
|
||||
before :each do # {{{
|
||||
before do # {{{
|
||||
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
||||
end # }}}
|
||||
|
||||
it "creates windows" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.each do |window|
|
||||
window.should be_an_instance_of Teamocil::Layout::Window
|
||||
end
|
||||
end # }}}
|
||||
|
||||
it "creates windows with names" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows[0].name.should == "foo"
|
||||
@ -23,6 +27,13 @@ describe Teamocil::Layout do
|
||||
session.windows[1].root.should == "/bar"
|
||||
end # }}}
|
||||
|
||||
it "creates splits" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.first.splits.each do |split|
|
||||
split.should be_an_instance_of Teamocil::Layout::Split
|
||||
end
|
||||
end # }}}
|
||||
|
||||
it "creates splits with dimensions" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.first.splits[0].width.should == nil
|
||||
@ -57,5 +68,31 @@ describe Teamocil::Layout do
|
||||
session.windows.first.filters["after"].last.should == "echo second after filter"
|
||||
end # }}}
|
||||
|
||||
it "should handle blank filters" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.first.filters.should have_key "after"
|
||||
session.windows.first.filters.should have_key "before"
|
||||
session.windows.first.filters["after"].should be_empty
|
||||
session.windows.first.filters["before"].should be_empty
|
||||
end # }}}
|
||||
|
||||
it "should handle splits without a target" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.last.splits.last.target.should == nil
|
||||
end # }}}
|
||||
|
||||
it "should handle splits with a target" do # {{{
|
||||
session = @layout.compile!
|
||||
session.windows.last.splits.first.target.should == "bottom-right"
|
||||
end # }}}
|
||||
|
||||
it "should handle windows within a session" do # {{{
|
||||
layout = Teamocil::Layout.new(layouts["three-windows-within-a-session"], {})
|
||||
session = layout.compile!
|
||||
session.windows.length.should == 3
|
||||
session.name.should == "my awesome session"
|
||||
end # }}}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
20
spec/mock/cli.rb
Normal file
20
spec/mock/cli.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Teamocil
|
||||
module Mock
|
||||
module CLI
|
||||
|
||||
def self.included(base) # {{{
|
||||
base.class_eval do
|
||||
|
||||
# Do not print anything
|
||||
def print_layouts
|
||||
# Nothing
|
||||
end
|
||||
|
||||
end
|
||||
end # }}}
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Teamocil::CLI.send :include, Teamocil::Mock::CLI
|
20
spec/mock/layout.rb
Normal file
20
spec/mock/layout.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Teamocil
|
||||
module Mock
|
||||
module Layout
|
||||
|
||||
def self.included(base) # {{{
|
||||
base.class_eval do
|
||||
|
||||
# Do not execute anything
|
||||
def execute_commands(commands)
|
||||
# Nothing
|
||||
end
|
||||
|
||||
end
|
||||
end # }}}
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Teamocil::Layout.send :include, Teamocil::Mock::Layout
|
@ -2,6 +2,8 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
||||
|
||||
require 'yaml'
|
||||
require 'teamocil'
|
||||
require File.join(File.dirname(__FILE__), "./mock/layout.rb")
|
||||
require File.join(File.dirname(__FILE__), "./mock/cli.rb")
|
||||
|
||||
module Helpers
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# encoding: utf-8
|
||||
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "teamocil"
|
||||
|
||||
@ -24,5 +25,4 @@ spec = Gem::Specification.new do |s|
|
||||
s.add_development_dependency "rspec"
|
||||
s.add_development_dependency "yard"
|
||||
s.add_development_dependency "maruku"
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user