scout-essentials 1.0.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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.vimproject +78 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +18 -0
  7. data/Rakefile +47 -0
  8. data/VERSION +1 -0
  9. data/lib/scout/cmd.rb +348 -0
  10. data/lib/scout/concurrent_stream.rb +284 -0
  11. data/lib/scout/config.rb +168 -0
  12. data/lib/scout/exceptions.rb +77 -0
  13. data/lib/scout/indiferent_hash/case_insensitive.rb +30 -0
  14. data/lib/scout/indiferent_hash/options.rb +115 -0
  15. data/lib/scout/indiferent_hash.rb +96 -0
  16. data/lib/scout/log/color.rb +224 -0
  17. data/lib/scout/log/color_class.rb +269 -0
  18. data/lib/scout/log/fingerprint.rb +69 -0
  19. data/lib/scout/log/progress/report.rb +244 -0
  20. data/lib/scout/log/progress/util.rb +173 -0
  21. data/lib/scout/log/progress.rb +106 -0
  22. data/lib/scout/log/trap.rb +107 -0
  23. data/lib/scout/log.rb +441 -0
  24. data/lib/scout/meta_extension.rb +100 -0
  25. data/lib/scout/misc/digest.rb +63 -0
  26. data/lib/scout/misc/filesystem.rb +25 -0
  27. data/lib/scout/misc/format.rb +255 -0
  28. data/lib/scout/misc/helper.rb +31 -0
  29. data/lib/scout/misc/insist.rb +56 -0
  30. data/lib/scout/misc/monitor.rb +66 -0
  31. data/lib/scout/misc/system.rb +73 -0
  32. data/lib/scout/misc.rb +10 -0
  33. data/lib/scout/named_array.rb +138 -0
  34. data/lib/scout/open/lock/lockfile.rb +587 -0
  35. data/lib/scout/open/lock.rb +68 -0
  36. data/lib/scout/open/remote.rb +135 -0
  37. data/lib/scout/open/stream.rb +491 -0
  38. data/lib/scout/open/util.rb +244 -0
  39. data/lib/scout/open.rb +170 -0
  40. data/lib/scout/path/find.rb +204 -0
  41. data/lib/scout/path/tmpfile.rb +8 -0
  42. data/lib/scout/path/util.rb +127 -0
  43. data/lib/scout/path.rb +51 -0
  44. data/lib/scout/persist/open.rb +17 -0
  45. data/lib/scout/persist/path.rb +15 -0
  46. data/lib/scout/persist/serialize.rb +157 -0
  47. data/lib/scout/persist.rb +104 -0
  48. data/lib/scout/resource/open.rb +8 -0
  49. data/lib/scout/resource/path.rb +80 -0
  50. data/lib/scout/resource/produce/rake.rb +69 -0
  51. data/lib/scout/resource/produce.rb +151 -0
  52. data/lib/scout/resource/scout.rb +3 -0
  53. data/lib/scout/resource/software.rb +178 -0
  54. data/lib/scout/resource/util.rb +59 -0
  55. data/lib/scout/resource.rb +40 -0
  56. data/lib/scout/simple_opt/accessor.rb +54 -0
  57. data/lib/scout/simple_opt/doc.rb +126 -0
  58. data/lib/scout/simple_opt/get.rb +57 -0
  59. data/lib/scout/simple_opt/parse.rb +67 -0
  60. data/lib/scout/simple_opt/setup.rb +26 -0
  61. data/lib/scout/simple_opt.rb +5 -0
  62. data/lib/scout/tmpfile.rb +129 -0
  63. data/lib/scout-essentials.rb +10 -0
  64. data/scout-essentials.gemspec +143 -0
  65. data/share/color/color_names +507 -0
  66. data/share/color/diverging_colors.hex +12 -0
  67. data/share/software/install_helpers +523 -0
  68. data/test/scout/indiferent_hash/test_case_insensitive.rb +16 -0
  69. data/test/scout/indiferent_hash/test_options.rb +46 -0
  70. data/test/scout/log/test_color.rb +0 -0
  71. data/test/scout/log/test_progress.rb +108 -0
  72. data/test/scout/misc/test_digest.rb +30 -0
  73. data/test/scout/misc/test_filesystem.rb +30 -0
  74. data/test/scout/misc/test_insist.rb +13 -0
  75. data/test/scout/misc/test_system.rb +21 -0
  76. data/test/scout/open/test_lock.rb +52 -0
  77. data/test/scout/open/test_remote.rb +25 -0
  78. data/test/scout/open/test_stream.rb +676 -0
  79. data/test/scout/open/test_util.rb +73 -0
  80. data/test/scout/path/test_find.rb +110 -0
  81. data/test/scout/path/test_util.rb +22 -0
  82. data/test/scout/persist/test_open.rb +37 -0
  83. data/test/scout/persist/test_path.rb +37 -0
  84. data/test/scout/persist/test_serialize.rb +114 -0
  85. data/test/scout/resource/test_path.rb +58 -0
  86. data/test/scout/resource/test_produce.rb +94 -0
  87. data/test/scout/resource/test_software.rb +24 -0
  88. data/test/scout/resource/test_util.rb +38 -0
  89. data/test/scout/simple_opt/test_doc.rb +16 -0
  90. data/test/scout/simple_opt/test_get.rb +11 -0
  91. data/test/scout/simple_opt/test_parse.rb +10 -0
  92. data/test/scout/simple_opt/test_setup.rb +77 -0
  93. data/test/scout/test_cmd.rb +85 -0
  94. data/test/scout/test_concurrent_stream.rb +29 -0
  95. data/test/scout/test_config.rb +66 -0
  96. data/test/scout/test_indiferent_hash.rb +26 -0
  97. data/test/scout/test_log.rb +32 -0
  98. data/test/scout/test_meta_extension.rb +80 -0
  99. data/test/scout/test_misc.rb +6 -0
  100. data/test/scout/test_named_array.rb +43 -0
  101. data/test/scout/test_open.rb +146 -0
  102. data/test/scout/test_path.rb +54 -0
  103. data/test/scout/test_persist.rb +186 -0
  104. data/test/scout/test_resource.rb +26 -0
  105. data/test/scout/test_tmpfile.rb +53 -0
  106. data/test/test_helper.rb +50 -0
  107. metadata +247 -0
