angry_mob 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +123 -0
  3. data/bin/mob +139 -0
  4. data/lib/angry_mob.rb +28 -0
  5. data/lib/angry_mob/act.rb +111 -0
  6. data/lib/angry_mob/act/scheduler.rb +143 -0
  7. data/lib/angry_mob/action.rb +11 -0
  8. data/lib/angry_mob/builder.rb +115 -0
  9. data/lib/angry_mob/extend.rb +10 -0
  10. data/lib/angry_mob/extend/array.rb +30 -0
  11. data/lib/angry_mob/extend/blank.rb +108 -0
  12. data/lib/angry_mob/extend/blankslate.rb +109 -0
  13. data/lib/angry_mob/extend/dictionary.rb +140 -0
  14. data/lib/angry_mob/extend/hash.rb +67 -0
  15. data/lib/angry_mob/extend/object.rb +21 -0
  16. data/lib/angry_mob/extend/pathname.rb +23 -0
  17. data/lib/angry_mob/extend/string.rb +8 -0
  18. data/lib/angry_mob/log.rb +28 -0
  19. data/lib/angry_mob/mob.rb +77 -0
  20. data/lib/angry_mob/mob_loader.rb +115 -0
  21. data/lib/angry_mob/node.rb +44 -0
  22. data/lib/angry_mob/notifier.rb +76 -0
  23. data/lib/angry_mob/target.rb +257 -0
  24. data/lib/angry_mob/target/arguments.rb +71 -0
  25. data/lib/angry_mob/target/call.rb +57 -0
  26. data/lib/angry_mob/target/default_resource_locator.rb +11 -0
  27. data/lib/angry_mob/target/defaults.rb +23 -0
  28. data/lib/angry_mob/target/mother.rb +66 -0
  29. data/lib/angry_mob/target/notify.rb +57 -0
  30. data/lib/angry_mob/target/tracking.rb +96 -0
  31. data/lib/angry_mob/ui.rb +247 -0
  32. data/lib/angry_mob/util.rb +11 -0
  33. data/lib/angry_mob/vendored.rb +8 -0
  34. data/lib/angry_mob/version.rb +3 -0
  35. data/vendor/angry_hash/Rakefile +17 -0
  36. data/vendor/angry_hash/VERSION +1 -0
  37. data/vendor/angry_hash/angry_hash.gemspec +47 -0
  38. data/vendor/angry_hash/examples/accessors_eg.rb +46 -0
  39. data/vendor/angry_hash/examples/creation_eg.rb +43 -0
  40. data/vendor/angry_hash/examples/dsl.eg.rb +18 -0
  41. data/vendor/angry_hash/examples/dup_eg.rb +86 -0
  42. data/vendor/angry_hash/examples/eg_helper.rb +24 -0
  43. data/vendor/angry_hash/examples/merge_eg.rb +135 -0
  44. data/vendor/angry_hash/lib/angry_hash.rb +215 -0
  45. data/vendor/angry_hash/lib/angry_hash/dsl.rb +44 -0
  46. data/vendor/angry_hash/lib/angry_hash/extension_tracking.rb +12 -0
  47. data/vendor/angry_hash/lib/angry_hash/merge_string.rb +58 -0
  48. data/vendor/json/COPYING +58 -0
  49. data/vendor/json/GPL +340 -0
  50. data/vendor/json/README +360 -0
  51. data/vendor/json/lib/json/common.rb +371 -0
  52. data/vendor/json/lib/json/pure.rb +77 -0
  53. data/vendor/json/lib/json/pure/generator.rb +443 -0
  54. data/vendor/json/lib/json/pure/parser.rb +303 -0
  55. data/vendor/json/lib/json/version.rb +8 -0
  56. data/vendor/thor/CHANGELOG.rdoc +89 -0
  57. data/vendor/thor/LICENSE +20 -0
  58. data/vendor/thor/README.rdoc +297 -0
  59. data/vendor/thor/Thorfile +69 -0
  60. data/vendor/thor/bin/rake2thor +86 -0
  61. data/vendor/thor/bin/thor +6 -0
  62. data/vendor/thor/lib/thor.rb +244 -0
  63. data/vendor/thor/lib/thor/actions.rb +275 -0
  64. data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  65. data/vendor/thor/lib/thor/actions/directory.rb +91 -0
  66. data/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
  67. data/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
  68. data/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
  69. data/vendor/thor/lib/thor/base.rb +540 -0
  70. data/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
  71. data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  72. data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  73. data/vendor/thor/lib/thor/error.rb +30 -0
  74. data/vendor/thor/lib/thor/group.rb +271 -0
  75. data/vendor/thor/lib/thor/invocation.rb +180 -0
  76. data/vendor/thor/lib/thor/parser.rb +4 -0
  77. data/vendor/thor/lib/thor/parser/argument.rb +67 -0
  78. data/vendor/thor/lib/thor/parser/arguments.rb +150 -0
  79. data/vendor/thor/lib/thor/parser/option.rb +128 -0
  80. data/vendor/thor/lib/thor/parser/options.rb +169 -0
  81. data/vendor/thor/lib/thor/rake_compat.rb +66 -0
  82. data/vendor/thor/lib/thor/runner.rb +314 -0
  83. data/vendor/thor/lib/thor/shell.rb +83 -0
  84. data/vendor/thor/lib/thor/shell/basic.rb +239 -0
  85. data/vendor/thor/lib/thor/shell/color.rb +108 -0
  86. data/vendor/thor/lib/thor/task.rb +102 -0
  87. data/vendor/thor/lib/thor/util.rb +224 -0
  88. data/vendor/thor/lib/thor/version.rb +3 -0
  89. data/vendor/thor/spec/actions/create_file_spec.rb +170 -0
  90. data/vendor/thor/spec/actions/directory_spec.rb +131 -0
  91. data/vendor/thor/spec/actions/empty_directory_spec.rb +91 -0
  92. data/vendor/thor/spec/actions/file_manipulation_spec.rb +271 -0
  93. data/vendor/thor/spec/actions/inject_into_file_spec.rb +135 -0
  94. data/vendor/thor/spec/actions_spec.rb +292 -0
  95. data/vendor/thor/spec/base_spec.rb +263 -0
  96. data/vendor/thor/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  97. data/vendor/thor/spec/core_ext/ordered_hash_spec.rb +115 -0
  98. data/vendor/thor/spec/fixtures/application.rb +2 -0
  99. data/vendor/thor/spec/fixtures/bundle/execute.rb +6 -0
  100. data/vendor/thor/spec/fixtures/bundle/main.thor +1 -0
  101. data/vendor/thor/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  102. data/vendor/thor/spec/fixtures/doc/README +3 -0
  103. data/vendor/thor/spec/fixtures/doc/config.rb +1 -0
  104. data/vendor/thor/spec/fixtures/group.thor +90 -0
  105. data/vendor/thor/spec/fixtures/invoke.thor +112 -0
  106. data/vendor/thor/spec/fixtures/script.thor +145 -0
  107. data/vendor/thor/spec/fixtures/task.thor +10 -0
  108. data/vendor/thor/spec/group_spec.rb +171 -0
  109. data/vendor/thor/spec/invocation_spec.rb +107 -0
  110. data/vendor/thor/spec/parser/argument_spec.rb +47 -0
  111. data/vendor/thor/spec/parser/arguments_spec.rb +64 -0
  112. data/vendor/thor/spec/parser/option_spec.rb +202 -0
  113. data/vendor/thor/spec/parser/options_spec.rb +292 -0
  114. data/vendor/thor/spec/rake_compat_spec.rb +68 -0
  115. data/vendor/thor/spec/runner_spec.rb +210 -0
  116. data/vendor/thor/spec/shell/basic_spec.rb +205 -0
  117. data/vendor/thor/spec/shell/color_spec.rb +41 -0
  118. data/vendor/thor/spec/shell_spec.rb +34 -0
  119. data/vendor/thor/spec/spec.opts +1 -0
  120. data/vendor/thor/spec/spec_helper.rb +54 -0
  121. data/vendor/thor/spec/task_spec.rb +69 -0
  122. data/vendor/thor/spec/thor_spec.rb +237 -0
  123. data/vendor/thor/spec/util_spec.rb +163 -0
  124. data/vendor/thor/thor.gemspec +120 -0
  125. 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,11 @@
1
+ class AngryMob
2
+ class Target
3
+ class DefaultResourceLocator
4
+ def resource(target,name)
5
+ path = target.act.definition_file.to_s.sub(/\.([^\.]+)$/,'')
6
+ (path.pathname + name.to_s)
7
+ end
8
+ alias_method :[], :resource
9
+ end
10
+ end
11
+ 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