async-enumerable 0.1.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.standard.yml +5 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +416 -0
  8. data/Rakefile +127 -0
  9. data/benchmark/async_all.yaml +38 -0
  10. data/benchmark/async_any.yaml +39 -0
  11. data/benchmark/async_each.yaml +51 -0
  12. data/benchmark/async_find.yaml +37 -0
  13. data/benchmark/async_map.yaml +50 -0
  14. data/benchmark/async_select.yaml +31 -0
  15. data/benchmark/early_termination/any_early.yaml +17 -0
  16. data/benchmark/early_termination/any_late.yaml +17 -0
  17. data/benchmark/early_termination/find_middle.yaml +17 -0
  18. data/benchmark/size_comparison/map_10.yaml +17 -0
  19. data/benchmark/size_comparison/map_100.yaml +17 -0
  20. data/benchmark/size_comparison/map_1000.yaml +20 -0
  21. data/benchmark/size_comparison/map_10000.yaml +23 -0
  22. data/docs/reference/README.md +43 -0
  23. data/docs/reference/concurrency_bounder.md +234 -0
  24. data/docs/reference/enumerable.md +258 -0
  25. data/docs/reference/enumerator.md +221 -0
  26. data/docs/reference/methods/converters.md +97 -0
  27. data/docs/reference/methods/predicates.md +254 -0
  28. data/docs/reference/methods/transformers.md +104 -0
  29. data/lib/async/enumerable/comparable.rb +26 -0
  30. data/lib/async/enumerable/concurrency_bounder.rb +37 -0
  31. data/lib/async/enumerable/configurable.rb +140 -0
  32. data/lib/async/enumerable/methods/aggregators.rb +40 -0
  33. data/lib/async/enumerable/methods/converters.rb +21 -0
  34. data/lib/async/enumerable/methods/each.rb +39 -0
  35. data/lib/async/enumerable/methods/iterators.rb +27 -0
  36. data/lib/async/enumerable/methods/predicates/all.rb +47 -0
  37. data/lib/async/enumerable/methods/predicates/any.rb +47 -0
  38. data/lib/async/enumerable/methods/predicates/find.rb +55 -0
  39. data/lib/async/enumerable/methods/predicates/find_index.rb +50 -0
  40. data/lib/async/enumerable/methods/predicates/include.rb +23 -0
  41. data/lib/async/enumerable/methods/predicates/none.rb +27 -0
  42. data/lib/async/enumerable/methods/predicates/one.rb +48 -0
  43. data/lib/async/enumerable/methods/predicates.rb +29 -0
  44. data/lib/async/enumerable/methods/slicers.rb +34 -0
  45. data/lib/async/enumerable/methods/transformers/compact.rb +18 -0
  46. data/lib/async/enumerable/methods/transformers/filter_map.rb +19 -0
  47. data/lib/async/enumerable/methods/transformers/flat_map.rb +20 -0
  48. data/lib/async/enumerable/methods/transformers/map.rb +22 -0
  49. data/lib/async/enumerable/methods/transformers/reject.rb +19 -0
  50. data/lib/async/enumerable/methods/transformers/select.rb +21 -0
  51. data/lib/async/enumerable/methods/transformers/sort.rb +18 -0
  52. data/lib/async/enumerable/methods/transformers/sort_by.rb +19 -0
  53. data/lib/async/enumerable/methods/transformers/uniq.rb +18 -0
  54. data/lib/async/enumerable/methods/transformers.rb +35 -0
  55. data/lib/async/enumerable/methods.rb +26 -0
  56. data/lib/async/enumerable/version.rb +10 -0
  57. data/lib/async/enumerable.rb +72 -0
  58. data/lib/async/enumerator.rb +33 -0
  59. data/lib/enumerable/async.rb +38 -0
  60. data/scripts/debug_config.rb +26 -0
  61. data/scripts/debug_config2.rb +34 -0
  62. data/scripts/sketch.rb +30 -0
  63. data/scripts/test_aggregators.rb +66 -0
  64. data/scripts/test_ancestors.rb +12 -0
  65. data/scripts/test_async_chaining.rb +30 -0
  66. data/scripts/test_direct_method_calls.rb +53 -0
  67. data/scripts/test_example.rb +37 -0
  68. data/scripts/test_issue_7.rb +69 -0
  69. data/scripts/test_method_source.rb +15 -0
  70. metadata +145 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ # Methods that convert enumerables to other types.