@@ -0,0 +1,255 @@
1
+ module Misc
2
+ COLOR_LIST = %w(#BC80BD #CCEBC5 #FFED6F #8DD3C7 #FFFFB3 #BEBADA #FB8072 #80B1D3 #FDB462 #B3DE69 #FCCDE5 #D9D9D9)
3
+
4
+ def self.colors_for(list)
5
+ unused = COLOR_LIST.dup
6
+
7
+ used = {}
8
+ colors = list.collect do |elem|
9
+ if used.include? elem
10
+ used[elem]
11
+ else
12
+ color = unused.shift
13
+ used[elem]=color
14
+ color
15
+ end
16
+ end
17
+
18
+ [colors, used]
19
+ end
20
+
21
+ def self.format_seconds(time, extended = false)
22
+ seconds = time.to_i
23
+ str = [seconds/3600, seconds/60 % 60, seconds % 60].map{|t| "%02i" % t }.join(':')
24
+ str << ".%02i" % ((time - seconds) * 100) if extended
25
+ str
26
+ end
27
+
28
+ CHAR_SENCONDS = ENV["SCOUT_NOCOLOR"] == "true" ? "sec" : "″"
29
+ def self.format_seconds_short(time)
30
+ if time < 0.0001
31
+ "%.5g" % time + CHAR_SENCONDS
32
+ elsif time < 60
33
+ "%.2g" % time + CHAR_SENCONDS
34
+ else
35
+ format_seconds(time)
36
+ end
37
+ end
38
+
39
+
40
+
41
+ MAX_TTY_LINE_WIDTH = 100
42
+ def self.format_paragraph(text, size = nil, indent = nil, offset = nil)
43
+ size ||= Log.tty_size || MAX_TTY_LINE_WIDTH
44
+ size = MAX_TTY_LINE_WIDTH if size > MAX_TTY_LINE_WIDTH
45
+ indent ||= 0
46
+ offset ||= 0
47
+
48
+ i = 0
49
+ size = size + offset + indent
50
+ re = /((?:\n\s*\n\s*)|(?:\n\s*(?=\*)))/
51
+ text.split(re).collect do |paragraph|
52
+ i += 1
53
+ str = if i % 2 == 1
54
+ words = paragraph.gsub(/\s+/, "\s").split(" ")
55
+ lines = []
56
+ line = " "*offset
57
+ word = words.shift
58
+ while word
59
+ word = word[0..size-indent-offset-4] + '...' if word.length >= size - indent - offset
60
+ while word and Log.uncolor(line).length + Log.uncolor(word).length <= size - indent
61
+ line << word << " "
62
+ word = words.shift
63
+ end
64
+ offset = 0
65
+ lines << ((" " * indent) << line[0..-2])
66
+ line = ""
67
+ end
68
+ (lines * "\n")
69
+ else
70
+ paragraph
71
+ end
72
+ offset = 0
73
+ str
74
+ end*""
75
+ end
76
+
77
+ def self.format_definition_list_item(dt, dd, indent = nil, size = nil, color = :yellow)
78
+ if size.nil?
79
+ base_size = MAX_TTY_LINE_WIDTH
80
+ base_indent = indent || (base_size / 3)
81
+ size = base_size - base_indent
82
+ end
83
+
84
+ indent ||= base_indent || size / 3
85
+
86
+ dd = "" if dd.nil?
87
+ dt = Log.color color, dt if color
88
+ dt = dt.to_s unless dd.empty?
89
+ len = Log.uncolor(dt).length
90
+
91
+ if indent < 0
92
+ text = format_paragraph(dd, size, indent.abs-1, 0)
93
+ text = dt << "\n" << text
94
+ else
95
+ offset = len - indent
96
+ offset = 0 if offset < 0
97
+ text = format_paragraph(dd, size, indent.abs+1, offset)
98
+ text[0..len-1] = dt
99
+ end
100
+ text
101
+ end
102
+
103
+ def self.format_definition_list(defs, indent = nil, size = nil, color = :yellow, sep = "\n\n")
104
+ indent ||= 30
105
+ size ||= (Log.tty_size || MAX_TTY_LINE_WIDTH) - indent
106
+ entries = []
107
+ defs.each do |dt,dd|
108
+ text = format_definition_list_item(dt,dd,indent, size,color)
109
+ entries << text
110
+ end
111
+ entries * sep
112
+ end
113
+
114
+ def self.camel_case(string)
115
+ return string if string !~ /_/ && string =~ /[A-Z]+.*/
116
+ string.split(/_|(\d+)/).map{|e|
117
+ (e =~ /^[A-Z]{2,}$/ ? e : e.capitalize)
118
+ }.join
119
+ end
120
+
121
+ def self.camel_case_lower(string)
122
+ string.split('_').inject([]){ |buffer,e|
123
+ buffer.push(buffer.empty? ? e.downcase : (e =~ /^[A-Z]{2,}$/ ? e : e.capitalize))
124
+ }.join
125
+ end
126
+
127
+ def self.snake_case(string)
128
+ return nil if string.nil?
129
+ string = string.to_s if Symbol === string
130
+ string.
131
+ gsub(/([A-Z]{2,})([A-Z][a-z])/,'\1_\2').
132
+ gsub(/([a-z])([A-Z])/,'\1_\2').
133
+ gsub(/\s/,'_').gsub(/[^\w]/, '').
134
+ split("_").collect{|p| p.match(/[A-Z]{2,}/) ? p : p.downcase } * "_"
135
+ end
136
+
137
+ # source: https://gist.github.com/ekdevdes/2450285
138
+ # author: Ethan Kramer (https://github.com/ekdevdes)
139
+ def self.humanize(value, options = {})
140
+ if options.empty?
141
+ options[:format] = :sentence
142
+ end
143
+
144
+ values = value.to_s.split('_')
145
+ values.each_index do |index|
146
+ # lower case each item in array
147
+ # Miguel Vazquez edit: Except for acronyms
148
+ values[index].downcase! unless values[index].match(/[a-zA-Z][A-Z]/)
149
+ end
150
+ if options[:format] == :allcaps
151
+ values.each do |value|
152
+ value.capitalize!
153
+ end
154
+
155
+ if options.empty?
156
+ options[:seperator] = " "
157
+ end
158
+
159
+ return values.join " "
160
+ end
161
+
162
+ if options[:format] == :class
163
+ values.each do |value|
164
+ value.capitalize!
165
+ end
166
+
167
+ return values.join ""
168
+ end
169
+
170
+ if options[:format] == :sentence
171
+ values[0].capitalize! unless values[0].match(/[a-zA-Z][A-Z]/)
172
+
173
+ return values.join " "
174
+ end
175
+
176
+ if options[:format] == :nocaps
177
+ return values.join " "
178
+ end
179
+ end
180
+
181
+ def self.fixascii(string)
182
+ if string.respond_to?(:encode)
183
+ self.fixutf8(string).encode("ASCII-8BIT")
184
+ else
185
+ string
186
+ end
187
+ end
188
+
189
+ def self.to_utf8(string)
190
+ string.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => "?").encode('UTF-8')
191
+ end
192
+
193
+ def self.fixutf8(string)
194
+ return nil if string.nil?
195
+ return string if string.respond_to?(:encoding) && string.encoding.to_s == "UTF-8" && (string.respond_to?(:valid_encoding?) && string.valid_encoding?) ||
196
+ (string.respond_to?(:valid_encoding) && string.valid_encoding)
197
+
198
+ if string.respond_to?(:encode)
199
+ string.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
200
+ else
201
+ require 'iconv'
202
+ @@ic ||= Iconv.new('UTF-8//IGNORE', 'UTF-8')
203
+ @@ic.iconv(string)
204
+ end
205
+ end
206
+
207
+ def self.humanize_list(list)
208
+ return "" if list.empty?
209
+ if list.length == 1
210
+ list.first
211
+ else
212
+ list[0..-2].collect{|e| e.to_s} * ", " << " and " << list[-1].to_s
213
+ end
214
+ end
215
+
216
+ def self.parse_sql_values(txt)
217
+ io = StringIO.new txt.strip
218
+
219
+ values = []
220
+ fields = []
221
+ current = nil
222
+ quoted = false
223
+ while c = io.getc
224
+ if quoted
225
+ if c == "'"
226
+ quoted = false
227
+ else
228
+ current << c
229
+ end
230
+ else
231
+ case c
232
+ when "("
233
+ current = ""
234
+ when ")"
235
+ fields << current
236
+ values << fields
237
+ fields = []
238
+ current = nil
239
+ when ','
240
+ if not current.nil?
241
+ fields << current
242
+ current = ""
243
+ end
244
+ when "'"
245
+ quoted = true
246
+ when ";"
247
+ break
248
+ else
249
+ current << c
250
+ end
251
+ end
252
+ end
253
+ values
254
+ end
255
+ end
@@ -0,0 +1,31 @@
1
+ module Misc
2
+ def self.intersect_sorted_arrays(a1, a2)
3
+ e1, e2 = a1.shift, a2.shift
4
+ intersect = []
5
+ while true
6
+ break if e1.nil? or e2.nil?
7
+ case e1 <=> e2
8
+ when 0
9
+ intersect << e1
10
+ e1, e2 = a1.shift, a2.shift
11
+ when -1
12
+ e1 = a1.shift while not e1.nil? and e1 < e2
13
+ when 1
14
+ e2 = a2.shift
15
+ e2 = a2.shift while not e2.nil? and e2 < e1
16
+ end
17
+ end
18
+ intersect
19
+ end
20
+
21
+ def self.counts(array)
22
+ counts = {}
23
+ array.each do |e|
24
+ counts[e] ||= 0
25
+ counts[e] += 1
26
+ end
27
+
28
+ counts
29
+ end
30
+
31
+ end
@@ -0,0 +1,56 @@
1
+ module Misc
2
+ def self.insist(times = 4, sleep = nil, msg = nil)
3
+ sleep_array = nil
4
+
5
+ try = 0
6
+ begin
7
+ begin
8
+ yield
9
+ rescue Exception
10
+ if Array === times
11
+ sleep_array = times
12
+ times = sleep_array.length
13
+ sleep = sleep_array.shift
14
+ end
15
+
16
+ if sleep.nil?
17
+ sleep_array = ([0] + [0.001, 0.01, 0.1, 0.5] * (times / 3)).sort[0..times-1]
18
+ sleep = sleep_array.shift
19
+ end
20
+ raise $!
21
+ end
22
+ rescue TryAgain
23
+ sleep sleep
24
+ retry
25
+ rescue StopInsist
26
+ raise $!.exception
27
+ rescue Aborted, Interrupt
28
+ if msg
29
+ Log.warn("Not Insisting after Aborted: #{$!.message} -- #{msg}")
30
+ else
31
+ Log.warn("Not Insisting after Aborted: #{$!.message}")
32
+ end
33
+ raise $!
34
+ rescue Exception
35
+ Log.exception $! if ENV["SCOUT_LOG_INSIST"] == 'true'
36
+ if msg
37
+ Log.warn("Insisting after exception: #{$!.class} #{$!.message} -- #{msg}")
38
+ elsif FalseClass === msg
39
+ nil
40
+ else
41
+ Log.warn("Insisting after exception: #{$!.class} #{$!.message}")
42
+ end
43
+
44
+ if sleep and try > 0
45
+ sleep sleep
46
+ sleep = sleep_array.shift || sleep if sleep_array
47
+ else
48
+ Thread.pass
49
+ end
50
+
51
+ try += 1
52
+ retry if try < times
53
+ raise $!
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ module Misc
2
+ def self.pid_alive?(pid)
3
+ !! Process.kill(0, pid) rescue false
4
+ end
5
+
6
+ def self.benchmark(repeats = 1, message = nil)
7
+ require 'benchmark'
8
+ res = nil
9
+ begin
10
+ measure = Benchmark.measure do
11
+ repeats.times do
12
+ res = yield
13
+ end
14
+ end
15
+ if message
16
+ puts "#{message }: #{ repeats } repeats"
17
+ else
18
+ puts "Benchmark for #{ repeats } repeats (#{caller.first})"
19
+ end
20
+ puts measure
21
+ rescue Exception
22
+ puts "Benchmark aborted"
23
+ raise $!
24
+ end
25
+ res
26
+ end
27
+
28
+ def self.profile(options = {})
29
+ require 'ruby-prof'
30
+ profiler = RubyProf::Profile.new
31
+ profiler.start
32
+ begin
33
+ res = yield
34
+ rescue Exception
35
+ puts "Profiling aborted"
36
+ raise $!
37
+ ensure
38
+ result = profiler.stop
39
+ printer = RubyProf::FlatPrinter.new(result)
40
+ printer.print(STDOUT, options)
41
+ end
42
+
43
+ res
44
+ end
45
+
46
+ def self.exec_time(&block)
47
+ start = Time.now
48
+ eend = nil
49
+ begin
50
+ yield
51
+ ensure
52
+ eend = Time.now
53
+ end
54
+ eend - start
55
+ end
56
+
57
+ def self.wait_for_interrupt
58
+ while true
59
+ begin
60
+ sleep 1
61
+ rescue Interrupt
62
+ break
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,73 @@
1
+ module Misc
2
+
3
+ def self.hostname
4
+ @@hostname ||= begin
5
+ `hostname`.strip
6
+ end
7
+ end
8
+
9
+ def self.children(ppid = nil)
10
+ require 'sys/proctable'
11
+
12
+ ppid ||= Process.pid
13
+ Sys::ProcTable.ps.select{ |pe| pe.ppid == ppid }
14
+ end
15
+
16
+ def self.env_add(var, value, sep = ":", prepend = true)
17
+ if ENV[var].nil?
18
+ ENV[var] = value
19
+ elsif ENV[var] =~ /(#{sep}|^)#{Regexp.quote value}(#{sep}|$)/
20
+ return
21
+ else
22
+ if prepend
23
+ ENV[var] = value + sep + ENV[var]
24
+ else
25
+ ENV[var] += sep + value
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.with_env(var, value, &block)
31
+ old_value = ENV[var]
32
+ begin
33
+ ENV[var] = value
34
+ yield
35
+ ensure
36
+ ENV[var] = old_value
37
+ end
38
+ end
39
+
40
+ def self.update_git(gem_name = 'scout-gear')
41
+ gem_name = 'scout-gear' if gem_name.nil?
42
+ dir = File.join(__dir__, '../../../../', gem_name)
43
+ return unless Open.exist?(dir)
44
+ Misc.in_dir dir do
45
+ begin
46
+ begin
47
+ CMD.cmd_log('git pull')
48
+ rescue
49
+ raise "Could not update #{gem_name}"
50
+ end
51
+
52
+ begin
53
+ CMD.cmd_log('git submodule update')
54
+ rescue
55
+ raise "Could not update #{gem_name} submodules"
56
+ end
57
+
58
+
59
+ begin
60
+ CMD.cmd_log('rake install')
61
+ rescue
62
+ raise "Could not install updated #{gem_name}"
63
+ end
64
+ rescue
65
+ Log.warn $!.message
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.processors
71
+ Etc.nprocessors
72
+ end
73
+ end
data/lib/scout/misc.rb ADDED
@@ -0,0 +1,10 @@
1
+ require_relative 'misc/format'
2
+ require_relative 'misc/insist'
3
+ require_relative 'misc/digest'
4
+ require_relative 'misc/filesystem'
5
+ require_relative 'misc/monitor'
6
+ require_relative 'misc/system'
7
+ require_relative 'misc/helper'
8
+
9
+ module Misc
10
+ end
@@ -0,0 +1,138 @@
1
+ require_relative 'meta_extension'
2
+ module NamedArray
3
+ extend MetaExtension
4
+ extension_attr :fields, :key
5
+
6
+ def self.field_match(field, name)
7
+ if (String === field) && (String === name)
8
+ field == name ||
9
+ field.start_with?(name) || field.include?("(" + name + ")") ||
10
+ name.start_with?(field) || name.include?("(" + field + ")")
11
+ else
12
+ field == name
13
+ end
14
+ end
15
+
16
+ def self.identify_name(names, selected, strict: false)
17
+ res = (Array === selected ? selected : [selected]).collect do |field|
18
+ case field
19
+ when nil
20
+ 0
21
+ when Range
22
+ field
23
+ when Integer
24
+ field
25
+ when Symbol
26
+ field == :key ? field : identify_name(names, field.to_s)
27
+ when (names.nil? and String)
28
+ if field =~ /^\d+$/
29
+ identify_field(key_field, fields, field.to_i)
30
+ else
31
+ raise "No name information available and specified name not numeric: #{ field }"
32
+ end
33
+ when Symbol
34
+ names.index{|f| f.to_s == field.to_s }
35
+ when String
36
+ pos = names.index{|f| f.to_s == field }
37
+ next pos if pos
38
+ if field =~ /^\d+$/
39
+ next identify_names(names, field.to_i)
40
+ end
41
+ next pos if strict
42
+ pos = names.index{|name| field_match(field, name) }
43
+ next pos if pos
44
+ nil
45
+ else
46
+ raise "Field '#{ Log.fingerprint field }' was not understood. Options: (#{ Log.fingerprint names })"
47
+ end
48
+ end
49
+
50
+ Array === selected ? res : res.first
51
+ end
52
+
53
+ def identify_name(selected)
54
+ NamedArray.identify_name(fields, selected)
55
+ end
56
+
57
+ def positions(fields)
58
+ if Array == fields
59
+ fields.collect{|field|
60
+ NamedArray.identify_name(@fields, field)
61
+ }
62
+ else
63
+ NamedArray.identify_name(@fields, fields)
64
+ end
65
+ end
66
+
67
+ def [](key)
68
+ pos = NamedArray.identify_name(@fields, key)
69
+ return nil if pos.nil?
70
+ super(pos)
71
+ end
72
+
73
+ def concat(other)
74
+ super(other)
75
+ self.fields.concat(other.fields) if NamedArray === other
76
+ self
77
+ end
78
+
79
+ def to_hash
80
+ hash = {}
81
+ self.fields.zip(self) do |field,value|
82
+ hash[field] = value
83
+ end
84
+ IndiferentHash.setup hash
85
+ end
86
+
87
+ def values_at(*positions)
88
+ super(*identify_name(positions))
89
+ end
90
+
91
+ def self._zip_fields(array, max = nil)
92
+ return [] if array.nil? or array.empty? or (first = array.first).nil?
93
+
94
+ max = array.collect{|l| l.length }.max if max.nil?
95
+
96
+ rest = array[1..-1].collect{|v|
97
+ v.length == 1 & max > 1 ? v * max : v
98
+ }
99
+
100
+ first = first * max if first.length == 1 and max > 1
101
+
102
+ first.zip(*rest)
103
+ end
104
+
105
+ def self.zip_fields(array)
106
+ if array.length < 10000
107
+ _zip_fields(array)
108
+ else
109
+ zipped_slices = []
110
+ max = array.collect{|l| l.length}.max
111
+ array.each_slice(10000) do |slice|
112
+ zipped_slices << _zip_fields(slice, max)
113
+ end
114
+ new = zipped_slices.first
115
+ zipped_slices[1..-1].each do |rest|
116
+ rest.each_with_index do |list,i|
117
+ new[i].concat list
118
+ end
119
+ end
120
+ new
121
+ end
122
+ end
123
+
124
+ def self.add_zipped(source, new)
125
+ source.zip(new).each do |s,n|
126
+ s.concat(n)
127
+ end
128
+ source
129
+ end
130
+
131
+ def method_missing(name, *args)
132
+ if identify_name(name)
133
+ return self[name]
134
+ else
135
+ return super(name, *args)
136
+ end
137
+ end
138
+ end