rmtools 2.0.0.rc5 → 2.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.
- 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
|