7
+ module Converters
8
+ def self.included(base) = base.include(Configurable) # Dependency for collection resolution
9
+
10
+ # Converts enumerable to array.
11
+ # @return [Array] Array representation
12
+ def to_a
13
+ source = __async_enumerable_collection
14
+ # If source is self, we need to use super to avoid infinite recursion
15
+ (source == self) ? super : source.to_a
16
+ end
17
+ alias_method :sync, :to_a
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Each
7
+ def self.included(base)
8
+ base.include(::Enumerable) # Dependency
9
+ base.include(ConcurrencyBounder) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ end
12
+
13
+ # Executes block for each element in parallel.
14
+ #
15
+ # This is the core of Async::Enumerable, as most of the enumerable
16
+ # methods _require_ #each in order to function. This definition of each
17
+ # is automatically included when `Async::Enumerable` is included, but it
18
+ # can be overridden by the including class. Here be dragons, though.
19
+ #
20
+ # @yield [item] Block to run for each element
21
+ # @return [self, Enumerator] Self for chaining or Enumerator without block
22
+ def each(&block)
23
+ return enum_for(__method__) unless block_given?
24
+
25
+ __async_enumerable_bounded_concurrency do |barrier|
26
+ __async_enumerable_collection.each do |item|
27
+ barrier.async do
28
+ block.call(item)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Return self to allow chaining, like standard each
34
+ self
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ # Iterators module for enumerable iteration helper methods.
7
+ #
8
+ # Iteration helper methods are inherited from the standard Enumerable
9
+ # module. When used with async enumerables, these methods build on our
10
+ # async #each implementation, though some maintain sequential semantics
11
+ # where required by their nature.
12
+ #
13
+ # Methods available through Enumerable:
14
+ # - each_with_index: Iterates with index (block executes async)
15
+ # - each_with_object: Iterates with an accumulator object (block executes async)
16
+ # - each_cons: Iterates over consecutive n-element slices (maintains order)
17
+ # - each_slice: Iterates over n-element slices (block executes async per slice)
18
+ # - cycle: Cycles through elements repeatedly (block executes async)
19
+ # - with_index: Adds index to any enumerator
20
+ module Iterators
21
+ def self.included(base) = base.include(Each) # Dependency
22
+ # This module is intentionally empty as iteration methods are
23
+ # inherited from Enumerable
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module All
8
+ def self.included(base)
9
+ base.include(::Enumerable) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ base.include(ConcurrencyBounder) # Dependency
12
+ end
13
+
14
+ # Returns true if all elements satisfy the condition (parallel, early termination).
15
+ # @yield [item] Test condition for each element
16
+ # @return [Boolean] true if all elements match
17
+ def all?(pattern = nil, &block)
18
+ # Delegate pattern/no-block cases to wrapped enumerable to avoid break issues
19
+ if pattern
20
+ return __async_enumerable_collection.all?(pattern)
21
+ elsif !block_given?
22
+ return __async_enumerable_collection.all?
23
+ end
24
+
25
+ failed = Concurrent::AtomicBoolean.new(false)
26
+
27
+ __async_enumerable_bounded_concurrency(early_termination: true) do |barrier|
28
+ __async_enumerable_collection.each do |item|
29
+ break if failed.true?
30
+
31
+ barrier.async do
32
+ unless block.call(item)
33
+ failed.make_true
34
+ # Stop the barrier early when we find a failure
35
+ barrier.stop
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ !failed.true?
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module Any
8
+ def self.included(base)
9
+ base.include(::Enumerable) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ base.include(ConcurrencyBounder) # Dependency
12
+ end
13
+
14
+ # Returns true if any element satisfies the condition (parallel, early termination).
15
+ # @yield [item] Test condition for each element
16
+ # @return [Boolean] true if any element matches
17
+ def any?(pattern = nil, &block)
18
+ # Delegate pattern/no-block cases to wrapped enumerable to avoid break issues
19
+ if pattern
20
+ return __async_enumerable_collection.any?(pattern)
21
+ elsif !block_given?
22
+ return __async_enumerable_collection.any?
23
+ end
24
+
25
+ found = Concurrent::AtomicBoolean.new(false)
26
+
27
+ __async_enumerable_bounded_concurrency(early_termination: true) do |barrier|
28
+ __async_enumerable_collection.each do |item|
29
+ break if found.true?
30
+
31
+ barrier.async do
32
+ if block.call(item)
33
+ found.make_true
34
+ # Stop the barrier early when we find a match
35
+ barrier.stop
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ found.true?
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module Find
8
+ def self.included(base)
9
+ base.include(::Enumerable) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ base.include(ConcurrencyBounder) # Dependency
12
+ end
13
+
14
+ # Returns first element that satisfies condition (parallel, early termination).
15
+ # @note Returns the **fastest completing** match, not necessarily the first by position.
16
+ # Due to parallel execution, whichever element completes evaluation first will be returned.
17
+ # Use synchronous `find` if positional order matters.
18
+ # @yield [item] Test condition for each element
19
+ # @return [Object, nil] First matching element or nil
20
+ def find(ifnone = nil, &block)
21
+ return super unless block_given?
22
+
23
+ result = Concurrent::AtomicReference.new(nil)
24
+
25
+ __async_enumerable_bounded_concurrency(early_termination: true) do |barrier|
26
+ __async_enumerable_collection.each do |item|
27
+ break unless result.get.nil?
28
+
29
+ barrier.async do
30
+ if block.call(item)
31
+ # Use compare_and_set to ensure only the first match wins
32
+ if result.compare_and_set(nil, item)
33
+ # Stop the barrier early when we find a match
34
+ barrier.stop
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ found = result.get
42
+ if found.nil? && ifnone
43
+ ifnone.call
44
+ else
45
+ found
46
+ end
47
+ end
48
+
49
+ # Alias for find.
50
+ alias_method :detect, :find
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module FindIndex
8
+ def self.included(base)
9
+ base.include(::Enumerable) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ base.include(ConcurrencyBounder) # Dependency
12
+ end
13
+
14
+ # Returns index of first matching element (parallel, early termination).
15
+ # @note Returns the index of the **fastest completing** match, not necessarily the first by position.
16
+ # Due to parallel execution, whichever element completes evaluation first will have its index returned.
17
+ # Use synchronous `find_index` if positional order matters.
18
+ # @param value [Object] Value to find or omit for block form
19
+ # @return [Integer, nil] Index of first match or nil
20
+ def find_index(value = (no_value = true), &block)
21
+ if no_value && !block_given?
22
+ return enum_for(__method__)
23
+ end
24
+
25
+ result_index = Concurrent::AtomicReference.new(nil)
26
+
27
+ __async_enumerable_bounded_concurrency(early_termination: true) do |barrier|
28
+ __async_enumerable_collection.each_with_index do |item, index|
29
+ break unless result_index.get.nil?
30
+
31
+ barrier.async do
32
+ match = no_value ? block.call(item) : (item == value)
33
+ if match
34
+ # Use compare_and_set to ensure only the first match wins
35
+ if result_index.compare_and_set(nil, index)
36
+ # Stop the barrier early when we find a match
37
+ barrier.stop
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ result_index.get
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module Include
8
+ def self.included(base) = base.include(Any) # Dependency
9
+
10
+ # Checks if enumerable includes the object (parallel, early termination).
11
+ # @param obj Object to search for
12
+ # @return [Boolean] true if found
13
+ def include?(obj)
14
+ any? { |item| item == obj }
15
+ end
16
+
17
+ # Alias for include?.
18
+ alias_method :member?, :include?
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module None
8
+ def self.included(base) = base.include(Any) # Dependency
9
+
10
+ # Returns true if no elements satisfy the condition (parallel, early termination).
11
+ # @yield [item] Test condition for each element
12
+ # @return [Boolean] true if no elements match
13
+ def none?(pattern = nil, &block)
14
+ # Delegate pattern/no-block cases to wrapped enumerable to avoid break issues
15
+ if pattern
16
+ return __async_enumerable_collection.none?(pattern)
17
+ elsif !block_given?
18
+ return __async_enumerable_collection.none?
19
+ end
20
+ # For blocks, use our async any? and negate
21
+ !any?(&block)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Predicates
7
+ module One
8
+ def self.included(base)
9
+ base.include(::Enumerable) # Dependency
10
+ base.include(Configurable) # Dependency for collection resolution
11
+ base.include(ConcurrencyBounder) # Dependency
12
+ end
13
+
14
+ # Returns true if exactly one element satisfies the condition (parallel).
15
+ # @yield [item] Test condition for each element
16
+ # @return [Boolean] true if exactly one element matches
17
+ def one?(pattern = nil, &block)
18
+ # Delegate pattern/no-block cases to wrapped enumerable to avoid break issues
19
+ if pattern
20
+ return __async_enumerable_collection.one?(pattern)
21
+ elsif !block_given?
22
+ return __async_enumerable_collection.one?
23
+ end
24
+
25
+ count = Concurrent::AtomicFixnum.new(0)
26
+
27
+ __async_enumerable_bounded_concurrency(early_termination: true) do |barrier|
28
+ __async_enumerable_collection.each do |item|
29
+ break if count.value > 1
30
+
31
+ barrier.async do
32
+ if block.call(item)
33
+ if count.increment > 1
34
+ # Stop the barrier early when we have too many matches
35
+ barrier.stop
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ count.value == 1
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async/enumerable/methods/predicates/all"
4
+ require "async/enumerable/methods/predicates/any"
5
+ require "async/enumerable/methods/predicates/find"
6
+ require "async/enumerable/methods/predicates/find_index"
7
+ require "async/enumerable/methods/predicates/include"
8
+ require "async/enumerable/methods/predicates/none"
9
+ require "async/enumerable/methods/predicates/one"
10
+
11
+ module Async
12
+ module Enumerable
13
+ module Methods
14
+ # Predicates contains async implementations of enumerable predicate methods
15
+ # that can terminate early when their condition is met or violated.
16
+ module Predicates
17
+ def self.included(base)
18
+ base.include All
19
+ base.include Any
20
+ base.include Find
21
+ base.include FindIndex
22
+ base.include Include
23
+ base.include None
24
+ base.include One
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ # Slicers module for enumerable slicing and filtering methods.
7
+ #
8
+ # Slicing and filtering methods are inherited from the standard Enumerable
9
+ # module. When used with async enumerables, methods that accept blocks
10
+ # automatically benefit from parallel execution through our async #each
11
+ # implementation.
12
+ #
13
+ # Methods available through Enumerable:
14
+ # - drop/drop_while: Drops elements from the beginning (block executes async)
15
+ # - take/take_while: Takes elements from the beginning (delegated for efficiency)
16
+ # - grep/grep_v: Filters elements matching/not matching a pattern (block executes async)
17
+ # - partition: Splits into two arrays based on a predicate (block executes async)
18
+ # - chunk/chunk_while: Groups consecutive elements (maintains order)
19
+ # - slice_before/slice_after/slice_when: Slices based on conditions (block executes async)
20
+ module Slicers
21
+ def self.included(base)
22
+ base.include(Each) # Dependency
23
+ base.include(Configurable) # Dependency for collection resolution
24
+
25
+ # Delegate non-parallelizable slicer methods directly to the collection
26
+ base.extend(Forwardable)
27
+ base.def_delegators :__async_enumerable_collection, :first, :take, :take_while
28
+ end
29
+ # This module is intentionally empty as slicing methods are
30
+ # inherited from Enumerable and automatically use our async #each
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Compact
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of compact that returns an Async::Enumerator for chaining
11
+ def compact
12
+ Async::Enumerator.new(super, __async_enumerable_config)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module FilterMap
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of filter_map that returns an Async::Enumerator for chaining
11
+ def filter_map(&block)
12
+ return enum_for(__method__) unless block_given?
13
+ Async::Enumerator.new(super, __async_enumerable_config)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module FlatMap
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of flat_map that returns an Async::Enumerator for chaining
11
+ def flat_map(&block)
12
+ return enum_for(__method__) unless block_given?
13
+ Async::Enumerator.new(super, __async_enumerable_config)
14
+ end
15
+ alias_method :collect_concat, :flat_map
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Map
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Maps elements in parallel, returns async enumerator.
11
+ # @yield [item] Transform for each element
12
+ # @return [Async::Enumerator] Transformed collection
13
+ def map(&block)
14
+ return enum_for(__method__) unless block_given?
15
+ Async::Enumerator.new(super, __async_enumerable_config)
16
+ end
17
+ alias_method :collect, :map
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Reject
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of reject that returns an Async::Enumerator for chaining
11
+ def reject(&block)
12
+ return enum_for(__method__) unless block_given?
13
+ Async::Enumerator.new(super, __async_enumerable_config)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Select
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of select that returns an Async::Enumerator for chaining
11
+ def select(&block)
12
+ return enum_for(__method__) unless block_given?
13
+ Async::Enumerator.new(super, __async_enumerable_config)
14
+ end
15
+ alias_method :filter, :select
16
+ alias_method :find_all, :select
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Sort
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of sort that returns an Async::Enumerator for chaining
11
+ def sort(&block)
12
+ Async::Enumerator.new(super, __async_enumerable_config)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module SortBy
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of sort_by that returns an Async::Enumerator for chaining
11
+ def sort_by(&block)
12
+ return enum_for(__method__) unless block_given?
13
+ Async::Enumerator.new(super, __async_enumerable_config)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Enumerable
5
+ module Methods
6
+ module Transformers
7
+ module Uniq
8
+ def self.included(base) = base.include(Each) # Dependency
9
+
10
+ # Async version of uniq that returns an Async::Enumerator for chaining
11
+ def uniq(&block)
12
+ Async::Enumerator.new(super, __async_enumerable_config)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async/enumerable/methods/transformers/compact"
4
+ require "async/enumerable/methods/transformers/filter_map"
5
+ require "async/enumerable/methods/transformers/flat_map"
6
+ require "async/enumerable/methods/transformers/map"
7
+ require "async/enumerable/methods/transformers/reject"
8
+ require "async/enumerable/methods/transformers/select"
9
+
10
+ require "async/enumerable/methods/transformers/sort"
11
+ require "async/enumerable/methods/transformers/sort_by"
12
+ require "async/enumerable/methods/transformers/uniq"
13
+
14
+ module Async
15
+ module Enumerable
16
+ module Methods
17
+ # Transformers contains async implementations of enumerable transformation methods
18
+ # that transform collections into new collections.
19
+ module Transformers
20
+ def self.included(base)
21
+ base.include Compact
22
+ base.include FilterMap
23
+ base.include FlatMap
24
+ base.include Map
25
+ base.include Reject
26
+ base.include Select
27
+
28
+ base.include Sort
29
+ base.include SortBy
30
+ base.include Uniq
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end