angry_mob 0.1.0
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/LICENSE +21 -0
- data/README.md +123 -0
- data/bin/mob +139 -0
- data/lib/angry_mob.rb +28 -0
- data/lib/angry_mob/act.rb +111 -0
- data/lib/angry_mob/act/scheduler.rb +143 -0
- data/lib/angry_mob/action.rb +11 -0
- data/lib/angry_mob/builder.rb +115 -0
- data/lib/angry_mob/extend.rb +10 -0
- data/lib/angry_mob/extend/array.rb +30 -0
- data/lib/angry_mob/extend/blank.rb +108 -0
- data/lib/angry_mob/extend/blankslate.rb +109 -0
- data/lib/angry_mob/extend/dictionary.rb +140 -0
- data/lib/angry_mob/extend/hash.rb +67 -0
- data/lib/angry_mob/extend/object.rb +21 -0
- data/lib/angry_mob/extend/pathname.rb +23 -0
- data/lib/angry_mob/extend/string.rb +8 -0
- data/lib/angry_mob/log.rb +28 -0
- data/lib/angry_mob/mob.rb +77 -0
- data/lib/angry_mob/mob_loader.rb +115 -0
- data/lib/angry_mob/node.rb +44 -0
- data/lib/angry_mob/notifier.rb +76 -0
- data/lib/angry_mob/target.rb +257 -0
- data/lib/angry_mob/target/arguments.rb +71 -0
- data/lib/angry_mob/target/call.rb +57 -0
- data/lib/angry_mob/target/default_resource_locator.rb +11 -0
- data/lib/angry_mob/target/defaults.rb +23 -0
- data/lib/angry_mob/target/mother.rb +66 -0
- data/lib/angry_mob/target/notify.rb +57 -0
- data/lib/angry_mob/target/tracking.rb +96 -0
- data/lib/angry_mob/ui.rb +247 -0
- data/lib/angry_mob/util.rb +11 -0
- data/lib/angry_mob/vendored.rb +8 -0
- data/lib/angry_mob/version.rb +3 -0
- data/vendor/angry_hash/Rakefile +17 -0
- data/vendor/angry_hash/VERSION +1 -0
- data/vendor/angry_hash/angry_hash.gemspec +47 -0
- data/vendor/angry_hash/examples/accessors_eg.rb +46 -0
- data/vendor/angry_hash/examples/creation_eg.rb +43 -0
- data/vendor/angry_hash/examples/dsl.eg.rb +18 -0
- data/vendor/angry_hash/examples/dup_eg.rb +86 -0
- data/vendor/angry_hash/examples/eg_helper.rb +24 -0
- data/vendor/angry_hash/examples/merge_eg.rb +135 -0
- data/vendor/angry_hash/lib/angry_hash.rb +215 -0
- data/vendor/angry_hash/lib/angry_hash/dsl.rb +44 -0
- data/vendor/angry_hash/lib/angry_hash/extension_tracking.rb +12 -0
- data/vendor/angry_hash/lib/angry_hash/merge_string.rb +58 -0
- data/vendor/json/COPYING +58 -0
- data/vendor/json/GPL +340 -0
- data/vendor/json/README +360 -0
- data/vendor/json/lib/json/common.rb +371 -0
- data/vendor/json/lib/json/pure.rb +77 -0
- data/vendor/json/lib/json/pure/generator.rb +443 -0
- data/vendor/json/lib/json/pure/parser.rb +303 -0
- data/vendor/json/lib/json/version.rb +8 -0
- data/vendor/thor/CHANGELOG.rdoc +89 -0
- data/vendor/thor/LICENSE +20 -0
- data/vendor/thor/README.rdoc +297 -0
- data/vendor/thor/Thorfile +69 -0
- data/vendor/thor/bin/rake2thor +86 -0
- data/vendor/thor/bin/thor +6 -0
- data/vendor/thor/lib/thor.rb +244 -0
- data/vendor/thor/lib/thor/actions.rb +275 -0
- data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
- data/vendor/thor/lib/thor/actions/directory.rb +91 -0
- data/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
- data/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
- data/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
- data/vendor/thor/lib/thor/base.rb +540 -0
- data/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
- data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/vendor/thor/lib/thor/error.rb +30 -0
- data/vendor/thor/lib/thor/group.rb +271 -0
- data/vendor/thor/lib/thor/invocation.rb +180 -0
- data/vendor/thor/lib/thor/parser.rb +4 -0
- data/vendor/thor/lib/thor/parser/argument.rb +67 -0
- data/vendor/thor/lib/thor/parser/arguments.rb +150 -0
- data/vendor/thor/lib/thor/parser/option.rb +128 -0
- data/vendor/thor/lib/thor/parser/options.rb +169 -0
- data/vendor/thor/lib/thor/rake_compat.rb +66 -0
- data/vendor/thor/lib/thor/runner.rb +314 -0
- data/vendor/thor/lib/thor/shell.rb +83 -0
- data/vendor/thor/lib/thor/shell/basic.rb +239 -0
- data/vendor/thor/lib/thor/shell/color.rb +108 -0
- data/vendor/thor/lib/thor/task.rb +102 -0
- data/vendor/thor/lib/thor/util.rb +224 -0
- data/vendor/thor/lib/thor/version.rb +3 -0
- data/vendor/thor/spec/actions/create_file_spec.rb +170 -0
- data/vendor/thor/spec/actions/directory_spec.rb +131 -0
- data/vendor/thor/spec/actions/empty_directory_spec.rb +91 -0
- data/vendor/thor/spec/actions/file_manipulation_spec.rb +271 -0
- data/vendor/thor/spec/actions/inject_into_file_spec.rb +135 -0
- data/vendor/thor/spec/actions_spec.rb +292 -0
- data/vendor/thor/spec/base_spec.rb +263 -0
- data/vendor/thor/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
- data/vendor/thor/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/vendor/thor/spec/fixtures/application.rb +2 -0
- data/vendor/thor/spec/fixtures/bundle/execute.rb +6 -0
- data/vendor/thor/spec/fixtures/bundle/main.thor +1 -0
- data/vendor/thor/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/vendor/thor/spec/fixtures/doc/README +3 -0
- data/vendor/thor/spec/fixtures/doc/config.rb +1 -0
- data/vendor/thor/spec/fixtures/group.thor +90 -0
- data/vendor/thor/spec/fixtures/invoke.thor +112 -0
- data/vendor/thor/spec/fixtures/script.thor +145 -0
- data/vendor/thor/spec/fixtures/task.thor +10 -0
- data/vendor/thor/spec/group_spec.rb +171 -0
- data/vendor/thor/spec/invocation_spec.rb +107 -0
- data/vendor/thor/spec/parser/argument_spec.rb +47 -0
- data/vendor/thor/spec/parser/arguments_spec.rb +64 -0
- data/vendor/thor/spec/parser/option_spec.rb +202 -0
- data/vendor/thor/spec/parser/options_spec.rb +292 -0
- data/vendor/thor/spec/rake_compat_spec.rb +68 -0
- data/vendor/thor/spec/runner_spec.rb +210 -0
- data/vendor/thor/spec/shell/basic_spec.rb +205 -0
- data/vendor/thor/spec/shell/color_spec.rb +41 -0
- data/vendor/thor/spec/shell_spec.rb +34 -0
- data/vendor/thor/spec/spec.opts +1 -0
- data/vendor/thor/spec/spec_helper.rb +54 -0
- data/vendor/thor/spec/task_spec.rb +69 -0
- data/vendor/thor/spec/thor_spec.rb +237 -0
- data/vendor/thor/spec/util_spec.rb +163 -0
- data/vendor/thor/thor.gemspec +120 -0
- metadata +199 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
require "angry_mob/target/tracking"
|
|
2
|
+
|
|
3
|
+
class AngryMob
|
|
4
|
+
class TargetError < StandardError; end
|
|
5
|
+
class Target
|
|
6
|
+
autoload :Mother , 'angry_mob/target/mother'
|
|
7
|
+
autoload :Call , 'angry_mob/target/call'
|
|
8
|
+
autoload :Defaults, "angry_mob/target/defaults"
|
|
9
|
+
autoload :Notify , "angry_mob/target/notify"
|
|
10
|
+
autoload :Arguments, "angry_mob/target/arguments"
|
|
11
|
+
|
|
12
|
+
autoload :DefaultResourceLocator, "angry_mob/target/default_resource_locator"
|
|
13
|
+
|
|
14
|
+
include Tracking
|
|
15
|
+
|
|
16
|
+
# Ok lets define some class level helpings.
|
|
17
|
+
class << self
|
|
18
|
+
def default_action
|
|
19
|
+
@set_default_action = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def actions
|
|
23
|
+
@actions ||= ['nothing']
|
|
24
|
+
end
|
|
25
|
+
def all_actions
|
|
26
|
+
@all_actions ||= from_superclass(:all_actions, ['nothing'])
|
|
27
|
+
@all_actions |= actions
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def default_action_name
|
|
31
|
+
@default_action
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Based on the args, makes a unique key for a target instance.
|
|
36
|
+
# This could be overridden by subclasses.
|
|
37
|
+
def instance_key(args)
|
|
38
|
+
args.key
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
def create_action(method)
|
|
43
|
+
return if self == AngryMob::Target # XXX protect methods properly and remove this
|
|
44
|
+
|
|
45
|
+
if @set_default_action && @default_action
|
|
46
|
+
raise ArgumentError, "#{nickname}() can only have one default_action"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@default_action = method.to_s if @set_default_action
|
|
50
|
+
actions << method.to_s
|
|
51
|
+
|
|
52
|
+
@set_default_action = nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def baseclass #:nodoc:
|
|
56
|
+
AngryMob::Target
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end # class << self
|
|
60
|
+
|
|
61
|
+
def nickname
|
|
62
|
+
self.class.nickname
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
attr_reader :args, :current_action
|
|
66
|
+
attr_accessor :act
|
|
67
|
+
|
|
68
|
+
def mob; act.mob end
|
|
69
|
+
def ui ; mob.ui end
|
|
70
|
+
|
|
71
|
+
def log(message); mob.ui.log message end
|
|
72
|
+
|
|
73
|
+
#### Call generation
|
|
74
|
+
|
|
75
|
+
# nothing actions are no-ops but by being called, prevent the default action being called
|
|
76
|
+
def nothing(*)
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#### Definition-time helpers
|
|
82
|
+
def to_s
|
|
83
|
+
if default_object
|
|
84
|
+
"#{nickname}(#{default_object})"
|
|
85
|
+
else
|
|
86
|
+
"#{nickname}()"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Executes actions with full context and all the trimmings.
|
|
92
|
+
def noticing_changes(args,&blk)
|
|
93
|
+
reset!
|
|
94
|
+
@args = args
|
|
95
|
+
|
|
96
|
+
ui.push(to_s) do
|
|
97
|
+
do_validation!
|
|
98
|
+
|
|
99
|
+
before_state
|
|
100
|
+
before_call if respond_to?(:before_call)
|
|
101
|
+
|
|
102
|
+
ui.debug "before_state=#{before_state.inspect}"
|
|
103
|
+
|
|
104
|
+
if skip?
|
|
105
|
+
ui.skipped! "skipping"
|
|
106
|
+
return
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Here's the core of the target:
|
|
110
|
+
if node.dry_run?
|
|
111
|
+
ui.skipped! "DRY RUN: skipping action"
|
|
112
|
+
return
|
|
113
|
+
else
|
|
114
|
+
# riiight here:
|
|
115
|
+
yield
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
ui.debug "after_state=#{state.inspect}"
|
|
119
|
+
|
|
120
|
+
# If the state's changed, let it be known
|
|
121
|
+
if state_changed?
|
|
122
|
+
changed
|
|
123
|
+
notify
|
|
124
|
+
ui.ok!
|
|
125
|
+
else
|
|
126
|
+
ui.skipped! "target didn't change"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Has the state changed?
|
|
134
|
+
# dir("/tmp/config").changed? && sh("echo it changed")
|
|
135
|
+
def changed?
|
|
136
|
+
state_changed?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
protected
|
|
141
|
+
|
|
142
|
+
def reset!
|
|
143
|
+
@skip = nil
|
|
144
|
+
@args = nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def skip!
|
|
148
|
+
@skip = true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def skip?
|
|
152
|
+
!! @skip
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
#### Runtime
|
|
156
|
+
|
|
157
|
+
# Do some very basic runtime validation. This validation is bound very late!
|
|
158
|
+
def do_validation!
|
|
159
|
+
validate!
|
|
160
|
+
unless @problems.blank?
|
|
161
|
+
@problems.each {|p| ui.error "problem: #{p}"}
|
|
162
|
+
raise "target[#{nickname}] wasn't valid"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Targets should override this (possibly calling super) to do their own validation.
|
|
167
|
+
def validate!
|
|
168
|
+
problem!("The default object wasn't set") if default_object.blank?
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Flag a validation problem.
|
|
172
|
+
def problem!(problem)
|
|
173
|
+
@problems ||= []
|
|
174
|
+
@problems << problem
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Calculate and cache the state before any actions have been performed.
|
|
179
|
+
def before_state
|
|
180
|
+
@before_state ||= state
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def node
|
|
186
|
+
mob.node
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def mk_notify
|
|
190
|
+
Notify.new(act)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Called when the state has changed.
|
|
194
|
+
# Very simply delegates notification to the target scheduler
|
|
195
|
+
def notify
|
|
196
|
+
mob.notifier.notify( args.notify ) if args.notify
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Give the target itself a neat place to react to changes.
|
|
200
|
+
# Default implementation is a no-op.
|
|
201
|
+
def changed
|
|
202
|
+
ui.log "target changed"
|
|
203
|
+
# no-op
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# has the state changed?
|
|
207
|
+
def state_changed?
|
|
208
|
+
before_state != state
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Returns the state of the target.
|
|
212
|
+
# Default implementation is a random number (i.e. it always changes)
|
|
213
|
+
def state
|
|
214
|
+
{
|
|
215
|
+
:rand => rand
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# returns the default object
|
|
220
|
+
# targets can customise this
|
|
221
|
+
# the default is the default_object argument.
|
|
222
|
+
# See #initialize for how the default_option argument is set.
|
|
223
|
+
def default_object(clear=false)
|
|
224
|
+
@default_object = nil if clear
|
|
225
|
+
@default_object ||= default_object!
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def default_object!
|
|
229
|
+
return unless args
|
|
230
|
+
|
|
231
|
+
case args.default_object
|
|
232
|
+
when Proc
|
|
233
|
+
args.default_object[]
|
|
234
|
+
else
|
|
235
|
+
args.default_object
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# delegates to the node's resource locator
|
|
240
|
+
def resource(name)
|
|
241
|
+
node.resource_locator[self,name]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def log(message="")
|
|
245
|
+
ui.log(message)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# delegate to the default object
|
|
249
|
+
def method_missing(method,*args,&blk)
|
|
250
|
+
if (dobj = default_object) && dobj.respond_to?(method)
|
|
251
|
+
dobj.send(method,*args,&blk)
|
|
252
|
+
else
|
|
253
|
+
super
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
class AngryMob
|
|
2
|
+
class Target
|
|
3
|
+
class Arguments
|
|
4
|
+
attr_reader :args, :actions
|
|
5
|
+
|
|
6
|
+
def initialize(*args,&blk)
|
|
7
|
+
@args = extract_args(args,&blk)
|
|
8
|
+
extract_actions!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.parse(input,&blk)
|
|
12
|
+
case input
|
|
13
|
+
when Arguments
|
|
14
|
+
input
|
|
15
|
+
when Array
|
|
16
|
+
if input.size == 1 && Arguments === input.first
|
|
17
|
+
input.first
|
|
18
|
+
else
|
|
19
|
+
self.new(input,&blk)
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
self.new(input,&blk)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# update args without updating actions too
|
|
27
|
+
def update_preserving_actions(other_args)
|
|
28
|
+
other_args = self.class.parse(other_args)
|
|
29
|
+
@args.deep_update(other_args.args)
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def method_missing(meth,*args,&block)
|
|
34
|
+
@args.send(meth,*args,&block)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def extract_actions!
|
|
38
|
+
@actions = [ @args.delete('actions'), @args.delete('action') ].norm.map {|s| s.to_s}
|
|
39
|
+
end
|
|
40
|
+
def actions=(array)
|
|
41
|
+
@actions = array.norm.map{|s| s.to_s}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_args(args,&blk)
|
|
45
|
+
args.flatten!
|
|
46
|
+
|
|
47
|
+
case args.size
|
|
48
|
+
when 0,1
|
|
49
|
+
if Hash === args[0]
|
|
50
|
+
new_args = AngryHash.__convert_without_dup(args[0])
|
|
51
|
+
new_args.default_object = new_args
|
|
52
|
+
else
|
|
53
|
+
new_args = AngryHash.new
|
|
54
|
+
new_args.default_object = args[0]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
when 2
|
|
58
|
+
new_args = AngryHash.__convert_without_dup(args[1])
|
|
59
|
+
new_args.default_object = args[0]
|
|
60
|
+
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError, "usage: nickname(default_object, [ :optional_hash_of => opts ])"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
new_args.__eval_as_dsl(&blk) if block_given?
|
|
66
|
+
|
|
67
|
+
new_args
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class AngryMob
|
|
2
|
+
class Target
|
|
3
|
+
class Call
|
|
4
|
+
attr_reader :args, :klass, :actions
|
|
5
|
+
attr_accessor :target
|
|
6
|
+
|
|
7
|
+
def initialize(klass,args)
|
|
8
|
+
@klass = klass
|
|
9
|
+
@args = Arguments.parse(args)
|
|
10
|
+
validate_actions!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.instance_key(klass,args)
|
|
14
|
+
klass.instance_key(Arguments.parse(args))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def instance_key
|
|
18
|
+
self.class.instance_key(klass,args)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_args(new_args)
|
|
22
|
+
@args = Arguments.parse(new_args).update_preserving_actions(args)
|
|
23
|
+
validate_actions!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def merge_defaults(defaults)
|
|
27
|
+
args.reverse_deep_merge!(defaults)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def call(act, hints={})
|
|
31
|
+
target.act = act
|
|
32
|
+
target.noticing_changes(args) {
|
|
33
|
+
actions.each {|action|
|
|
34
|
+
target.send(action)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def nickname
|
|
40
|
+
klass.nickname
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate_actions!
|
|
44
|
+
@actions = args.actions
|
|
45
|
+
|
|
46
|
+
extras = actions - klass.all_actions
|
|
47
|
+
raise(ArgumentError, "#{nickname}() unknown actions #{extras.inspect} known=#{klass.all_actions.inspect}") unless extras.empty? || extras == ['nothing']
|
|
48
|
+
|
|
49
|
+
actions << klass.default_action_name if actions.empty?
|
|
50
|
+
|
|
51
|
+
if actions.norm.empty?
|
|
52
|
+
raise ArgumentError, "#{klass.nickname}() no actions selected, and no default action defined"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class AngryMob
|
|
2
|
+
class Target
|
|
3
|
+
class Defaults
|
|
4
|
+
def initialize
|
|
5
|
+
@contexts = Hash.new {|h,k| h[k] = []}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def defaults_for(name)
|
|
9
|
+
@contexts[name].last || {}
|
|
10
|
+
# TODO -> merge under some circumstances
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def method_missing(method, *args, &blk)
|
|
14
|
+
@contexts[method] << args.first
|
|
15
|
+
|
|
16
|
+
if block_given?
|
|
17
|
+
yield
|
|
18
|
+
@contexts[method].pop
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class AngryMob
|
|
2
|
+
class Target
|
|
3
|
+
class Mother
|
|
4
|
+
attr_reader :mob
|
|
5
|
+
|
|
6
|
+
def initialize(mob)
|
|
7
|
+
@mob = mob
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def target_classes
|
|
11
|
+
Target::Tracking.subclasses
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def pose_as(nickname,nickname_to_pose_as)
|
|
15
|
+
nickname = nickname.to_s
|
|
16
|
+
nickname_to_pose_as = nickname_to_pose_as.to_s
|
|
17
|
+
|
|
18
|
+
posing_class = target_classes[nickname ] || raise(TargetError, "posing class '#{nickname}' doesn't exist!")
|
|
19
|
+
old_class = target_classes[nickname_to_pose_as]
|
|
20
|
+
|
|
21
|
+
target_classes[nickname_to_pose_as] = posing_class
|
|
22
|
+
|
|
23
|
+
if old_class && instances = key_classes.delete(old_class)
|
|
24
|
+
# TODO - merge instances if required
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def clear_instances!
|
|
29
|
+
@target_instances = nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def target_instances
|
|
33
|
+
@target_instances ||= {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def unkeyed_instances
|
|
37
|
+
@unkeyed_instances ||= []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def key_classes
|
|
41
|
+
@key_classes ||= Hash.new {|h,k| h[k] = []}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def target_call(nickname, *args, &blk)
|
|
45
|
+
raise(MobError, "no target nicknamed '#{nickname}'\n#{target_classes.keys.inspect}") unless target_classes.key?(nickname.to_s)
|
|
46
|
+
klass = target_classes[nickname.to_s]
|
|
47
|
+
|
|
48
|
+
args = Arguments.parse(args,&blk)
|
|
49
|
+
|
|
50
|
+
if key = Target::Call.instance_key(klass,args)
|
|
51
|
+
if call = target_instances[key]
|
|
52
|
+
call.add_args(args)
|
|
53
|
+
else
|
|
54
|
+
call = target_instances[key] = Target::Call.new( klass, args )
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
unkeyed_instances << call = Target::Call.new( klass, args )
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
call.target ||= klass.new
|
|
61
|
+
|
|
62
|
+
call
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|