jinx 2.1.3 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +27 -0
- data/History.md +4 -0
- data/lib/jinx/helpers/class.rb +16 -12
- data/lib/jinx/helpers/collection.rb +277 -29
- data/lib/jinx/helpers/collections.rb +35 -2
- data/lib/jinx/helpers/conditional_enumerator.rb +2 -2
- data/lib/jinx/helpers/filter.rb +8 -2
- data/lib/jinx/helpers/flattener.rb +2 -2
- data/lib/jinx/helpers/hash.rb +3 -2
- data/lib/jinx/helpers/{hashable.rb → hasher.rb} +125 -77
- data/lib/jinx/helpers/module.rb +1 -1
- data/lib/jinx/helpers/multi_enumerator.rb +25 -9
- data/lib/jinx/helpers/options.rb +4 -3
- data/lib/jinx/helpers/partial_order.rb +16 -8
- data/lib/jinx/helpers/pretty_print.rb +14 -4
- data/lib/jinx/helpers/transformer.rb +3 -1
- data/lib/jinx/helpers/transitive_closure.rb +3 -3
- data/lib/jinx/helpers/visitor.rb +33 -42
- data/lib/jinx/import/java.rb +40 -27
- data/lib/jinx/importer.rb +86 -33
- data/lib/jinx/metadata/attribute_enumerator.rb +5 -11
- data/lib/jinx/metadata/dependency.rb +65 -30
- data/lib/jinx/metadata/id_alias.rb +1 -0
- data/lib/jinx/metadata/introspector.rb +21 -9
- data/lib/jinx/metadata/inverse.rb +14 -11
- data/lib/jinx/metadata/java_property.rb +15 -26
- data/lib/jinx/metadata/propertied.rb +80 -19
- data/lib/jinx/metadata/property.rb +13 -8
- data/lib/jinx/metadata/property_characteristics.rb +2 -2
- data/lib/jinx/resource.rb +62 -32
- data/lib/jinx/resource/inversible.rb +4 -0
- data/lib/jinx/resource/match_visitor.rb +0 -1
- data/lib/jinx/resource/mergeable.rb +16 -6
- data/lib/jinx/resource/reference_enumerator.rb +1 -2
- data/lib/jinx/version.rb +1 -1
- data/test/lib/jinx/helpers/collections_test.rb +29 -14
- data/test/lib/jinx/helpers/visitor_test.rb +7 -20
- data/test/lib/jinx/import/mixed_case_test.rb +17 -3
- metadata +4 -4
- data/lib/jinx/helpers/enumerable.rb +0 -245
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
jinx (2.1.3)
|
5
|
+
bundler
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.3)
|
11
|
+
rake (0.9.2.2)
|
12
|
+
rspec (2.10.0)
|
13
|
+
rspec-core (~> 2.10.0)
|
14
|
+
rspec-expectations (~> 2.10.0)
|
15
|
+
rspec-mocks (~> 2.10.0)
|
16
|
+
rspec-core (2.10.1)
|
17
|
+
rspec-expectations (2.10.0)
|
18
|
+
diff-lcs (~> 1.1.3)
|
19
|
+
rspec-mocks (2.10.1)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
java
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
jinx!
|
26
|
+
rake
|
27
|
+
rspec (>= 2.6)
|
data/History.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
This history lists major release themes. See the GitHub commits (https://github.com/jinx/core)
|
2
2
|
for change details.
|
3
3
|
|
4
|
+
2.1.4 / 2012-10-31
|
5
|
+
------------------
|
6
|
+
* Push Enumerable helper methods to Jinx::Collection.
|
7
|
+
|
4
8
|
2.1.3 / 2012-08-18
|
5
9
|
------------------
|
6
10
|
* Pull UID out of uniquifier.
|
data/lib/jinx/helpers/class.rb
CHANGED
@@ -95,7 +95,7 @@ class Class
|
|
95
95
|
attr_writer(symbol)
|
96
96
|
wtr = "#{symbol}=".to_sym
|
97
97
|
iv = "@#{symbol}".to_sym
|
98
|
-
# the attribute reader creates a new
|
98
|
+
# the attribute reader creates a new value on demand
|
99
99
|
define_method(symbol) do
|
100
100
|
instance_variable_defined?(iv) ? instance_variable_get(iv) : send(wtr, yield(self))
|
101
101
|
end
|
@@ -123,10 +123,12 @@ class Class
|
|
123
123
|
# @yieldparam old_method [Symbol] the method being redefined
|
124
124
|
# @return [Symbol] an alias to the old method implementation
|
125
125
|
def redefine_method(method)
|
126
|
-
#
|
127
|
-
#
|
126
|
+
# Make a new alias id method__base for the existing method.
|
127
|
+
# Disambiguate with a counter suffix if necessary.
|
128
128
|
counter = 2
|
129
|
-
#
|
129
|
+
# Make a valid alias base. The base includes this class's object id in order
|
130
|
+
# to ensure uniqueness within the class hierarchy. This alias uniqueness
|
131
|
+
# guards against a superclass occluding a subclass redefinition.
|
130
132
|
old, eq = /^([^=]*)(=)?$/.match(method.to_s).captures
|
131
133
|
old.tr!('|', 'or')
|
132
134
|
old.tr!('&', 'and')
|
@@ -134,16 +136,18 @@ class Class
|
|
134
136
|
old.tr!('*', 'mult')
|
135
137
|
old.tr!('/', 'div')
|
136
138
|
old.gsub!(/[^\w]/, 'op')
|
137
|
-
base = "redefined__#{old}"
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
base = "redefined__#{object_id}_#{old}"
|
140
|
+
# Make a unique method alias for the old method.
|
141
|
+
old_sym = "#{base}#{eq}".to_sym
|
142
|
+
while method_defined?(old_sym)
|
143
|
+
old_sym = "#{base}#{counter}#{eq}".to_sym
|
142
144
|
counter = counter + 1
|
143
145
|
end
|
144
|
-
|
145
|
-
|
146
|
+
# Alias the old method before it is replaced.
|
147
|
+
alias_method(old_sym, method)
|
148
|
+
# The body defines the new method given the old method.
|
149
|
+
body = yield old_sym
|
146
150
|
define_method(method, body)
|
147
|
-
|
151
|
+
old_sym
|
148
152
|
end
|
149
153
|
end
|
@@ -1,33 +1,281 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Jinx
|
2
|
+
# This Collection mix-in augments Enumerable with utility methods.
|
3
|
+
module Collection
|
4
|
+
# Returns a new Hash generated from this Collection and an optional value generator block.
|
5
|
+
# This Enumerable contains the Hash keys. If the value generator block is given to this
|
6
|
+
# method then the block is called with each enumerated element as an argument to
|
7
|
+
# generate the associated hash value. If no block is given, then the values are nil.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# [1, 2, 3].hashify { |item| item.modulo(2) } #=> { 1 => 1, 2 => 0, 3 => 1 }
|
11
|
+
# [:a].hashify #=> { :a => nil }
|
12
|
+
# @return [Hash]
|
13
|
+
def hashify
|
14
|
+
hash = {}
|
15
|
+
each { |item| hash[item] = yield item if block_given? }
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a new Hash generated from this Collection and a required value generator block.
|
20
|
+
# This Enumerable contains the Hash keys. The block is called with each enumerated
|
21
|
+
# element as an argument to generate the associated hash value.
|
22
|
+
# Only non-nil, non-empty values are included in the hash.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# [1, 2, 3].to_compact_hash { |item| item.modulo(2) } #=> { 1 => 1, 2 => 0, 3 => 1 }
|
26
|
+
# [1, 2, 3].to_compact_hash { |n| n.modulo(2) unless item > 2 } #=> {1 => 1, 2 => 0}
|
27
|
+
# [1, 2, 3].to_compact_hash { |n| n > 2 } #=> {1 => false, 2 => false, 3 => true}
|
28
|
+
# [1, 2, 3].to_compact_hash { |n| Array.new(n - 1, n) } #=> {2 => [2], 3 => [2, 3]}
|
29
|
+
# @return [Hash]
|
30
|
+
# @raise [ArgumentError] if the generator block is not given
|
31
|
+
# @see #hashify
|
32
|
+
def to_compact_hash
|
33
|
+
raise ArgumentError.new("Compact hash builder is missing the value generator block") unless block_given?
|
34
|
+
to_compact_hash_with_index { |item, index| yield item }
|
35
|
+
end
|
8
36
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
37
|
+
# Returns a new Hash generated from this Collection with a block whose arguments include the enumerated item
|
38
|
+
# and its index. Every value which is nil or empty is excluded.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# [1, 2, 3].to_compact_hash_with_index { |item, index| item + index } #=> { 1 => 1, 2 => 3, 3 => 5 }
|
42
|
+
# @yield [item, index] the hash value
|
43
|
+
# @yieldparam item the enumerated value
|
44
|
+
# @yieldparam index the enumeration index
|
45
|
+
# @return [Hash] this {Enumerable} converted to a hash by the given block
|
46
|
+
def to_compact_hash_with_index
|
47
|
+
hash = {}
|
48
|
+
self.each_with_index do |item, index|
|
49
|
+
next if item.nil?
|
50
|
+
value = yield(item, index)
|
51
|
+
next if value.nil_or_empty?
|
52
|
+
hash[item] = value
|
53
|
+
end
|
54
|
+
hash
|
55
|
+
end
|
16
56
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
57
|
+
# This method is functionally equivalent to +to_a.empty+ but is more concise and efficient.
|
58
|
+
#
|
59
|
+
# @return [Boolean] whether this Collection iterates over at least one item
|
60
|
+
def empty?
|
61
|
+
not any? { true }
|
62
|
+
end
|
24
63
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
64
|
+
# This method is functionally equivalent to +to_a.first+ but is more concise and efficient.
|
65
|
+
#
|
66
|
+
# @return the first enumerated item in this Collection, or nil if this Collection is empty
|
67
|
+
def first
|
68
|
+
detect { true }
|
69
|
+
end
|
70
|
+
|
71
|
+
# This method is functionally equivalent to +to_a.last+ but is more concise and efficient.
|
72
|
+
#
|
73
|
+
# @return the last enumerated item in this Collection, or nil if this Collection is empty
|
74
|
+
def last
|
75
|
+
detect { true }
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method is functionally equivalent to +to_a.size+ but is more concise and efficient
|
79
|
+
# for an Enumerable which does not implement the {#size} method.
|
80
|
+
#
|
81
|
+
# @return [Integer] the count of items enumerated in this Collection
|
82
|
+
def size
|
83
|
+
inject(0) { |size, item| size + 1 }
|
84
|
+
end
|
85
|
+
|
86
|
+
alias :length :size
|
87
|
+
|
88
|
+
# @return [String] the content of this Collection as a series using {Array#to_series}
|
89
|
+
def to_series(conjunction=nil)
|
90
|
+
to_a.to_series
|
91
|
+
end
|
30
92
|
|
31
|
-
|
32
|
-
|
33
|
-
|
93
|
+
# Returns the first non-nil, non-false enumerated value resulting from a call to the block given to this method,
|
94
|
+
# or nil if no value detected.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# [1, 2].detect_value { |item| item / 2 if item % 2 == 0 } #=> 1
|
98
|
+
# @return [Object] the detected block result
|
99
|
+
# @see #detect_with_value
|
100
|
+
def detect_value
|
101
|
+
each do |*item|
|
102
|
+
value = yield(*item)
|
103
|
+
return value if value
|
104
|
+
end
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the first item and value for which an enumeration on the block given to this method returns
|
109
|
+
# a non-nil, non-false value.
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# [1, 2].detect_with_value { |item| item / 2 if item % 2 == 0 } #=> [2, 1]
|
113
|
+
# @return [(Object, Object)] the detected [item, value] pair
|
114
|
+
# @see #detect_value
|
115
|
+
def detect_with_value
|
116
|
+
value = nil
|
117
|
+
match = detect do |*item|
|
118
|
+
value = yield(*item)
|
119
|
+
end
|
120
|
+
[match, value]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns a new Enumerable that iterates over the base Enumerable items for which filter evaluates to a non-nil,
|
124
|
+
# non-false value, e.g.:
|
125
|
+
# [1, 2, 3].filter { |n| n != 2 }.to_a #=> [1, 3]
|
126
|
+
#
|
127
|
+
# Unlike select, filter reflects changes to the base Enumerable, e.g.:
|
128
|
+
# a = [1, 2, 3]
|
129
|
+
# filter = a.filter { |n| n != 2 }
|
130
|
+
# a << 4
|
131
|
+
# filter.to_a #=> [1, 3, 4]
|
132
|
+
#
|
133
|
+
# In addition, filter has a small, fixed storage requirement, making it preferable to select for large collections.
|
134
|
+
# Note, however, that unlike select, filter does not return an Array.
|
135
|
+
# The default filter block returns the passed item.
|
136
|
+
#
|
137
|
+
# @example
|
138
|
+
# [1, nil, 3].filter.to_a #=> [1, 3]
|
139
|
+
# @yield [item] filter the selection filter
|
140
|
+
# @yieldparam item the collection member to filter
|
141
|
+
# @return [Enumerable] the filtered result
|
142
|
+
def filter(&filter)
|
143
|
+
Jinx::Filter.new(self, &filter)
|
144
|
+
end
|
145
|
+
|
146
|
+
# @return [Enumerable] an iterator over the non-nil items in this Collection
|
147
|
+
def compact
|
148
|
+
filter { |item| not item.nil? }
|
149
|
+
end
|
150
|
+
|
151
|
+
# @example
|
152
|
+
# {:a => {:b => :c}, :d => [:e]}.enum_values.flatten.to_a #=> [:b, :c, :e]
|
153
|
+
# @return [Enumerable] the flattened result
|
154
|
+
def flatten
|
155
|
+
Jinx::Flattener.new(self).to_a
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns an Enumerable which iterates over items in this Collection and the other Enumerable in sequence.
|
159
|
+
# Unlike the Array plus (+) operator, {#union} reflects changes to the underlying enumerators.
|
160
|
+
#
|
161
|
+
# @quirk Cucumber Cucumber defines it's own Enumerable union monkey-patch. Work around this in the short
|
162
|
+
# term by trying to call the super first.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# a = [1, 2]
|
166
|
+
# b = [4, 5]
|
167
|
+
# ab = a.union(b)
|
168
|
+
# ab #=> [1, 2, 4, 5]
|
169
|
+
# a << 3
|
170
|
+
# a + b #=> [1, 2, 4, 5]
|
171
|
+
# ab #=> [1, 2, 3, 4, 5]
|
172
|
+
# @param [Enumerable] other the Enumerable to compose with this Collection
|
173
|
+
# @return [Enumerable] an enumerator over self followed by other
|
174
|
+
# @yield (see Jinx::MultiEnumerator#intializer)
|
175
|
+
def union(other, &appender)
|
176
|
+
Jinx::MultiEnumerator.new(self, other, &appender)
|
177
|
+
end
|
178
|
+
|
179
|
+
alias :+ :union
|
180
|
+
|
181
|
+
# @return an Enumerable which iterates over items in this Collection but not the other Enumerable
|
182
|
+
def difference(other)
|
183
|
+
filter { |item| not other.include?(item) }
|
184
|
+
end
|
185
|
+
|
186
|
+
alias :- :difference
|
187
|
+
|
188
|
+
# @return an Enumerable which iterates over items in this Collection which are also in the other
|
189
|
+
# Enumerable
|
190
|
+
def intersect(other)
|
191
|
+
filter { |item| other.include?(item) }
|
192
|
+
end
|
193
|
+
|
194
|
+
alias :& :intersect
|
195
|
+
|
196
|
+
# Returns a new Enumerable that iterates over the base Enumerable applying the transformer block
|
197
|
+
# to each item, e.g.:
|
198
|
+
# [1, 2, 3].transform_value { |n| n * 2 }.to_a #=> [2, 4, 6]
|
199
|
+
#
|
200
|
+
# Unlike Array.map, {#wrap} reflects changes to the base Enumerable, e.g.:
|
201
|
+
# a = [2, 4, 6]
|
202
|
+
``# transformed = a.wrap { |n| n * 2 }
|
203
|
+
# a << 4
|
204
|
+
# transformed.to_a #=> [2, 4, 6, 8]
|
205
|
+
#
|
206
|
+
# In addition, transform has a small, fixed storage requirement, making it preferable to select
|
207
|
+
# for large collections. Note, however, that unlike map, transform does not return an Array.
|
208
|
+
#
|
209
|
+
# @yield [item] the transformer on the enumerated items
|
210
|
+
# @yieldparam item an enumerated item
|
211
|
+
# @return [Enumerable] an enumeration on the transformed values
|
212
|
+
def transform(&mapper)
|
213
|
+
Jinx::Transformer.new(self, &mapper)
|
214
|
+
end
|
215
|
+
|
216
|
+
alias :wrap :transform
|
217
|
+
|
218
|
+
def join(sep = $,)
|
219
|
+
to_a.join(sep)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Sorts this collection's members with a partial comparator block. A partial
|
223
|
+
# comparator block returns -1, 0, 1 or nil. The resulting sorted order places
|
224
|
+
# comparable items in their relative sort order. If two items are not
|
225
|
+
# directly comparable, then the relative order of those items is
|
226
|
+
# indeterminate. In all cases the relative order is transitive, i.e.:
|
227
|
+
# * a < b and b < c => a occurs before c in the sort result
|
228
|
+
# * a > b and b > c => a occurs after c in the sort result
|
229
|
+
#
|
230
|
+
# @example
|
231
|
+
# sorted = [Enumerable, Array, String].partial_sort
|
232
|
+
# sorted.index(Array) < sorted.index(Enumerable) #=> true
|
233
|
+
# sorted.index(String) < sorted.index(Enumerable) #=> true
|
234
|
+
#
|
235
|
+
# @yield [item1, item2] the partial comparison result (-1, 0, 1 or nil)
|
236
|
+
# @yieldparam item1 an item to compare
|
237
|
+
# @yieldparam item2 another item to compare
|
238
|
+
# @return [Enumerable] a new collection consisting of the items in this collection
|
239
|
+
# in partial sort order
|
240
|
+
def partial_sort(&block)
|
241
|
+
copy = dup.to_a
|
242
|
+
copy.partial_sort!(&block)
|
243
|
+
copy
|
244
|
+
end
|
245
|
+
|
246
|
+
# Sorts this collection in-place with a partial sort operator block
|
247
|
+
#
|
248
|
+
# @see #partial_sort
|
249
|
+
# @yield (see #partial_sort)
|
250
|
+
# @yieldparam (see #partial_sort)
|
251
|
+
# @raise [NoMethodError] if this Collection does not support the +sort!+ sort in-place method
|
252
|
+
def partial_sort!
|
253
|
+
unless block_given? then return partial_sort! { |item1, item2| item1 <=> item2 } end
|
254
|
+
# The comparison hash
|
255
|
+
h = Hash.new { |h, k| h[k] = Hash.new }
|
256
|
+
sort! do |a, b|
|
257
|
+
# * If a and b are comparable, then use the comparison result.
|
258
|
+
# * Otherwise, if there is a member c such that (a <=> c) == (c <=> b),
|
259
|
+
# then a <=> b has the transitive comparison result.
|
260
|
+
# * Otherwise, a <=> b is arbitrarily set to 1.
|
261
|
+
yield(a, b) || h[a][b] ||= -h[b][a] ||= h[a].detect_value { |c, v| v if v == yield(c, b) } || 1
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Sorts this collection's members with a partial sort operator on the results of applying the block.
|
266
|
+
#
|
267
|
+
# @yield [item] transform the item to a Comparable value
|
268
|
+
# @yieldparam item an enumerated item
|
269
|
+
# @return [Enumerable] the items in this collection in partial sort order
|
270
|
+
def partial_sort_by
|
271
|
+
partial_sort { |item1, item2| yield(item1) <=> yield(item2) }
|
272
|
+
end
|
273
|
+
|
274
|
+
# @yield [item] the transformer on the enumerated items
|
275
|
+
# @yieldparam item an enumerated item
|
276
|
+
# @return [Enumerable] the mapped values excluding null values
|
277
|
+
def compact_map(&mapper)
|
278
|
+
wrap(&mapper).compact
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -1,10 +1,43 @@
|
|
1
1
|
# This file loads the definitions of useful collection mix-ins and utility classes.
|
2
|
+
require 'set'
|
3
|
+
require 'enumerator'
|
2
4
|
require 'jinx/helpers/collection'
|
3
5
|
require 'jinx/helpers/array'
|
4
|
-
require 'jinx/helpers/
|
6
|
+
require 'jinx/helpers/hasher'
|
5
7
|
require 'jinx/helpers/hash'
|
6
8
|
require 'jinx/helpers/set'
|
7
|
-
require 'jinx/helpers/enumerable'
|
8
9
|
require 'jinx/helpers/enumerate'
|
9
10
|
require 'jinx/helpers/filter'
|
11
|
+
require 'jinx/helpers/transformer'
|
10
12
|
require 'jinx/helpers/flattener'
|
13
|
+
require 'jinx/helpers/multi_enumerator'
|
14
|
+
require 'jinx/helpers/hasher'
|
15
|
+
|
16
|
+
class Object
|
17
|
+
# @return [Boolean] whether this object is a {Jinx::Collection}
|
18
|
+
def collection?
|
19
|
+
Jinx::Collection === self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
### Extend common non-String Enumerable classes and interfaces with Jinx::Collection. ###
|
24
|
+
|
25
|
+
class Enumerable::Enumerator
|
26
|
+
include Jinx::Collection
|
27
|
+
end
|
28
|
+
|
29
|
+
class Array
|
30
|
+
include Jinx::Collection
|
31
|
+
end
|
32
|
+
|
33
|
+
class Set
|
34
|
+
include Jinx::Collection
|
35
|
+
end
|
36
|
+
|
37
|
+
class File
|
38
|
+
include Jinx::Collection
|
39
|
+
end
|
40
|
+
|
41
|
+
module Java::JavaUtil::Collection
|
42
|
+
include Jinx::Collection
|
43
|
+
end
|
@@ -4,7 +4,7 @@ module Jinx
|
|
4
4
|
# @example
|
5
5
|
# ConditionalEnumerator.new([1, 2, 3]) { |i| i < 3 }.to_a #=> [1, 2]
|
6
6
|
class ConditionalEnumerator
|
7
|
-
include Collection
|
7
|
+
include Enumerable, Collection
|
8
8
|
|
9
9
|
# Creates a ConditionalEnumerator which wraps the base Enumerator with a conditional filter.
|
10
10
|
def initialize(base, &filter)
|
@@ -18,4 +18,4 @@ module Jinx
|
|
18
18
|
@base.each { |item| (yield item) if @filter.call(item) }
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|