fate 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +34 -0
  2. data/lib/fate.rb +4 -2
  3. metadata +72 -76
  4. data/lib/hash_tree.rb +0 -313
@@ -0,0 +1,34 @@
1
+
2
+ Example of a service spec:
3
+
4
+ ```json
5
+ {
6
+ "commands": {
7
+ "redis": "redis-server ./redis.conf",
8
+ "mongod": "mongod run --quiet",
9
+ "http_server": "./bin/server -h 127.0.0.1 -p 8080",
10
+ }
11
+ }
12
+
13
+ ```
14
+
15
+ Command line usage:
16
+
17
+ fate -c service.json
18
+
19
+
20
+
21
+ Usage within Ruby:
22
+
23
+ ```ruby
24
+ require "fate"
25
+ require "json"
26
+ string = File.read("service.json")
27
+ configuration = JSON.parse(string, :symbolize_names => true)
28
+ spawner = Fate.new(configuration, :service_log => "logs/service.log")
29
+
30
+ spawner.start do
31
+ # run your tests
32
+ # when this block finishes evaluation, Fate shuts down the service
33
+ end
34
+ ```
@@ -1,8 +1,10 @@
1
1
  require "set"
2
2
 
3
+ gem "term-ansicolor"
4
+ gem "squeeze"
3
5
  require "term/ansicolor"
6
+ require "squeeze/hash_tree"
4
7
 
5
- require "hash_tree"
6
8
  # Cross-VM compatibility
7
9
  # thanks to http://ku1ik.com/2010/09/18/open3-and-the-pid-of-the-spawn.html
8
10
  # TODO: consider using systemu: https://github.com/ahoward/systemu/
@@ -30,7 +32,7 @@ class Fate
30
32
  else
31
33
  @log = STDOUT
32
34
  end
33
- commands = HashTree[@configuration[:commands]]
35
+ commands = Squeeze::HashTree[@configuration[:commands]]
34
36
 
35
37
  @completions = Set.new
36
38
 
metadata CHANGED
@@ -1,118 +1,114 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: fate
3
- version: !ruby/object:Gem::Version
4
- hash: 19
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 2
10
- version: 0.2.2
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Matthew King
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-08-29 00:00:00 -05:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: consolize
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 23
30
- segments:
31
- - 0
32
- - 2
33
- - 0
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
34
21
  version: 0.2.0
35
22
  type: :runtime
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.2.0
30
+ - !ruby/object:Gem::Dependency
38
31
  name: open4
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.3.0
38
+ type: :runtime
39
39
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- hash: 27
46
- segments:
47
- - 1
48
- - 3
49
- - 0
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
50
45
  version: 1.3.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: squeeze
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.2.0
51
54
  type: :runtime
52
- version_requirements: *id002
53
- - !ruby/object:Gem::Dependency
54
- name: term-ansicolor
55
55
  prerelease: false
56
- requirement: &id003 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: term-ansicolor
64
+ requirement: !ruby/object:Gem::Requirement
57
65
  none: false
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- hash: 23
62
- segments:
63
- - 1
64
- - 0
65
- - 0
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
66
69
  version: 1.0.0
67
70
  type: :runtime
68
- version_requirements: *id003
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.0
69
78
  description:
70
79
  email:
71
- executables:
80
+ executables:
72
81
  - fate
73
82
  extensions: []
74
-
75
83
  extra_rdoc_files: []
76
-
77
- files:
84
+ files:
78
85
  - bin/fate
79
86
  - LICENSE
87
+ - README.md
80
88
  - lib/fate.rb
81
- - lib/hash_tree.rb
82
89
  - lib/fate/console.rb
83
- has_rdoc: true
84
90
  homepage: https://github.com/automatthew/fate
85
91
  licenses: []
86
-
87
92
  post_install_message:
88
93
  rdoc_options: []
89
-
90
- require_paths:
94
+ require_paths:
91
95
  - lib
92
- required_ruby_version: !ruby/object:Gem::Requirement
96
+ required_ruby_version: !ruby/object:Gem::Requirement
93
97
  none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
101
- required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
103
  none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- hash: 3
107
- segments:
108
- - 0
109
- version: "0"
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
110
108
  requirements: []
111
-
112
109
  rubyforge_project:
