sortsmith 0.2.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +146 -1
- data/README.md +269 -60
- data/flake.lock +3 -3
- data/flake.nix +11 -5
- data/lib/sortsmith/core_ext/enumerable.rb +109 -0
- data/lib/sortsmith/sorter.rb +692 -99
- data/lib/sortsmith/version.rb +4 -1
- data/lib/sortsmith.rb +49 -2
- metadata +4 -9
- data/Steepfile +0 -8
- data/lib/sortsmith/step.rb +0 -17
- data/sig/sortsmith/sorter.rbs +0 -107
- data/sig/sortsmith/step.rbs +0 -28
- data/sig/sortsmith/version.rbs +0 -3
- data/sig/sortsmith.rbs +0 -4
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Extensions to Ruby's built-in Enumerable module.
|
5
|
+
#
|
6
|
+
# Sortsmith extends {Enumerable} to provide enhanced sorting capabilities
|
7
|
+
# while preserving the original behavior when used with blocks.
|
8
|
+
#
|
9
|
+
# The key enhancement is allowing `sort_by` to be called without a block,
|
10
|
+
# which returns a {Sortsmith::Sorter} instance for method chaining.
|
11
|
+
#
|
12
|
+
# @example Original behavior (unchanged)
|
13
|
+
# [1, 2, 3].sort_by { |n| -n }
|
14
|
+
# # => [3, 2, 1]
|
15
|
+
#
|
16
|
+
# @example New chainable behavior
|
17
|
+
# users.sort_by.dig(:name).downcase.sort
|
18
|
+
# # => Returns Sorter instance for chaining
|
19
|
+
#
|
20
|
+
# @see Sortsmith::Sorter The chainable sorting interface
|
21
|
+
#
|
22
|
+
module Enumerable
|
23
|
+
##
|
24
|
+
# Stores the original sort_by method before extension.
|
25
|
+
#
|
26
|
+
# This alias preserves Ruby's original `sort_by` behavior, allowing
|
27
|
+
# Sortsmith to enhance the method while maintaining full backward
|
28
|
+
# compatibility when blocks are provided.
|
29
|
+
#
|
30
|
+
# @see #sort_by The enhanced version
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
alias_method :og_sort_by, :sort_by
|
34
|
+
|
35
|
+
##
|
36
|
+
# Enhanced sort_by that supports both traditional block usage and direct field extraction.
|
37
|
+
#
|
38
|
+
# This method extends Ruby's built-in `sort_by` to provide a fluent, chainable API
|
39
|
+
# for sorting operations. When called with a block, it behaves exactly like the
|
40
|
+
# original `sort_by`. When called without a block, it returns a {Sortsmith::Sorter}
|
41
|
+
# instance for method chaining.
|
42
|
+
#
|
43
|
+
# The direct syntax (`sort_by(field)`) provides a concise way to sort by a specific
|
44
|
+
# field or method without verbose chaining, making simple sorting operations more
|
45
|
+
# readable and intuitive.
|
46
|
+
#
|
47
|
+
# @param field [Symbol, String, nil] Optional field name for direct extraction
|
48
|
+
# @param positional [Array] Additional positional arguments for extraction
|
49
|
+
# @param keyword [Hash] Additional keyword arguments for extraction
|
50
|
+
# @param block [Proc, nil] Optional block for traditional sort_by behavior
|
51
|
+
#
|
52
|
+
# @return [Array, Sortsmith::Sorter] Array when block given, Sorter instance otherwise
|
53
|
+
#
|
54
|
+
# @example Traditional block usage (unchanged)
|
55
|
+
# users.sort_by { |user| user.name.downcase }
|
56
|
+
# # => sorted array
|
57
|
+
#
|
58
|
+
# @example Direct field syntax (new)
|
59
|
+
# users.sort_by(:name).insensitive.sort
|
60
|
+
# # => sorted array via method chaining
|
61
|
+
#
|
62
|
+
# @example Direct syntax with immediate result
|
63
|
+
# users.sort_by(:score).desc.first(3)
|
64
|
+
# # => top 3 users by score
|
65
|
+
#
|
66
|
+
# @example Chainable interface without field
|
67
|
+
# users.sort_by.dig(:profile, :email).sort
|
68
|
+
# # => sorted by nested email field
|
69
|
+
#
|
70
|
+
# @example Mixed key types
|
71
|
+
# mixed_data = [
|
72
|
+
# { name: "Bob" }, # symbol key
|
73
|
+
# { "name" => "Alice" } # string key
|
74
|
+
# ]
|
75
|
+
# mixed_data.sort_by(:name, indifferent: true).sort
|
76
|
+
# # => handles both key types gracefully
|
77
|
+
#
|
78
|
+
# @example Object method sorting
|
79
|
+
# products.sort_by(:calculate_price).desc.sort
|
80
|
+
# # => sorted by calculated price method
|
81
|
+
#
|
82
|
+
# @example Dynamic field selection
|
83
|
+
# sort_field = params[:sort_by] # might be nil
|
84
|
+
# users.sort_by(sort_field).sort
|
85
|
+
# # => gracefully handles nil field
|
86
|
+
#
|
87
|
+
# @example Integration with enumerable methods
|
88
|
+
# users.sort_by(:created_at).desc.take(10)
|
89
|
+
# # => newest 10 users without breaking the chain
|
90
|
+
#
|
91
|
+
# @raise [ArgumentError] When extraction results in incomparable types
|
92
|
+
#
|
93
|
+
# @note This method maintains full backward compatibility with Ruby's original sort_by
|
94
|
+
# @note When field is nil, returns a plain Sorter instance for manual chaining
|
95
|
+
#
|
96
|
+
# @see Sortsmith::Sorter The chainable sorting interface
|
97
|
+
# @see #extract Universal extraction method
|
98
|
+
# @see Enumerable#sort_by Original Ruby method (aliased as og_sort_by)
|
99
|
+
# @since 1.0.0
|
100
|
+
#
|
101
|
+
def sort_by(field = nil, *positional, **keyword, &block)
|
102
|
+
return og_sort_by(&block) if block
|
103
|
+
|
104
|
+
sorter = Sortsmith::Sorter.new(self)
|
105
|
+
return sorter if field.nil?
|
106
|
+
|
107
|
+
sorter.extract(field, *positional, **keyword)
|
108
|
+
end
|
109
|
+
end
|