automux 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +20 -0
  4. data/LICENSE.txt +22 -0
  5. data/VERSION +1 -0
  6. data/automux.gemspec +18 -0
  7. data/bin/automux +22 -0
  8. data/data/automux/blueprints/default.yml +26 -0
  9. data/data/automux/recipes/default.sh.erb +46 -0
  10. data/features/custom_recipes.feature +63 -0
  11. data/features/error_messages.feature +8 -0
  12. data/features/general_usage.feature +180 -0
  13. data/features/managing_blueprints.feature +66 -0
  14. data/features/runtime_options.feature +84 -0
  15. data/features/session_and_window_options.feature +67 -0
  16. data/features/session_hooks.feature +68 -0
  17. data/features/setup_windows.feature +22 -0
  18. data/features/step_definitions/custom_recipes_steps.rb +9 -0
  19. data/features/step_definitions/managing_blueprints_steps.rb +9 -0
  20. data/features/step_definitions/setup_windows_steps.rb +18 -0
  21. data/features/step_definitions/shared_steps.rb +43 -0
  22. data/features/support/env.rb +8 -0
  23. data/features/support/hooks.rb +4 -0
  24. data/features/support/transformations.rb +6 -0
  25. data/lib/automux.rb +9 -0
  26. data/lib/automux/cache.rb +10 -0
  27. data/lib/automux/cache/blueprint.rb +20 -0
  28. data/lib/automux/cache/recipe.rb +27 -0
  29. data/lib/automux/controller.rb +14 -0
  30. data/lib/automux/controller/base.rb +18 -0
  31. data/lib/automux/controller/blueprints.rb +45 -0
  32. data/lib/automux/controller/messages.rb +11 -0
  33. data/lib/automux/controller/recipes.rb +39 -0
  34. data/lib/automux/controller/setup.rb +11 -0
  35. data/lib/automux/controller/support.rb +5 -0
  36. data/lib/automux/controller/support/filters.rb +55 -0
  37. data/lib/automux/controller/support/rendering.rb +54 -0
  38. data/lib/automux/core.rb +11 -0
  39. data/lib/automux/core/base.rb +7 -0
  40. data/lib/automux/core/blueprint.rb +31 -0
  41. data/lib/automux/core/error.rb +15 -0
  42. data/lib/automux/core/hook.rb +26 -0
  43. data/lib/automux/core/option.rb +20 -0
  44. data/lib/automux/core/recipe.rb +12 -0
  45. data/lib/automux/core/support.rb +6 -0
  46. data/lib/automux/core/support/custom_accessors.rb +15 -0
  47. data/lib/automux/core/support/hooks_helper.rb +28 -0
  48. data/lib/automux/core/support/options_helper.rb +18 -0
  49. data/lib/automux/core/tmux.rb +6 -0
  50. data/lib/automux/core/tmux/pane.rb +19 -0
  51. data/lib/automux/core/tmux/session.rb +137 -0
  52. data/lib/automux/core/tmux/window.rb +76 -0
  53. data/lib/automux/initializers.rb +2 -0
  54. data/lib/automux/initializers/custom_hooks.rb +4 -0
  55. data/lib/automux/initializers/setup_caches.rb +3 -0
  56. data/lib/automux/library.rb +5 -0
  57. data/lib/automux/library/mini_erb.rb +22 -0
  58. data/lib/automux/library/yaml_parser.rb +41 -0
  59. data/lib/automux/paths.rb +41 -0
  60. data/lib/automux/setup.rb +0 -0
  61. data/lib/automux/views/blueprints/copy.sh.erb +2 -0
  62. data/lib/automux/views/blueprints/create.sh.erb +2 -0
  63. data/lib/automux/views/blueprints/edit.sh.erb +1 -0
  64. data/lib/automux/views/messages/error.sh.erb +3 -0
  65. data/lib/automux/views/setup/clone_defaults.sh.erb +21 -0
  66. data/test/session_test.rb +16 -0
  67. data/test/support/common_methods.rb +0 -0
  68. data/test/support/factories.rb +13 -0
  69. data/test/support/hash_factory.rb +19 -0
  70. data/test/support/test_helper.rb +7 -0
  71. metadata +136 -0