113
- rubygems_version: 1.6.2
110
+ rubygems_version: 1.8.23
114
111
  signing_key:
115
112
  specification_version: 3
116
113
  summary: Tool for running and interacting with a multi-process service
117
114
  test_files: []
118
-
@@ -1,313 +0,0 @@
1
- module Traversable
2
-
3
- # Follow or create the path specified by the signature and assign
4
- # the value as a terminating leaf node.
5
- #
6
- # h.set([:a, :b, :c], "This is a retrievable value")
7
- #
8
- def set(sig, val)
9
- raise ArgumentError if sig.empty?
10
- create_path(sig) do |node, key|
11
- node[key] = val
12
- end
13
- end
14
-
15
- def reduce(sig, base=0)
16
- create_path(sig) do |node, key|
17
- node[key] = base unless node.has_key?(key)
18
- node[key] = yield node[key]
19
- end
20
- end
21
-
22
- def increment(sig, val=1)
23
- val = yield if block_given?
24
- create_path(sig) do |node, key|
25
- if node.has_key?(key)
26
- node[key] = node[key] + val
27
- else
28
- node[key] = val
29
- end
30
- end
31
- end
32
-
33
- # Usage:
34
- # a = ht.reducer([:a, :b, :c], 0) {|acc, v| acc + v }
35
- # a[1]
36
- def reducer(sig, base, &block)
37
- p = nil
38
- create_path(sig) do |node, key|
39
- unless node.has_key?(key)
40
- node[key] = base
41
- end
42
- p = lambda do |newval|
43
- node[key] = block.call(node[key], newval)
44
- end
45
- end
46
- p
47
- end
48
-
49
- def sum(*args)
50
- out = 0
51
- retrieve(*args) { |v| out += v }
52
- out
53
- end
54
-
55
- def count(*args)
56
- args = args + [:_count]
57
- sum(*args)
58
- end
59
-
60
- def unique(*args)
61
- out = 0
62
- filter(*args) { |v| out += v.size }
63
- out
64
- end
65
-
66
- # like retrieve, but will return any kind of node
67
- def filter(*sig)
68
- results = []
69
- search(sig) do |node|
70
- results << node
71
- yield(node) if block_given?
72
- end
73
- results
74
- end
75
-
76
- # Given a signature array, attempt to retrieve matching leaf values.
77
- def retrieve(*sig)
78
- results = []
79
- search(sig) do |node|
80
- results << node unless node.respond_to?(:children)
81
- yield(node) if block_given?
82
- end
83
- results
84
- end
85
-
86
- # Generic tree search method
87
- def search(sig)
88
- current_nodes = [self]
89
-
90
- while !current_nodes.empty?
91
- next_nodes = []
92
- matcher = sig.shift
93
- if matcher
94
- current_nodes.each do |node|
95
- if node.respond_to?(:children)
96
- next_nodes += node.children(matcher)
97
- end
98
- end
99
- else
100
- current_nodes.each {|n| yield(n) }
101
- end
102
- current_nodes = next_nodes
103
- end
104
- end
105
-
106
- def traverse
107
- current_nodes = [self]
108
- while !current_nodes.empty?
109
- next_nodes = []
110
- current_nodes.each do |node|
111
- if node.respond_to?(:children)
112
- next_nodes += node.children(true)
113
- yield(node)
114
- end
115
- end
116
-
117
- current_nodes = next_nodes
118
- end
119
- end
120
-
121
- end
122
-
123
- class HashTree < Hash
124
- include Traversable
125
-
126
- # Override the constructor to provide a default_proc
127
- # NOTE: there's a better way to do this in 1.9.2, it seems.
128
- # See Hash#default_proc=
129
- def self.new()
130
- hash = Hash.new { |h,k| h[k] = HashTree.new }
131
- super.replace(hash)
132
- end
133
-
134
- def self.[](hash)
135
- ht = self.new
136
- ht << hash
137
- ht
138
- end
139
-
140
- def _dump(depth)
141
- h = Hash[self]
142
- h.delete_if {|k,v| v.is_a? Proc }
143
- Marshal.dump(h)
144
- end
145
-
146
- def self._load(*args)
147
- h = Marshal.load(*args)
148
- ht = self.new
149
- ht.replace(h)
150
- ht
151
- end
152
-
153
- # Follow the path specified, creating new nodes where necessary.
154
- # Returns the value at the end of the path. If a block is supplied,
155
- # it will be called with the last node and the last key as parameters,
156
- # analogous to Hash.new's default proc. This is necessary to allow
157
- # setting a value at the end of the path. See the implementation of #insert.
158
- def create_path(sig)
159
- final_key = sig.pop
160
- hash = self
161
- sig.each do |a|
162
- hash = hash[a]
163
- end
164
- yield(hash, final_key) if block_given?
165
- hash[final_key]
166
- end
167
-
168
- # Attempt to retrieve the value at the end of the path specified,
169
- # without creating new nodes. Returns nil on failure.
170
- # TODO: consider whether splatting the signature is wise.
171
- def find(sig)
172
- stage = self
173
- sig.each do |a|
174
- if stage.has_key?(a)
175
- stage = stage[a]
176
- else
177
- return nil
178
- end
179
- end
180
- stage
181
- end
182
-
183
- def remove(*sig)
184
- stage = self
185
- s2 = sig.slice(0..-2)
186
- s2.each do |a|
187
- if stage.has_key?(a)
188
- stage = stage[a]
189
- else
190
- return nil
191
- end
192
- end
193
- stage.delete(sig.last)
194
- end
195
-
196
- def children(matcher=true)
197
- next_keys = self.keys.select do |key|
198
- match?(matcher, key)
199
- end
200
- self.values_at(*next_keys)
201
- end
202
-
203
- def +(other)
204
- out = HashTree.new
205
- _plus(other, out)
206
- out
207
- end
208
-
209
- def _plus(ht2, out)
210
- self.each do |k1,v1|
211
- v1 = v1.respond_to?(:dup) ? v1 : v1.dup
212
- if ht2.has_key?(k1)
213
- v2 = ht2[k1]
214
- if v1.respond_to?(:_plus)
215
- out[k1] = v1
216
- v1._plus(v2, out[k1])
217
- elsif v2.respond_to?(:_plus)
218
- raise ArgumentError,
219
- "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
220
- else
221
- if v2.is_a?(Numeric) && v1.is_a?(Numeric)
222
- out[k1] = v1 + v2
223
- else
224
- out[k1] = [v1, ht2[k1]]
225
- end
226
- end
227
- else
228
- # should anything happen here?
229
- end
230
- end
231
- ht2.each do |k,v|
232
- if self.has_key?(k)
233
- # should anything happen here?
234
- else
235
- v = v.respond_to?(:dup) ? v : v.dup
236
- out[k] = v
237
- end
238
- end
239
- end
240
-
241
- def <<(other)
242
- other.each do |k,v1|
243
- if self.has_key?(k)
244
- v2 = self[k]
245
- if v1.respond_to?(:has_key?) && v2.respond_to?(:has_key?)
246
- v2 << v1
247
- elsif v1.is_a?(Numeric) && v2.is_a?(Numeric)
248
- self[k] = v1 + v2
249
- else
250
- raise ArgumentError,
251
- "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
252
- end
253
- else
254
- if v1.respond_to?(:has_key?)
255
- self[k] << v1
256
- else
257
- self[k] = v1
258
- end
259
- end
260
- end
261
- end
262
-
263
- def match?(val, key)
264
- case val
265
- when true
266
- true
267
- when String, Symbol
268
- key == val
269
- when Regexp
270
- key =~ val
271
- when Proc
272
- val.call(key)
273
- when nil
274
- false
275
- else
276
- raise ArgumentError, "Unexpected matcher type: #{val.inspect}"
277
- end
278
- end
279
-
280
- def each_path(stack=[], &block)
281
- self.each do |k, v|
282
- stack.push(k)
283
- if v.respond_to?(:each_path)
284
- v.each_path(stack, &block)
285
- else
286
- block.call(stack, v)
287
- end
288
- stack.pop
289
- end
290
- end
291
-
292
- def paths
293
- out = []
294
- end
295
-
296
- def each_leaf(stack=[], &block)
297
- self.each do |k,v|
298
- stack.push(k)
299
- if v.respond_to?(:each_leaf)
300
- v.each_leaf(stack, &block)
301
- else
302
- block.call(v)
303
- end
304
- stack.pop
305
- end
306
- end
307
-
308
- end
309
-
310
-
311
-
312
-
313
-