jinx 2.1.3 → 2.1.4

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.
Files changed (40) hide show
  1. data/Gemfile.lock +27 -0
  2. data/History.md +4 -0
  3. data/lib/jinx/helpers/class.rb +16 -12
  4. data/lib/jinx/helpers/collection.rb +277 -29
  5. data/lib/jinx/helpers/collections.rb +35 -2
  6. data/lib/jinx/helpers/conditional_enumerator.rb +2 -2
  7. data/lib/jinx/helpers/filter.rb +8 -2
  8. data/lib/jinx/helpers/flattener.rb +2 -2
  9. data/lib/jinx/helpers/hash.rb +3 -2
  10. data/lib/jinx/helpers/{hashable.rb → hasher.rb} +125 -77
  11. data/lib/jinx/helpers/module.rb +1 -1
  12. data/lib/jinx/helpers/multi_enumerator.rb +25 -9
  13. data/lib/jinx/helpers/options.rb +4 -3
  14. data/lib/jinx/helpers/partial_order.rb +16 -8
  15. data/lib/jinx/helpers/pretty_print.rb +14 -4
  16. data/lib/jinx/helpers/transformer.rb +3 -1
  17. data/lib/jinx/helpers/transitive_closure.rb +3 -3
  18. data/lib/jinx/helpers/visitor.rb +33 -42
  19. data/lib/jinx/import/java.rb +40 -27
  20. data/lib/jinx/importer.rb +86 -33
  21. data/lib/jinx/metadata/attribute_enumerator.rb +5 -11
  22. data/lib/jinx/metadata/dependency.rb +65 -30
  23. data/lib/jinx/metadata/id_alias.rb +1 -0
  24. data/lib/jinx/metadata/introspector.rb +21 -9
  25. data/lib/jinx/metadata/inverse.rb +14 -11
  26. data/lib/jinx/metadata/java_property.rb +15 -26
  27. data/lib/jinx/metadata/propertied.rb +80 -19
  28. data/lib/jinx/metadata/property.rb +13 -8
  29. data/lib/jinx/metadata/property_characteristics.rb +2 -2
  30. data/lib/jinx/resource.rb +62 -32
  31. data/lib/jinx/resource/inversible.rb +4 -0
  32. data/lib/jinx/resource/match_visitor.rb +0 -1
  33. data/lib/jinx/resource/mergeable.rb +16 -6
  34. data/lib/jinx/resource/reference_enumerator.rb +1 -2
  35. data/lib/jinx/version.rb +1 -1
  36. data/test/lib/jinx/helpers/collections_test.rb +29 -14
  37. data/test/lib/jinx/helpers/visitor_test.rb +7 -20
  38. data/test/lib/jinx/import/mixed_case_test.rb +17 -3
  39. metadata +4 -4
  40. data/lib/jinx/helpers/enumerable.rb +0 -245
@@ -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.
@@ -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 proxy on demand
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
- # make a new alias id method__base for the existing method.
127
- # disambiguate with a counter suffix if necessary.
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
- # make a valid alias base
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
- old_id = "#{base}#{eq}".to_sym
139
- while method_defined?(old_id)
140
- base = "#{base}#{counter}"
141
- old_id = "#{base}#{eq}".to_sym
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
- alias_method(old_id, method)
145
- body = yield old_id
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
- old_id
151
+ old_sym
148
152
  end
149
153
  end
@@ -1,33 +1,281 @@
1
- class Object
2
- # Returns whether this object is a collection capable of holding heterogenous items.
3
- # An Object is a not a collection by default. Subclasses can override this method.
4
- def collection?
5
- false
6
- end
7
- end
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
- module Enumerable
10
- # Overrides {Object#collection?} to returns +true+, since an Enumerable is capable of
11
- # holding heterogenous items by default. Subclasses can override this method.
12
- def collection?
13
- true
14
- end
15
- end
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
- class String
18
- # Overrides {Enumerable#collection?} to returns +false+, since a String is constrained
19
- # to hold characters.
20
- def collection?
21
- false
22
- end
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
- module Jinx
26
- module Collection
27
- include Enumerable
28
- end
29
- end
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
- module Java::JavaUtil::Collection
32
- include Jinx::Collection
33
- end
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/hashable'
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