ursa 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +22 -0
- data/bin/ursa +27 -0
- data/lib/ursa.rb +119 -0
- data/lib/ursa/cherry-tree/cherry-tree.rb +3 -0
- data/lib/ursa/cherry-tree/node.rb +25 -0
- data/lib/ursa/cherry-tree/transformers/source_coder.rb +69 -0
- data/lib/ursa/cherry-tree/tree.rb +37 -0
- data/lib/ursa/packages/iterm.rb +34 -0
- data/lib/ursa/packages/system_events.rb +42 -0
- data/lib/ursa/recipes/openproject.rb +26 -0
- data/lib/ursa/version.rb +3 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8343444cd1c5b84671c792baf81a687bf13c63a1
|
4
|
+
data.tar.gz: 8baff3d6099d245a71c8f9001e0d76727175f38d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 948390dd7252b030a4430c437947506709b60c9360f2e3b6562c1ac35efceb8a847bdb77771b371b1e26f2f28e2008b97fd1e0d489d7237a675bd23091b5a08e
|
7
|
+
data.tar.gz: 8dc64412d124f6f831d662b2163ff2516c50af1ae98ea5f509896107ca36dc7e6fbbbc7d6541852c622689c08aa064d95c52a63461a8973fb22508fc8f868abe
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) [year] [fullname]
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
TODO: Write an awesome README here with examples
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
task :default => :prepare
|
5
|
+
|
6
|
+
desc 'Copy recipes to ~/.ursa/recipes'
|
7
|
+
task :prepare do
|
8
|
+
recipes_folder = File.join(Dir.home, '.ursa', 'recipes')
|
9
|
+
mkdir_p recipes_folder
|
10
|
+
|
11
|
+
Dir.glob('lib/ursa/recipes/*') do |file|
|
12
|
+
file_name = Pathname.new(file).basename
|
13
|
+
recipe_file = File.join(recipes_folder, file_name)
|
14
|
+
|
15
|
+
unless File.exist?(recipe_file)
|
16
|
+
recipe_content = File.read(file)
|
17
|
+
File.open(recipe_file, 'w') do |f|
|
18
|
+
f.write(recipe_content)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/bin/ursa
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ursa'
|
4
|
+
|
5
|
+
def get_recipe_file(recipe_name)
|
6
|
+
file_name = File.join(Dir.home, '.ursa', 'recipes', recipe_name)
|
7
|
+
if File.exist?(file_name)
|
8
|
+
file_name
|
9
|
+
else
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_rb_extension(filename)
|
15
|
+
if File.extname(filename) != ''
|
16
|
+
filename
|
17
|
+
else
|
18
|
+
"#{filename}.rb"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
recipe_name = with_rb_extension(ARGV.first)
|
23
|
+
recipe_file = get_recipe_file(recipe_name) || recipe_name
|
24
|
+
|
25
|
+
ursa = Ursa.new
|
26
|
+
ursa.instance_eval File.read(recipe_file)
|
27
|
+
ursa.execute_script
|
data/lib/ursa.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require_relative './ursa/cherry-tree/cherry-tree'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
class Statement
|
5
|
+
attr_accessor :open, :close
|
6
|
+
|
7
|
+
def initialize(open = '', close = '')
|
8
|
+
self.open = open
|
9
|
+
self.close = close
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AliasObject
|
14
|
+
extend Forwardable
|
15
|
+
attr_accessor :ursa
|
16
|
+
|
17
|
+
def_delegator :ursa, 'tell'
|
18
|
+
def_delegator :ursa, 'app'
|
19
|
+
def_delegator :ursa, 'raw'
|
20
|
+
def_delegator :ursa, 'send_key'
|
21
|
+
def_delegator :ursa, 'key'
|
22
|
+
def_delegator :ursa, 'key_and_kind'
|
23
|
+
|
24
|
+
def initialize(ursa)
|
25
|
+
self.ursa = ursa
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Ursa
|
30
|
+
attr_accessor :tree
|
31
|
+
|
32
|
+
KEY_CODES = %w{
|
33
|
+
A S D F H G Z X C V ISO_Section B Q W E R Y T 1
|
34
|
+
2 3 4 6 5 Equal 9 7 Minus 8 0 RightBracket O U LeftBracket I P Return L J Quote K Semicolon Backslash Comma Slash N M Period Tab Space Grave Delete Escape Command Shift CapsLock Option Control RightShift RightOption RightControl Function F17 KeypadDecimal KeypadMultiply KeypadPlus KeypadClear VolumeUp VolumeDown Mute KeypadDivide KeypadEnter KeypadMinus F18 F19 KeypadEquals Keypad0 Keypad1 Keypad2 Keypad3 Keypad4 Keypad5 Keypad6 Keypad7 F20 Keypad8 Keypad9 JIS_Yen JIS_Underscore JIS_KeypadComma F5 F6 F7 F3 F8 F9 JIS_Eisu F11 JIS_Kana F13 F16 F14 F10 F12 F15 Help Home PageUp ForwardDelete F4 End F2 PageDown F1 LeftArrow RightArrow DownArrow UpArrow
|
35
|
+
}
|
36
|
+
|
37
|
+
KEY_ALIASES = {
|
38
|
+
:command => "command down",
|
39
|
+
:shift => "shift down",
|
40
|
+
:option => "option down"
|
41
|
+
}
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
self.tree = CherryTree::Tree.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def tell(name, to_statement = '', &block)
|
48
|
+
statement = if to_statement != ''
|
49
|
+
Statement.new "tell #{name} to #{to_statement}"
|
50
|
+
else
|
51
|
+
Statement.new "tell #{name}", "end tell"
|
52
|
+
end
|
53
|
+
|
54
|
+
tree.make_node(statement, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def raw(statement, &block)
|
58
|
+
statement = Statement.new(statement)
|
59
|
+
tree.make_node(statement, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def app(app_name)
|
63
|
+
"application \"#{app_name}\""
|
64
|
+
end
|
65
|
+
|
66
|
+
def send_key(code, modifiers)
|
67
|
+
"key code #{code} using {#{modifiers.join(', ')}}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def key(letter)
|
71
|
+
key_found, _ = key_and_kind(letter)
|
72
|
+
key_found
|
73
|
+
end
|
74
|
+
|
75
|
+
def key_and_kind(letter)
|
76
|
+
case letter
|
77
|
+
when String
|
78
|
+
[KEY_CODES.index(letter), :normal]
|
79
|
+
when Symbol
|
80
|
+
[KEY_ALIASES[letter], :special]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def source_code
|
85
|
+
source_coder = CherryTree::SourceCoder.new tree, :indent => 4
|
86
|
+
|
87
|
+
source_coder.open do |node, indentation|
|
88
|
+
"#{node.data.open}\n"
|
89
|
+
end.close do |node, indentation|
|
90
|
+
"#{node.data.close}\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
source_coder.source
|
94
|
+
end
|
95
|
+
|
96
|
+
def execute_script
|
97
|
+
`osascript -e "#{source_code.gsub('"', '\"')}"`
|
98
|
+
end
|
99
|
+
|
100
|
+
def delay(time)
|
101
|
+
raw "delay #{time}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def include(package, alias_name)
|
105
|
+
alias_object = create_alias_object
|
106
|
+
self.send :define_singleton_method, alias_name do
|
107
|
+
alias_object.extend package
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def create_alias_object
|
114
|
+
AliasObject.new(self)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
require_relative './ursa/packages/system_events'
|
119
|
+
require_relative './ursa/packages/iterm'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CherryTree
|
2
|
+
class Node
|
3
|
+
attr_accessor :children, :data, :parent, :deepness
|
4
|
+
|
5
|
+
DEEPNESS_SYMBOL = '----'
|
6
|
+
|
7
|
+
def initialize(data, parent, deepness)
|
8
|
+
self.data = data
|
9
|
+
self.parent = parent
|
10
|
+
self.deepness = deepness
|
11
|
+
self.children = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
str = ""
|
16
|
+
str << "#{DEEPNESS_SYMBOL * deepness}#{DEEPNESS_SYMBOL}#{data}\n"
|
17
|
+
if children.any?
|
18
|
+
children.each do |child|
|
19
|
+
str << child.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
str
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class CherryTree::SourceCoder
|
2
|
+
attr_accessor :buffer, :tree, :open_block, :close_block, :indent
|
3
|
+
|
4
|
+
def initialize(tree, options = {})
|
5
|
+
self.tree = tree
|
6
|
+
self.buffer = ''
|
7
|
+
self.indent = options[:indent] || 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def open(&block)
|
11
|
+
self.open_block = block
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def close(&block)
|
16
|
+
self.close_block = block
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def source
|
21
|
+
@source ||= get_source
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_accessor :current_indentation, :current_node
|
27
|
+
|
28
|
+
def get_source(node = tree.root)
|
29
|
+
self.current_node = node
|
30
|
+
|
31
|
+
buffer_open_block if should_open_block?
|
32
|
+
|
33
|
+
buffer_node_children
|
34
|
+
|
35
|
+
self.current_node = node
|
36
|
+
buffer_close_block if should_close_block?
|
37
|
+
|
38
|
+
buffer
|
39
|
+
end
|
40
|
+
|
41
|
+
def buffer_node_children
|
42
|
+
children = current_node.children
|
43
|
+
children.each do |child|
|
44
|
+
get_source(child)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def buffer_open_block
|
49
|
+
buffer << "#{indentation}#{open_block.call(current_node)}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def buffer_close_block
|
53
|
+
buffer << "#{indentation}#{close_block.call(current_node)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def should_close_block?
|
57
|
+
return false unless should_open_block?
|
58
|
+
close_code = close_block.call(current_node)
|
59
|
+
close_code && close_code.strip != ''
|
60
|
+
end
|
61
|
+
|
62
|
+
def should_open_block?
|
63
|
+
!current_node.data.nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
def indentation
|
67
|
+
' ' * indent * current_node.deepness
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CherryTree
|
2
|
+
class Tree
|
3
|
+
attr_accessor :current_deep_level, :current_parent, :root
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
self.current_deep_level = 0
|
7
|
+
self.root = Node.new(nil, nil, current_deep_level)
|
8
|
+
self.current_parent = root
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
root.to_s if root
|
13
|
+
end
|
14
|
+
|
15
|
+
def make_node(data)
|
16
|
+
node = Node.new(data, current_parent, current_deep_level)
|
17
|
+
|
18
|
+
if current_parent
|
19
|
+
current_parent.children << node
|
20
|
+
end
|
21
|
+
|
22
|
+
if block_given?
|
23
|
+
old_current_parent = self.current_parent
|
24
|
+
|
25
|
+
self.current_deep_level += 1
|
26
|
+
self.current_parent = node
|
27
|
+
|
28
|
+
yield
|
29
|
+
|
30
|
+
self.current_parent = old_current_parent
|
31
|
+
self.current_deep_level -= 1
|
32
|
+
end
|
33
|
+
|
34
|
+
node
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Ursa::Packages
|
2
|
+
module ITerm
|
3
|
+
include Ursa::Packages::SystemEvents
|
4
|
+
|
5
|
+
def execute(*commands)
|
6
|
+
run_in_current_session do
|
7
|
+
commands.each do |command|
|
8
|
+
raw "write text \"#{command}\""
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(*words)
|
14
|
+
execute words.map(&:to_s).join(' ')
|
15
|
+
end
|
16
|
+
|
17
|
+
def split_vertical
|
18
|
+
press_keys { command + shift + d }
|
19
|
+
end
|
20
|
+
|
21
|
+
def split_horizontal
|
22
|
+
press_keys { command + d }
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_in_current_session
|
26
|
+
tell app('iTerm') do
|
27
|
+
tell 'the current terminal' do
|
28
|
+
raw "activate current session"
|
29
|
+
tell('the last session') { yield }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Ursa::Packages
|
2
|
+
class KeyDSL
|
3
|
+
attr_accessor :ursa, :normal_keys, :special_keys
|
4
|
+
|
5
|
+
def initialize(ursa)
|
6
|
+
self.ursa = ursa
|
7
|
+
self.normal_keys = []
|
8
|
+
self.special_keys = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(meth, *args, &block)
|
12
|
+
key, kind = ursa.key_and_kind(meth.to_sym)
|
13
|
+
if key.nil?
|
14
|
+
key, kind = ursa.key_and_kind(meth.to_s.upcase)
|
15
|
+
end
|
16
|
+
|
17
|
+
if kind == :normal
|
18
|
+
normal_keys << key
|
19
|
+
elsif kind == :special
|
20
|
+
special_keys << key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module SystemEvents
|
26
|
+
def press_keys(&key_block)
|
27
|
+
codes, modifiers = parse_key(key_block)
|
28
|
+
|
29
|
+
if codes.size == 1
|
30
|
+
code = codes.first
|
31
|
+
tell app('System Events'), send_key(code, modifiers)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_key(key_block)
|
36
|
+
dsl = KeyDSL.new(self)
|
37
|
+
dsl.instance_eval &key_block
|
38
|
+
|
39
|
+
[dsl.normal_keys, dsl.special_keys]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This is a sample recipe
|
2
|
+
|
3
|
+
# It assumes that this is a rails project
|
4
|
+
# run in iTerm using Zeus, Guard and Sublime Text 2/3.
|
5
|
+
|
6
|
+
# I should make a more generic recipe later.
|
7
|
+
# This file is now in your home folder and Ursa
|
8
|
+
# will never attempt to replace your file so
|
9
|
+
# feel free to modify it with your own needs.
|
10
|
+
|
11
|
+
@location = ARGV[1]
|
12
|
+
|
13
|
+
include Ursa::Packages::ITerm, :iTerm
|
14
|
+
|
15
|
+
def jump_and_run(*commands)
|
16
|
+
iTerm.execute "cd #{@location}", *commands
|
17
|
+
end
|
18
|
+
|
19
|
+
iTerm.split_vertical
|
20
|
+
jump_and_run('zeus start')
|
21
|
+
iTerm.split_horizontal
|
22
|
+
jump_and_run('bundle exec guard')
|
23
|
+
iTerm.split_vertical
|
24
|
+
jump_and_run('zeus server')
|
25
|
+
iTerm.split_horizontal
|
26
|
+
jump_and_run("subl .")
|
data/lib/ursa/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ursa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julio García
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.14'
|
27
|
+
description: Ursa generates Applescript from Ruby Code allowing you to automate many
|
28
|
+
things in your mac without suffering with a somewhat strange language.
|
29
|
+
email: julioggonz@gmail.com
|
30
|
+
executables:
|
31
|
+
- ursa
|
32
|
+
extensions:
|
33
|
+
- Rakefile
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- LICENSE
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- bin/ursa
|
40
|
+
- lib/ursa.rb
|
41
|
+
- lib/ursa/cherry-tree/cherry-tree.rb
|
42
|
+
- lib/ursa/cherry-tree/node.rb
|
43
|
+
- lib/ursa/cherry-tree/transformers/source_coder.rb
|
44
|
+
- lib/ursa/cherry-tree/tree.rb
|
45
|
+
- lib/ursa/packages/iterm.rb
|
46
|
+
- lib/ursa/packages/system_events.rb
|
47
|
+
- lib/ursa/recipes/openproject.rb
|
48
|
+
- lib/ursa/version.rb
|
49
|
+
homepage: http://github.com/juliogarciag/ursa
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.2.0
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Programming-friendly Applescript creation.
|
73
|
+
test_files: []
|
74
|
+
has_rdoc:
|