@@ -0,0 +1,19 @@
1
+ module Automux
2
+ module Core
3
+ module Tmux
4
+ class Pane < Base
5
+ attr_reader :command, :window
6
+ private :window
7
+
8
+ def initialize(window, command)
9
+ @window = window
10
+ @command = command
11
+ end
12
+
13
+ def index
14
+ window.panes.find_index(self)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,137 @@
1
+ module Automux
2
+ module Core
3
+ module Tmux
4
+ class Session < Base
5
+ include Support::HooksHelper
6
+ include Support::OptionsHelper
7
+
8
+ attr_reader :data, :name, :root, :data_windows, :flags, :base_index
9
+ dup_attr_reader :windows, :hooks, :options
10
+ private :data, :data_windows
11
+
12
+ def initialize(blueprint_data)
13
+ @data = blueprint_data
14
+ @name = data['name']
15
+ @root = data['root'] || '.'
16
+ @data_windows = data['windows'] || []
17
+ @data_hooks = data['hooks'] || []
18
+ @windows = []
19
+ @flags = data['flags']
20
+ @hooks = []
21
+ @data_options = data['options'] || []
22
+ @options = []
23
+ end
24
+
25
+ def start_server
26
+ %[tmux start-server]
27
+ end
28
+
29
+ def new_session
30
+ %[#{ tmux_with_flags } new-session -d -s #{ name }]
31
+ end
32
+
33
+ def set_option(option)
34
+ %[tmux set-option #{ option.name } '#{ option.value }']
35
+ end
36
+
37
+ def new_window(window)
38
+ %[tmux new-window -t #{ name }:#{ window.index } 2> /dev/null]
39
+ end
40
+
41
+ def rename_window(window, window_name = window.name)
42
+ %[tmux rename-window -t #{ name }:#{ window.index } #{ window_name }]
43
+ end
44
+
45
+ def send_keys(identifier, command)
46
+ window = get_window(identifier)
47
+ %[tmux send-keys -t #{ name }:#{ window.index } "#{ command }" C-m]
48
+ end
49
+
50
+ def attach_session
51
+ %[#{ tmux_with_flags } attach-session -t #{ name }]
52
+ end
53
+
54
+ def select_layout(window)
55
+ %[tmux select-layout -t #{ name }:#{ window.index } #{ window.layout }]
56
+ end
57
+
58
+ def create_pane
59
+ %[tmux split-window]
60
+ end
61
+
62
+ def get_binding
63
+ binding
64
+ end
65
+
66
+ def setup
67
+ setup_options
68
+ setup_base_index
69
+ setup_windows
70
+ setup_hooks
71
+ end
72
+
73
+ def window_indexes
74
+ @windows.map(&:index).compact
75
+ end
76
+
77
+ def next_available_window_index
78
+ n = base_index + number_of_windows
79
+ (Array(base_index..n) - window_indexes).first
80
+ end
81
+
82
+ private ###
83
+
84
+ def tmux_with_flags
85
+ %[tmux #{ flags }].strip
86
+ end
87
+
88
+ def number_of_windows
89
+ @windows.length
90
+ end
91
+
92
+ def add_window(window)
93
+ @windows << window
94
+ end
95
+
96
+ # When multiple windows have been assigned the same index by the user, the additional window indexes will be removed.
97
+ def remove_duplicate_indexes(original_data)
98
+ non_indexed_data = original_data.select { |h| h['index'].to_s.empty? }
99
+ indexed_data = original_data - non_indexed_data
100
+
101
+ uniq_indexed_data = indexed_data.uniq { |h| h['index'] }
102
+ conflicting_data = indexed_data - uniq_indexed_data
103
+ removed_index_data = conflicting_data.each { |h| h.delete('index') }
104
+
105
+ non_indexed_data + uniq_indexed_data + removed_index_data
106
+ end
107
+
108
+ def add_windows(windows_data)
109
+ windows_data.each do |window_data|
110
+ window = Automux::Core::Tmux::Window.new(self, window_data)
111
+ add_window(window) if window.opted_in?
112
+ end
113
+ end
114
+
115
+ def setup_windows
116
+ windows_data = remove_duplicate_indexes(data_windows)
117
+ add_windows(windows_data)
118
+ @windows.each(&:update_index)
119
+ @windows.each(&:setup)
120
+ end
121
+
122
+ def setup_base_index
123
+ @base_index = 0
124
+ if option = options.find { |option| option.name == 'base-index' }
125
+ @base_index = option.value.to_i
126
+ end
127
+ end
128
+
129
+ def get_window(identifier)
130
+ return identifier if identifier.is_a?(Window)
131
+
132
+ @windows.find { |window| [window.index, window.name].include?(identifier) }
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,76 @@
1
+ module Automux
2
+ module Core
3
+ module Tmux
4
+ class Window < Base
5
+ include Support::HooksHelper
6
+ include Support::OptionsHelper
7
+
8
+ attr_reader :data, :session, :index, :root
9
+ dup_attr_reader :panes, :hooks, :options
10
+ private :data, :session
11
+
12
+ def initialize(session, data)
13
+ @session = session
14
+ @data = data
15
+ @opt = data['opt']
16
+ @index = data['index']
17
+ @root = data['root']
18
+ @data_hooks = data['hooks'] || []
19
+ @hooks = []
20
+ @data_options = data['options'] || []
21
+ @options = []
22
+ @panes = []
23
+ end
24
+
25
+ def set_option(option)
26
+ %[tmux set-window-option -t #{ session.name }:#{ index } #{ option.name } '#{ option.value }']
27
+ end
28
+
29
+ def name
30
+ data['name']
31
+ end
32
+
33
+ def setup
34
+ setup_options
35
+ setup_panes
36
+ setup_hooks
37
+ end
38
+
39
+ def has_panes?
40
+ !@panes.nil?
41
+ end
42
+
43
+ def layout
44
+ data['layout']
45
+ end
46
+
47
+ def update_index
48
+ @index ||= session.next_available_window_index
49
+ end
50
+
51
+ def opted_in?
52
+ return true if @opt.nil?
53
+
54
+ @opt
55
+ end
56
+
57
+ def change_root_command
58
+ %[cd #{ root }]
59
+ end
60
+
61
+ def get_binding
62
+ binding
63
+ end
64
+
65
+ private ###
66
+
67
+ def setup_panes
68
+ [data['panes']].flatten.each do |command|
69
+ pane = Automux::Core::Tmux::Pane.new(self, command)
70
+ @panes << pane
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,2 @@
1
+ require 'automux/initializers/custom_hooks'
2
+ require 'automux/initializers/setup_caches'
@@ -0,0 +1,4 @@
1
+ # Call after_inherited hooks for all controller classes inheriting from Base.
2
+ Automux::Controller.base_inheriting_classes.each do |class_name|
3
+ Automux::Controller::Base.after_inherited(Automux::Controller.const_get(class_name))
4
+ end
@@ -0,0 +1,3 @@
1
+ Automux::Cache.module_constants.each do |constant_name|
2
+ Automux::Cache.const_get(constant_name).setup
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'automux/library/mini_erb'
2
+ require 'automux/library/yaml_parser'
3
+
4
+ module Automux::Library
5
+ end
@@ -0,0 +1,22 @@
1
+ require 'erb'
2
+
3
+ module Automux
4
+ module Library
5
+ class MiniErb < ERB
6
+
7
+ def initialize(template)
8
+ modified_template = erb_parseable_template(template)
9
+ super(modified_template, nil, '%<>')
10
+ end
11
+
12
+ private ###
13
+
14
+ # - [1, 2, 3] => % [1, 2, 3]
15
+ # = [1, 2, 3] => <%= [1, 2, 3] %>
16
+ def erb_parseable_template(string)
17
+ string.gsub(/^\s*-(.+)$/, '%\1').
18
+ gsub(/^\s*=(.+)$/, '<%=\1 %> ')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ require 'yaml'
2
+ require 'optparse'
3
+
4
+ module Automux
5
+ module Library
6
+ class YamlParser
7
+ class << self
8
+ def load_file(file)
9
+ data_string = File.read(file)
10
+ opts_replaced_string = replace_opts_with_user_input(data_string)
11
+ YAML.load(opts_replaced_string)
12
+ end
13
+
14
+ private ###
15
+
16
+ # Scan blueprint for patterns like "-\w" or '-\w' to get a options list.
17
+ # The options list is used by OptionsParser to read the options from commandline.
18
+ def replace_opts_with_user_input(string)
19
+ blueprint_opts = string.scan(/['|"]-(\w:?)['|"]/m).flatten
20
+ user_input_values = get_options(blueprint_opts)
21
+ replace_opts(string, user_input_values)
22
+ end
23
+
24
+ # Replace patterns like "-\w" with matching commandline option.
25
+ def replace_opts(string, values)
26
+ values.each do |k, v|
27
+ # Interpolate #{ v } - to substitute literal booleans.
28
+ string.gsub!(/['|"]-(#{ k }):?['|"]/m, "#{ v }")
29
+ end
30
+
31
+ string
32
+ end
33
+
34
+ def get_options(opts)
35
+ ARGV.shift while ARGV[0].to_s.match(/^\w/)
36
+ ARGV.getopts(*opts)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ module Automux
2
+ module Paths
3
+ extend self
4
+
5
+ def default_blueprint
6
+ File.join(data, 'blueprints', 'default.yml')
7
+ end
8
+
9
+ def default_recipe
10
+ File.join(data, 'recipes', 'default.sh.erb')
11
+ end
12
+
13
+ def data
14
+ File.join(root, 'data/automux')
15
+ end
16
+
17
+ def root
18
+ File.expand_path('../../../', __FILE__)
19
+ end
20
+
21
+ def views
22
+ 'lib/automux/views'
23
+ end
24
+
25
+ def user_assets
26
+ File.join(ENV['HOME'], '.automux')
27
+ end
28
+
29
+ %w(blueprints recipes).each do |name|
30
+ define_method(name) do
31
+ Dir[File.join(user_assets, name, '*')]
32
+ end
33
+ end
34
+
35
+ %w(blueprints recipes).each do |name|
36
+ define_method("#{ name }_container") do
37
+ File.join(user_assets, name)
38
+ end
39
+ end
40
+ end
41
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ cp <%= source_blueprint.path %> <%= path %>
2
+ echo <%= File.basename(source_blueprint.path) %> copied
@@ -0,0 +1,2 @@
1
+ cp <%= source_blueprint.path %> <%= path %>
2
+ $EDITOR <%= path %>
@@ -0,0 +1 @@
1
+ $EDITOR <%= path %>
@@ -0,0 +1,3 @@
1
+ printf "\033[0;31m"
2
+ echo <%= message %>
3
+ printf "\033[0m"
@@ -0,0 +1,21 @@
1
+ setup() {
2
+ mkdir -p <%= Automux::Paths.blueprints_container %>
3
+ mkdir -p <%= Automux::Paths.recipes_container %>
4
+
5
+ cp <%= Automux::Paths.default_blueprint %> <%= Automux::Paths.blueprints_container %>
6
+ cp <%= Automux::Paths.default_recipe %> <%= Automux::Paths.recipes_container %>
7
+
8
+ echo default files copied
9
+ }
10
+
11
+ if [ -d <%= Automux::Paths.blueprints_container %> ]; then
12
+ read -r -p "This will replace the default blueprint and recipe under ~/.automux. Continue? [Y/n] " response
13
+
14
+ case $response in
15
+ [yY][eE][sS]|[yY])
16
+ setup
17
+ ;;
18
+ esac
19
+ else
20
+ setup
21
+ fi
@@ -0,0 +1,16 @@
1
+ require 'support/test_helper'
2
+
3
+ describe 'Session' do
4
+ it "should assign window indexes" do
5
+
6
+ windows_data = [nil, nil, 1, 0].map do |i|
7
+ HashFactory.build(:window, index: i)
8
+ end
9
+ data = HashFactory.build(:session, windows: windows_data)
10
+
11
+ session = Automux::Core::Tmux::Session.new(data)
12
+ session.setup_windows_and_hooks
13
+
14
+ session.window_indexes.must_equal [2, 3, 1, 0]
15
+ end
16
+ end
File without changes
@@ -0,0 +1,13 @@
1
+ HashFactory.define :session do
2
+ {
3
+ project_name: :test,
4
+ project_root: '~/',
5
+ }
6
+ end
7
+
8
+ HashFactory.define :window do
9
+ {
10
+ name: "name",
11
+ panes: ['ls']
12
+ }
13
+ end
@@ -0,0 +1,19 @@
1
+ class HashFactory
2
+ @@factories = {}
3
+
4
+ class << self
5
+ def define(name, &block)
6
+ @@factories[name.to_s] = block
7
+ end
8
+
9
+ def build(name, attrs = {})
10
+ defaults = stringify(@@factories[name.to_s].call)
11
+ attributes = stringify(attrs)
12
+ defaults.merge(attributes)
13
+ end
14
+
15
+ def stringify(data)
16
+ Hash[data.map { |k, v| [k.to_s, v] }]
17
+ end
18
+ end
19
+ end