duct_tape 0.0.4
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.
- data/Gemfile +10 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +25 -0
- data/README.md +223 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/duct_tape.gemspec +106 -0
- data/ext/mkrf_conf.rb +36 -0
- data/git_hooks/env_vars.sh +287 -0
- data/git_hooks/post-commit +43 -0
- data/git_hooks/post-merge +8 -0
- data/git_hooks/pre-commit +273 -0
- data/install_git_hooks +17 -0
- data/lib/algorithms/containers/heap.rb +15 -0
- data/lib/algorithms/containers/priority_queue.rb +11 -0
- data/lib/algorithms/containers.rb +1 -0
- data/lib/duct_tape/autoassociative_array.rb +110 -0
- data/lib/duct_tape.rb +10 -0
- data/lib/ext/array.rb +327 -0
- data/lib/ext/boolean.rb +3 -0
- data/lib/ext/datetime.rb +7 -0
- data/lib/ext/dir.rb +59 -0
- data/lib/ext/file.rb +40 -0
- data/lib/ext/hash.rb +83 -0
- data/lib/ext/kernel.rb +593 -0
- data/lib/ext/numeric.rb +74 -0
- data/lib/ext/object.rb +17 -0
- data/lib/ext/pathname.rb +114 -0
- data/lib/ext/range.rb +7 -0
- data/lib/ext/regexp.rb +12 -0
- data/lib/ext/string.rb +54 -0
- data/lib/ext/symbol.rb +8 -0
- data/lib/ext/time.rb +32 -0
- data/lib/ext/uri.rb +16 -0
- data/spec/algorithms/containers/heap_spec.rb +19 -0
- data/spec/algorithms/containers/priority_queue_spec.rb +19 -0
- data/spec/duct_tape/autoassociative_array_spec.rb +139 -0
- data/spec/ext/array_spec.rb +407 -0
- data/spec/ext/boolean_spec.rb +19 -0
- data/spec/ext/datetime_spec.rb +10 -0
- data/spec/ext/dir_spec.rb +46 -0
- data/spec/ext/file_spec.rb +10 -0
- data/spec/ext/hash_spec.rb +73 -0
- data/spec/ext/kernel_spec.rb +64 -0
- data/spec/ext/numeric_spec.rb +61 -0
- data/spec/ext/object_spec.rb +19 -0
- data/spec/ext/pathname_spec.rb +13 -0
- data/spec/ext/range_spec.rb +10 -0
- data/spec/ext/regexp_spec.rb +10 -0
- data/spec/ext/string_spec.rb +73 -0
- data/spec/ext/symbol_spec.rb +10 -0
- data/spec/ext/time_spec.rb +19 -0
- data/spec/ext/uri_spec.rb +28 -0
- data/spec/spec_helper.rb +7 -0
- metadata +183 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
module Containers
|
2
|
+
class AutoassociativeArray
|
3
|
+
def initialize
|
4
|
+
@values = []
|
5
|
+
@columns = Hash.new { |hsh,key| hsh[key] = {} }
|
6
|
+
@hash = Hash.new { |hsh,key| hsh[key] = [] }
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def insert(*args)
|
11
|
+
@values |= [args]
|
12
|
+
args.size.times do |i|
|
13
|
+
@columns[i][@values[-1][i]] = @values[-1]
|
14
|
+
@hash[@values[-1][i]].push(@values[-1])
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def partial_match(*args)
|
20
|
+
matches = {}
|
21
|
+
ret = []
|
22
|
+
(1..args.size).reverse_each do |i|
|
23
|
+
args.combination(i) do |subset|
|
24
|
+
check = match_impl(*subset)
|
25
|
+
matches[subset] = check unless check.empty?
|
26
|
+
end
|
27
|
+
most_matches = matches.map { |k,v| v.size }.max
|
28
|
+
matches.reject! { |k,v| v.size < most_matches }
|
29
|
+
unless matches.empty?
|
30
|
+
matches.each { |k,v| ret |= v }
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
(ret && ret.one? ? ret[0] : ret)
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](*args)
|
38
|
+
ret = match_impl(*args)
|
39
|
+
(ret && ret.one? ? ret[0] : ret)
|
40
|
+
end
|
41
|
+
|
42
|
+
def by_column(col, key)
|
43
|
+
(@columns.has_key?(col) && @columns[col].has_key?(key)) ? @columns[col][key] : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def empty?
|
47
|
+
@values.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def length
|
51
|
+
@values.size
|
52
|
+
end
|
53
|
+
alias_method :size, :length
|
54
|
+
|
55
|
+
def method_missing(*args, &block)
|
56
|
+
ret = @values.__send__(*args, &block)
|
57
|
+
rebuild_hash
|
58
|
+
ret
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear
|
62
|
+
@hash.clear
|
63
|
+
@values.clear
|
64
|
+
@columns.clear
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
@values.inspect
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
@values.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def dup
|
77
|
+
ret = self.class.new
|
78
|
+
@values.each { |set| ret.insert(*set) }
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
|
82
|
+
def <<(ary)
|
83
|
+
insert(*ary)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def rebuild_hash
|
89
|
+
@hash.clear
|
90
|
+
@columns.clear
|
91
|
+
@values.each_with_index do |ary,idx|
|
92
|
+
type_assert(ary, Array)
|
93
|
+
ary.size.times do |i|
|
94
|
+
@columns[i][@values[idx][i]] = @values[idx]
|
95
|
+
@hash[@values[idx][i]].push(@values[idx])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def match_impl(*args)
|
102
|
+
ret = @hash[args.first]
|
103
|
+
args[1..-1].each do |k|
|
104
|
+
ret &= @hash[k]
|
105
|
+
break if ret.empty?
|
106
|
+
end
|
107
|
+
ret
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/duct_tape.rb
ADDED
data/lib/ext/array.rb
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
class Array
|
2
|
+
# Returns a copy of self deep_merged with another array
|
3
|
+
def deep_merge(second)
|
4
|
+
target = deep_dup
|
5
|
+
target.deep_merge!(second.deep_dup)
|
6
|
+
target
|
7
|
+
end
|
8
|
+
|
9
|
+
# Deep_merge self with another array
|
10
|
+
def deep_merge!(second)
|
11
|
+
return nil unless second
|
12
|
+
type_assert(second, Array)
|
13
|
+
changed = nil
|
14
|
+
second.each_index do |k|
|
15
|
+
if self[k].is_a?(Array) and second[k].is_a?(Array)
|
16
|
+
changed |= true if self[k].deep_merge!(second[k])
|
17
|
+
elsif self[k].is_a?(Hash) and second[k].is_a?(Hash)
|
18
|
+
changed |= true if self[k].deep_merge!(second[k])
|
19
|
+
elsif exclude?(second[k])
|
20
|
+
self << second[k]
|
21
|
+
changed |= true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return nil unless changed
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# 3 Forms:
|
29
|
+
# Multiplied by an Integer, returns a single array with the repeated contents of self
|
30
|
+
# Multiplied by a String, returns self.join
|
31
|
+
# Multiplied by an Array, returns the set-wise cross-product of the two Arrays
|
32
|
+
def *(second)
|
33
|
+
ret = []
|
34
|
+
case second
|
35
|
+
when Integer
|
36
|
+
second.times { |i| ret += dup }
|
37
|
+
when String
|
38
|
+
return join(second)
|
39
|
+
when Array
|
40
|
+
each { |x| second.each { |y| ret << [x,y].flatten } }
|
41
|
+
else
|
42
|
+
raise TypeError.new("can't convert #{second.class} into Integer")
|
43
|
+
end
|
44
|
+
return ret
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the set-wise n-th power of self. (The length of the return value
|
48
|
+
# is equal to: self.length ** n)
|
49
|
+
# e.g [0,1] ** 0 #=> []
|
50
|
+
# e.g [0,1] ** 1 #=> [[0], [1]]
|
51
|
+
# e.g [0,1] ** 2 #=> [[0, 0], [0, 1], [1, 0], [1, 1]]
|
52
|
+
# e.g [0,1] ** 3 #=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
|
53
|
+
# # [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
|
54
|
+
def **(n)
|
55
|
+
type_assert(n, Integer)
|
56
|
+
ret = []
|
57
|
+
if n > 1
|
58
|
+
ret = dup
|
59
|
+
(n - 1).times {
|
60
|
+
temp = []
|
61
|
+
ret.each { |x| each { |y| temp << [x,y].flatten } }
|
62
|
+
ret = temp
|
63
|
+
}
|
64
|
+
elsif n == 1
|
65
|
+
ret = map { |item| [item] }
|
66
|
+
end
|
67
|
+
return ret
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a new Array rejecting objects based on if the block-mapped
|
71
|
+
# values are unique
|
72
|
+
def uniq_by(&block)
|
73
|
+
ret = dup
|
74
|
+
ret.uniq_by!(&block)
|
75
|
+
ret
|
76
|
+
end
|
77
|
+
|
78
|
+
# 1st Form:
|
79
|
+
# [1,1,1].unanimous? #=> true
|
80
|
+
# [1,1,2].unanimous? #=> false
|
81
|
+
#
|
82
|
+
# 2nd Form:
|
83
|
+
# [1,1,1].unanimous?(1) #=> true
|
84
|
+
# [1,1,1].unanimous?(2) #=> false
|
85
|
+
#
|
86
|
+
# 3rd Form:
|
87
|
+
# [1,1,1].unanimous? { |i| i == 2 } #=> true
|
88
|
+
#
|
89
|
+
# 4th Form:
|
90
|
+
# [1,3,5].unanimous?(true) { |i| i % 2 == 1 } #=> true
|
91
|
+
def unanimous?(arg=nil, &block)
|
92
|
+
ret = true
|
93
|
+
cmp = (arg.nil? ? (block_given? ? block[first] : first) : arg)
|
94
|
+
each_with_index do |elem,index|
|
95
|
+
next if index == 0 && arg.nil?
|
96
|
+
ret &&= (block_given? ? (cmp == block[elem]) : (cmp == elem))
|
97
|
+
break unless ret
|
98
|
+
end
|
99
|
+
ret
|
100
|
+
end
|
101
|
+
|
102
|
+
# Converts certain Arrays to Hashes:
|
103
|
+
# ["a","b","c"].to_h #=> {0=>"a", 1=>"b", 2=>"c"}
|
104
|
+
# [[1,2], [3,4]].to_h #=> {1=>2, 3=>4}
|
105
|
+
# [{1 => 2}, {3 => 4}].to_h #=> {1=>2, 3=>4}
|
106
|
+
# [{"name" => 1, "value" => 2},
|
107
|
+
# {"name" => 3, "value" => 4}].to_h #=> {1=>2, 3=>4}
|
108
|
+
# [{:x => 1, :y => 2},
|
109
|
+
# {:x => 3, :y => 4}].to_h(:x, :y) #=> {1=>2, 3=>4}
|
110
|
+
# [{1 => 2, 3 => 4}, {1 => 4}].to_h #=> {1=>[2, 4], 3=>4}
|
111
|
+
def to_h(name_key="name", value_key="value")
|
112
|
+
#raise "Elements are not unique!" unless self == uniq
|
113
|
+
ret = {}
|
114
|
+
collisions = Hash.new { |hsh,key| hsh[key] = 0 }
|
115
|
+
each_with_index do |elem,index|
|
116
|
+
temp = {}
|
117
|
+
if elem.is_a?(Hash)
|
118
|
+
temp = elem
|
119
|
+
if elem[name_key] and elem[value_key] and elem.length == 2
|
120
|
+
temp = {elem[name_key] => elem[value_key]}
|
121
|
+
elsif elem[name_key.to_s] and elem[value_key.to_s] and elem.length == 2
|
122
|
+
temp = {elem[name_key.to_s] => elem[value_key.to_s]}
|
123
|
+
elsif elem[name_key.to_sym] and elem[value_key.to_sym] and elem.length == 2
|
124
|
+
temp = {elem[name_key.to_sym] => elem[value_key.to_sym]}
|
125
|
+
end
|
126
|
+
elsif elem.is_a?(Array)
|
127
|
+
if elem.length == 2
|
128
|
+
temp[elem.first] = elem.last
|
129
|
+
elsif elem.length > 2
|
130
|
+
temp[elem.first] = elem[1..-1]
|
131
|
+
else
|
132
|
+
temp[index] = elem.first
|
133
|
+
end
|
134
|
+
else
|
135
|
+
temp[index] = elem
|
136
|
+
end
|
137
|
+
temp.each do |k,v|
|
138
|
+
if ret.has_key?(k)
|
139
|
+
if collisions[k] == 0
|
140
|
+
if ret[k] != v
|
141
|
+
collisions[k] += 1
|
142
|
+
ret[k] = [ret[k], v]
|
143
|
+
end
|
144
|
+
elsif ret[k].exclude?(v)
|
145
|
+
collisions[k] += 1
|
146
|
+
ret[k] << v
|
147
|
+
end
|
148
|
+
else
|
149
|
+
ret[k] = v
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
ret
|
154
|
+
end
|
155
|
+
alias_method :to_hash, :to_h
|
156
|
+
|
157
|
+
# Creates a hash with keys equal to the contents of self and values equal to the
|
158
|
+
# mapped array's values
|
159
|
+
def map_to_h(&block)
|
160
|
+
[self, map(&block)].transpose.to_h
|
161
|
+
end
|
162
|
+
alias_method :map_to_hash, :map_to_h
|
163
|
+
|
164
|
+
# Chunks an array into segments of maximum length
|
165
|
+
# [1,2,3,4,5,6,7,8,9,10].chunk(3) #=> [[1,2,3], [4,5,6], [7,8,9], [10]]
|
166
|
+
def chunk(max_length=nil, &block)
|
167
|
+
if ::RUBY_VERSION >= "1.9" && block_given?
|
168
|
+
super(&block)
|
169
|
+
else
|
170
|
+
each_slice(max_length).to_a
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def not_empty?
|
175
|
+
!empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# Statistics from:
|
180
|
+
# http://github.com/christianblais/utilities.git
|
181
|
+
#
|
182
|
+
|
183
|
+
# Add each object of the array to each other in order to get the sum, as long as all objects respond to + operator
|
184
|
+
def sum
|
185
|
+
flatten.compact.inject(:+)
|
186
|
+
end
|
187
|
+
|
188
|
+
def product
|
189
|
+
flatten.compact.inject(:*)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Calculate squares of each item
|
193
|
+
def squares
|
194
|
+
map { |i| i ** 2 }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Return a new array containing the rank of each value
|
198
|
+
# Ex: [1, 2, 2, 8, 9] #=> [0.0, 1.5, 1.5, 3.0, 4.0]
|
199
|
+
def ranks(already_sorted=false)
|
200
|
+
a = already_sorted ? self : sort
|
201
|
+
map { |i| (a.index(i) + a.rindex(i)) / 2.0 }
|
202
|
+
end
|
203
|
+
|
204
|
+
# Calculate square roots of each item
|
205
|
+
def sqrts
|
206
|
+
map { |i| Math.sqrt(i) }
|
207
|
+
end
|
208
|
+
|
209
|
+
# Calculate the arithmetic mean of the array, as long as all objects respond to / operator
|
210
|
+
def mean
|
211
|
+
a = flatten.compact
|
212
|
+
(a.size > 0) ? a.sum.to_f / a.size : 0.0
|
213
|
+
end
|
214
|
+
alias_method :average, :mean
|
215
|
+
|
216
|
+
# TODO - Geometric mean
|
217
|
+
|
218
|
+
# Calculate the number of occurences for each element of the array
|
219
|
+
def frequencies
|
220
|
+
inject(Hash.new(0)) { |h,v| h[v] += 1; h }
|
221
|
+
end
|
222
|
+
|
223
|
+
# Return the variance of self
|
224
|
+
def variance(population=false)
|
225
|
+
m = mean.to_f
|
226
|
+
map { |v| (v - m) ** 2 }.sum / (size - (population ? 0 : 1))
|
227
|
+
end
|
228
|
+
|
229
|
+
# Return the (sample|population) standard deviation of self
|
230
|
+
# If population is set to true, then we consider the dataset as the complete population
|
231
|
+
# Else, we consider the dataset as a sample, so we use the sample standard deviation (size - 1)
|
232
|
+
def standard_deviation(population=false)
|
233
|
+
size > 1 ? Math.sqrt(variance(population)) : 0.0
|
234
|
+
end
|
235
|
+
alias_method :std_dev, :standard_deviation
|
236
|
+
|
237
|
+
# Return the median of sorted self
|
238
|
+
def median(already_sorted=false)
|
239
|
+
return nil if empty?
|
240
|
+
a = already_sorted ? self : sort
|
241
|
+
m_pos = size / 2
|
242
|
+
size % 2 == 1 ? a[m_pos] : (a[m_pos-1] + a[m_pos]).to_f / 2
|
243
|
+
end
|
244
|
+
alias_method :second_quartile, :median
|
245
|
+
|
246
|
+
# Return the first quartile of self
|
247
|
+
def first_quartile(already_sorted=false)
|
248
|
+
return nil if size < 4
|
249
|
+
a = already_sorted ? self : sort
|
250
|
+
a[0..((size / 2) - 1)].median(true)
|
251
|
+
end
|
252
|
+
alias_method :lower_quartile, :first_quartile
|
253
|
+
|
254
|
+
# Return the last quartile of self
|
255
|
+
def last_quartile(already_sorted=false)
|
256
|
+
return nil if size < 4
|
257
|
+
a = already_sorted ? self : sort
|
258
|
+
a[((size / 2) + 1)..-1].median(true)
|
259
|
+
end
|
260
|
+
alias_method :upper_quartile, :last_quartile
|
261
|
+
|
262
|
+
# Return an array containing the first, the second and the last quartile of self
|
263
|
+
def quartiles(already_sorted=false)
|
264
|
+
a = already_sorted ? self : sort
|
265
|
+
[a.first_quartile(true), a.median(true), a.last_quartile(true)]
|
266
|
+
end
|
267
|
+
|
268
|
+
# Calculate the interquartile range of self
|
269
|
+
def interquartile_range(already_sorted=false)
|
270
|
+
return nil if size < 4
|
271
|
+
a = already_sorted ? self : sort
|
272
|
+
a.last_quartile - a.first_quartile
|
273
|
+
end
|
274
|
+
|
275
|
+
# Return a hash of modes with their corresponding occurences
|
276
|
+
def modes
|
277
|
+
fre = frequencies
|
278
|
+
max = fre.values.max
|
279
|
+
fre.select { |k, f| f == max }
|
280
|
+
end
|
281
|
+
|
282
|
+
# Return the midrange of sorted self
|
283
|
+
def midrange(already_sorted=false)
|
284
|
+
return nil if empty?
|
285
|
+
a = already_sorted ? self : sort
|
286
|
+
(a.first + a.last) / 2.0
|
287
|
+
end
|
288
|
+
|
289
|
+
# Return the statistical range of sorted self
|
290
|
+
def statistical_range(already_sorted=false)
|
291
|
+
return nil if empty?
|
292
|
+
a = already_sorted ? self : sort
|
293
|
+
(a.last - a.first)
|
294
|
+
end
|
295
|
+
|
296
|
+
# Return all statistics from self in a simple hash
|
297
|
+
def statistics(already_sorted=false)
|
298
|
+
sorted = sort
|
299
|
+
|
300
|
+
{
|
301
|
+
:first => self.first,
|
302
|
+
:last => self.last,
|
303
|
+
:size => self.size,
|
304
|
+
:sum => self.sum,
|
305
|
+
:squares => self.squares,
|
306
|
+
:sqrts => self.sqrts,
|
307
|
+
:min => self.min,
|
308
|
+
:max => self.max,
|
309
|
+
:mean => self.mean,
|
310
|
+
:frequencies => self.frequencies,
|
311
|
+
:variance => self.variance,
|
312
|
+
:standard_deviation => self.standard_deviation,
|
313
|
+
:population_variance => self.variance(true),
|
314
|
+
:population_standard_deviation => self.standard_deviation(true),
|
315
|
+
:modes => self.modes,
|
316
|
+
|
317
|
+
# Need to be sorted...
|
318
|
+
:ranks => sorted.ranks(true),
|
319
|
+
:median => sorted.median(true),
|
320
|
+
:midrange => sorted.midrange(true),
|
321
|
+
:statistical_range => sorted.statistical_range(true),
|
322
|
+
:quartiles => sorted.quartiles(true),
|
323
|
+
:interquartile_range => sorted.interquartile_range(true)
|
324
|
+
}
|
325
|
+
end
|
326
|
+
alias_method :stats, :statistics
|
327
|
+
end
|
data/lib/ext/boolean.rb
ADDED
data/lib/ext/datetime.rb
ADDED
data/lib/ext/dir.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class Dir
|
4
|
+
# Determine the relative path based on pwd
|
5
|
+
def self.relative_path(abs_path, from=Dir.pwd)
|
6
|
+
Pathname.new(abs_path).expand_path.relative_path_from(Pathname.new(from)).to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.absolute_path(path)
|
10
|
+
Pathname.new(path).expand_path.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.empty?(dir)
|
14
|
+
entries(dir).join == "..."
|
15
|
+
end
|
16
|
+
|
17
|
+
def relative_path
|
18
|
+
Dir.relative_path(self.path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty?
|
22
|
+
Dir.empty?(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def absolute_path
|
26
|
+
Dir.absolute_path(self.path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def children
|
30
|
+
entries.map_to_h { |name| Pathname.join(absolute_path, name) }
|
31
|
+
end
|
32
|
+
alias_method :to_h, :children
|
33
|
+
|
34
|
+
def [](*path)
|
35
|
+
pathname = Pathname.new(File.join(path.flatten))
|
36
|
+
return nil unless pathname.exist?
|
37
|
+
pathname
|
38
|
+
end
|
39
|
+
|
40
|
+
def /(path)
|
41
|
+
self[path]
|
42
|
+
end
|
43
|
+
|
44
|
+
def files
|
45
|
+
children.values.reject { |v| !(File.file?(v)) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def directories
|
49
|
+
children.values.reject { |v| !(File.directory?(v)) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def writable?
|
53
|
+
File.writable?(self.path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def readable?
|
57
|
+
File.readable?(self.path)
|
58
|
+
end
|
59
|
+
end
|
data/lib/ext/file.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class File
|
4
|
+
|
5
|
+
def self.relative_path(abs_path, pwd=Dir.pwd)
|
6
|
+
Dir.relative_path(abs_path, pwd)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.absolute_path(path)
|
10
|
+
Dir.absolute_path(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def relative_path
|
14
|
+
Dir.relative_path(self.path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def absolute_path
|
18
|
+
Dir.absolute_path(self.path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def dirname
|
22
|
+
File.dirname(self.path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def basename(*args)
|
26
|
+
File.basename(self.path, *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def writable?
|
30
|
+
File.writable?(self.path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def readable?
|
34
|
+
File.readable?(self.path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def executable?
|
38
|
+
File.executable?(self.path)
|
39
|
+
end
|
40
|
+
end
|
data/lib/ext/hash.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
class Hash
|
2
|
+
# Merges self with another second, recursively.
|
3
|
+
#
|
4
|
+
# This code was lovingly stolen from some random gem:
|
5
|
+
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
6
|
+
#
|
7
|
+
# Thanks to whoever made it.
|
8
|
+
#
|
9
|
+
# Modified to provide same functionality with Arrays
|
10
|
+
|
11
|
+
def deep_merge(second)
|
12
|
+
target = deep_dup
|
13
|
+
target.deep_merge!(second.deep_dup)
|
14
|
+
target
|
15
|
+
end
|
16
|
+
|
17
|
+
# From: http://www.gemtacular.com/gemdocs/cerberus-0.2.2/doc/classes/Hash.html
|
18
|
+
# File lib/cerberus/utils.rb, line 42
|
19
|
+
# Modified to provide same functionality with Arrays
|
20
|
+
|
21
|
+
def deep_merge!(second)
|
22
|
+
return nil unless second
|
23
|
+
type_assert(second, Hash)
|
24
|
+
second.each_pair do |k,v|
|
25
|
+
if self[k].is_a?(Array) and second[k].is_a?(Array)
|
26
|
+
self[k].deep_merge!(second[k])
|
27
|
+
elsif self[k].is_a?(Hash) and second[k].is_a?(Hash)
|
28
|
+
self[k].deep_merge!(second[k])
|
29
|
+
else
|
30
|
+
self[k] = second[k]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_h
|
36
|
+
self
|
37
|
+
end
|
38
|
+
alias_method :to_hash, :to_h
|
39
|
+
|
40
|
+
def select_keys(other, &block)
|
41
|
+
target = dup
|
42
|
+
target.select_keys!(other, &block)
|
43
|
+
target
|
44
|
+
end
|
45
|
+
alias_method :&, :select_keys
|
46
|
+
|
47
|
+
def select_keys!(other, &block)
|
48
|
+
type_assert(other, Array, Hash)
|
49
|
+
unless block_given?
|
50
|
+
other = other.keys if Hash === other
|
51
|
+
block = proc { |k| other.include?(k) }
|
52
|
+
end
|
53
|
+
self.reject! { |key,val| !block[key] }
|
54
|
+
end
|
55
|
+
|
56
|
+
def reject_keys(other, &block)
|
57
|
+
target = dup
|
58
|
+
target.reject_keys!(other, &block)
|
59
|
+
target
|
60
|
+
end
|
61
|
+
alias_method :-, :reject_keys
|
62
|
+
|
63
|
+
def reject_keys!(other, &block)
|
64
|
+
type_assert(other, Array, Hash)
|
65
|
+
unless block_given?
|
66
|
+
other = other.keys if Hash === other
|
67
|
+
block = proc { |k| other.include?(k) }
|
68
|
+
end
|
69
|
+
self.reject! { |key,val| block[key] }
|
70
|
+
end
|
71
|
+
|
72
|
+
def chunk(max_length=nil, &block)
|
73
|
+
if ::RUBY_VERSION >= "1.9" && block_given?
|
74
|
+
super(&block)
|
75
|
+
else
|
76
|
+
each_slice(max_length).to_a.map! { |ary| ary.to_h }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def not_empty?(arg)
|
81
|
+
!empty?
|
82
|
+
end
|
83
|
+
end
|