billygoat 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +9 -0
- data/bin/goat +5 -0
- data/lib/billygoat/brain.rb +66 -0
- data/lib/billygoat/config.rb +35 -0
- data/lib/billygoat/credentials.rb +28 -0
- data/lib/billygoat/dependencies.rb +16 -0
- data/lib/billygoat/disk.rb +15 -0
- data/lib/billygoat/documentation.rb +133 -0
- data/lib/billygoat/goat.rb +17 -0
- data/lib/billygoat/knowledge.rb +73 -0
- data/lib/billygoat/memory.rb +18 -0
- data/lib/billygoat/settings.rb +36 -0
- data/lib/billygoat/skill.rb +70 -0
- data/lib/billygoat/skills.rb +29 -0
- data/lib/billygoat/task.rb +34 -0
- data/lib/billygoat/version.rb +3 -0
- data/lib/billygoat.rb +17 -0
- data/lib/skills/document.rb +41 -0
- data/lib/skills/git.rb +46 -0
- data/lib/skills/help.rb +40 -0
- data/lib/skills/learn.rb +59 -0
- data/lib/skills/show.rb +27 -0
- data/lib/skills/update.rb +25 -0
- data/spec/goat_spec.rb +14 -0
- data/spec/spec_helper.rb +2 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDc0NDZkNDcyZTIyOWE5ZWNlNzExMmQ5NGUxNGJiODM1ZjI3NzEzYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ODE5ZDhjYjM5YTM3NjgwM2UwYWQyZGEzOTYxM2U1NzIyODkyMDI4NA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzFmN2VmYzMwMDNmNzJlNThiZTY5ZDNhMjZlZTA3YzdhNTI5Yjc5ZmI1Nzll
|
10
|
+
NWU1M2JmNTE2NjBiY2U5ZmUwY2E1NjQ3NmQwZmY3MmE5YTIxODM4NTllZWE0
|
11
|
+
MTY2NjhlZjJlNWRkZDA2MzY0ZTE2ODBlN2EzZmNjOTZmNmIxZWM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ODAxOTZmODcwNTI0YmYwMDMzODIzZDlkZTRkZDAzNzFmMWYwZDMwOWE1MTc3
|
14
|
+
YjliZDNhNDVlOWY4MWY5YTY1MzIyOTIxM2FjMmQyNGM4NjkyODhkM2Q1MWE0
|
15
|
+
MGVmMWJmMDg4OWViYTVmOTlkMTYxYmNkNmFkZGVkZmRmNWM4OTc=
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Derrick Parkhurst
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Billy::Goat
|
2
|
+
|
3
|
+
Goat do this for me.
|
4
|
+
|
5
|
+
<a title="By Kuebi = Armin Kübelbeck (Own work) [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons" href="http://commons.wikimedia.org/wiki/File%3AHausziege_04.jpg"><img width="100%" alt="Hausziege 04" src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Hausziege_04.jpg/512px-Hausziege_04.jpg"/></a>
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
```
|
11
|
+
$ gem install billygoat
|
12
|
+
```
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Getting the goat to do a task
|
17
|
+
```
|
18
|
+
> goat [skill] [task]
|
19
|
+
```
|
20
|
+
|
21
|
+
[A list of all skills and tasks goat already knows](COMMANDS.md)
|
22
|
+
|
23
|
+
|
24
|
+
Getting the goat to do the default task for a given skill
|
25
|
+
```
|
26
|
+
> goat [skill]
|
27
|
+
```
|
28
|
+
|
29
|
+
Asking the goat about the tasks it knows
|
30
|
+
```
|
31
|
+
> goat help
|
32
|
+
> goat help [skill]
|
33
|
+
> goat help [skill] [task]
|
34
|
+
```
|
35
|
+
|
36
|
+
Teaching the goat new tasks
|
37
|
+
```
|
38
|
+
> goat learn file [file name]
|
39
|
+
> goat learn directory [file path]
|
40
|
+
```
|
41
|
+
|
42
|
+
Which services have you authorized your goat to use?
|
43
|
+
```
|
44
|
+
> goat show credentials
|
45
|
+
```
|
46
|
+
|
47
|
+
Give your goat permission to use a service
|
48
|
+
```
|
49
|
+
> goat update [service] [key=value] [key=value]
|
50
|
+
> goat update github access_token=XXXXXXXX
|
51
|
+
> goat update jenkins username=XXXX password=XXXX server_url=XXXX
|
52
|
+
```
|
53
|
+
|
54
|
+
Goat knows git
|
55
|
+
```
|
56
|
+
> goat branches
|
57
|
+
> goat git commit [message]
|
58
|
+
> goat git checkout (branch)
|
59
|
+
> goat git add_tag [name]
|
60
|
+
```
|
61
|
+
|
62
|
+
Talk like a goat
|
63
|
+
```
|
64
|
+
> goat say Naaaaaaa!
|
65
|
+
+-----------+
|
66
|
+
| Naaaaaaa! |
|
67
|
+
+-----------+
|
68
|
+
| (_(
|
69
|
+
+-- /_/'_____/)
|
70
|
+
" | |
|
71
|
+
|""""""|
|
72
|
+
```
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
Copyright (c) 2015 Derrick Parkhurst (derrick.parkhurst@gmail.com),
|
77
|
+
|
78
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
79
|
+
|
80
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
81
|
+
|
82
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/goat
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
class Brain
|
4
|
+
|
5
|
+
include Disk
|
6
|
+
include Config
|
7
|
+
include Settings
|
8
|
+
include Knowledge
|
9
|
+
include Credentials
|
10
|
+
include Skills
|
11
|
+
|
12
|
+
attr_accessor :skill
|
13
|
+
|
14
|
+
#
|
15
|
+
# Short term memory serving as a workspace
|
16
|
+
# for executing tasks
|
17
|
+
#
|
18
|
+
def memory
|
19
|
+
return @memory if @memory
|
20
|
+
@memory = Memory.new
|
21
|
+
@memory.brain = self
|
22
|
+
@memory.credentials = credentials
|
23
|
+
@memory.skills = skills
|
24
|
+
@memory
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Clear the workspace for executing tasks
|
29
|
+
#
|
30
|
+
def forget
|
31
|
+
@memory = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Recall a skill and place it in memory
|
36
|
+
#
|
37
|
+
def recall(name)
|
38
|
+
self.skill = find_skill(name)
|
39
|
+
return unless skill
|
40
|
+
|
41
|
+
require skill.internal_location if skill.internal_location
|
42
|
+
memory.extend(skill.module)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Execute a task in memory
|
47
|
+
#
|
48
|
+
def execute(*args)
|
49
|
+
assert_skill_loaded
|
50
|
+
|
51
|
+
if args.empty? || !skill.has_a_task?(args.first)
|
52
|
+
args.unshift skill.default_task.name
|
53
|
+
end
|
54
|
+
|
55
|
+
memory.send *args
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def assert_skill_loaded
|
61
|
+
fail "Naaaa! Unknown skill" unless skill
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
module Config
|
4
|
+
|
5
|
+
def root
|
6
|
+
@root ||= ENV['HOME']
|
7
|
+
end
|
8
|
+
attr_writer :root
|
9
|
+
|
10
|
+
def config_dir
|
11
|
+
@config_dir ||= ".goat"
|
12
|
+
end
|
13
|
+
attr_writer :config_dir
|
14
|
+
|
15
|
+
def config_path
|
16
|
+
"#{root}/#{config_dir}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_config_path
|
20
|
+
create_path(config_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Save all in-memory configuration to disk
|
25
|
+
#
|
26
|
+
def save_configuration
|
27
|
+
save_settings
|
28
|
+
update_knowledge
|
29
|
+
save_knowledge
|
30
|
+
save_credentials
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
module Credentials
|
4
|
+
|
5
|
+
def credentials_file
|
6
|
+
@credentials_file ||= "credentials.yml"
|
7
|
+
end
|
8
|
+
attr_writer :credentials_file
|
9
|
+
|
10
|
+
def credentials_path
|
11
|
+
"#{config_path}/#{credentials_file}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def credentials
|
15
|
+
return @credentials if @credentials
|
16
|
+
save_credentials({}) unless File.exists?(credentials_path)
|
17
|
+
@credentials = YAML.load_file(credentials_path)
|
18
|
+
@credentials.default_proc = ->(h,k) { h[k] = {} }
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_credentials(credentials = @credentials)
|
22
|
+
create_config_path
|
23
|
+
save_yaml(credentials_path, credentials)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dependencies
|
2
|
+
|
3
|
+
def gem_require(name)
|
4
|
+
|
5
|
+
begin
|
6
|
+
require(name)
|
7
|
+
rescue LoadError
|
8
|
+
puts "#{name} gem not available but is required"
|
9
|
+
puts "try: gem install #{name}"
|
10
|
+
fail "required gem not available"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
module_function :gem_require
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
class Documentation
|
4
|
+
|
5
|
+
#
|
6
|
+
# Documentation includes the method definition and any
|
7
|
+
# commented lines preceding the method definition
|
8
|
+
#
|
9
|
+
|
10
|
+
attr_accessor :method
|
11
|
+
attr_writer :owner
|
12
|
+
attr_accessor :mode
|
13
|
+
|
14
|
+
def initialize(args)
|
15
|
+
args.each { |k,v| public_send("#{k}=",v) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
return unless file_name && line_number
|
20
|
+
case mode
|
21
|
+
when :markdown
|
22
|
+
build_markdown
|
23
|
+
else
|
24
|
+
build
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect_markdown
|
29
|
+
return unless file_name && line_number
|
30
|
+
build_markdown
|
31
|
+
end
|
32
|
+
|
33
|
+
def file_name
|
34
|
+
@file_name ||= self.method.source_location.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def line_number
|
38
|
+
@line_number ||= self.method.source_location.last
|
39
|
+
end
|
40
|
+
|
41
|
+
def parameters
|
42
|
+
@parameters ||= self.method.parameters
|
43
|
+
end
|
44
|
+
|
45
|
+
def owner
|
46
|
+
@owner ||= self.method.owner.name.downcase
|
47
|
+
end
|
48
|
+
|
49
|
+
def name
|
50
|
+
@name ||= self.method.name.downcase
|
51
|
+
end
|
52
|
+
|
53
|
+
def required_parameters
|
54
|
+
parameters
|
55
|
+
.select{ |pair| pair.first == :req }
|
56
|
+
.map{ |pair| pair.last}
|
57
|
+
end
|
58
|
+
|
59
|
+
def optional_parameters
|
60
|
+
parameters
|
61
|
+
.select{ |pair| pair.first == :opt }
|
62
|
+
.map{ |pair| pair.last}
|
63
|
+
end
|
64
|
+
|
65
|
+
def variable_parameters
|
66
|
+
parameters
|
67
|
+
.select{ |pair| pair.first == :rest }
|
68
|
+
.map{ |pair| pair.last}
|
69
|
+
end
|
70
|
+
|
71
|
+
def block_parameters
|
72
|
+
parameters
|
73
|
+
.select{ |pair| pair.first == :block }
|
74
|
+
.map{ |pair| pair.last}
|
75
|
+
end
|
76
|
+
|
77
|
+
def source_file
|
78
|
+
File.read(file_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
def source
|
82
|
+
source_file.split("\n")
|
83
|
+
end
|
84
|
+
|
85
|
+
def range
|
86
|
+
head = tail = line_number - 1
|
87
|
+
tail.downto(0).each do
|
88
|
+
break unless source[head-1].match(/^\s*#/)
|
89
|
+
head -= 1
|
90
|
+
end
|
91
|
+
(head..tail)
|
92
|
+
end
|
93
|
+
|
94
|
+
def unindent
|
95
|
+
->(line) { line.gsub(/^\s*/, '') }
|
96
|
+
end
|
97
|
+
|
98
|
+
def lines
|
99
|
+
source[range].map(&unindent)
|
100
|
+
end
|
101
|
+
|
102
|
+
def build
|
103
|
+
doc = lines
|
104
|
+
doc[-1] = "goat #{owner} #{name} "
|
105
|
+
required_parameters.each do |parameter|
|
106
|
+
doc[-1] << "[#{parameter}] "
|
107
|
+
end
|
108
|
+
optional_parameters.each do |parameter|
|
109
|
+
doc[-1] << "(#{parameter}) "
|
110
|
+
end
|
111
|
+
variable_parameters.each do |parameter|
|
112
|
+
doc[-1] << "(*#{parameter}) "
|
113
|
+
end
|
114
|
+
block_parameters.each do |parameter|
|
115
|
+
doc[-1] << "{&#{parameter}} "
|
116
|
+
end
|
117
|
+
doc << "\n"
|
118
|
+
doc
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_markdown
|
122
|
+
doc = build
|
123
|
+
doc.pop
|
124
|
+
doc.map! { |line| line.match(/^\s*#/) ? " #{line}" : line }
|
125
|
+
doc.unshift "### #{doc.pop}"
|
126
|
+
doc << "\n"
|
127
|
+
doc.map! { |line| line.gsub(/(\[)/,'\[').gsub(/(\])/,'\]') }
|
128
|
+
doc
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
module Knowledge
|
4
|
+
|
5
|
+
def knowledge_file
|
6
|
+
@knowledge_file ||= "knowledge.yml"
|
7
|
+
end
|
8
|
+
attr_writer :knowledge_file
|
9
|
+
|
10
|
+
def knowledge_path
|
11
|
+
"#{config_path}/#{knowledge_file}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def knowledge
|
15
|
+
return @knowledge if @knowledge
|
16
|
+
save_knowledge(default_knowledge) unless File.exists?(knowledge_path)
|
17
|
+
@knowledge = YAML.load_file(knowledge_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_knowledge
|
21
|
+
knowledge[:skills] = skill_set
|
22
|
+
.skills
|
23
|
+
.reject{ |skill| autoloaded_skills.map{|s| s[:name]}.include? skill.name }
|
24
|
+
.uniq{ |skill| skill.name }
|
25
|
+
.map(&:to_h)
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_knowledge
|
29
|
+
{
|
30
|
+
skills: [
|
31
|
+
{
|
32
|
+
name: 'Learn',
|
33
|
+
internal_location: system_skills_path("learn.rb")
|
34
|
+
},
|
35
|
+
{
|
36
|
+
name: 'Show',
|
37
|
+
internal_location: system_skills_path("show.rb")
|
38
|
+
},
|
39
|
+
{
|
40
|
+
name: 'Update',
|
41
|
+
internal_location: system_skills_path("update.rb")
|
42
|
+
},
|
43
|
+
{
|
44
|
+
name: 'Help',
|
45
|
+
internal_location: system_skills_path("help.rb")
|
46
|
+
},
|
47
|
+
{
|
48
|
+
name: 'Git',
|
49
|
+
internal_location: system_skills_path("git.rb")
|
50
|
+
},
|
51
|
+
{
|
52
|
+
name: 'Document',
|
53
|
+
internal_location: system_skills_path("document.rb")
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_knowledge
|
60
|
+
return if knowledge &&
|
61
|
+
knowledge.is_a?(Hash) &&
|
62
|
+
knowledge.include?(:skills)
|
63
|
+
fail "Naaa! Broken knowledge file."
|
64
|
+
end
|
65
|
+
|
66
|
+
def save_knowledge(knowledge = @knowledge)
|
67
|
+
create_config_path
|
68
|
+
save_yaml(knowledge_path, knowledge)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Memory
|
2
|
+
|
3
|
+
attr_accessor :brain, :skills, :credentials
|
4
|
+
|
5
|
+
def say(*args)
|
6
|
+
words = args.join(" ")
|
7
|
+
puts <<-EOS
|
8
|
+
+-#{"-"*words.length}-+
|
9
|
+
| #{words} |
|
10
|
+
+-#{"-"*words.length}-+
|
11
|
+
| (_(
|
12
|
+
+-- /_/'_____/)
|
13
|
+
" | |
|
14
|
+
|""""""|
|
15
|
+
EOS
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
#
|
4
|
+
# Storage for all goat specific settings
|
5
|
+
#
|
6
|
+
module Settings
|
7
|
+
|
8
|
+
def settings_file
|
9
|
+
@settings ||= "settings.yml"
|
10
|
+
end
|
11
|
+
attr_writer :settings
|
12
|
+
|
13
|
+
def settings_path
|
14
|
+
"#{config_path}/#{settings_file}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_settings
|
18
|
+
{
|
19
|
+
version: VERSION
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def settings
|
24
|
+
return @settings if @settings
|
25
|
+
save_settings(default_settings) unless File.exists?(settings_path)
|
26
|
+
@settings = YAML.load_file(settings_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def save_settings(settings = @settings)
|
30
|
+
create_config_path
|
31
|
+
save_yaml(settings_path, settings)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
class Skill
|
4
|
+
|
5
|
+
ATTRIBUTES = [:name, :internal_location, :external_location]
|
6
|
+
attr_accessor *ATTRIBUTES
|
7
|
+
|
8
|
+
def initialize(config = {})
|
9
|
+
config.each { |k,v| public_send("#{k}=",v) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
name
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
ATTRIBUTES.each_with_object({}) do |attribute, hash|
|
18
|
+
hash[attribute] = public_send(attribute)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def module
|
23
|
+
module_from_string(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def tasks
|
27
|
+
return @tasks if @tasks
|
28
|
+
require internal_location if internal_location
|
29
|
+
methods = self.module.instance_methods - Object.new.methods
|
30
|
+
@tasks = methods.map { |method| Task.new(name: method, skill: self) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_task
|
34
|
+
tasks.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_a_task?(name)
|
38
|
+
!!find_task(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_task(name)
|
42
|
+
tasks.detect{ |task| task.name.to_s.downcase == name.to_s.downcase }
|
43
|
+
end
|
44
|
+
|
45
|
+
def documentation?
|
46
|
+
!!has_a_task?(:documentation)
|
47
|
+
end
|
48
|
+
|
49
|
+
def documentation(*args)
|
50
|
+
execute_task(:documentation, self, *args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute_task(name, *args)
|
54
|
+
m = Module.new
|
55
|
+
require internal_location
|
56
|
+
m.extend(self.module)
|
57
|
+
m.send(name, *args)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def module_from_string(str)
|
63
|
+
str.split('::').inject(Object) do |mod, module_name|
|
64
|
+
mod.const_get(module_name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
module Skills
|
4
|
+
|
5
|
+
def skills_dir
|
6
|
+
@skill_dir ||= "skills"
|
7
|
+
end
|
8
|
+
attr_writer :skill_dir
|
9
|
+
|
10
|
+
def skills_path
|
11
|
+
"#{config_path}/#{skills_dir}/"
|
12
|
+
end
|
13
|
+
|
14
|
+
def system_skills_path(name=nil)
|
15
|
+
File.expand_path("../../skills/#{name}", __FILE__)
|
16
|
+
end
|
17
|
+
|
18
|
+
def skills
|
19
|
+
validate_knowledge
|
20
|
+
knowledge[:skills].map{ |config| Skill.new(config) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_skill(name)
|
24
|
+
skills.detect{ |skill| skill.name.downcase == name.downcase }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Billy
|
2
|
+
|
3
|
+
class Task
|
4
|
+
|
5
|
+
attr_accessor :skill
|
6
|
+
|
7
|
+
def initialize(config = {})
|
8
|
+
config.each { |k,v| public_send("#{k}=",v) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name
|
13
|
+
end
|
14
|
+
|
15
|
+
def name=(value)
|
16
|
+
@name = value.to_s.downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
name
|
21
|
+
end
|
22
|
+
|
23
|
+
def method
|
24
|
+
require skill.internal_location if skill.internal_location
|
25
|
+
skill.module.instance_method(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def source
|
29
|
+
self.method.source_location
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/billygoat.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require "billygoat/version"
|
5
|
+
require "billygoat/goat"
|
6
|
+
require "billygoat/disk"
|
7
|
+
require "billygoat/config"
|
8
|
+
require "billygoat/settings"
|
9
|
+
require "billygoat/credentials"
|
10
|
+
require "billygoat/knowledge"
|
11
|
+
require "billygoat/skill"
|
12
|
+
require "billygoat/skills"
|
13
|
+
require "billygoat/memory"
|
14
|
+
require "billygoat/task"
|
15
|
+
require "billygoat/brain"
|
16
|
+
require "billygoat/dependencies"
|
17
|
+
require "billygoat/documentation"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Document
|
2
|
+
|
3
|
+
#
|
4
|
+
# Generate markdown documentation
|
5
|
+
#
|
6
|
+
def markdown(skill = nil, task = nil)
|
7
|
+
|
8
|
+
skills = skill ? [brain.find_skill(skill)] : brain.skills
|
9
|
+
puts "# Default Goat Skills and Tasks"
|
10
|
+
skills.each do |skill|
|
11
|
+
puts "* [#{skill}](#user-content-skill-#{skill.name.downcase})"
|
12
|
+
end
|
13
|
+
puts
|
14
|
+
|
15
|
+
skills.each do |skill|
|
16
|
+
|
17
|
+
puts "## Skill: #{skill}"
|
18
|
+
|
19
|
+
if skill.documentation?
|
20
|
+
puts skill.documentation(task, :markdown)
|
21
|
+
else
|
22
|
+
puts documentation(skill, task)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def documentation(skill, task=nil)
|
32
|
+
tasks = task ? [skill.find_task(task)] : skill.tasks
|
33
|
+
tasks.map { |task| task_documentation(skill, task) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def task_documentation(skill, task)
|
37
|
+
Billy::Documentation.new(method: task.method, mode: :markdown).inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
data/lib/skills/git.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
def self.extended(mod)
|
4
|
+
Dependencies.gem_require('git')
|
5
|
+
require 'logger'
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
# Delegate command
|
10
|
+
#
|
11
|
+
#
|
12
|
+
def delegate(arg)
|
13
|
+
case
|
14
|
+
when repository.respond_to?(arg)
|
15
|
+
puts repository.send(arg)
|
16
|
+
when repository.lib.respond_to?(arg)
|
17
|
+
puts repository.lib.send(arg)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Documentation
|
23
|
+
#
|
24
|
+
def documentation(skill, task=nil, mode=nil)
|
25
|
+
|
26
|
+
base_skill = Billy::Skill.new(name: 'Git::Base')
|
27
|
+
lib_skill = Billy::Skill.new(name: 'Git::Lib')
|
28
|
+
all_tasks = base_skill.tasks + base_skill.tasks
|
29
|
+
tasks = task ? [base_skill.find_task(task) || lib_skill.find_task(task)] : all_tasks
|
30
|
+
tasks.map do |task|
|
31
|
+
Billy::Documentation.new(
|
32
|
+
method: task.method,
|
33
|
+
owner: skill.name.downcase,
|
34
|
+
mode: mode
|
35
|
+
).inspect
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def repository(path = '../chef')
|
43
|
+
Git.open(path, :log => Logger.new(StringIO.new))
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/skills/help.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Help
|
2
|
+
|
3
|
+
#
|
4
|
+
# Show all documentation
|
5
|
+
# Show the documentation for a skill
|
6
|
+
# Show the documentation for a skill and task
|
7
|
+
#
|
8
|
+
def explain(skill = nil, task = nil)
|
9
|
+
|
10
|
+
skills = skill ? [brain.find_skill(skill)] : brain.skills
|
11
|
+
skills.each do |skill|
|
12
|
+
|
13
|
+
puts "#"*80
|
14
|
+
puts "## Skill: #{skill}"
|
15
|
+
puts "##"
|
16
|
+
puts
|
17
|
+
|
18
|
+
if skill.documentation?
|
19
|
+
puts skill.documentation(task)
|
20
|
+
else
|
21
|
+
puts documentation(skill, task)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def documentation(skill, task=nil)
|
31
|
+
tasks = task ? [skill.find_task(task)] : skill.tasks
|
32
|
+
tasks.map { |task| task_documentation(skill, task) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def task_documentation(skill, task)
|
36
|
+
Billy::Documentation.new(method: task.method).inspect
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
data/lib/skills/learn.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Learn
|
2
|
+
|
3
|
+
#
|
4
|
+
# Learn a new task described in a local file
|
5
|
+
#
|
6
|
+
def file(path)
|
7
|
+
self.filepath = filepath
|
8
|
+
puts "Learning the skill from file #{@filepath}"
|
9
|
+
|
10
|
+
assert_file_exists
|
11
|
+
create_stash
|
12
|
+
stash_file
|
13
|
+
|
14
|
+
slate = Module.new
|
15
|
+
slate.module_eval(File.read(internal_location))
|
16
|
+
skill_name = slate.constants.first.to_s
|
17
|
+
skill = Billy::Skill.new(
|
18
|
+
name: skill_name,
|
19
|
+
internal_location: internal_location
|
20
|
+
)
|
21
|
+
brain.skill_set.skills << skill
|
22
|
+
brain.update_knowledge
|
23
|
+
brain.save_knowledge
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Learn a set of new tasks described in a local directory
|
28
|
+
#
|
29
|
+
def directory(path)
|
30
|
+
Dir.glob("#{path}/*.rb").each do |path|
|
31
|
+
file(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_accessor :filepath
|
38
|
+
|
39
|
+
def filename
|
40
|
+
filepath[/(?<=\/)[^\/]*?$/]
|
41
|
+
end
|
42
|
+
|
43
|
+
def internal_location
|
44
|
+
"#{brain.skills_path}#{filename}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def assert_file_exists
|
48
|
+
fail "Naaa, cannot find that file" unless File.exist?(filepath)
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_stash
|
52
|
+
FileUtils.mkdir_p(brain.skills_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def stash_file
|
56
|
+
FileUtils.cp(filepath, internal_location)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/skills/show.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Show
|
2
|
+
|
3
|
+
#
|
4
|
+
# Show all goat settings
|
5
|
+
#
|
6
|
+
def settings
|
7
|
+
puts brain.settings.to_yaml
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# Show a given goat setting
|
12
|
+
#
|
13
|
+
def setting(name)
|
14
|
+
puts brain.settings[name.to_sym].to_yaml
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Show the credentials for a particular service
|
19
|
+
#
|
20
|
+
def credentials(service = nil)
|
21
|
+
credentials = brain.credentials
|
22
|
+
credentials = credentials[service.to_sym] if service
|
23
|
+
puts credentials.to_yaml
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Update
|
2
|
+
|
3
|
+
#
|
4
|
+
# Update a given goat setting
|
5
|
+
#
|
6
|
+
def setting(name, value)
|
7
|
+
brain.settings[name.to_sym] = value
|
8
|
+
brain.save_settings
|
9
|
+
puts brain.settings[name.to_sym].to_yaml
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Update the credentials for a particular service
|
14
|
+
#
|
15
|
+
def credentials(service, *args)
|
16
|
+
args.each do |pair|
|
17
|
+
key, value = pair.split('=')
|
18
|
+
brain.credentials[service.to_sym][key.to_sym] = value
|
19
|
+
end
|
20
|
+
brain.save_credentials
|
21
|
+
puts brain.credentials[service.to_sym].to_yaml
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
data/spec/goat_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: billygoat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Derrick Parkhurst
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: git
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.2'
|
69
|
+
description: Goat do this for me
|
70
|
+
email:
|
71
|
+
- derrick.parkhurst@gmail.com
|
72
|
+
executables:
|
73
|
+
- goat
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE.txt
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- bin/goat
|
81
|
+
- lib/billygoat.rb
|
82
|
+
- lib/billygoat/brain.rb
|
83
|
+
- lib/billygoat/config.rb
|
84
|
+
- lib/billygoat/credentials.rb
|
85
|
+
- lib/billygoat/dependencies.rb
|
86
|
+
- lib/billygoat/disk.rb
|
87
|
+
- lib/billygoat/documentation.rb
|
88
|
+
- lib/billygoat/goat.rb
|
89
|
+
- lib/billygoat/knowledge.rb
|
90
|
+
- lib/billygoat/memory.rb
|
91
|
+
- lib/billygoat/settings.rb
|
92
|
+
- lib/billygoat/skill.rb
|
93
|
+
- lib/billygoat/skills.rb
|
94
|
+
- lib/billygoat/task.rb
|
95
|
+
- lib/billygoat/version.rb
|
96
|
+
- lib/skills/document.rb
|
97
|
+
- lib/skills/git.rb
|
98
|
+
- lib/skills/help.rb
|
99
|
+
- lib/skills/learn.rb
|
100
|
+
- lib/skills/show.rb
|
101
|
+
- lib/skills/update.rb
|
102
|
+
- spec/goat_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
homepage: https://github.com/thirtysixthspan/billygoat
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.4.5
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Goat do this for me
|
128
|
+
test_files: []
|