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.
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