terminitor 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +41 -0
- data/README.md +120 -13
- data/Termfile +11 -11
- data/lib/templates/example.term.tt +17 -0
- data/lib/terminitor.rb +13 -69
- data/lib/terminitor/abstract_core.rb +68 -0
- data/lib/terminitor/cli.rb +77 -0
- data/lib/terminitor/cores/konsole_core.rb +54 -0
- data/lib/terminitor/cores/mac_core.rb +79 -0
- data/lib/terminitor/dsl.rb +81 -0
- data/lib/terminitor/runner.rb +76 -40
- data/lib/terminitor/version.rb +1 -1
- data/lib/terminitor/yaml.rb +20 -0
- data/templates/example.yml.tt +16 -0
- data/terminitor.gemspec +11 -4
- data/test.watchr +70 -0
- data/test/abstract_core_test.rb +79 -0
- data/test/cli_test.rb +121 -0
- data/test/cores/konsole_core_test.rb +56 -0
- data/test/cores/mac_core_test.rb +84 -0
- data/test/dsl_test.rb +15 -0
- data/test/fixtures/bar.term +16 -0
- data/test/fixtures/foo.term +21 -0
- data/test/fixtures/foo.yml +1 -0
- data/test/runner_test.rb +234 -0
- data/test/teststrap.rb +6 -37
- data/test/yaml_test.rb +13 -0
- metadata +49 -17
- data/test/terminitor_test.rb +0 -152
@@ -0,0 +1,54 @@
|
|
1
|
+
module Terminitor
|
2
|
+
# KDE Konsole Core for Terminitor
|
3
|
+
# This Core manages all the interaction with Konsole's dbus interface
|
4
|
+
class KonsoleCore < AbstractCore
|
5
|
+
def initialize(path)
|
6
|
+
super
|
7
|
+
bus = DBus::SessionBus.instance
|
8
|
+
@konsole_service = bus.service("org.kde.konsole")
|
9
|
+
@konsole = get_konsole
|
10
|
+
end
|
11
|
+
|
12
|
+
# Executes the Command
|
13
|
+
# execute_command 'cd /path/to', {}
|
14
|
+
def execute_command(cmd, options = {})
|
15
|
+
# add carriange return if missing, otherwise the command won't be executed
|
16
|
+
cmd += "\n" if (cmd =~ /\n\Z/).nil?
|
17
|
+
options[:in].sendText(cmd)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Opens a new tab and returns itself.
|
21
|
+
def open_tab
|
22
|
+
session_number = @konsole.newSession
|
23
|
+
session_object = @konsole_service.object("/Sessions/#{session_number}")
|
24
|
+
session_object.introspect
|
25
|
+
session_object["org.kde.konsole.Session"]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Opens a new window and returns the tab object.
|
29
|
+
def open_window
|
30
|
+
session_number = @konsole.currentSession
|
31
|
+
session_object = @konsole_service.object("/Sessions/#{session_number}")
|
32
|
+
session_object.introspect
|
33
|
+
session_object["org.kde.konsole.Session"]
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def get_konsole
|
39
|
+
begin
|
40
|
+
konsole_object = @konsole_service.object("/Konsole")
|
41
|
+
konsole_object.introspect
|
42
|
+
return konsole_object["org.kde.konsole.Konsole"]
|
43
|
+
rescue DBus::Error => e
|
44
|
+
if e.dbus_message.error_name =="org.freedesktop.DBus.Error.ServiceUnknown"
|
45
|
+
system "konsole"
|
46
|
+
sleep(2)
|
47
|
+
retry
|
48
|
+
else
|
49
|
+
raise e
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Terminitor
|
2
|
+
# Mac OS X Core for Terminitor
|
3
|
+
# This Core manages all the interaction with Appscript and the Terminal
|
4
|
+
class MacCore < AbstractCore
|
5
|
+
include Appscript
|
6
|
+
|
7
|
+
# Initialize @terminal with Terminal.app, Load the Windows, store the Termfile
|
8
|
+
# Terminitor::MacCore.new('/path')
|
9
|
+
def initialize(path)
|
10
|
+
super
|
11
|
+
@terminal = app('Terminal')
|
12
|
+
@windows = @terminal.windows
|
13
|
+
end
|
14
|
+
|
15
|
+
# executes the given command via appscript
|
16
|
+
# execute_command 'cd /path/to', :in => #<tab>
|
17
|
+
def execute_command(cmd, options = {})
|
18
|
+
active_window.do_script(cmd, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Opens a new tab and returns itself.
|
22
|
+
def open_tab
|
23
|
+
super
|
24
|
+
terminal_process.keystroke("t", :using => :command_down)
|
25
|
+
return_last_tab
|
26
|
+
end
|
27
|
+
|
28
|
+
# Opens A New Window and returns the tab object.
|
29
|
+
def open_window
|
30
|
+
terminal_process.keystroke("n", :using => :command_down)
|
31
|
+
return_last_tab
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the Terminal Process
|
35
|
+
# We need this method to workaround appscript so that we can instantiate new tabs and windows.
|
36
|
+
# otherwise it would have looked something like window.make(:new => :tab) but that doesn't work.
|
37
|
+
def terminal_process
|
38
|
+
app("System Events").application_processes["Terminal.app"]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the last instantiated tab from active window
|
42
|
+
def return_last_tab
|
43
|
+
local_window = active_window
|
44
|
+
local_tabs = local_window.tabs if local_window
|
45
|
+
local_tabs.last.get if local_tabs
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns the active window by checking if its the :frontmost
|
49
|
+
def active_window
|
50
|
+
windows = @terminal.windows.get
|
51
|
+
windows.detect do |window|
|
52
|
+
window.properties_.get[:frontmost] rescue false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# These methods are here for reference so I can ponder later
|
59
|
+
# how I could possibly use them.
|
60
|
+
# And Currently aren't tested. =(
|
61
|
+
|
62
|
+
# returns a window by the id
|
63
|
+
def window_by_id(id)
|
64
|
+
@windows.ID(id)
|
65
|
+
end
|
66
|
+
|
67
|
+
# grabs the window id.
|
68
|
+
def window_id(window)
|
69
|
+
window.id_.get
|
70
|
+
end
|
71
|
+
|
72
|
+
# set_window_title #<Window>, "hi"
|
73
|
+
# Note: This sets all the windows to the same title.
|
74
|
+
def set_window_title(window, title)
|
75
|
+
window.custom_title.set(title)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Terminitor
|
2
|
+
# This class parses the Termfile to fit the new Ruby Dsl Syntax
|
3
|
+
class Dsl
|
4
|
+
|
5
|
+
def initialize(path)
|
6
|
+
file = File.read(path)
|
7
|
+
@setup = []
|
8
|
+
@windows = { 'default' => {}}
|
9
|
+
@_context = @windows['default']
|
10
|
+
instance_eval(file)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Contains all commands that will be run prior to the usual 'workflow'
|
14
|
+
# e.g bundle install, setup forks, etc ...
|
15
|
+
# setup "bundle install", "brew update"
|
16
|
+
# setup { run('bundle install') }
|
17
|
+
def setup(*commands, &block)
|
18
|
+
setup_tasks = @setup
|
19
|
+
if block_given?
|
20
|
+
@_context, @_old_context = setup_tasks, @_context
|
21
|
+
instance_eval(&block)
|
22
|
+
@_context = @_old_context
|
23
|
+
else
|
24
|
+
setup_tasks.concat(commands)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# sets command context to be run inside a specific window
|
29
|
+
# window('new window') { tab('ls','gitx') }
|
30
|
+
def window(name = nil, &block)
|
31
|
+
window_tabs = @windows[name || "window#{@windows.keys.size}"] = {}
|
32
|
+
@_context, @_old_context = window_tabs, @_context
|
33
|
+
instance_eval(&block)
|
34
|
+
@_context = @_old_context
|
35
|
+
end
|
36
|
+
|
37
|
+
# stores command in context
|
38
|
+
# run 'brew update'
|
39
|
+
def run(command)
|
40
|
+
@_context << command
|
41
|
+
end
|
42
|
+
|
43
|
+
# sets command context to be run inside specific tab
|
44
|
+
# tab('new tab') { run 'mate .' }
|
45
|
+
# tab 'ls', 'gitx'
|
46
|
+
def tab(name= nil, *commands, &block)
|
47
|
+
if block_given?
|
48
|
+
tab_tasks = @_context[name || "tab#{@_context.keys.size}"] = []
|
49
|
+
@_context, @_old_context = tab_tasks, @_context
|
50
|
+
instance_eval(&block)
|
51
|
+
@_context = @_old_context
|
52
|
+
else
|
53
|
+
tab_tasks = @_context["tab#{@_context.keys.size}"] = []
|
54
|
+
tab_tasks.concat([name] + commands)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns yaml file as Terminitor formmatted hash
|
59
|
+
def to_hash
|
60
|
+
{ :setup => @setup, :windows => @windows }
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
#
|
67
|
+
# in_context @setup, commands, &block
|
68
|
+
# in_context @tabs["name"], commands, &block
|
69
|
+
def in_context(tasks_instance,*commands, &block)
|
70
|
+
if block_given?
|
71
|
+
@_context, @_old_context = instance_variable_get(name), @_context
|
72
|
+
instance_eval(&block)
|
73
|
+
@_context = @_old_context
|
74
|
+
else
|
75
|
+
@setup << commands
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/terminitor/runner.rb
CHANGED
@@ -1,67 +1,103 @@
|
|
1
1
|
module Terminitor
|
2
|
+
# This module contains all the helper methods for the Cli component.
|
2
3
|
module Runner
|
3
4
|
|
5
|
+
# Finds the appropriate platform core, else say you don't got it.
|
6
|
+
# find_core RUBY_PLATFORM
|
7
|
+
def find_core(platform)
|
8
|
+
core = case platform.downcase
|
9
|
+
when %r{darwin} then Terminitor::MacCore
|
10
|
+
when %r{linux} then Terminitor::KonsoleCore # TODO check for gnome and others
|
11
|
+
else nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Execute the core with the given method.
|
16
|
+
# execute_core :process!, 'project'
|
17
|
+
# execute_core :setup!, 'my_project'
|
18
|
+
def execute_core(method, project)
|
19
|
+
if path = resolve_path(project)
|
20
|
+
core = find_core(RUBY_PLATFORM)
|
21
|
+
core ? core.new(path).send(method) : say("No suitable core found!")
|
22
|
+
else
|
23
|
+
return_error_message(project)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
4
27
|
# opens doc in system designated editor
|
28
|
+
# open_in_editor '/path/to', 'nano'
|
5
29
|
def open_in_editor(path, editor=nil)
|
6
30
|
editor = editor || ENV['TERM_EDITOR'] || ENV['EDITOR']
|
7
31
|
say "please set $EDITOR or $TERM_EDITOR in your .bash_profile." unless editor
|
8
32
|
system("#{editor || 'open'} #{path}")
|
9
33
|
end
|
10
34
|
|
11
|
-
|
12
|
-
|
13
|
-
|
35
|
+
# returns path to file
|
36
|
+
# resolve_path 'my_project'
|
37
|
+
def resolve_path(project)
|
38
|
+
unless project.empty?
|
39
|
+
path = config_path(project, :yml) # Give old yml path
|
40
|
+
return path if File.exists?(path)
|
41
|
+
path = config_path(project, :term) # Give new term path.
|
42
|
+
return path if File.exists?(path)
|
43
|
+
nil
|
44
|
+
else
|
45
|
+
path = File.join(options[:root],"Termfile")
|
46
|
+
return path if File.exists?(path)
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
14
50
|
|
15
|
-
|
16
|
-
|
17
|
-
|
51
|
+
# returns first line of file
|
52
|
+
# grab_comment_for_file '/path/to'
|
53
|
+
def grab_comment_for_file(file)
|
54
|
+
first_line = File.readlines(file).first
|
55
|
+
first_line =~ /^\s*?#/ ? ("-" + first_line.gsub("#","")) : "\n"
|
56
|
+
end
|
18
57
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
58
|
+
# Return file in config_path
|
59
|
+
# config_path '/path/to', :term
|
60
|
+
def config_path(file, type = :yml)
|
61
|
+
return File.join(options[:root],"Termfile") if file.empty?
|
62
|
+
dir = File.join(ENV['HOME'],'.terminitor')
|
63
|
+
if type == :yml
|
64
|
+
File.join(dir, "#{file.sub(/\.yml$/, '')}.yml")
|
65
|
+
else
|
66
|
+
File.join(dir, "#{file.sub(/\.term$/, '')}.term")
|
25
67
|
end
|
26
68
|
end
|
27
69
|
|
28
|
-
|
70
|
+
# Returns error message depending if project is specified
|
71
|
+
# return_error_message 'hi
|
72
|
+
def return_error_message(project)
|
29
73
|
unless project.empty?
|
30
|
-
|
74
|
+
say "'#{project}' doesn't exist! Please run 'terminitor open #{project.gsub(/\..*/,'')}'"
|
31
75
|
else
|
32
|
-
|
76
|
+
say "Termfile doesn't exist! Please run 'terminitor open' in project directory"
|
33
77
|
end
|
34
78
|
end
|
35
79
|
|
36
|
-
|
37
|
-
|
80
|
+
# This will clone a repo in the current directory.
|
81
|
+
# It will first try to clone via ssh(read/write),
|
82
|
+
# if not fall back to git-read only, else, fail.
|
83
|
+
def clone_repo(username, project)
|
84
|
+
github = `which github`
|
85
|
+
return false if github.empty?
|
86
|
+
command = "github clone #{username} #{project}"
|
87
|
+
system(command + " --ssh") || system(command)
|
38
88
|
end
|
39
89
|
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if @got_first_tab_already
|
50
|
-
app("System Events").application_processes["Terminal.app"].keystroke("t", :using => :command_down)
|
90
|
+
# Fetch the git repo and run the setup block
|
91
|
+
# fetch_repo 'achiu', 'terminitor', :setup => true
|
92
|
+
def fetch_repo(username, project, options ={})
|
93
|
+
if clone_repo(username, project)
|
94
|
+
path = File.join(Dir.pwd, project)
|
95
|
+
FileUtils.cd(path)
|
96
|
+
invoke(:setup, []) if options[:setup]
|
97
|
+
else
|
98
|
+
say("could not fetch repo!")
|
51
99
|
end
|
52
|
-
@got_first_tab_already = true
|
53
|
-
local_window = active_window(terminal)
|
54
|
-
@working_dir = Dir.pwd
|
55
|
-
local_tabs = local_window.tabs if local_window
|
56
|
-
local_tabs.last.get if local_tabs
|
57
100
|
end
|
58
101
|
|
59
|
-
# makes sure to set active window as frontmost.
|
60
|
-
def active_window(terminal)
|
61
|
-
(1..terminal.windows.count).each do |i|
|
62
|
-
window = terminal.windows[i]
|
63
|
-
return window if window.properties_.get[:frontmost]
|
64
|
-
end
|
65
|
-
end
|
66
102
|
end
|
67
103
|
end
|
data/lib/terminitor/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Terminitor
|
4
|
+
# This class holds the legacy YAML sytnax for Terminitor
|
5
|
+
class Yaml
|
6
|
+
attr_accessor :file
|
7
|
+
|
8
|
+
# Load in the Yaml file...
|
9
|
+
def initialize(path)
|
10
|
+
@file = YAML.load File.read(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns yaml file as Terminitor formmatted hash
|
14
|
+
def to_hash
|
15
|
+
combined = @file.inject({}) {|base, item| base.merge!(item) ; base } # merge the array of hashes.
|
16
|
+
{ :setup => nil, :windows => { 'default' => combined } }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# COMMENT OF SCRIPT HERE
|
2
|
+
# you can make as many tabs as you wish...
|
3
|
+
# tab names are actually arbitrary at this point too.
|
4
|
+
---
|
5
|
+
- tab1:
|
6
|
+
- cd ~/foo/bar
|
7
|
+
- gitx
|
8
|
+
- tab2:
|
9
|
+
- mysql -u root
|
10
|
+
- use test;
|
11
|
+
- show tables;
|
12
|
+
- tab3: echo "hello world"
|
13
|
+
- tab4: cd ~/baz/ && git pull
|
14
|
+
- tab5:
|
15
|
+
- cd ~/foo/project
|
16
|
+
- autotest
|
data/terminitor.gemspec
CHANGED
@@ -13,17 +13,24 @@ Gem::Specification.new do |s|
|
|
13
13
|
|
14
14
|
s.required_rubygems_version = ">= 1.3.6"
|
15
15
|
s.rubyforge_project = "terminitor"
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
# Platform Specific Dependencies
|
18
|
+
case RUBY_PLATFORM.downcase
|
19
|
+
when %r{darwin}
|
20
|
+
s.add_dependency "rb-appscript"
|
21
|
+
else
|
22
|
+
end
|
23
|
+
|
18
24
|
s.add_dependency "thor", "~>0.14.0"
|
25
|
+
s.add_dependency "github"
|
19
26
|
s.add_development_dependency "bundler", "~>1.0.0"
|
20
|
-
s.add_development_dependency "riot", "~>0.
|
27
|
+
s.add_development_dependency "riot", "~>0.11.0"
|
21
28
|
s.add_development_dependency "rr"
|
22
29
|
s.add_development_dependency "fakefs"
|
23
30
|
s.post_install_message = %q{********************************************************************************
|
24
31
|
|
25
32
|
Terminitor is installed!
|
26
|
-
Please run 'terminitor
|
33
|
+
Please run 'terminitor init'.
|
27
34
|
This will create a directory at ~/.terminitor which will hold all your global scripts.
|
28
35
|
Thanks!
|
29
36
|
|
data/test.watchr
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
ENV["WATCHR"] = "1"
|
2
|
+
system 'clear'
|
3
|
+
|
4
|
+
def growl(message)
|
5
|
+
growlnotify = `which growlnotify`.chomp
|
6
|
+
title = "Watchr Test Results"
|
7
|
+
image = message.include?('0 failures, 0 errors') ? "~/.watchr_images/passed.png" : "~/.watchr_images/failed.png"
|
8
|
+
options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
|
9
|
+
system %(#{growlnotify} #{options} &)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(cmd)
|
13
|
+
puts(cmd)
|
14
|
+
`#{cmd}`
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_test_file(file)
|
18
|
+
system('clear')
|
19
|
+
result = run(%Q(ruby -I"lib:test" -rubygems #{file}))
|
20
|
+
growl result.split("\n").last rescue nil
|
21
|
+
puts result
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_all_tests
|
25
|
+
system('clear')
|
26
|
+
result = run "rake test"
|
27
|
+
growl result.split("\n").last rescue nil
|
28
|
+
puts result
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_all_features
|
32
|
+
system('clear')
|
33
|
+
run "cucumber"
|
34
|
+
end
|
35
|
+
|
36
|
+
def related_test_files(path)
|
37
|
+
Dir['test/**/*.rb'].select { |file| file =~ /#{File.basename(path).split(".").first}_test.rb/ }
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_suite
|
41
|
+
run_all_tests
|
42
|
+
# run_all_features
|
43
|
+
end
|
44
|
+
|
45
|
+
watch('test/teststrap\.rb') { run_all_tests }
|
46
|
+
watch('test/(.*).*_test\.rb') { |m| run_test_file(m[0]) }
|
47
|
+
watch('lib/.*/.*\.rb') { |m| related_test_files(m[0]).map {|tf| run_test_file(tf) } }
|
48
|
+
# watch('features/.*/.*\.feature') { run_all_features }
|
49
|
+
|
50
|
+
# Ctrl-\
|
51
|
+
Signal.trap 'QUIT' do
|
52
|
+
puts " --- Running all tests ---\n\n"
|
53
|
+
run_all_tests
|
54
|
+
end
|
55
|
+
|
56
|
+
@interrupted = false
|
57
|
+
|
58
|
+
# Ctrl-C
|
59
|
+
Signal.trap 'INT' do
|
60
|
+
if @interrupted then
|
61
|
+
@wants_to_quit = true
|
62
|
+
abort("\n")
|
63
|
+
else
|
64
|
+
puts "Interrupt a second time to quit"
|
65
|
+
@interrupted = true
|
66
|
+
Kernel.sleep 1.5
|
67
|
+
# raise Interrupt, nil # let the run loop catch it
|
68
|
+
run_suite
|
69
|
+
end
|
70
|
+
end
|