jinx 2.1.3 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
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