scout-essentials 1.3.0 → 1.4.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +33 -1
  3. data/VERSION +1 -1
  4. data/lib/scout/annotation/annotated_object.rb +69 -0
  5. data/lib/scout/annotation/annotation_module.rb +59 -0
  6. data/lib/scout/annotation/array.rb +74 -0
  7. data/lib/scout/annotation.rb +45 -0
  8. data/lib/scout/concurrent_stream.rb +4 -1
  9. data/lib/scout/config.rb +3 -3
  10. data/lib/scout/exceptions.rb +2 -1
  11. data/lib/scout/indiferent_hash/options.rb +2 -1
  12. data/lib/scout/log/color.rb +1 -1
  13. data/lib/scout/log.rb +11 -11
  14. data/lib/scout/misc/digest.rb +39 -8
  15. data/lib/scout/misc/filesystem.rb +23 -0
  16. data/lib/scout/misc/format.rb +32 -0
  17. data/lib/scout/misc/helper.rb +37 -0
  18. data/lib/scout/misc/math.rb +109 -0
  19. data/lib/scout/misc/system.rb +2 -2
  20. data/lib/scout/misc.rb +1 -0
  21. data/lib/scout/named_array.rb +8 -6
  22. data/lib/scout/open/remote.rb +8 -3
  23. data/lib/scout/open/stream.rb +5 -1
  24. data/lib/scout/open/util.rb +1 -1
  25. data/lib/scout/path/find.rb +18 -6
  26. data/lib/scout/path/util.rb +1 -1
  27. data/lib/scout/path.rb +10 -4
  28. data/lib/scout/persist/open.rb +1 -1
  29. data/lib/scout/persist/serialize.rb +6 -2
  30. data/lib/scout/persist.rb +49 -20
  31. data/lib/scout/resource/path.rb +1 -1
  32. data/lib/scout/resource/scout.rb +2 -0
  33. data/lib/scout/resource/util.rb +8 -3
  34. data/lib/scout/resource.rb +15 -3
  35. data/scout-essentials.gemspec +21 -13
  36. data/test/scout/annotation/test_annotated_object.rb +0 -0
  37. data/test/scout/annotation/test_array.rb +119 -0
  38. data/test/scout/misc/test_digest.rb +54 -0
  39. data/test/scout/misc/test_filesystem.rb +28 -0
  40. data/test/scout/misc/test_helper.rb +14 -0
  41. data/test/scout/misc/test_math.rb +9 -0
  42. data/test/scout/path/test_find.rb +32 -0
  43. data/test/scout/test_annotation.rb +169 -0
  44. data/test/scout/test_persist.rb +17 -1
  45. data/test/scout/test_resource.rb +8 -8
  46. metadata +13 -5
  47. data/lib/scout/meta_extension.rb +0 -101
  48. data/test/scout/test_meta_extension.rb +0 -80
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 635a27a40f5897e83e1632e43a1cfa9343edd5c53e7eee522fc99f4a74e03171
4
- data.tar.gz: 19bb00a9be1cc0ab21661cedc46bfd1d21118cd0fed9f0d240b205580e8a733a
3
+ metadata.gz: 01ebe0cbbfda369f1d1b3de5009566eed0749cdb0a418c19c663a21ec68214c1
4
+ data.tar.gz: 2303029301ea7b4ef1f9d7cf44bd906cb8db637b31a5147ae2950e4eb1c6e37e
5
5
  SHA512:
6
- metadata.gz: 11813ec485609e4275258e12aee416663bb70e99c453dc9e7ec2363a864370c07506c74c38b20ac43426a12e7872456eb37e9e368fc10e3f2d3a5b1083d12281
7
- data.tar.gz: da326b8fa64b0850fd2d4e50ab1e0e13d4a881039b4945503a56debcd800a8c5850b5b09f6f63d2631746ae177dcce16ce235ae9deb803b095e8b7d7e588c0a0
6
+ metadata.gz: 707278b8bdd09658049035622021bd822d8b53ecef993f4136b550f2acb47be30cf515a460b07f5c45ed51769d782639b06e128947dab80832a4de3b6b66f574
7
+ data.tar.gz: d8a5ce01ca753d159734871447f961a39d48992af934f7ad69a0f95ff708794d00962cd36fbabbcd535a721611f5d0fa9194f87d5662cd8ab66ed80d8abeed38
data/.vimproject CHANGED
@@ -5,7 +5,12 @@ scout-essentials=/$PWD filter="*.rb *.txt *.md *.conf *.yaml" {
5
5
  scout-essentials.rb
6
6
  scout=scout{
7
7
  exceptions.rb
8
- meta_extension.rb
8
+ annotation.rb
9
+ annotation=annotation{
10
+ array.rb
11
+ annotated_object.rb
12
+ annotation_module.rb
13
+ }
9
14
  misc.rb
10
15
  misc=misc{
11
16
  digest.rb
@@ -15,6 +20,7 @@ scout-essentials=/$PWD filter="*.rb *.txt *.md *.conf *.yaml" {
15
20
  insist.rb
16
21
  monitor.rb
17
22
  system.rb
23
+ math.rb
18
24
  }
19
25
  named_array.rb
20
26
  indiferent_hash.rb
@@ -74,5 +80,31 @@ scout-essentials=/$PWD filter="*.rb *.txt *.md *.conf *.yaml" {
74
80
  }
75
81
  test=test{
76
82
  test_helper.rb
83
+ scout=scout{
84
+ test_annotation.rb
85
+ annotation=annotation{
86
+ test_annotated_object.rb
87
+ test_array.rb
88
+ }
89
+ test_cmd.rb
90
+ test_concurrent_stream.rb
91
+ test_config.rb
92
+ test_indiferent_hash.rb
93
+ test_log.rb
94
+ test_misc.rb
95
+ test_named_array.rb
96
+ test_open.rb
97
+ test_path.rb
98
+ test_persist.rb
99
+ test_resource.rb
100
+ test_tmpfile.rb
101
+ misc=misc{
102
+ test_digest.rb
103
+ test_filesystem.rb
104
+ test_helper.rb
105
+ test_insist.rb
106
+ test_system.rb
107
+ }
108
+ }
77
109
  }
78
110
  }
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -0,0 +1,69 @@
1
+ module Annotation
2
+ module AnnotatedObject
3
+ def annotations
4
+ @annotations ||= []
5
+ end
6
+
7
+ def annotation_types
8
+ @annotation_types ||= []
9
+ end
10
+
11
+ def annotation_hash
12
+ attr_hash = {}
13
+ @annotations.each do |name|
14
+ attr_hash[name] = self.instance_variable_get("@#{name}")
15
+ end if @annotations
16
+ attr_hash
17
+ end
18
+
19
+ def annotation_info
20
+ annotation_hash.merge(annotation_types: annotation_types, annotated_array: (AnnotatedArray === self))
21
+ end
22
+
23
+ def self.serialize(obj)
24
+ Annotation.purge(obj.annotation_info.merge(literal: obj))
25
+ end
26
+
27
+ def serialize
28
+ AnnotatedObject.serialize(self)
29
+ end
30
+
31
+ def annotation_id
32
+ Misc.digest([self, annotation_info])
33
+ end
34
+
35
+ alias id annotation_id
36
+
37
+ def annotate(other)
38
+ annotation_types.each do |type|
39
+ type.setup(other, annotation_hash)
40
+ end
41
+ other
42
+ end
43
+
44
+ def purge
45
+ new = self.dup
46
+
47
+ if new.instance_variables.include?(:@annotations)
48
+ new.instance_variable_get(:@annotations).each do |a|
49
+ var_name = "@#{a}".to_sym
50
+ new.remove_instance_variable(var_name) if new.instance_variables.include? var_name
51
+ end
52
+ new.remove_instance_variable(:@annotations)
53
+ end
54
+
55
+ if new.instance_variables.include?(:@annotation_types)
56
+ new.remove_instance_variable(:@annotation_types)
57
+ end
58
+
59
+ new
60
+ end
61
+
62
+ def make_array
63
+ new = [self]
64
+ self.annotate(new)
65
+ new.extend AnnotatedArray
66
+ new
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,59 @@
1
+ module Annotation
2
+ module AnnotationModule
3
+ def annotation(*attrs)
4
+ self.instance_variable_get("@annotations").concat attrs
5
+ attrs.each do |a|
6
+ self.attr_accessor a
7
+ end
8
+ end
9
+
10
+ def annotations
11
+ @annotations ||= []
12
+ end
13
+
14
+ def included(mod)
15
+ mod.instance_variable_set(:@annotations, []) unless mod.instance_variables.include?(:@annotations)
16
+ mod.instance_variable_get(:@annotations).concat self.instance_variable_get(:@annotations)
17
+ end
18
+
19
+ def extended(obj)
20
+ attrs = self.instance_variable_get("@annotations")
21
+
22
+ obj.instance_variable_set(:@annotations, []) unless obj.instance_variables.include?(:@annotations)
23
+ obj.annotation_types << self
24
+
25
+ annotations = obj.instance_variable_get(:@annotations)
26
+ annotations.concat attrs
27
+ end
28
+
29
+ def setup(*args,&block)
30
+ if block_given?
31
+ obj, rest = block, args
32
+ else
33
+ obj, *rest = args
34
+ end
35
+ obj = block if obj.nil?
36
+ return nil if obj.nil?
37
+ obj.extend self unless self === obj
38
+ attrs = self.instance_variable_get("@annotations")
39
+
40
+ return obj if attrs.nil? || attrs.empty?
41
+
42
+ if rest.length == 1 && Hash === (rlast = rest.last) &&
43
+ ((! (rlkey = rlast.keys.first).nil? && attrs.include?(rlkey.to_sym)) ||
44
+ (! attrs.length != 1 ))
45
+
46
+ pairs = rlast
47
+ else
48
+ pairs = attrs.zip(rest)
49
+ end
50
+
51
+ pairs.each do |name,value|
52
+ next if name.to_sym === :annotation_types
53
+ obj.instance_variable_set("@#{name}", value) unless value.nil?
54
+ end
55
+
56
+ obj
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,74 @@
1
+ module AnnotatedArray
2
+
3
+ module AnnotatedArrayItem
4
+ attr_accessor :container, :container_index
5
+ end
6
+
7
+ def self.is_contained?(obj)
8
+ AnnotatedArrayItem === obj
9
+ end
10
+
11
+ def annotate_item(obj, position = nil)
12
+ obj = obj.dup if obj.frozen?
13
+ obj.extend AnnotatedArray if Array === obj
14
+ obj.extend AnnotatedArrayItem
15
+ obj.container = self
16
+ obj.container_index = position
17
+ self.annotate(obj)
18
+ end
19
+
20
+ def [](pos, clean = false)
21
+ item = super(pos)
22
+ return item if item.nil? or clean
23
+ annotate_item(item, pos)
24
+ end
25
+
26
+ def first
27
+ annotate_item(super, 0)
28
+ end
29
+
30
+ def last
31
+ annotate_item(super, self.length - 1)
32
+ end
33
+
34
+ def each_with_index(&block)
35
+ super do |item,i|
36
+ block.call annotate_item(item, i)
37
+ end
38
+ end
39
+
40
+ def each(&block)
41
+ i = 0
42
+ super do |item|
43
+ block.call annotate_item(item, i)
44
+ i += 1
45
+ end
46
+ end
47
+
48
+ def inject(acc, &block)
49
+ each do |item|
50
+ acc = block.call acc, item
51
+ end
52
+ acc
53
+ end
54
+
55
+ def collect(&block)
56
+ if block_given?
57
+ inject([]){|acc,item| acc.push(block.call(item)); acc }
58
+ else
59
+ inject([]){|acc,item| acc.push(item); acc }
60
+ end
61
+ end
62
+
63
+ %w(compact uniq flatten reverse sort_by).each do |method|
64
+
65
+ self.define_method(method) do |*args|
66
+ res = super(*args)
67
+
68
+ annotate(res)
69
+ res.extend AnnotatedArray
70
+
71
+ res
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'annotation/array'
2
+ require_relative 'annotation/annotated_object'
3
+ require_relative 'annotation/annotation_module'
4
+
5
+ module Annotation
6
+
7
+ def self.setup(obj, annotation_types, annotation_hash)
8
+ return nil if obj.nil?
9
+ annotation_types = annotation_types.split("|") if String === annotation_types
10
+ annotation_types = [annotation_types] unless Array === annotation_types
11
+ annotation_types.each do |type|
12
+ type = Kernel.const_get(type) if String === type
13
+ type.setup(obj, annotation_hash)
14
+ end
15
+ obj
16
+ end
17
+
18
+ def self.extended(base)
19
+ base.instance_variable_set(:@annotations, []) unless base.instance_variables.include?(:@annotations)
20
+ base.include Annotation::AnnotatedObject
21
+ base.extend Annotation::AnnotationModule
22
+ end
23
+
24
+ def self.is_annotated?(obj)
25
+ obj.instance_variables.include?(:@annotation_types)
26
+ end
27
+
28
+ def self.purge(obj)
29
+ case obj
30
+ when nil
31
+ nil
32
+ when Array
33
+ obj = obj.purge if is_annotated?(obj)
34
+ obj.collect{|e| purge(e) }
35
+ when Hash
36
+ new = {}
37
+ obj.each do |k,v|
38
+ new[purge(k)] = purge(v)
39
+ end
40
+ new
41
+ else
42
+ is_annotated?(obj) ? obj.purge : obj
43
+ end
44
+ end
45
+ end
@@ -223,7 +223,10 @@ module ConcurrentStream
223
223
  self.join if ! @stream_exception && (self.closed? || self.eof?)
224
224
  end
225
225
  else
226
- super(*args)
226
+ begin
227
+ super(*args)
228
+ rescue IOError
229
+ end unless self.closed?
227
230
  end
228
231
  end
229
232
 
data/lib/scout/config.rb CHANGED
@@ -17,16 +17,16 @@ module Scout::Config
17
17
 
18
18
  def self.load_file(file)
19
19
  Log.debug "Loading config file: #{ file }"
20
- TSV.traverse file, :type => :array do |line|
20
+ Open.read(file).split("\n").each do |line|
21
21
  next if line =~ /^#/
22
- key, value, *tokens = line.strip.split(/\s/)
22
+ key, value, *tokens = line.strip.split(/\s+/)
23
23
 
24
24
  self.add_entry(key, value, tokens) if key
25
25
  end
26
26
  end
27
27
 
28
28
  def self.load_config
29
- Scout.etc.config.find_all.reverse.each do |file|
29
+ Path.setup("etc").config.find_all.reverse.each do |file|
30
30
  self.load_file(file)
31
31
  end
32
32
  end
@@ -54,8 +54,9 @@ class DontClose < Exception
54
54
  end
55
55
  end
56
56
 
57
+ class DontPersist < Exception; end
57
58
 
58
- class KeepLocked < Exception
59
+ class KeepLocked < DontPersist
59
60
  attr_accessor :payload
60
61
  def initialize(payload)
61
62
  @payload = payload
@@ -28,7 +28,8 @@ module IndiferentHash
28
28
  end
29
29
 
30
30
  def self.pull_keys(hash, prefix)
31
- new = {}
31
+ IndiferentHash.setup(hash)
32
+ new = hash.include?("#{prefix}_options") ? hash.delete("#{prefix}_options") : {}
32
33
  prefix = prefix.to_s
33
34
  hash.keys.each do |key|
34
35
  if key.to_s =~ /#{ prefix }_(.*)/
@@ -203,7 +203,7 @@ module Log
203
203
  str = str.to_s unless str.nil?
204
204
  return str if Symbol === color
205
205
  color_str = reset ? Term::ANSIColor.reset : ""
206
- color_str << color
206
+ color_str << color if color
207
207
  if str.nil?
208
208
  color_str
209
209
  else
data/lib/scout/log.rb CHANGED
@@ -109,20 +109,19 @@ module Log
109
109
 
110
110
  def self.logfile(file=nil)
111
111
  if file.nil?
112
- @logfile ||= nil
112
+ @@logfile = nil
113
113
  else
114
114
  case file
115
115
  when String
116
- @logfile = File.open(file, :mode => 'a')
117
- @logfile.sync = true
116
+ @@logfile = File.open(file, :mode => 'a')
117
+ @@logfile.sync = true
118
118
  when IO, File
119
- @logfile = file
119
+ @@logfile = file
120
120
  else
121
121
  raise "Unkown logfile format: #{file.inspect}"
122
122
  end
123
123
  end
124
124
  end
125
-
126
125
  def self.up_lines(num = 1)
127
126
  nocolor ? "" : "\033[#{num+1}F\033[2K"
128
127
  end
@@ -142,26 +141,26 @@ module Log
142
141
  MUTEX = Mutex.new
143
142
  def self.log_write(str)
144
143
  MUTEX.synchronize do
145
- if logfile.nil?
144
+ if defined?(@@logfile) && @@logfile
145
+ @@logfile.write str
146
+ else
146
147
  begin
147
148
  STDERR.write str
148
149
  rescue
149
150
  end
150
- else
151
- logfile.write str
152
151
  end
153
152
  end
154
153
  end
155
154
 
156
155
  def self.log_puts(str)
157
156
  MUTEX.synchronize do
158
- if logfile.nil?
157
+ if defined?(@@logfile) && @@logfile
158
+ @@logfile.puts str
159
+ else
159
160
  begin
160
161
  STDERR.puts str
161
162
  rescue
162
163
  end
163
- else
164
- logfile.puts str
165
164
  end
166
165
  end
167
166
  end
@@ -309,6 +308,7 @@ module Log
309
308
  end
310
309
  end
311
310
  end
311
+ Log::LAST.replace "log"
312
312
  end
313
313
 
314
314
  def self.stack(stack)
@@ -5,14 +5,24 @@ module Misc
5
5
  obj.digest_str
6
6
  else
7
7
  case obj
8
- when String
9
- #'\'' << obj << '\''
10
- if Path === obj || ! Open.exists?(obj)
11
- '\'' << obj << '\''
12
- elsif File.directory?(obj)
8
+ when Path
9
+ case
10
+ when File.directory?(obj)
13
11
  "Directory MD5: #{digest_str(Dir.glob(File.join(obj, "*")))}"
12
+ when obj.located? && File.exist?(obj)
13
+ "File MD5: #{Misc.digest_file(obj)}"
14
14
  else
15
- "File MD5: #{Misc.file_md5(obj)}"
15
+ '\'' << obj << '\''
16
+ end
17
+ when String
18
+ if Path.is_filename?(obj) && Open.exists?(obj)
19
+ if File.directory?(obj)
20
+ "Directory MD5: #{digest_str(Dir.glob(File.join(obj, "*")))}"
21
+ else
22
+ "File MD5: #{Misc.digest_file(obj)}"
23
+ end
24
+ else
25
+ obj.dup
16
26
  end
17
27
  when Integer, Symbol
18
28
  obj.to_s
@@ -57,8 +67,29 @@ module Misc
57
67
  def self.file_md5(file)
58
68
  file = file.find if Path === file
59
69
  file = File.expand_path(file)
60
- Persist.persist("MD5:#{file}", :string) do
61
- Digest::MD5.file(file).hexdigest
70
+ Digest::MD5.file(file).hexdigest
71
+ end
72
+
73
+ def self.fast_file_md5(file, sample = 3_000_000)
74
+ size = File.size(file)
75
+ sample_txt = size.to_s << ":"
76
+ File.open(file) do |f|
77
+ sample_txt << f.read(sample)
78
+ f.seek(size/2)
79
+ sample_txt << f.read(sample)
80
+ f.seek(size - sample - 1)
81
+ sample_txt << f.read(sample)
82
+ end
83
+ Digest::MD5.hexdigest(sample_txt)
84
+ end
85
+
86
+ def self.digest_file(file)
87
+ file = file.find if Path === file
88
+ file = File.expand_path(file)
89
+ if File.size(file) > 10_000_000
90
+ fast_file_md5(file)
91
+ else
92
+ file_md5(file)
62
93
  end
63
94
  end
64
95
  end
@@ -22,4 +22,27 @@ module Misc
22
22
  return nil
23
23
  end
24
24
  end
25
+
26
+ def self.tarize(path, dest = nil)
27
+ Misc.in_dir(path) do
28
+ if dest
29
+ CMD.cmd("tar cvfz '#{dest}' '.'")
30
+ else
31
+ CMD.cmd("tar cvfz - '.'", :pipe => true)
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.untar(file, target = '.')
37
+ target = target.find if Path === target
38
+ file = file.find if Path === file
39
+ Misc.in_dir target do
40
+ if IO === file
41
+ CMD.cmd("tar xvfz -", in: file)
42
+ else
43
+ CMD.cmd("tar xvfz '#{file}'")
44
+ end
45
+ end
46
+ end
47
+
25
48
  end
@@ -252,4 +252,36 @@ module Misc
252
252
  end
253
253
  values
254
254
  end
255
+
256
+ def self.timespan(str, default = "s")
257
+
258
+ return - timespan(str[1..-1], default) if str[0] == "-"
259
+
260
+ if str.include?(":")
261
+ seconds, minutes, hours = str.split(":").reverse
262
+ return seconds.to_i + minutes.to_i * 60 + hours.to_i * 60 * 60
263
+ end
264
+
265
+ tokens = {
266
+ "s" => (1),
267
+ "sec" => (1),
268
+ "m" => (60),
269
+ "min" => (60),
270
+ "''" => (1),
271
+ "'" => (60),
272
+ "h" => (60 * 60),
273
+ "d" => (60 * 60 * 24),
274
+ "w" => (60 * 60 * 24 * 7),
275
+ "mo" => (60 * 60 * 24 * 31),
276
+ "y" => (60 * 60 * 24 * 365),
277
+ }
278
+
279
+ tokens[nil] = tokens[default]
280
+ tokens[""] = tokens[default]
281
+ time = 0
282
+ str.scan(/(\d+)(\w*)/).each do |amount, measure|
283
+ time += amount.to_i * tokens[measure]
284
+ end
285
+ time
286
+ end
255
287
  end
@@ -28,4 +28,41 @@ module Misc
28
28
  counts
29
29
  end
30
30
 
31
+ def self.chunk(array, num)
32
+ total = array.length
33
+ current = 0
34
+ while current < total
35
+ last = current + num - 1
36
+ yield array[current..last]
37
+ current = last + 1
38
+ end
39
+ end
40
+
41
+ # Divides the array into +num+ chunks of the same size by placing one
42
+ # element in each chunk iteratively.
43
+ def self.divide(array, num)
44
+ num = 1 if num == 0
45
+ chunks = []
46
+ num.to_i.times do chunks << [] end
47
+ array.each_with_index{|e, i|
48
+ c = i % num
49
+ chunks[c] << e
50
+ }
51
+ chunks
52
+ end
53
+
54
+ # Divides the array into chunks of +num+ same size by placing one
55
+ # element in each chunk iteratively.
56
+ def self.ordered_divide(array, num)
57
+ last = array.length - 1
58
+ chunks = []
59
+ current = 0
60
+ while current <= last
61
+ next_current = [last, current + num - 1].min
62
+ chunks << array[current..next_current]
63
+ current = next_current + 1
64
+ end
65
+ chunks
66
+ end
67
+
31
68
  end