goon 0.0.2
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.
- data/README.md +188 -0
- data/lib/goon/competency.rb +41 -0
- data/lib/goon/heist.rb +39 -0
- data/lib/goon/version.rb +3 -0
- data/lib/goon.rb +58 -0
- metadata +112 -0
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
## Goon ##
|
2
|
+
|
3
|
+
Goon is a minion that pulls off heists
|
4
|
+
|
5
|
+
## Release Notes ##
|
6
|
+
|
7
|
+
* 0.0.2 - For example, our naivite caused us to think that we could cut a gem without jumping through hoops.
|
8
|
+
* 0.0.1 - Initial release. Basically usable and spec'd, but probably has some
|
9
|
+
naive implementation.
|
10
|
+
|
11
|
+
## Goons? ##
|
12
|
+
|
13
|
+
That is, a goon object, being taught the proper competencies, will pull off a
|
14
|
+
valid Heist and remember facts related to the process.
|
15
|
+
|
16
|
+
### .new ###
|
17
|
+
|
18
|
+
Creating a Goon is pretty easy, but a Goon created without a Heist is just
|
19
|
+
about worthless. The following will actually do the trick:
|
20
|
+
|
21
|
+
goon = Goon.new
|
22
|
+
|
23
|
+
The following, however, is a lot more useful (or would be if the Heist wasn't
|
24
|
+
just terribly cordial):
|
25
|
+
|
26
|
+
goon = Goon.new(
|
27
|
+
:heist => OpenStruct(
|
28
|
+
:name => 'Heist!!!',
|
29
|
+
:body => 'puts "hello"'
|
30
|
+
)
|
31
|
+
)
|
32
|
+
|
33
|
+
In addition to the :heist option, you can pass in an array of Competency objects
|
34
|
+
with the :competencies option or a hash of facts with the :facts option.
|
35
|
+
|
36
|
+
### #learn_competency ###
|
37
|
+
|
38
|
+
A goon can be taught a new competency. The learn_competency method takes an
|
39
|
+
object conforming to the Competency API and injects it into the goon that is
|
40
|
+
learning. There is also a plural form, learn_competencies, that takes an array
|
41
|
+
of competencies.
|
42
|
+
|
43
|
+
### #remember ###
|
44
|
+
|
45
|
+
A goon can remember facts. The remember method takes a hash where the keys are
|
46
|
+
fact names and the values are the meat of the fact.
|
47
|
+
|
48
|
+
### #recall ###
|
49
|
+
|
50
|
+
A goon can recall facts that it has remembered. The recall method takes a fact name.
|
51
|
+
|
52
|
+
### #forget ###
|
53
|
+
|
54
|
+
A goon can forget facts that it has remembered. The forget method takes a fact name.
|
55
|
+
|
56
|
+
### #run ###
|
57
|
+
|
58
|
+
A goon can pull off a Heist. The run method takes no arguments, but returns the facts that it has remembered when it is done with the job.
|
59
|
+
|
60
|
+
## Heists? ##
|
61
|
+
|
62
|
+
Yeah. Heists. We like metaphors.
|
63
|
+
|
64
|
+
A Heist, when it comes down, is any object that fits this bill:
|
65
|
+
|
66
|
+
* Has a name method that returns a non-empty string
|
67
|
+
* Has a body method that returns a non-empty string
|
68
|
+
|
69
|
+
That said, goon/heist contains the Goon::Heist class that will absolutely always
|
70
|
+
work with Goon, and it also validates itself on creation. Unfortunately, using
|
71
|
+
this class to make a Heist means that exceptions will be raised if an invalid
|
72
|
+
name or body is provided.
|
73
|
+
|
74
|
+
### name ###
|
75
|
+
|
76
|
+
The name of a Heist is just that ... it is a (preferably) unique identifier.
|
77
|
+
|
78
|
+
### body ###
|
79
|
+
|
80
|
+
The body of a Heist is a snippet of code. Most typically,
|
81
|
+
|
82
|
+
## Competencies? ##
|
83
|
+
|
84
|
+
A Competency is a skill that one can teach one's goons. Much like a Heist, a
|
85
|
+
competency can be pretty much any object so long as it fits the following:
|
86
|
+
|
87
|
+
* Has a name method that returns a non-empty string
|
88
|
+
* Has a body method that returns a non-empty string
|
89
|
+
|
90
|
+
If you would like to use our reference Competency, it lives in goon/competency.
|
91
|
+
The same caveats (and then some) apply to this as do to our reference Heist.
|
92
|
+
|
93
|
+
When a Goon learns a Competency, the Goon in question gains an equivalent
|
94
|
+
instance method to said Competency. So, say that we have a Competency named
|
95
|
+
"hello" with the following body:
|
96
|
+
|
97
|
+
puts "hello"
|
98
|
+
|
99
|
+
After learning "hello," my goon will have the following method:
|
100
|
+
|
101
|
+
def hello(options = {})
|
102
|
+
puts "hello"
|
103
|
+
end
|
104
|
+
|
105
|
+
As you can see, a Competency receives a hash of options so that you can pass
|
106
|
+
information into them.
|
107
|
+
|
108
|
+
### name ###
|
109
|
+
|
110
|
+
The name of a Competency is a string that could be used as a Ruby method name.
|
111
|
+
That being the case, it cannot contain spaces, it must be punctuated properly,
|
112
|
+
so on. A good rule of thumb is that if at all possible, make your Competency
|
113
|
+
name a single word without punctuation, et cetera.
|
114
|
+
|
115
|
+
### body ###
|
116
|
+
|
117
|
+
The body of a Competency is the meat of the method that is generated upon
|
118
|
+
learning the Competency. Aside from emptyness, we don't really do any checking
|
119
|
+
on this, but take our word that you absolutely want this to be valid Ruby.
|
120
|
+
|
121
|
+
## Example ##
|
122
|
+
|
123
|
+
<pre>
|
124
|
+
require 'goon'
|
125
|
+
require 'goon/competency'
|
126
|
+
require 'goon/heist'
|
127
|
+
require 'json'
|
128
|
+
|
129
|
+
competencies = []
|
130
|
+
|
131
|
+
# The following are well-formed competencies. Those that are not well-formed
|
132
|
+
# raise Goon::Competency::InvalidCompetency
|
133
|
+
competencies << Goon::Competency.new(
|
134
|
+
:name => 'hello',
|
135
|
+
:description => "Say hello",
|
136
|
+
:body => "puts 'hello'"
|
137
|
+
)
|
138
|
+
|
139
|
+
competencies << Goon::Competency.new(
|
140
|
+
:name => 'parrot',
|
141
|
+
:description => "Repeat after me, Polly",
|
142
|
+
:body => "puts options[:phrase]"
|
143
|
+
)
|
144
|
+
|
145
|
+
heist = Goon::Heist.new(
|
146
|
+
:name => 'The Stinky Teen Job',
|
147
|
+
:body => <<-EOS
|
148
|
+
hello
|
149
|
+
hello
|
150
|
+
hello
|
151
|
+
hello
|
152
|
+
puts 'with the lights down'
|
153
|
+
parrot :phrase => "it's less dangerous"
|
154
|
+
EOS
|
155
|
+
)
|
156
|
+
|
157
|
+
my_goon = Goon.new(:competencies => competencies, :heist => heist)
|
158
|
+
puts "## Running the '#{heist.name}' heist ..."
|
159
|
+
goon_results = my_goon.run
|
160
|
+
puts "## Finished the '#{heist.name}' heist"
|
161
|
+
|
162
|
+
# Since the goon was not instructed to remember anything, an empty hash is
|
163
|
+
# returned.
|
164
|
+
|
165
|
+
puts "## '#{heist.name}' Results: #{goon_results.to_json}\n"
|
166
|
+
|
167
|
+
heist = Goon::Heist.new(
|
168
|
+
:name => 'Remember Remember',
|
169
|
+
:body => <<-EOS
|
170
|
+
puts "remembering the date"
|
171
|
+
remember :when => '1605-11-05'
|
172
|
+
puts "remembering the act"
|
173
|
+
remember :what => 'The Gunpowder (Treason and) Plot'
|
174
|
+
EOS
|
175
|
+
)
|
176
|
+
|
177
|
+
my_goon = Goon.new(:heist => heist)
|
178
|
+
|
179
|
+
puts "## Running the '#{heist.name}' heist ..."
|
180
|
+
|
181
|
+
goon_results = my_goon.run
|
182
|
+
|
183
|
+
puts "## Finished the '#{heist.name}' heist"
|
184
|
+
|
185
|
+
# Since the goon was told to remember things, we get back a non-empty hash.
|
186
|
+
|
187
|
+
puts "## '#{heist.name}' Results: #{goon_results.to_json}"
|
188
|
+
</pre>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'goon'
|
2
|
+
|
3
|
+
class Goon::Competency
|
4
|
+
class InvalidCompetency < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_accessor :description
|
8
|
+
attr_reader :name, :body
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@name = options[:name].to_s
|
12
|
+
@description = options[:description].to_s || "This competency has no description"
|
13
|
+
@body = options[:body].to_s
|
14
|
+
|
15
|
+
validate_name!
|
16
|
+
validate_body!
|
17
|
+
end
|
18
|
+
|
19
|
+
def name=(new_name)
|
20
|
+
@name = new_name
|
21
|
+
validate_name!
|
22
|
+
end
|
23
|
+
|
24
|
+
def body=(new_body)
|
25
|
+
@body = new_body
|
26
|
+
validate_body!
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validate_name!
|
32
|
+
raise InvalidCompetency, "name cannot be nil" if @name.nil?
|
33
|
+
raise InvalidCompetency, "name cannot be empty" if @name.gsub(/\s+/, '').empty?
|
34
|
+
raise InvalidCompetency, "'#{@name}' is not a valid method name" unless (@name.to_sym.inspect =~ /[@$\"]/).nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_body!
|
38
|
+
raise InvalidCompetency, "body cannot be nil" if @body.nil?
|
39
|
+
raise InvalidCompetency, "body cannot be blank" if @body.gsub(/\s+/, '').empty?
|
40
|
+
end
|
41
|
+
end
|
data/lib/goon/heist.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'goon'
|
2
|
+
|
3
|
+
class Goon::Heist
|
4
|
+
class InvalidHeist < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :name, :body
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@name = options[:name]
|
11
|
+
@description = options[:description]
|
12
|
+
@body = options[:body]
|
13
|
+
|
14
|
+
validate_name!
|
15
|
+
validate_body!
|
16
|
+
end
|
17
|
+
|
18
|
+
def name=(new_name)
|
19
|
+
@name = new_name
|
20
|
+
validate_name!
|
21
|
+
end
|
22
|
+
|
23
|
+
def body=(new_body)
|
24
|
+
@body = new_body
|
25
|
+
validate_body!
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_name!
|
31
|
+
raise InvalidHeist, "name cannot be nil" if @name.nil?
|
32
|
+
raise InvalidHeist, "name cannot be blank" if @name.gsub(/\s+/, '').empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_body!
|
36
|
+
raise InvalidHeist, "body cannot be nil" if @body.nil?
|
37
|
+
raise InvalidHeist, "body cannot be blank" if @body.gsub(/\s+/, '').empty?
|
38
|
+
end
|
39
|
+
end
|
data/lib/goon/version.rb
ADDED
data/lib/goon.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'goon/version'
|
2
|
+
|
3
|
+
class Goon
|
4
|
+
attr_reader :competencies, :heist, :facts
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
competencies = options[:competencies] || []
|
8
|
+
@competencies = []
|
9
|
+
@heist = options[:heist]
|
10
|
+
@facts = options[:facts] || {}
|
11
|
+
learn_competencies(competencies)
|
12
|
+
end
|
13
|
+
|
14
|
+
def learn_competency(competency)
|
15
|
+
unless @competencies.include? competency
|
16
|
+
instance_eval <<-EOS
|
17
|
+
def #{competency.name}(options = {})
|
18
|
+
#{competency.body}
|
19
|
+
end
|
20
|
+
EOS
|
21
|
+
@competencies << competency
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def learn_competencies(competencies)
|
26
|
+
competencies.each do |competency|
|
27
|
+
learn_competency(competency)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#def unlearn_competency(name)
|
32
|
+
#instance_eval "undef #{name}"
|
33
|
+
#@competencies.delete(@competencies.select {|x| x.name == name}.first)
|
34
|
+
#end
|
35
|
+
|
36
|
+
#def unlearn_competencies
|
37
|
+
#@competencies.each do |competency|
|
38
|
+
#unlearn_competency(competency.name)
|
39
|
+
#end
|
40
|
+
#end
|
41
|
+
|
42
|
+
def remember(options = {})
|
43
|
+
@facts.merge!(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def recall(name)
|
47
|
+
@facts[name]
|
48
|
+
end
|
49
|
+
|
50
|
+
def forget(name)
|
51
|
+
@facts.delete(name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def run
|
55
|
+
instance_eval @heist.body
|
56
|
+
@facts
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: goon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dennis Walters
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &10212600 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.9.2
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *10212600
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rdoc
|
27
|
+
requirement: &10212140 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.8'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *10212140
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &10211680 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.8.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *10211680
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: coco
|
49
|
+
requirement: &10211220 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.6'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *10211220
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: mocha
|
60
|
+
requirement: &10210760 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 0.10.5
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *10210760
|
69
|
+
description: ! ' Goon is a minion that pulls off Heists
|
70
|
+
|
71
|
+
'
|
72
|
+
email: pooster@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/goon.rb
|
78
|
+
- lib/goon/version.rb
|
79
|
+
- lib/goon/heist.rb
|
80
|
+
- lib/goon/competency.rb
|
81
|
+
- README.md
|
82
|
+
homepage: https://github.com/ess/goon
|
83
|
+
licenses: []
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options:
|
86
|
+
- --title
|
87
|
+
- goon
|
88
|
+
- --main
|
89
|
+
- README.md
|
90
|
+
- -ri
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.10
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: A loyal lackey
|
112
|
+
test_files: []
|