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 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 and #avg_by for an empty array now return 0
48
- * Fixed #rand_by for an empty array
49
- * Added #intersects? aliased as #x?
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? ? 0 : sum.to_f/size
72
+ empty? ? nil : sum.to_f/size
73
73
  end
74
74
 
75
75
  # for use with iterators
76
- def avg_by(&b)
77
- empty? ? 0 : sum(&b).to_f/size
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
- # selectors
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) foldl(:+, &b) || identity end
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
- # rightmost #uniq
261
- def runiq
262
- reverse.uniq.reverse
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.__send__ meth, value[i]}
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.__send__ meth, value}
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, BUT
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
- # This extension leaves the first problem for a purpose of solving the second, saving the capability of a Range syntactic sugar. The present extension applies Set operations to ranges considered as lists of contained integers.
24
- # It means:
25
- # (x..y) equivalent (x...y+0.5) equivalent (x...y+1) equivalent [x, x+1, ..., y]
26
- # Note quantity of dots (end exclusion)
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
- def included_end
30
- exclude_end? ? last.integer? ? last - 1 : last.to_i : last
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..included_end : self
52
+ exclude_end? ? first..prev_int(last) : self
35
53
  end
36
54
 
37
- # Since it's not represent an interval...
38
- # (0..0).size # => 1 (equivalent list of one zero)
39
- # (0...0).size # => 0 (equivalent empty list)
40
- # There is no empty ranges with end included (since it includes at least an end, right?)
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
- exclude_end? ? (last - first).abs : (last - first).abs+1
71
+ [int_end - first.ceil + 1, 0].max
43
72
  end
44
73
 
74
+ # Include any integer?
45
75
  def empty?
46
- exclude_end? && last == first
76
+ size == 0
47
77
  end
48
78
 
49
79
  def b
50
- !empty? && self
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
- # Let significant content of a range used this way be:
88
+ # Significant content of a range then.
54
89
  def integers
55
- (first.ceil..included_end).to_a
90
+ integerize.to_a
56
91
  end
57
92
  alias :to_is :integers
58
93
 
59
- # -(1..2)
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 lazy indices: [1, 2]
62
- ### => XRange(-∞..0, 3..∞)
98
+ # (i.e. all excluding these indices: [1, 2])
99
+ ### => XRange(-∞..0, 3..+∞)
63
100
  def -@
64
- XRange(-Inf..first.ceil-1, (exclude_end? && last.integer? ? last : last.to_i+1)..Inf)
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
- ### => XRange(0..0, 3..3.0)
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
- # This function corresponds with ruby's default one in that we consider any number as a point on a segment.
84
- # Thus, any of these 0..1, 0..1.0
85
- # would return true as for 1 so as for 1.0
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
- def x?(range)
97
- return false if empty?
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
- range_end = range.included_end
100
- if range_end < range.first
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 = range.include_end
121
- self_ = self.include_end
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
- def center
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
- ie = include_end.end
179
- return (1..ie).sum - (0..-self.begin).sum if self.begin < 0
180
- return 0 if ie < self.begin
181
- ie*(ie+1)/2 - (1..self.begin-1).sum
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
@@ -1,3 +1,3 @@
1
1
  module RMTools
2
- VERSION = '2.0.0.rc5'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -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+:)?(\w+|\*)#{ # [ns:]name
25
- }(\[(@\w+(!?="[^"]+")?|"[^"]+")\])*#{# attributes [@href!="pics/xynta.jpeg"]
26
- }(\[-?\d+\]|\{[^\}]+\})?#{ # inruby-filters (see finder functions ^)
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.rc5
5
- prerelease: 6
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-21 00:00:00.000000000 Z
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: 1.3.1
187
+ version: '0'
188
188
  requirements: []
189
189
  rubyforge_project:
190
190
  rubygems_version: 1.8.24