duct_tape 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|