rmtools 2.0.0.rc5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -3
- data/lib/rmtools/enumerable/array.rb +40 -23
- data/lib/rmtools/enumerable/array_iterators.rb +3 -3
- data/lib/rmtools/enumerable/range.rb +121 -95
- data/lib/rmtools/version.rb +1 -1
- data/lib/rmtools/xml/xpath.rb +5 -5
- metadata +5 -5
data/README.md
CHANGED
@@ -44,9 +44,9 @@ It's still randomly documented since it's just my working tool.
|
|
44
44
|
* Added private #alias_constant
|
45
45
|
|
46
46
|
* Array
|
47
|
-
* #avg
|
48
|
-
*
|
49
|
-
* Added #
|
47
|
+
* Fixed #avg, #avg_by, #rand_by in case of an empty array: return nil
|
48
|
+
* Added #intersects? (alias #x?)
|
49
|
+
* Added #rfind_by, #runiq_by and few more iterators
|
50
50
|
|
51
51
|
* Symbol
|
52
52
|
* Added #+, #split and #method_missing to proxy all possible methods to #to_s
|
@@ -69,12 +69,12 @@ class Array
|
|
69
69
|
|
70
70
|
# arithmetics
|
71
71
|
def avg
|
72
|
-
empty? ?
|
72
|
+
empty? ? nil : sum.to_f/size
|
73
73
|
end
|
74
74
|
|
75
75
|
# for use with iterators
|
76
|
-
def avg_by(&
|
77
|
-
empty? ?
|
76
|
+
def avg_by(&block)
|
77
|
+
empty? ? nil : sum(&block).to_f/size
|
78
78
|
end
|
79
79
|
|
80
80
|
def scale(top)
|
@@ -140,7 +140,7 @@ class Array
|
|
140
140
|
end
|
141
141
|
|
142
142
|
|
143
|
-
#
|
143
|
+
# filters
|
144
144
|
def odds
|
145
145
|
values_at(*(0...size).odds)
|
146
146
|
end
|
@@ -150,10 +150,6 @@ class Array
|
|
150
150
|
end
|
151
151
|
|
152
152
|
# conditional
|
153
|
-
def uniq?
|
154
|
-
uniq == self
|
155
|
-
end
|
156
|
-
|
157
153
|
def every?
|
158
154
|
!find {|e| !yield(e)}
|
159
155
|
end
|
@@ -162,10 +158,24 @@ class Array
|
|
162
158
|
!find {|e| yield(e)}
|
163
159
|
end
|
164
160
|
|
161
|
+
# rightmost #find
|
162
|
+
def rfind
|
163
|
+
reverse_each {|e| return e if yield e}
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# find-by-value filters
|
168
|
+
# It's more of a pattern. Use of respective meta-iterators is prefered.
|
165
169
|
def find_by(key, value)
|
166
170
|
find {|e| e.__send__(key) == value}
|
167
171
|
end
|
168
172
|
|
173
|
+
# rightmost #find_by
|
174
|
+
def rfind_by(key, value)
|
175
|
+
reverse_each {|e| return e if e.__send__(key) == value}
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
169
179
|
def select_by(key, value)
|
170
180
|
select {|e| e.__send__(key) == value}
|
171
181
|
end
|
@@ -174,11 +184,28 @@ class Array
|
|
174
184
|
reject {|e| e.__send__(key) == value}
|
175
185
|
end
|
176
186
|
|
187
|
+
|
188
|
+
# uniqs
|
189
|
+
def uniq?
|
190
|
+
uniq == self
|
191
|
+
end
|
192
|
+
|
193
|
+
# rightmost #uniq
|
194
|
+
def runiq
|
195
|
+
reverse.uniq.reverse
|
196
|
+
end
|
197
|
+
|
198
|
+
# rightmost #uniq_by
|
199
|
+
def runiq_by(&block)
|
200
|
+
reverse.uniq_by(&block).reverse
|
201
|
+
end
|
202
|
+
|
177
203
|
# sort / group
|
178
204
|
def sorted_uniq_by(&block)
|
179
205
|
uniq_by(&block).sort_by(&block)
|
180
206
|
end
|
181
207
|
|
208
|
+
# C-function, see it in /ext
|
182
209
|
def arrange_by(*args, &block)
|
183
210
|
arrange(*args, &block)
|
184
211
|
end
|
@@ -243,23 +270,13 @@ class Array
|
|
243
270
|
|
244
271
|
|
245
272
|
# mapreduce
|
246
|
-
def sum(identity=0, &b)
|
247
|
-
|
248
|
-
# fastering activesupport's method
|
249
|
-
def group_by(&b) arrange(:group, &b) end
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
# rightmost #find
|
255
|
-
def rfind
|
256
|
-
reverse_each {|e| return e if yield e}
|
257
|
-
nil
|
273
|
+
def sum(identity=0, &b)
|
274
|
+
foldl(:+, &b) || identity
|
258
275
|
end
|
259
276
|
|
260
|
-
#
|
261
|
-
def
|
262
|
-
|
277
|
+
# fastering activesupport's method
|
278
|
+
def group_by(&b)
|
279
|
+
arrange(:group, &b)
|
263
280
|
end
|
264
281
|
|
265
282
|
end
|
@@ -236,7 +236,7 @@ class Array
|
|
236
236
|
err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
|
237
237
|
raise err
|
238
238
|
end}
|
239
|
-
when :find_by, :select_by, :reject_by
|
239
|
+
when :find_by, :rfind_by,:select_by, :reject_by
|
240
240
|
Array.class_eval %{
|
241
241
|
def #{method}(val)
|
242
242
|
# select_by_count(max_count) =>
|
@@ -268,11 +268,11 @@ class Array
|
|
268
268
|
if Array === value
|
269
269
|
# owner_ids = users_ids =>
|
270
270
|
# each_with_index {|e, i| e.owner_id = users_ids[i]}
|
271
|
-
each_with_index {|e, i| e
|
271
|
+
each_with_index {|e, i| e.#{meth} value[i]}
|
272
272
|
else
|
273
273
|
# owner_ids = user_id =>
|
274
274
|
# each {|e, i| e.owner_id = user_id}
|
275
|
-
each {|e| e
|
275
|
+
each {|e| e.#{meth} value}
|
276
276
|
end
|
277
277
|
rescue NoMethodError => err
|
278
278
|
err.message << " (`#{method}' interpreted as map-function `#{meth}')"
|
@@ -17,53 +17,91 @@ end
|
|
17
17
|
# # => Interval[[-Inf, -2], [-1, +Inf]]
|
18
18
|
# 2) Hardly we can use Range second way, when it defined by non-integers:
|
19
19
|
# [0,1,2,3,4,5][1.9..4] # => [1, 2, 3, 4]
|
20
|
-
# (1.9...4.1).include? 4 # => true,
|
20
|
+
# (1.9...4.1).include? 4 # => true, AND
|
21
|
+
# (1.9...4.1).include? 1 # => false, BUT
|
21
22
|
# [0,1,2,3,4,5][1.9...4.1] # => [1, 2, 3]
|
22
23
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# A domain of the present extension is Set operations with ranges considered as lazy lists of integers.
|
25
|
+
# The present extension is solving the second problem, yet
|
26
|
+
# * saving the capability of a Range syntactic sugar;
|
27
|
+
# * does exactly extend and not modify the Range behaviour.
|
28
|
+
# These methods support only numeric ranges, it won't work with chars and datetime borders,
|
29
|
+
# though I'll make a support for the Date and Time in a future version.
|
27
30
|
class Range
|
28
31
|
|
29
|
-
|
30
|
-
|
32
|
+
private
|
33
|
+
def next_int(n)
|
34
|
+
n.integer? || n.to_i == n ? n.to_i + 1 : n.ceil
|
35
|
+
end
|
36
|
+
|
37
|
+
def prev_int(n)
|
38
|
+
n.integer? || n.to_i == n ? n.to_i - 1 : n.to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
def int_end
|
42
|
+
exclude_end? ? prev_int(last) : last.to_i
|
31
43
|
end
|
44
|
+
public
|
32
45
|
|
46
|
+
# End inclusion need to universalize ranges for use in XRange list.
|
47
|
+
# Considering the extension domain, one simply should not use "..." notation, but if such a range nevertheless appears as an argument, we reduce that to an operable one at the cost of a fractional accuracy
|
48
|
+
# #include_end should not be coupled with #size and #empty? which have their own "..." handling
|
49
|
+
# (0.9...1.3).include_end # => 0.9..1, BUT
|
50
|
+
# (0.3...0.5).include_end # => 0.3..0
|
33
51
|
def include_end
|
34
|
-
exclude_end? ? first..
|
52
|
+
exclude_end? ? first..prev_int(last) : self
|
35
53
|
end
|
36
54
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
55
|
+
def included_end
|
56
|
+
exclude_end? ? prev_int(last) : last
|
57
|
+
end
|
58
|
+
|
59
|
+
# Represent a count of integers that range include and not real interval length
|
60
|
+
# (0..0).size
|
61
|
+
# => 1 (equivalent list of one 0)
|
62
|
+
# (0...0).size
|
63
|
+
# => 0 (equivalent empty list)
|
64
|
+
# (0.3..0.5).size
|
65
|
+
# => 0 (there is no integer between 0.3 and 0.5)
|
66
|
+
# (0.9...1.1).size
|
67
|
+
# => 1 (equivalent list of one 1 which is between 0.9 and 1.1)
|
68
|
+
# (2..1).size
|
69
|
+
# => 0 (such a range just does't make sense)
|
41
70
|
def size
|
42
|
-
|
71
|
+
[int_end - first.ceil + 1, 0].max
|
43
72
|
end
|
44
73
|
|
74
|
+
# Include any integer?
|
45
75
|
def empty?
|
46
|
-
|
76
|
+
size == 0
|
47
77
|
end
|
48
78
|
|
49
79
|
def b
|
50
|
-
|
80
|
+
size != 0 && self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Simplify a range to in-domain equivalent with integer edges.
|
84
|
+
def integerize
|
85
|
+
first.ceil..int_end
|
51
86
|
end
|
52
87
|
|
53
|
-
#
|
88
|
+
# Significant content of a range then.
|
54
89
|
def integers
|
55
|
-
|
90
|
+
integerize.to_a
|
56
91
|
end
|
57
92
|
alias :to_is :integers
|
58
93
|
|
59
|
-
#
|
94
|
+
# Unfortunately, Range's start point can not be excluded, thus there is no *true inversion* of a range with included end.
|
95
|
+
# Though, is this domain we can "integerize" range, then
|
96
|
+
# -(1..2)
|
60
97
|
# -(0.5..2.1)
|
61
|
-
# i.e. all excluding these
|
62
|
-
### => XRange(-∞..0, 3
|
98
|
+
# (i.e. all excluding these indices: [1, 2])
|
99
|
+
### => XRange(-∞..0, 3..+∞)
|
63
100
|
def -@
|
64
|
-
XRange(-Inf..first
|
101
|
+
XRange(-Inf..prev_int(first), (exclude_end? ? last.ceil : next_int(last))..Inf)
|
65
102
|
end
|
66
103
|
|
104
|
+
# Intersection
|
67
105
|
def &(range)
|
68
106
|
return range & self if range.is XRange
|
69
107
|
fst = [first, range.first].max
|
@@ -72,17 +110,20 @@ class Range
|
|
72
110
|
end
|
73
111
|
|
74
112
|
# On the basis of #-@ for non-integers,
|
75
|
-
# (0..3) - (0.5..2.1)
|
76
113
|
# (0..3) - (1..2)
|
77
|
-
|
114
|
+
# (0..3) - (0.5..2.1)
|
115
|
+
### => XRange(0..0, 3..3)
|
78
116
|
def -(range)
|
79
117
|
self & -range
|
80
118
|
end
|
81
119
|
|
82
120
|
alias :include_number? :include?
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
121
|
+
# #include? corresponds with Ruby's default one, which considers a range as an interval
|
122
|
+
# (0..1).include? 1.0
|
123
|
+
# 1.in 0..1
|
124
|
+
# => true
|
125
|
+
# and (0...1.0).include? 1.0
|
126
|
+
# => false
|
86
127
|
def include?(number_or_range)
|
87
128
|
if Numeric === number_or_range
|
88
129
|
include_number? number_or_range
|
@@ -93,36 +134,38 @@ class Range
|
|
93
134
|
end
|
94
135
|
end
|
95
136
|
|
96
|
-
|
97
|
-
|
137
|
+
# Does these ranges have at least one common point?
|
138
|
+
# (0..1).x? 1..2
|
139
|
+
# (1...2).x? 0..1
|
140
|
+
# (0..3).x? 1..2
|
141
|
+
# (1..2).x? 0..3
|
142
|
+
# => true
|
143
|
+
# (0..1.4).x? 1.5..2
|
144
|
+
# (0...1).x? 1..2
|
145
|
+
# (2..3).x? 0..1
|
146
|
+
# => false
|
147
|
+
def x?(range, pretend_not_exclude=false)
|
98
148
|
return range.x? self if range.is XRange
|
99
|
-
|
100
|
-
|
101
|
-
return x?(range_end..range.first)
|
102
|
-
end
|
103
|
-
self_end = included_end
|
104
|
-
if self_end < first
|
105
|
-
return (first..self_end).x?(range)
|
106
|
-
end
|
107
|
-
case self_end <=> range_end
|
108
|
-
when -1
|
109
|
-
self_end >= range.first
|
110
|
-
when 1
|
111
|
-
first <= range_end >= first
|
112
|
-
else
|
113
|
-
true
|
114
|
-
end
|
149
|
+
(range.last > first or ((!range.exclude_end? or pretend_not_exclude) and range.last == first)) and
|
150
|
+
(range.first < last or ((!exclude_end? or pretend_not_exclude) and range.first == last))
|
115
151
|
end
|
116
152
|
alias :intersects? :x?
|
117
153
|
|
154
|
+
# Union
|
155
|
+
# (1..3) | (2..4)
|
156
|
+
# => 1..4
|
157
|
+
# (1...2) | (2..4)
|
158
|
+
# => 1..4
|
159
|
+
# (1..2) | (3..4)
|
160
|
+
# => XRange(1..2, 3..4)
|
161
|
+
# A result will be inadequate if any range is not integered and excludes end
|
118
162
|
def |(range)
|
119
163
|
return range | self if range.is XRange
|
120
|
-
range
|
121
|
-
|
122
|
-
return XRange.new self, range if !x?(range)
|
123
|
-
[self.begin, range.begin].min..[self_.end, range.end].max
|
164
|
+
return XRange.new self, range if !x?(range, true)
|
165
|
+
[first, range.first].min..[included_end, range.included_end].max
|
124
166
|
end
|
125
167
|
|
168
|
+
# Diff
|
126
169
|
def ^(range)
|
127
170
|
common = self & range
|
128
171
|
self - common | range - common
|
@@ -132,53 +175,12 @@ class Range
|
|
132
175
|
(first <=> range.first).b || included_end <=> range.included_end
|
133
176
|
end
|
134
177
|
|
135
|
-
|
136
|
-
(first + include_end.last)/2
|
137
|
-
end
|
138
|
-
|
139
|
-
def part(i, j)
|
140
|
-
first + (i-1)*size/j .. first - 1 + i*size/j unless i < 1 or j < 1 or j < i
|
141
|
-
end
|
142
|
-
|
143
|
-
def div(n)
|
144
|
-
unless n < 1
|
145
|
-
rarray = []
|
146
|
-
j = self.begin
|
147
|
-
iend = include_end.end
|
148
|
-
rarray << (j..(j+=n)-1) until j+n > iend
|
149
|
-
rarray << (j..iend)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def /(i)
|
154
|
-
part 1, i
|
155
|
-
end
|
156
|
-
|
157
|
-
def >>(i)
|
158
|
-
self.begin + i .. include_end.end + i
|
159
|
-
end
|
160
|
-
|
161
|
-
def <<(i)
|
162
|
-
self.begin - i .. include_end.end - i
|
163
|
-
end
|
164
|
-
|
165
|
-
def of(ary)
|
166
|
-
ary[self]
|
167
|
-
end
|
168
|
-
|
169
|
-
def odds
|
170
|
-
select {|i| i%2 != 0}
|
171
|
-
end
|
172
|
-
|
173
|
-
def evens
|
174
|
-
select {|i| i%2 == 0}
|
175
|
-
end
|
176
|
-
|
178
|
+
# Sum of integers in a range
|
177
179
|
def sum
|
178
|
-
|
179
|
-
return (1..
|
180
|
-
return 0 if
|
181
|
-
|
180
|
+
last = included_end
|
181
|
+
return (1..last).sum - (0..-first).sum if first < 0
|
182
|
+
return 0 if last <= first
|
183
|
+
last*(last+1)/2 - (1..first-1).sum
|
182
184
|
end
|
183
185
|
|
184
186
|
# minimum of monotone function definition interval
|
@@ -203,6 +205,30 @@ class Range
|
|
203
205
|
end
|
204
206
|
end
|
205
207
|
|
208
|
+
def odds
|
209
|
+
select {|i| i%2 != 0}
|
210
|
+
end
|
211
|
+
|
212
|
+
def evens
|
213
|
+
select {|i| i%2 == 0}
|
214
|
+
end
|
215
|
+
|
216
|
+
# Average
|
217
|
+
def center
|
218
|
+
(first + included_end)/2
|
219
|
+
end
|
220
|
+
alias :avg :center
|
221
|
+
|
222
|
+
# Move range as interval right
|
223
|
+
def >>(i)
|
224
|
+
first + i .. included_end + i
|
225
|
+
end
|
226
|
+
|
227
|
+
# Move range as interval left
|
228
|
+
def <<(i)
|
229
|
+
first - i .. included_end - i
|
230
|
+
end
|
231
|
+
|
206
232
|
end
|
207
233
|
|
208
234
|
class XRange
|
@@ -337,7 +363,7 @@ public
|
|
337
363
|
end
|
338
364
|
|
339
365
|
def inspect
|
340
|
-
"XRange(#{@ranges.join(', ').gsub('Infinity', '
|
366
|
+
"XRange(#{@ranges.join(', ').gsub('-Infinity', '-∞').gsub('Infinity', '+∞')})"
|
341
367
|
end
|
342
368
|
|
343
369
|
end
|
data/lib/rmtools/version.rb
CHANGED
data/lib/rmtools/xml/xpath.rb
CHANGED
@@ -19,11 +19,11 @@ module LibXML
|
|
19
19
|
x.gsub! %r{(^|/)([^.#\w*/'"])}, '\1*\2'
|
20
20
|
x.gsub! %r{\[([a-z]+)}, '[@\1'
|
21
21
|
x.gsub! %r{(\[(?:@\w+!?=)?)['"]?([^'"\]@]+)['"]?\]}, '\1"\2"]'
|
22
|
-
if x !~%r{^(
|
23
|
-
}(
|
24
|
-
}(\w+:)?
|
25
|
-
}(
|
26
|
-
}(
|
22
|
+
if x !~%r{^(?:#{
|
23
|
+
}(?:\./|\.//|/|//)?#{
|
24
|
+
}(?:(?:[\w\-]+:)?[\w\-]+|\*)#{ # [ns:]name
|
25
|
+
}(?:\[(?:@\w+(!?="[^"]+")?|"[^"]+")\])*#{# attributes [@href!="pics/xynta.jpeg"]
|
26
|
+
}(?:\[-?\d+\]|\{[^\}]+\})?#{ # inruby-filters (see finder functions ^)
|
27
27
|
})+$}
|
28
28
|
raise XML::Error, "can't process `#{xpath}`; #{x}"
|
29
29
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rmtools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Sergey Baev
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -182,9 +182,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
182
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
183
|
none: false
|
184
184
|
requirements:
|
185
|
-
- - ! '
|
185
|
+
- - ! '>='
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version:
|
187
|
+
version: '0'
|
188
188
|
requirements: []
|
189
189
|
rubyforge_project:
|
190
190
|
rubygems_version: 1.8.24
|