automux 0.0.1

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