consular-osx 1.0.0.rc1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in consular-osx.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'consular', :path => '/Volumes/Storage/code/github/terminitor'
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:spec) do |test|
5
+ test.libs << 'lib' << 'spec'
6
+ test.pattern = 'spec/**/*_spec.rb'
7
+ test.verbose = true
8
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "consular-osx"
6
+ s.version = '1.0.0.rc1'
7
+ s.authors = ["Arthur Chiu"]
8
+ s.email = ["mr.arthur.chiu@gmail.com"]
9
+ s.homepage = ""
10
+ s.summary = %q{Mac OSX Terminal core for Consular}
11
+ s.description = %q{Automate Mac OSX Terminal with Consular}
12
+
13
+ s.rubyforge_project = "consular-osx"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'consular'
21
+ s.add_dependency 'rb-appscript'
22
+ s.add_development_dependency 'minitest'
23
+ s.add_development_dependency 'mocha'
24
+ end
@@ -0,0 +1,245 @@
1
+ require 'consular'
2
+ require 'appscript'
3
+
4
+ module Consular
5
+ # Consular Core to interact with Mac OS X Terminal.
6
+ #
7
+ class OSX < Core
8
+ include Appscript
9
+
10
+ Consular.add_core self
11
+
12
+ # The acceptable options that OSX Terminal will register.
13
+ #
14
+ ALLOWED_OPTIONS = {
15
+ :window => [:bounds, :visible, :miniaturized],
16
+ :tab => [:settings, :selected]
17
+ } unless defined?(ALLOWED_OPTIONS)
18
+
19
+ class << self
20
+
21
+ # Checks to see if the current system is on darwin.
22
+ # TODO: strengthen this system check.
23
+ #
24
+ # @api public
25
+ def valid_system?
26
+ RUBY_PLATFORM.downcase =~ /darwin/
27
+ end
28
+ end
29
+
30
+
31
+ # Initializes a reference to the Terminal.app via appscript
32
+ #
33
+ # @param [String] path
34
+ # path to Termfile.
35
+ #
36
+ # @api public
37
+ def initialize(path)
38
+ super
39
+ @terminal = app('Terminal')
40
+ end
41
+
42
+ # Method called by runner to Execute Termfile setup.
43
+ #
44
+ # @api public
45
+ def setup!
46
+ @termfile[:setup].each { |cmd| execute_command(cmd, :in => active_window) }
47
+ end
48
+
49
+ # Method called by runner to execute Termfile.
50
+ #
51
+ # @api public
52
+ def process!
53
+ windows = @termfile[:windows]
54
+ default = windows.delete('default')
55
+ execute_window(default, :default => true) unless default[:tabs].empty?
56
+ windows.each_pair { |_, cont| execute_window(cont) }
57
+ end
58
+
59
+ # Executes the commands for each designated window.
60
+ # .run_windows will iterate through each of the tabs in
61
+ # sorted order to execute the tabs in the order they were set.
62
+ # The logic follows this:
63
+ #
64
+ # If the content is for the 'default' window,
65
+ # then use the current active window and generate the commands.
66
+ #
67
+ # If the content is for a new window,
68
+ # then generate a new window and activate the windows.
69
+ #
70
+ # Otherwise, open a new tab and execute the commands.
71
+ #
72
+ # @param [Hash] content
73
+ # The hash contents of the window from the Termfile.
74
+ # @param [Hash] options
75
+ # Addional options to pass. You can use:
76
+ # :default - Whether this is being run as the default window.
77
+ #
78
+ # @example
79
+ # @core.execute_window contents, :default => true
80
+ # @core.execute_window contents, :default => true
81
+ #
82
+ # @api public
83
+ def execute_window(content, options = {})
84
+ window_options = content[:options]
85
+ _contents = content[:tabs]
86
+ _first_run = true
87
+
88
+ _contents.keys.sort.each do |key|
89
+ _content = _contents[key]
90
+ _options = content[:options]
91
+ _name = options[:name]
92
+
93
+ _tab =
94
+ if _first_run && !options[:default]
95
+ open_window options.merge(window_options)
96
+ else
97
+ key == 'default' ? active_window : open_tab(_options)
98
+ end
99
+
100
+ _first_run = false
101
+ commands = prepend_befores _content[:commands], _contents[:befores]
102
+ commands = set_title _name, commands
103
+ commands.each { |cmd| execute_command cmd, :in => _tab }
104
+ end
105
+
106
+ end
107
+
108
+ # Prepend a title setting command prior to the other commands.
109
+ #
110
+ # @param [String] title
111
+ # The title to set for the context of the commands.
112
+ # @param [Array<String>] commands
113
+ # The context of commands to preprend to.
114
+ #
115
+ # @api public
116
+ def set_title(title, commands)
117
+ cmd = "PS1=\"$PS1\\[\\e]2;#{title}\\a\\]\""
118
+ title ? commands.insert(0, cmd) : commands
119
+ end
120
+
121
+ # Prepends the :before commands to the current context's
122
+ # commands if it exists.
123
+ #
124
+ # @param [Array<String>] commands
125
+ # The current tab commands
126
+ # @param [Array<String>] befores
127
+ # The current window's :befores
128
+ #
129
+ # @return [Array<String>]
130
+ # The current context commands with the :before commands prepended
131
+ #
132
+ # @api public
133
+ def prepend_befores(commands, befores = nil)
134
+ unless befores.nil? || befores.empty?
135
+ commands.insert(0, befores).flatten!
136
+ else
137
+ commands
138
+ end
139
+ end
140
+
141
+ # Execute the given command in the context of the
142
+ # active window.
143
+ #
144
+ # @param [String] cmd
145
+ # The command to execute.
146
+ # @param [Hash] options
147
+ # Additional options to pass into appscript for the context.
148
+ #
149
+ # @example
150
+ # @osx.execute_command 'ps aux', :in => @tab_object
151
+ #
152
+ # @api public
153
+ def execute_command(cmd, options = {})
154
+ active_window.do_script cmd, options
155
+ end
156
+
157
+
158
+ # Opens a new tab and return the last instantiated tab(itself).
159
+ #
160
+ # @param [Hash] options
161
+ # Options to further customize the window. You can use:
162
+ # :settings -
163
+ # :selected -
164
+ #
165
+ # @return
166
+ # Returns a refernce to the last instantiated tab of the
167
+ # window.
168
+ #
169
+ # @api public
170
+ def open_tab(options = nil)
171
+ open_terminal_with 't', options
172
+ end
173
+
174
+ # Opens a new window and returns its
175
+ # last instantiated tab.(The first 'tab' in the window).
176
+ #
177
+ # @param [Hash] options
178
+ # Options to further customize the window. You can use:
179
+ # :bound - Set the bounds of the windows
180
+ # :visible - Set whether or not the current window is visible
181
+ # :miniaturized - Set whether or not the window is minimized
182
+ #
183
+ # @return
184
+ # Returns a refernce to the last instantiated tab of the
185
+ # window.
186
+ #
187
+ # @api public
188
+ def open_window(options = nil)
189
+ open_terminal_with 'n', options
190
+ end
191
+
192
+
193
+ # Sets the options for the windows/tabs. Will filter options
194
+ # based on what options can be set for either windows or tabs.
195
+ #
196
+ # @param [Hash] options
197
+ # Options to set for the window/tab
198
+ #
199
+ # @api semipublic
200
+ def set_options(options = {})
201
+ # raise NotImplementedError
202
+ end
203
+
204
+ # Returns the current terminal process.
205
+ # We need this method to workaround appscript so that we can
206
+ # instantiate new tabs and windows. Otherwise it would have
207
+ # looked something like # window.make(:new => :tab) but that
208
+ # doesn't work.
209
+ #
210
+ # @api semipublic
211
+ def terminal_process
212
+ app('System Events').application_processes['Terminal.app']
213
+ end
214
+
215
+ # Returns the last instantiated tab from active window
216
+ #
217
+ # @api semipublic
218
+ def active_tab
219
+ window = active_window
220
+ tabs = window.tabs if window
221
+ tabs.last.get if tabs
222
+ end
223
+
224
+ # Returns the currently active window by
225
+ # checking to see if it is the :frontmost window.
226
+ #
227
+ # @api semipublic
228
+ def active_window
229
+ windows = @terminal.windows.get
230
+ windows.detect do |window|
231
+ window.properties_.get[:frontmost] rescue false
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ # @api private
238
+ def open_terminal_with(key, options = nil)
239
+ terminal_process.keystroke(key, :using => :command_down)
240
+ set_options options
241
+ active_tab
242
+ end
243
+
244
+ end
245
+ end
@@ -0,0 +1,67 @@
1
+ ENV["WATCHR"] = "1"
2
+ system 'clear'
3
+
4
+ def growl(message)
5
+ growlnotify = `which growlnotify`.chomp
6
+ if not growlnotify.empty?
7
+ title = "Watchr Test Results"
8
+ image = message.include?('0 failures, 0 errors') ? "~/.watchr_images/passed.png" : "~/.watchr_images/failed.png"
9
+ options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
10
+ system %(#{growlnotify} #{options} &)
11
+ else
12
+ puts message
13
+ end
14
+ end
15
+
16
+ def run(cmd)
17
+ puts(cmd)
18
+ `#{cmd}`
19
+ end
20
+
21
+ def run_test_file(file)
22
+ system('clear')
23
+ result = run(%Q(ruby -I"lib:test" -rubygems #{file}))
24
+ growl result.split("\n").last rescue nil
25
+ puts result
26
+ end
27
+
28
+ def run_all_tests
29
+ system('clear')
30
+ result = run "rake spec"
31
+ growl result.split("\n").last rescue nil
32
+ puts result
33
+ end
34
+
35
+ def related_test_files(path)
36
+ Dir['spec/**/*.rb'].select { |file| file =~ /#{File.basename(path).split(".").first}_spec.rb/ }
37
+ end
38
+
39
+ def run_suite
40
+ run_all_tests
41
+ end
42
+
43
+ watch('spec/spec_helper\.rb') { run_all_tests }
44
+ watch('spec/(.*).*_spec\.rb') { |m| run_test_file(m[0]) }
45
+ watch('lib/.*/.*\.rb') { |m| related_test_files(m[0]).map {|tf| run_test_file(tf) } }
46
+
47
+ # Ctrl-\
48
+ Signal.trap 'QUIT' do
49
+ puts " --- Running all tests ---\n\n"
50
+ run_all_tests
51
+ end
52
+
53
+ @interrupted = false
54
+
55
+ # Ctrl-C
56
+ Signal.trap 'INT' do
57
+ if @interrupted then
58
+ @wants_to_quit = true
59
+ abort("\n")
60
+ else
61
+ puts "Interrupt a second time to quit"
62
+ @interrupted = true
63
+ Kernel.sleep 1.5
64
+ # raise Interrupt, nil # let the run loop catch it
65
+ run_suite
66
+ end
67
+ end
File without changes
@@ -0,0 +1,85 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require File.expand_path('../../lib/consular/osx',__FILE__)
3
+
4
+ describe Consular::OSX do
5
+ before do
6
+ @core = Consular::OSX.new File.expand_path('../fixtures/bar.term', __FILE__)
7
+ end
8
+
9
+ it "should have ALLOWED_OPTIONS" do
10
+ options = Consular::OSX::ALLOWED_OPTIONS
11
+
12
+ assert_equal [:bounds, :visible, :miniaturized], options[:window]
13
+ assert_equal [:settings, :selected], options[:tab]
14
+ end
15
+
16
+ it "should be added as a core to Consular" do
17
+ assert_includes Consular.cores, Consular::OSX
18
+ end
19
+
20
+ it "should set ivars on .initialize" do
21
+ refute_nil @core.instance_variable_get(:@termfile)
22
+ refute_nil @core.instance_variable_get(:@terminal)
23
+ end
24
+
25
+ it "should set .set_title" do
26
+ assert_equal ["PS1=\"$PS1\\[\\e]2;hey\\a\\]\"",'ls'], @core.set_title('hey', ['ls'])
27
+ assert_equal ['ls'], @core.set_title(nil, ['ls'])
28
+ end
29
+
30
+ it "should prepend commands with .prepend_befores" do
31
+ assert_equal ['ps', 'ls'], @core.prepend_befores(['ls'], ['ps'])
32
+ assert_equal ['ls'], @core.prepend_befores(['ls'])
33
+ end
34
+
35
+ it "should .execute_command" do
36
+ window = mock()
37
+ window.expects(:do_script).with('ls',{}).returns(true)
38
+ @core.expects(:active_window).returns(window)
39
+ assert @core.execute_command('ls', {}), "that core executes do_script"
40
+ end
41
+
42
+ it "should executes setup block with .setup!" do
43
+ @core.termfile[:setup] = ['ls','ls']
44
+ @core.expects(:execute_command).with('ls', anything).twice
45
+ assert @core.setup!
46
+ end
47
+
48
+ it "should executes window context with .process!" do
49
+ @core.termfile[:windows] = {
50
+ 'default' => { :tabs => {'default' => {:commands => ['ls']}}},
51
+ 'window1' => { :tabs => {'default' => {:commands => ['whoami']}}},
52
+ 'window2' => { :tabs => {'default' => {:commands => ['whoami']}}},
53
+ }
54
+
55
+ @core.expects(:execute_window).with(@core.termfile[:windows]['default'], :default => true).once
56
+ @core.expects(:execute_window).with(@core.termfile[:windows]['window1']).twice
57
+ assert @core.process!
58
+ end
59
+
60
+ it "should .open_tab" do
61
+ @core.expects(:open_terminal_with).with('t', nil).returns(true)
62
+ assert @core.open_tab
63
+ end
64
+
65
+ it "should .open_window" do
66
+ @core.expects(:open_terminal_with).with('n', nil).returns(true)
67
+ assert @core.open_window
68
+ end
69
+
70
+ describe ".execute_window" do
71
+ it "should use the current active window with 'default' window" do
72
+ skip
73
+ end
74
+
75
+ it "should use the open a new window if its teh first run and not the 'default' window" do
76
+ skip
77
+ end
78
+
79
+ it "should open a new tab if its not the 'default' window" do
80
+ skip
81
+ end
82
+ end
83
+
84
+
85
+ end
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'mocha'
5
+ require File.expand_path('../../lib/consular/osx', __FILE__)
6
+
7
+
8
+ class ColoredIO
9
+ ESC = "\e["
10
+ NND = "#{ESC}0m"
11
+
12
+ def initialize(io)
13
+ @io = io
14
+ end
15
+
16
+ def print(o)
17
+ case o
18
+ when "."
19
+ @io.send(:print, "#{ESC}32m#{o}#{NND}")
20
+ when "E"
21
+ @io.send(:print, "#{ESC}33m#{o}#{NND}")
22
+ when "F"
23
+ @io.send(:print, "#{ESC}31m#{o}#{NND}")
24
+ else
25
+ @io.send(:print, o)
26
+ end
27
+ end
28
+
29
+ def puts(*o)
30
+ super
31
+ end
32
+ end
33
+
34
+ MiniTest::Unit.output = ColoredIO.new(MiniTest::Unit.output)
35
+
36
+ # This is to silence the 'task' warning for the mocks.
37
+ #
38
+ class Thor
39
+ class << self
40
+ def create_task(meth) #:nodoc:
41
+ if @usage && @desc
42
+ base_class = @hide ? Thor::HiddenTask : Thor::Task
43
+ tasks[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
44
+ @usage, @desc, @long_desc, @method_options, @hide = nil
45
+ true
46
+ elsif self.all_tasks[meth] || meth == "method_missing"
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: consular-osx
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15424055
5
+ prerelease: 6
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ - rc
11
+ - 1
12
+ version: 1.0.0.rc1
13
+ platform: ruby
14
+ authors:
15
+ - Arthur Chiu
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-10-09 00:00:00 -07:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: consular
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 3
32
+ segments:
33
+ - 0
34
+ version: "0"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rb-appscript
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: minitest
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: mocha
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ description: Automate Mac OSX Terminal with Consular
80
+ email:
81
+ - mr.arthur.chiu@gmail.com
82
+ executables: []
83
+
84
+ extensions: []
85
+
86
+ extra_rdoc_files: []
87
+
88
+ files:
89
+ - .gitignore
90
+ - Gemfile
91
+ - Rakefile
92
+ - consular-osx.gemspec
93
+ - lib/consular/osx.rb
94
+ - spec.watchr
95
+ - spec/fixtures/bar.term
96
+ - spec/osx_spec.rb
97
+ - spec/spec_helper.rb
98
+ has_rdoc: true
99
+ homepage: ""
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">"
120
+ - !ruby/object:Gem::Version
121
+ hash: 25
122
+ segments:
123
+ - 1
124
+ - 3
125
+ - 1
126
+ version: 1.3.1
127
+ requirements: []
128
+
129
+ rubyforge_project: consular-osx
130
+ rubygems_version: 1.6.2
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Mac OSX Terminal core for Consular
134
+ test_files:
135
+ - spec/fixtures/bar.term
136
+ - spec/osx_spec.rb
137
+ - spec/spec_helper.rb