idempotent_enumerable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ac991903381b9f945b1f4cc0d2336efc0523b2d
4
+ data.tar.gz: 963db8eb4ae066178ce0b22df3b9fbf1315549db
5
+ SHA512:
6
+ metadata.gz: 97fa573e2be5ae08a6837e988f75adc737fbcbd436223b37581dfdce045062312d9b30590a300e9dbaa4b4147bef6935181ed77e3e9c4cc913e0d4fd64d28f8a
7
+ data.tar.gz: ef45aa843263e4232d3f09b6b89970cf971e607d06262d2ec6a4094692238792de99e06b37d9ed2e0cd660426098db546f6dbfa0421ab79e4d9e9c1e193e1c3e
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,217 @@
1
+ # IdempotentEnumerable
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/idempotent_enumerable.svg)](http://badge.fury.io/rb/idempotent_enumerable)
4
+ [![Build Status](https://travis-ci.org/zverok/idempotent_enumerable.svg?branch=master)](https://travis-ci.org/zverok/idempotent_enumerable)
5
+ [![Coverage Status](https://coveralls.io/repos/zverok/idempotent_enumerable/badge.svg?branch=master)](https://coveralls.io/r/zverok/idempotent_enumerable?branch=master)
6
+
7
+
8
+ `IdempotentEnumerable` is like Ruby core's `Enumerable` but tries to preserve the class of the
9
+ collection it included in, where reasonable.
10
+
11
+ ## Features/Showcase
12
+
13
+ ```ruby
14
+ require 'set'
15
+
16
+ s = Set.new(1..5)
17
+ # => #<Set: {1, 2, 3, 4, 5}>
18
+ s.reject(&:odd?)
19
+ # => [2, 4] -- FFFUUUU
20
+
21
+ require 'idempotent_enumerable'
22
+ Set.include IdempotentEnumerable
23
+
24
+ s.reject(&:odd?)
25
+ # => #<Set: {2, 4}> -- Nice!
26
+ ```
27
+
28
+ `IdempotentEnumerable` relies on fact your `each` method returns an instance of `Enumerator` (or
29
+ other `Enumerable` object) when called without block. Which, honestly, it should do anyways.
30
+
31
+ To construct back an instance of original class, `IdempotentEnumerable` relies on the fact
32
+ `OriginalClass.new(array)` call will work. But, if your class provides another way for construction
33
+ from array, you can still use the module:
34
+
35
+ ```ruby
36
+ h = {a: 1, b: 2, c: 3}
37
+ # => {:a=>1, :b=>2, :c=>3}
38
+ h.first(2)
39
+ # => [[:a, 1], [:b, 2]]
40
+
41
+ Hash.include IdempotentEnumerable
42
+
43
+ # To make hash from array of pairs, one should use `Hash[array]` notation.
44
+ Hash.idempotent_enumerable.constructor = :[]
45
+
46
+ h.first(2)
47
+ # => {:a=>1, :b=>2}
48
+ ```
49
+
50
+ `IdempotentEnumerable` also supports complicated collections, with `each` accepting additional
51
+ arguments, out of the box ([daru](https://github.com/SciRuby/daru) used as an example):
52
+
53
+ ```ruby
54
+ require 'daru'
55
+
56
+ Daru::DataFrame.include IdempotentEnumerable
57
+
58
+ df = Daru::DataFrame.new([[1,2,3], [4,5,6], [7,8,9]])
59
+ # #<Daru::DataFrame(3x3)>
60
+ # 0 1 2
61
+ # 0 1 4 7
62
+ # 1 2 5 8
63
+ # 2 3 6 9
64
+
65
+ # :column argument would be passed to DataFrame#each, so we are selecting columns
66
+ df.select(:column) { |col| col.sum > 6 }
67
+ # #<Daru::DataFrame(3x2)>
68
+ # 0 1
69
+ # 0 4 7
70
+ # 1 5 8
71
+ # 2 6 9
72
+ ```
73
+
74
+ ## Reasons
75
+
76
+ `IdempotentEnumerable` can be used as:
77
+
78
+ * soft patch to existing Ruby collections (like `Set` or `Hash`);
79
+ * custom reimplementations of generic collections (some `FasterArray`);
80
+ * custom specialized collection, like [Nokogiri::XML::NodeSet](http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/NodeSet),
81
+ which quacks like `Array`, but also provides XML/CSS navigation methods. Unfortunately, if you'll
82
+ do something like `doc.search('a').reject { |a| a.text.include?('Google') }`, you'll receive regular
83
+ `Array` that haven't any useful `#at`/`#search` methods anymore.
84
+
85
+ ## Installation and usage
86
+
87
+ `gem install idempotent_enumerable` or `gem 'idempotent_enumerable'` in your `Gemfile`.
88
+
89
+ Then follow examples in this README.
90
+
91
+ ## List of methods redefined
92
+
93
+ ### Methods that return single collection
94
+
95
+ * [drop](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-drop);
96
+ * [drop_while](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-drop_while);
97
+ * [first](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-first) (when used with argument);
98
+ * [grep](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-grep);
99
+ * [grep_v](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-grep_v) (RUBY_VERSION >= 2.3);
100
+ * [max](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-max) (when used with argument, RUBY_VERSION >= 2.2);
101
+ * [max_by](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-max_by) (when used with argument, RUBY_VERSION >= 2.2);
102
+ * [min](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-min) (when used with argument, RUBY_VERSION >= 2.2);
103
+ * [min_by](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-min_by) (when used with argument, RUBY_VERSION >= 2.2);
104
+ * [reduce](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-reduce);
105
+ * [reject](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-reject);
106
+ * [select](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-select);
107
+ * [sort](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-sort);
108
+ * [sort_by](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-sort_by);
109
+ * [take](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-take);
110
+ * [take_while](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-take_while);
111
+ * [uniq](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-uniq) (RUBY_VERSION >= 2.4).
112
+
113
+ ### Methods that return (or emit) several collections
114
+
115
+ For methods like [partition](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-partition) that
116
+ somehow split an enumerable sequence into several, `IdempotentEnumerable` preserves the type of
117
+ **internal** sequence. E.g.:
118
+
119
+ ```ruby
120
+ Set.include IdempotentEnumerable
121
+ set = Set.new(1..5)
122
+ set.partition(&:odd?)
123
+ # => [#<Set: {1, 3, 5}>, #<Set: {2, 4}>]
124
+ set.each_slice(3).to_a
125
+ # => [#<Set: {1, 2, 3}>, #<Set: {4, 5}>]
126
+ ```
127
+
128
+ * [chunk](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-chunk);
129
+ * [chunk_while](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-chunk_while) (RUBY_VERSION >= 2.3);
130
+ * [each_cons](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-each_cons);
131
+ * [each_slice](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-each_slice);
132
+ * [group_by](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-group_by) (returns hash with
133
+ keys being group keys and values being original collection type);
134
+ * [partition](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-partition);
135
+ * [slice_after](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-slice_after) (RUBY_VERSION >= 2.2);
136
+ * [slice_before](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-slice_before);
137
+ * [slice_when](https://ruby-doc.org/core-2.4.2/Enumerable.html#method-i-slice_when) (RUBY_VERSION >= 2.2).
138
+
139
+ ### Optionally redefined methods
140
+
141
+ Generally speaking, `map` and `flat_map` can return collection of anything, probably not coercible
142
+ to original collection type, so they are **not** redefined by default.
143
+
144
+ But they can be redefined with optional `idempotent_enumerable.redefine_map!` call:
145
+
146
+ ```ruby
147
+ Set.include IdempotentEnumerable
148
+ set = Set.new(1..5)
149
+ set.map(&:to_s)
150
+ # => ["1", "2", "3", "4", "5"]
151
+ Set.idempotent_enumerable.redefine_map!
152
+ set.map(&:to_s)
153
+ # => #<Set: {"1", "2", "3", "4", "5"}>
154
+ ```
155
+
156
+ `redefine_map!` has two options:
157
+ * `only:` (by default `[:map, :flat_map]`) to specify that you want to redefine only one of those
158
+ methods;
159
+ * `all:` to specify which condition all elements of produced collection should satisfy to coerce.
160
+
161
+ Example of the latter:
162
+
163
+ ```ruby
164
+ Hash.include IdempotentEnumerable
165
+ Hash.idempotent_enumerable.constructor = :[]
166
+ # only convert back to hash if `map` have returned array of pairs
167
+ Hash.idempotent_enumerable.redefine_map! all: ->(e) { e.is_a?(Array) && e.count == 2 }
168
+ {a: 1, b: 2}.map(&:join)
169
+ # => ["a1", "b2"] -- no coercion
170
+ {a: 1, b: 2}.map { |k, v| [k.to_s, v.to_s] }
171
+ # => {"a"=>"1", "b"=>"2"} -- coercion
172
+ ```
173
+
174
+ ## Performance penalty
175
+
176
+ ...is, of course, present, yet not that awful (depends on your standards).
177
+
178
+ ```ruby
179
+ require 'benchmark/ips'
180
+
181
+ set1 = Set.new((1..100))
182
+
183
+ class SetI < Set
184
+ include IdempotentEnumerable
185
+ end
186
+ set2 = SetI.new((1..100))
187
+
188
+ Benchmark.ips do |x|
189
+ x.report('Enumerable') { set1.reject(&:odd?) }
190
+ x.report('IdempotentEnumerable') { set2.reject(&:odd?) }
191
+
192
+ x.compare!
193
+ end
194
+ ```
195
+
196
+ Output:
197
+
198
+ ```
199
+ Warming up --------------------------------------
200
+ Enumerable 10.681k i/100ms
201
+ IdempotentEnumerable 4.035k i/100ms
202
+ Calculating -------------------------------------
203
+ Enumerable 112.134k (± 3.5%) i/s - 566.093k in 5.055148s
204
+ IdempotentEnumerable 42.197k (± 4.1%) i/s - 213.855k in 5.078339s
205
+
206
+ Comparison:
207
+ Enumerable: 112134.2 i/s
208
+ IdempotentEnumerable: 42196.6 i/s - 2.66x slower
209
+ ```
210
+
211
+ ## Author
212
+
213
+ [Victor Shepelev](http://zverok.github.io/)
214
+
215
+ ## License
216
+
217
+ MIT
@@ -0,0 +1,17 @@
1
+ require 'bundler/setup'
2
+ require 'idempotent_enumerable'
3
+ require 'set'
4
+ require 'benchmark/ips'
5
+
6
+ set1 = Set.new((1..100))
7
+ class SetI < Set
8
+ include IdempotentEnumerable
9
+ end
10
+ set2 = SetI.new((1..100))
11
+
12
+ Benchmark.ips do |x|
13
+ x.report('Enumerable') { set1.reject(&:odd?) }
14
+ x.report('IdempotentEnumerable') { set2.reject(&:odd?) }
15
+
16
+ x.compare!
17
+ end
@@ -0,0 +1,39 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'idempotent_enumerable'
3
+ s.version = '0.0.1'
4
+ s.authors = ['Victor Shepelev']
5
+ s.email = 'zverok.offline@gmail.com'
6
+ s.homepage = 'https://github.com/zverok/idempotent_enumerable'
7
+
8
+ s.summary = 'Like Enumerable but preserves original collection class'
9
+ s.description = <<-EOF
10
+ IdempotentEnumerable is like Enumerable, but tries to preserve original collection type when possible.
11
+ EOF
12
+ s.licenses = ['MIT']
13
+
14
+ s.required_ruby_version = '>= 2.1.0'
15
+
16
+ s.files = `git ls-files`.split($RS).reject do |file|
17
+ file =~ /^(?:
18
+ spec\/.*
19
+ |Gemfile
20
+ |Rakefile
21
+ |\.rspec
22
+ |\.gitignore
23
+ |\.rubocop.yml
24
+ |\.travis.yml
25
+ )$/x
26
+ end
27
+ s.require_paths = ["lib"]
28
+
29
+ s.add_development_dependency 'rubocop', '>= 0.50'
30
+ s.add_development_dependency 'rspec', '>= 3'
31
+ s.add_development_dependency 'rubocop-rspec', '>= 1.17.1'
32
+ s.add_development_dependency 'rspec-its', '~> 1'
33
+ s.add_development_dependency 'saharspec', '~> 0.0.2'
34
+ s.add_development_dependency 'rake'
35
+ s.add_development_dependency 'rubygems-tasks'
36
+ s.add_development_dependency 'yard'
37
+ s.add_development_dependency 'benchmark-ips'
38
+ s.add_development_dependency 'coveralls'
39
+ end
@@ -0,0 +1,142 @@
1
+ module IdempotentEnumerable
2
+ include Enumerable
3
+
4
+ def self.included(klass)
5
+ def klass.idempotent_enumerable
6
+ @idempotent_enumerable ||= Configurator.new(self)
7
+ end
8
+ end
9
+
10
+ %i[drop_while reject select sort_by take_while].each do |method|
11
+ define_method(method) do |*arg, &block|
12
+ return to_enum(method, *arg) unless block
13
+ idempotently_construct(each(*arg).send(method, &block))
14
+ end
15
+ end
16
+
17
+ alias find_all select
18
+
19
+ def chunk(*arg, &block)
20
+ # FIXME: should return enumerator
21
+ return to_enum(:chunk, *arg) unless block
22
+ each(*arg).chunk(&block).map { |key, group| [key, idempotently_construct(group)] }
23
+ end
24
+
25
+ if RUBY_VERSION >= '2.3.0'
26
+ def chunk_while(*arg, &block)
27
+ idempotent_enumerator(each(*arg).chunk_while(&block))
28
+ end
29
+ end
30
+
31
+ def drop(num, *arg)
32
+ idempotently_construct(each(*arg).drop(num))
33
+ end
34
+
35
+ def each_cons(num, *arg)
36
+ return to_enum(:each_cons, num, *arg) unless block_given?
37
+ each(*arg).each_cons(num) { |slice| yield(idempotently_construct(slice)) }
38
+ end
39
+
40
+ def each_slice(num, *arg)
41
+ return to_enum(:each_slice, num, *arg) unless block_given?
42
+ each(*arg).each_slice(num) { |slice| yield(idempotently_construct(slice)) }
43
+ end
44
+
45
+ def first(*arg)
46
+ arg, num = idempotent_split_arg(arg)
47
+ num ? idempotently_construct(each(*arg).first(*num)) : each(*arg).first
48
+ end
49
+
50
+ def grep(*arg, pattern, &block)
51
+ idempotently_construct(each(*arg).grep(pattern, &block))
52
+ end
53
+
54
+ if RUBY_VERSION >= '2.3.0'
55
+ def grep_v(pattern, *arg, &block)
56
+ idempotently_construct(each(*arg).grep_v(pattern, &block))
57
+ end
58
+ end
59
+
60
+ def group_by(*arg, &block)
61
+ each(*arg).group_by(&block).map { |key, val| [key, idempotently_construct(val)] }.to_h
62
+ end
63
+
64
+ def min(*arg)
65
+ arg, num = idempotent_split_arg(arg)
66
+ num ? idempotently_construct(each(*arg).min(*num)) : each(*arg).min
67
+ end
68
+
69
+ def min_by(*arg, &block)
70
+ return to_enum(:min_by, *arg) unless block
71
+ arg, num = idempotent_split_arg(arg)
72
+ num ? idempotently_construct(each(*arg).min_by(*num, &block)) : each(*arg).min_by(&block)
73
+ end
74
+
75
+ def max(*arg)
76
+ arg, num = idempotent_split_arg(arg)
77
+ num ? idempotently_construct(each(*arg).max(*num)) : each(*arg).max
78
+ end
79
+
80
+ def max_by(*arg, &block)
81
+ return to_enum(:max_by, *arg) unless block
82
+ arg, num = idempotent_split_arg(arg)
83
+ num ? idempotently_construct(each(*arg).max_by(*num, &block)) : each(*arg).max_by(&block)
84
+ end
85
+
86
+ def partition(*arg, &block)
87
+ return to_enum(:partition, *arg) unless block
88
+ each(*arg).partition(&block).map(&method(:idempotently_construct))
89
+ end
90
+
91
+ def slice_before(pattern = nil, &block)
92
+ idempotent_enumerator(pattern ? each.slice_before(pattern) : each.slice_before(&block))
93
+ end
94
+
95
+ if RUBY_VERSION >= '2.2'
96
+ def slice_after(pattern = nil, &block)
97
+ idempotent_enumerator(pattern ? each.slice_after(pattern) : each.slice_after(&block))
98
+ end
99
+
100
+ def slice_when(*arg, &block)
101
+ idempotent_enumerator(each(*arg).slice_when(&block))
102
+ end
103
+ end
104
+
105
+ def sort(*arg, &block)
106
+ idempotently_construct(each(*arg).sort(&block))
107
+ end
108
+
109
+ def take(num, *arg)
110
+ idempotently_construct(each(*arg).take(num))
111
+ end
112
+
113
+ if RUBY_VERSION >= '2.4.0'
114
+ def uniq(*arg, &block)
115
+ idempotently_construct(each(*arg).uniq(&block))
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def idempotently_construct(array)
122
+ self.class.send(self.class.idempotent_enumerable.constructor, array)
123
+ end
124
+
125
+ def idempotent_enumerator(enumerator)
126
+ Enumerator.new { |y| enumerator.each { |chunk| y << idempotently_construct(chunk) } }
127
+ end
128
+
129
+ # For methods like min(), with optional argument (count of min elements), AND complex collections
130
+ # that have args for their #each method, we need to split args to "args to #each" and
131
+ # "args to #min".
132
+ #
133
+ # Doesn't work if #each has variable number of args, unfortunately.
134
+ #
135
+ def idempotent_split_arg(arg)
136
+ arity = method(:each).arity
137
+ return [arg] if arity < 0 || arg.count <= arity
138
+ [arg[0...arity], arg[arity..-1]]
139
+ end
140
+ end
141
+
142
+ require_relative 'idempotent_enumerable/configurator'
@@ -0,0 +1,34 @@
1
+ module IdempotentEnumerable
2
+ class Configurator
3
+ attr_writer :constructor
4
+
5
+ def initialize(host)
6
+ @host = host
7
+ end
8
+
9
+ def constructor
10
+ @constructor || :new
11
+ end
12
+
13
+ REDEFINEABLE = %i[map flat_map collect collect_concat].freeze
14
+
15
+ def redefine_map!(only: REDEFINEABLE, all: nil)
16
+ (Array(only) & REDEFINEABLE).each { |method| redefine(method, all) }
17
+ self
18
+ end
19
+
20
+ private
21
+
22
+ def redefine(method, all)
23
+ @host.send(:define_method, method) do |*arg, &block|
24
+ return to_enum(method) unless block
25
+ res = each(*arg).send(method, &block)
26
+ if !all || res.all?(&all)
27
+ idempotently_construct(res)
28
+ else
29
+ res
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: idempotent_enumerable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Shepelev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.50'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.50'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.17.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.17.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-its
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: saharspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubygems-tasks
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: benchmark-ips
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: coveralls
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: " IdempotentEnumerable is like Enumerable, but tries to preserve original
154
+ collection type when possible.\n"
155
+ email: zverok.offline@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".rubocop_todo.yml"
161
+ - README.md
162
+ - benchmarks/set.rb
163
+ - idempotent_enumerable.gemspec
164
+ - lib/idempotent_enumerable.rb
165
+ - lib/idempotent_enumerable/configurator.rb
166
+ homepage: https://github.com/zverok/idempotent_enumerable
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: 2.1.0
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.6.10
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Like Enumerable but preserves original collection class
190
+ test_files: []