sortsmith 0.2.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb5b3aee0492a9a38de3999f07444c05e0e7ad00c86c3c3f6a87be19151e5b23
4
- data.tar.gz: e3a1f4497ffc436f56ee881e58086e23dc0009357e1bfe3cd9393dc4642162de
3
+ metadata.gz: a2145a4f96e357b7c580dd3bbd5c672120788c422d76737fcf7015539ace75c7
4
+ data.tar.gz: 80376b95b0cb7cee7a0b741a69d90a91307384623007b63384a1551871fdb2d5
5
5
  SHA512:
6
- metadata.gz: 64d378b0764c272de8ba01a1230ef7457ea36627712c5c38f1531b9d57e1ca44e2627bb880aeede760f0bfc168cfdffccea8d1b3577e07daa8fe64181e3cb8cd
7
- data.tar.gz: 373dea1996fbe61a6c2e0cf3095c761b4b388f52932e78e8e43f1d3e7cac696d949ecf2233cd4f8d89c77cd1133c9aa6b2e84d40fc0e9c70d7e5795b5e65b84a
6
+ metadata.gz: 5de79e4da81b5dbf9096a9c0cbf21dd6c00500a89b8fcec679b3116b324864b3b70bf8d8e4759069412dc0979eeadbe5e3a05e8da28016ea2638de7ab8fbc54a
7
+ data.tar.gz: 1e7270386a18f165954b41810143d89920f66195f311826a19f4465a60c8865cbf3dd9e0de29cc7408d9bd41cbac43f8803030aa5724bc64e0e63f96e077f456
data/CHANGELOG.md CHANGED
@@ -13,6 +13,98 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Removed
15
15
 
16
+ ## [0.9.0] - 12025-07-06
17
+
18
+ ### 🎉 MAJOR REWRITE: Fluent Chainable API
19
+
20
+ **BREAKING CHANGES**: Complete API redesign introducing a fluent, chainable interface. See migration guide below.
21
+
22
+ **Pre-1.0 Notice**: This release represents our new stable API design, but we're seeking community feedback before locking in 1.0.0 compatibility guarantees.
23
+
24
+ ### Added
25
+
26
+ #### Core API Transformation
27
+ - **Fluent API**: Direct extension of `Enumerable#sort_by` for natural Ruby integration
28
+ - **Chainable Interface**: Method chaining that reads like English: `users.sort_by.dig(:name).downcase.desc.sort`
29
+ - **Universal `dig` Method**: Single extraction method that works with hashes, objects, and nested structures
30
+ - **Indifferent Key Access**: Handle mixed symbol/string hash keys with `dig(:name, indifferent: true)`
31
+
32
+ #### New Extraction Methods
33
+ - `dig(*identifiers, indifferent: false)` - Extract values from hashes, objects, or nested structures
34
+ - Support for nested extraction: `dig(:user, :profile, :email)`
35
+ - Automatic fallback to method calls for non-hash objects
36
+
37
+ #### Enhanced Modifiers
38
+ - `downcase` / `upcase` - Case transformations with automatic type checking
39
+ - `insensitive` - Alias for `downcase` for semantic clarity
40
+ - `asc` / `desc` - Explicit sort direction control
41
+ - Smart modifier chaining that only affects compatible data types
42
+
43
+ #### Multiple Terminators
44
+ - `sort` - Returns new sorted array (non-mutating)
45
+ - `sort!` - Mutates original array in place
46
+ - `reverse` - Shorthand for `desc.sort`
47
+ - `reverse!` - Shorthand for `desc.sort!`
48
+ - `to_a` - Alias for `sort` for semantic clarity
49
+
50
+ #### Backward Compatibility
51
+ - `sort_by` with block maintains original Ruby behavior
52
+ - `sort_by` without block returns Sortsmith::Sorter instance
53
+ - Zero breaking changes for existing Ruby code
54
+
55
+ ### Changed
56
+
57
+ #### API Design Philosophy
58
+ - **Before**: `Sortsmith::Sorter.new(collection).by_key(:name).case_insensitive.desc.sort`
59
+ - **After**: `collection.sort_by.dig(:name).insensitive.desc.sort`
60
+
61
+ #### Improved Ergonomics
62
+ - No more explicit `Sorter.new()` instantiation required
63
+ - Tab-completable method discovery
64
+ - Natural language flow in method chains
65
+ - Unified `dig` method replaces separate `by_key`/`by_method` methods
66
+
67
+ #### Enhanced Ruby Version Support
68
+ - **Restored Ruby 3.0 and 3.1 compatibility** - Previously removed in v0.2.0, now supported again
69
+ - Full compatibility matrix: Ruby 3.0.7, 3.1.7, 3.2.8, 3.3.8, 3.4.4
70
+ - Expanded from Ruby 3.2+ requirement back to Ruby 3.0+ for broader accessibility
71
+
72
+ ### Removed
73
+
74
+ #### Legacy API (Breaking Changes)
75
+ - `by_key` method (replaced by `dig`)
76
+ - `by_method`/`by_attribute` methods (replaced by `dig`)
77
+ - `case_insensitive` method (replaced by `insensitive`/`downcase`)
78
+ - `Sortsmith::Step` class (internal restructure)
79
+ - Manual `Sorter.new()` instantiation requirement
80
+ - `rbs` and `steep` type checking
81
+
82
+ ### Migration Guide
83
+
84
+ The new API is more concise and intuitive, but requires updating existing code:
85
+
86
+ ```ruby
87
+ # v0.2.x (OLD)
88
+ Sortsmith::Sorter.new(users).by_key(:name).case_insensitive.desc.sort
89
+
90
+ # v0.9.x (NEW)
91
+ users.sort_by.dig(:name).insensitive.desc.sort
92
+
93
+ # v0.2.x (OLD)
94
+ Sortsmith::Sorter.new(objects).by_method(:calculate_score).sort
95
+
96
+ # v0.9.x (NEW)
97
+ objects.sort_by.dig(:calculate_score).sort
98
+ ```
99
+
100
+ ### Technical Improvements
101
+
102
+ - Complete test suite rewrite with comprehensive edge case coverage
103
+ - Enhanced error handling for mixed data types
104
+ - Improved performance through reduced object allocations
105
+ - Cleaner internal architecture with separation of concerns
106
+ - Better documentation with extensive API examples
107
+
16
108
  ## [0.2.0] - 12025-02-17
17
109
 
18
110
  ### Added
@@ -42,7 +134,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
134
  - Type checking with Steep/RBS
43
135
  - GitHub Actions workflow for automated testing and type checking
44
136
 
45
- [unreleased]: https://github.com/itsthedevman/sortsmith/compare/v0.2.0...HEAD
137
+ [unreleased]: https://github.com/itsthedevman/sortsmith/compare/v0.9.0...HEAD
138
+ [0.9.0]: https://github.com/itsthedevman/sortsmith/compare/v0.2.0...v0.9.0
46
139
  [0.2.0]: https://github.com/itsthedevman/sortsmith/compare/v0.1.1...v0.2.0
47
140
  [0.1.1]: https://github.com/itsthedevman/sortsmith/compare/v0.1.0...v0.1.1
48
141
  [0.1.0]: https://github.com/itsthedevman/sortsmith/compare/ac357965a1bc641d187333a5b032c5a423020ae9...v0.1.0
data/README.md CHANGED
@@ -1,17 +1,66 @@
1
1
  # Sortsmith
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/sortsmith.svg)](https://badge.fury.io/rb/sortsmith)
4
+ ![Ruby Version](https://img.shields.io/badge/ruby-3.0+-ruby)
4
5
  [![Tests](https://github.com/itsthedevman/sortsmith/actions/workflows/main.yml/badge.svg)](https://github.com/itsthedevman/sortsmith/actions/workflows/main.yml)
5
- ![Ruby Version](https://img.shields.io/badge/ruby-3.3.6-ruby)
6
6
 
7
- Sortsmith is a flexible sorting library for Ruby that makes complex sorting operations simple and composable. It makes handling common sorting patterns like case-insensitive sorting of hashes and objects easy, while remaining extensible for custom sorting needs.
7
+ **Sortsmith** makes sorting Ruby collections feel natural and fun. Instead of wrestling with verbose blocks or complex comparisons, just chain together what you want in plain English.
8
8
 
9
- ## Features
9
+ ```ruby
10
+ # Instead of this...
11
+ users.sort_by { |user| user[:name].downcase }.reverse
12
+
13
+ # Write this!
14
+ users.sort_by.dig(:name).downcase.reverse
15
+ ```
16
+
17
+ Sortsmith extends Ruby's built-in `sort_by` method with a fluent, chainable API that handles the messy details so you can focus on expressing *what* you want sorted, not *how* to sort it.
18
+
19
+ ## Table of Contents
20
+
21
+ - [Why Sortsmith?](#why-sortsmith)
22
+ - [Installation](#installation)
23
+ - [Quick Start](#quick-start)
24
+ - [Core Concepts](#core-concepts)
25
+ - [Usage Examples](#usage-examples)
26
+ - [Array Sorting](#array-sorting)
27
+ - [Hash Collections](#hash-collections)
28
+ - [Object Collections](#object-collections)
29
+ - [Mixed Key Types](#mixed-key-types)
30
+ - [API Reference](#api-reference)
31
+ - [Migration from v0.2.x](#migration-from-v02x)
32
+ - [Development](#development)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
35
+
36
+ ## Why Sortsmith?
37
+
38
+ Ruby's `sort_by` is powerful, but real-world sorting often gets messy:
39
+
40
+ ```ruby
41
+ # Sorting users by name, case-insensitive, descending
42
+ users.sort_by { |u| u[:name].to_s.downcase }.reverse
10
43
 
11
- - Builder pattern for chainable sorting configuration
12
- - Built-in support for case-insensitive sorting
13
- - Hash key and method/attribute sorting
14
- - Flexible transformation pipeline
44
+ # What if some names are nil?
45
+ users.sort_by { |u| (u[:name] || "").downcase }.reverse
46
+
47
+ # What about mixed string/symbol keys?
48
+ users.sort_by { |u| (u[:name] || u["name"] || "").downcase }.reverse
49
+ ```
50
+
51
+ Sortsmith handles all the edge cases and gives you a clean, readable API:
52
+
53
+ ```ruby
54
+ users.sort_by.dig(:name, indifferent: true).insensitive.desc.sort
55
+ ```
56
+
57
+ **Features:**
58
+ - **Fluent chaining** - Reads like English
59
+ - **Universal extraction** - Works with hashes, objects, and nested data
60
+ - **Indifferent key access** - Handles mixed symbol/string keys automatically
61
+ - **Nil-safe** - Graceful handling of missing data
62
+ - **Minimal overhead** - Extends existing Ruby methods without breaking compatibility
63
+ - **Tab-completable** - Discoverable API through your editor
15
64
 
16
65
  ## Installation
17
66
 
@@ -33,89 +82,229 @@ Or install it yourself as:
33
82
  $ gem install sortsmith
34
83
  ```
35
84
 
36
- ## Usage
85
+ ## Quick Start
37
86
 
38
- ### Basic Sorting
87
+ Sortsmith extends Ruby's `sort_by` method. When called without a block, it returns a chainable sorter:
39
88
 
40
89
  ```ruby
41
- # Sort an array of Strings
42
- users = ["Bob", "Alice", "Carol"]
90
+ require "sortsmith"
43
91
 
44
- sorted_users = Sortsmith::Sorter.new(users).sort
92
+ # Basic string sorting
93
+ names = ["charlie", "Alice", "bob"]
94
+ names.sort_by.insensitive.sort
95
+ # => ["Alice", "bob", "charlie"]
45
96
 
46
- # Result: ["Alice", "Bob", "Carol"]
97
+ # Hash sorting
98
+ users = [
99
+ { name: "Charlie", age: 25 },
100
+ { name: "Alice", age: 30 },
101
+ { name: "Bob", age: 20 }
102
+ ]
103
+
104
+ users.sort_by.dig(:name).sort
105
+ # => [{ name: "Alice", age: 30 }, { name: "Bob", age: 20 }, { name: "Charlie", age: 25 }]
106
+
107
+ # The original sort_by with blocks still works exactly the same!
108
+ users.sort_by { |u| u[:age] }
109
+ # => [{ name: "Bob", age: 20 }, { name: "Charlie", age: 25 }, { name: "Alice", age: 30 }]
110
+ ```
111
+
112
+ ## Core Concepts
113
+
114
+ Sortsmith uses a simple pipeline concept where each step is **optional** except for the terminator:
115
+
116
+ 1. **Extract** - Get the value to sort by (`dig`) - *optional*
117
+ 2. **Transform** - Modify the value for comparison (`downcase`, `upcase`) - *optional*
118
+ 3. **Order** - Choose sort direction (`asc`, `desc`) - *optional*
119
+ 4. **Execute** - Run the sort (`sort`, `sort!`, `reverse`) - **required**
120
+
121
+ ```ruby
122
+ collection.sort_by.dig(:field).downcase.desc.sort
123
+ # ↑ ↑ ↑ ↑ ↑
124
+ # | extract transform order execute
125
+ # chainable (opt) (opt) (opt) (required)
126
+ ```
127
+
128
+ **Minimal example:**
129
+ ```ruby
130
+ # This works! (though not particularly useful)
131
+ users.sort_by.sort # Same as users.sort
132
+
133
+ # More practical examples
134
+ users.sort_by.dig(:name).sort # Extract only
135
+ users.sort_by.downcase.sort # Transform only
136
+ users.sort_by.desc.sort # Order only
137
+ users.sort_by.dig(:name).desc.sort # Extract + order
47
138
  ```
48
139
 
49
- ### Hash Sorting
140
+ Each step builds on the previous ones, so you can mix and match based on what your data needs. The only requirement is ending with a terminator to actually execute the sort.
141
+
142
+ ## Usage Examples
143
+
144
+ ### Array Sorting
145
+
146
+ ```ruby
147
+ # Basic sorting
148
+ words = ["banana", "Apple", "cherry"]
149
+ words.sort_by.sort
150
+ # => ["Apple", "banana", "cherry"]
151
+
152
+ # Case-insensitive
153
+ words.sort_by.insensitive.sort
154
+ # => ["Apple", "banana", "cherry"]
155
+
156
+ # Descending order
157
+ words.sort_by.downcase.desc.sort
158
+ # => ["cherry", "banana", "Apple"]
159
+
160
+ # In-place mutation
161
+ words.sort_by.insensitive.sort!
162
+ # Modifies the original array
163
+ ```
164
+
165
+ ### Hash Collections
50
166
 
51
167
  ```ruby
52
- # Sort an array of hashes by a key
53
168
  users = [
54
- { name: "Carol", age: 35 },
55
- { name: "Bob", age: 25 },
56
- { name: "Alice", age: 30 }
169
+ { name: "Charlie", score: 85, team: "red" },
170
+ { name: "Alice", score: 92, team: "blue" },
171
+ { name: "bob", score: 78, team: "red" }
57
172
  ]
58
173
 
59
- sorted_users = Sortsmith::Sorter.new(users)
60
- .by_key(:name)
61
- .sort
174
+ # Sort by name (case-sensitive)
175
+ users.sort_by.dig(:name).sort
176
+ # => [{ name: "Alice" }, { name: "Charlie" }, { name: "bob" }]
177
+
178
+ # Sort by name (case-insensitive)
179
+ users.sort_by.dig(:name).insensitive.sort
180
+ # => [{ name: "Alice" }, { name: "bob" }, { name: "Charlie" }]
62
181
 
63
- # Result: [{ name: "Alice" }, { name: "Bob" }, { name: "Carol" }]
182
+ # Sort by score (descending)
183
+ users.sort_by.dig(:score).desc.sort
184
+ # => [{ score: 92 }, { score: 85 }, { score: 78 }]
185
+
186
+ # Multiple field extraction (nested digging)
187
+ data = [
188
+ { user: { profile: { name: "Charlie" } } },
189
+ { user: { profile: { name: "Alice" } } }
190
+ ]
191
+
192
+ data.sort_by.dig(:user, :profile, :name).sort
193
+ # => [{ user: { profile: { name: "Alice" } } }, ...]
64
194
  ```
65
195
 
66
- ### Object Sorting
196
+ ### Object Collections
67
197
 
68
198
  ```ruby
69
- # Sort objects by method/attribute
70
- class User < Data.define(:name)
71
- end
199
+ User = Struct.new(:name, :age, :city)
200
+
201
+ users = [
202
+ User.new("Charlie", 25, "NYC"),
203
+ User.new("Alice", 30, "LA"),
204
+ User.new("bob", 20, "Chicago")
205
+ ]
72
206
 
73
- users = [User.new(name: "Bob"), User.new(name: "Carol"), User.new(name: "Alice")]
207
+ # Sort by any method/attribute
208
+ users.sort_by.dig(:name).insensitive.sort
209
+ # => [User.new("Alice"), User.new("bob"), User.new("Charlie")]
74
210
 
75
- sorted_users = Sortsmith::Sorter.new(users)
76
- .by_method(:name)
77
- .sort
211
+ users.sort_by.dig(:age).reverse
212
+ # => [User.new("Alice", 30), User.new("Charlie", 25), User.new("bob", 20)]
78
213
 
79
- # Result: [#<data User name="Alice">, #<data User name="Bob">, #<data User name="Carol">]
214
+ # Works with any object that responds to the method
215
+ class Score
216
+ def calculate; rand(100); end
217
+ end
218
+
219
+ scores = [Score.new, Score.new, Score.new]
220
+ scores.sort_by.dig(:calculate).desc.sort
80
221
  ```
81
222
 
82
- ### Case Insensitive Sorting
223
+ ### Mixed Key Types
224
+
225
+ Real-world data often has inconsistent key types. Sortsmith handles this gracefully:
83
226
 
84
227
  ```ruby
85
- users = [
86
- {"name" => "bob"},
87
- {"name" => "Billy"},
88
- {"name" => "Alice"},
89
- {"name" => "carol"},
90
- {"name" => "Cassidy"},
91
- {"name" => "alex"}
228
+ mixed_users = [
229
+ { name: "Charlie" }, # symbol key
230
+ { "name" => "Alice" }, # string key
231
+ { :name => "Bob" }, # symbol key again
232
+ { "name" => "Diana" } # string key again
92
233
  ]
93
234
 
94
- # Order of methods does not matter
95
- # However, the hash's key type does
96
- sorted_users = Sortsmith::Sorter.new(users)
97
- .case_insensitive
98
- .by_key("name")
99
- .sort
235
+ # The indifferent option handles both key types
236
+ mixed_users.sort_by.dig(:name, indifferent: true).sort
237
+ # => [{ "name" => "Alice" }, { :name => "Bob" }, { name: "Charlie" }, { "name" => "Diana" }]
100
238
 
101
- # Result: [{"name"=>"Alice"}, {"name"=>"alex"}, {"name"=>"Billy"}, {"name"=>"bob"}, {"name"=>"Cassidy"}, {"name"=>"carol"}]
239
+ # Without indifferent access, you'd get sorting failures or unexpected results
102
240
  ```
103
241
 
104
- ### Reverse Sorting
242
+ **Performance Note**: Indifferent key access adds modest overhead (~2x slower depending on the machine) but operates in microseconds and is typically worth the convenience for mixed-key scenarios.
105
243
 
106
244
  ```ruby
107
- # Sort in descending order
108
- sorted_desc = Sortsmith::Sorter.new(array)
109
- .by_attribute(:name)
110
- .desc
111
- .sort
245
+ # Rails users can also normalize keys upfront for better performance
246
+ mixed_users.map(&:symbolize_keys).sort_by.dig(:name).sort
247
+
248
+ # But indifferent access is handy when you can't control the data source
249
+ api_response.sort_by.dig(:name, indifferent: true).sort
112
250
  ```
113
251
 
252
+ ## API Reference
253
+
254
+ ### Extractors
255
+
256
+ - **`dig(*identifiers, indifferent: false)`** - Extract values from hashes, objects, or nested structures
257
+ - Works with hash keys, object methods, and nested paths
258
+ - `indifferent: true` normalizes hash keys for consistent lookup across string/symbol keys
259
+
260
+ ### Modifiers
261
+
262
+ - **`downcase`** - Convert extracted strings to lowercase for comparison
263
+ - **`upcase`** - Convert extracted strings to uppercase for comparison
264
+ - **`insensitive`** - Alias for `downcase` (semantic clarity)
265
+
266
+ ### Ordering
267
+
268
+ - **`asc`** - Sort in ascending order (default)
269
+ - **`desc`** - Sort in descending order
270
+
271
+ ### Terminators
272
+
273
+ - **`sort`** - Execute sort and return new array
274
+ - **`sort!`** - Execute sort and mutate original array
275
+ - **`to_a`** - Alias for `sort`
276
+ - **`reverse`** - Shorthand for `desc.sort`
277
+ - **`reverse!`** - Shorthand for `desc.sort!`
278
+
279
+ ## Migration from v0.2.x
280
+
281
+ The v0.3.x API is more concise and intuitive:
282
+
283
+ ```ruby
284
+ # v0.2.x (OLD - no longer works)
285
+ Sortsmith::Sorter.new(users).by_key(:name).case_insensitive.desc.sort
286
+
287
+ # v0.3.x (NEW)
288
+ users.sort_by.dig(:name).insensitive.desc.sort
289
+
290
+ # v0.2.x (OLD)
291
+ Sortsmith::Sorter.new(objects).by_method(:calculate_score).sort
292
+
293
+ # v0.3.x (NEW)
294
+ objects.sort_by.dig(:calculate_score).sort
295
+ ```
296
+
297
+ **Key Changes:**
298
+ - `by_key` → `dig`
299
+ - `by_method`/`by_attribute` → `dig`
300
+ - `case_insensitive` → `insensitive` or `downcase`
301
+ - No more manual `Sorter.new()` - just call `sort_by` without a block
302
+
114
303
  ## Development
115
304
 
116
305
  ### Prerequisites
117
306
 
118
- - Ruby 3.3.6
307
+ - Ruby 3.0+
119
308
  - Nix with Direnv (optional, but recommended)
120
309
 
121
310
  ### Setting Up the Development Environment
@@ -136,12 +325,6 @@ bundle install
136
325
  bundle exec rake test
137
326
  ```
138
327
 
139
- ### Type Checking
140
-
141
- ```bash
142
- bundle exec steep check
143
- ```
144
-
145
328
  ### Code Style
146
329
 
147
330
  This project uses StandardRB. To check your code:
@@ -168,7 +351,7 @@ Please note that this project is released with a [Contributor Code of Conduct](C
168
351
 
169
352
  ## License
170
353
 
171
- The gem is available as open source under the terms of the [MIT License](LICENSE.md).
354
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
172
355
 
173
356
  ## Changelog
174
357
 
@@ -177,3 +360,9 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes.
177
360
  ## Credits
178
361
 
179
362
  - Author: Bryan "itsthedevman"
363
+
364
+ ## Looking for a Software Engineer?
365
+
366
+ I'm currently looking for opportunities where I can tackle meaningful problems and help build reliable software while mentoring the next generation of developers. If you're looking for a senior engineer with full-stack Rails expertise and a passion for clean, maintainable code, let's talk!
367
+
368
+ [bryan@itsthedevman.com](mailto:bryan@itsthedevman.com)
data/flake.nix CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
- description = "Ruby 3.4 development environment";
2
+ description = "Ruby 3.2 development environment";
3
3
 
4
4
  inputs = {
5
5
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
6
  flake-utils.url = "github:numtide/flake-utils";
7
7
  };
8
8
 
9
- outputs = { self, nixpkgs, flake-utils }:
10
- flake-utils.lib.eachDefaultSystem (system:
9
+ outputs =
10
+ {
11
+ self,
12
+ nixpkgs,
13
+ flake-utils,
14
+ }:
15
+ flake-utils.lib.eachDefaultSystem (
16
+ system:
11
17
  let
12
18
  pkgs = nixpkgs.legacyPackages.${system};
13
19
  in
14
20
  {
15
21
  devShells.default = pkgs.mkShell {
16
22
  buildInputs = with pkgs; [
17
- (ruby_3_4.override {
18
- jemallocSupport = true;
23
+ (ruby_3_2.override {
24
+ jemallocSupport = false;
19
25
  docSupport = false;
20
26
  })
21
27
 
@@ -0,0 +1,75 @@
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 chainable API.
37
+ #
38
+ # When called with a block, behaves exactly like Ruby's original `sort_by`.
39
+ # When called without a block, returns a {Sortsmith::Sorter} instance that
40
+ # provides a chainable interface for complex sorting operations.
41
+ #
42
+ # This dual behavior ensures complete backward compatibility while unlocking
43
+ # powerful new sorting capabilities.
44
+ #
45
+ # @param block [Proc, nil] Optional block for traditional sort_by behavior
46
+ # @return [Array, Sortsmith::Sorter] Array when block given, Sorter when block nil
47
+ #
48
+ # @example Traditional usage (unchanged)
49
+ # users.sort_by { |user| user.name.downcase }
50
+ # # => sorted array
51
+ #
52
+ # @example Chainable usage (new)
53
+ # users.sort_by.dig(:name).downcase.desc.sort
54
+ # # => sorted array via method chaining
55
+ #
56
+ # @example Mixed key types with indifferent access
57
+ # mixed_data = [
58
+ # { name: "Bob" }, # symbol key
59
+ # { "name" => "Alice" } # string key
60
+ # ]
61
+ # mixed_data.sort_by.dig(:name, indifferent: true).sort
62
+ # # => handles both key types gracefully
63
+ #
64
+ # @see Sortsmith::Sorter#dig
65
+ # @see Sortsmith::Sorter#sort
66
+ # @see #og_sort_by Original sort_by behavior
67
+ #
68
+ # @since 0.1.0
69
+ #
70
+ def sort_by(&block)
71
+ return Sortsmith::Sorter.new(self) if block.nil?
72
+
73
+ og_sort_by(&block)
74
+ end
75
+ end
@@ -1,178 +1,317 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sortsmith
4
+ ##
5
+ # A chainable sorting interface that provides a fluent API for complex sorting operations.
6
+ #
7
+ # The Sorter class allows you to build sorting pipelines by chaining extractors,
8
+ # modifiers, and ordering methods before executing the sort with a terminator method.
9
+ #
10
+ # @example Basic usage
11
+ # users.sort_by.dig(:name).sort
12
+ # # => sorted array by name
13
+ #
14
+ # @example Complex chaining
15
+ # users.sort_by.dig(:name, indifferent: true).downcase.desc.sort
16
+ # # => sorted by name (case-insensitive, descending, with indifferent key access)
17
+ #
18
+ # @example Mixed key types
19
+ # mixed_data = [
20
+ # {name: "Bob"}, # symbol key
21
+ # {"name" => "Alice"} # string key
22
+ # ]
23
+ # mixed_data.sort_by.dig(:name, indifferent: true).sort
24
+ # # => handles both key types gracefully
25
+ #
4
26
  class Sorter
27
+ ##
28
+ # Initialize a new Sorter instance
5
29
  #
6
- # Creates a Sorter builder instance
30
+ # @param input [Array, Enumerable] The collection to be sorted
31
+ def initialize(input)
32
+ @input = input
33
+ @extractors = []
34
+ @modifiers = []
35
+ @ordering = []
36
+ end
37
+
38
+ ############################################################################
39
+ # Extractors
40
+ ############################################################################
41
+
42
+ ##
43
+ # Extract values from objects using hash keys or object methods
44
+ #
45
+ # Works with hashes, structs, and any object that responds to the given identifiers.
46
+ # Supports nested digging with multiple arguments.
47
+ #
48
+ # @param identifiers [Array<Symbol, String, Integer>] Keys, method names, or indices to extract
49
+ # @param indifferent [Boolean] When true, normalizes hash keys to symbols for consistent lookup
50
+ #
51
+ # @return [Sorter] Returns self for method chaining
52
+ #
53
+ # @example Hash extraction
54
+ # users.sort_by.dig(:name).sort
55
+ #
56
+ # @example Nested extraction
57
+ # users.sort_by.dig(:profile, :email).sort
7
58
  #
8
- # @param enumerable [Enumerable] The enumerable (Array, Hash) to sort
59
+ # @example Mixed key types
60
+ # users.sort_by.dig(:name, indifferent: true).sort
9
61
  #
10
- def initialize(enumerable)
11
- @enumerable = enumerable
12
- @pipeline = []
13
- @direction = :asc
62
+ # @example Object method calls
63
+ # objects.sort_by.dig(:calculate_score).sort
64
+ #
65
+ def dig(*identifiers, indifferent: false)
66
+ @extractors << {method: :dig, positional: identifiers, indifferent: indifferent}
67
+ self
14
68
  end
15
69
 
70
+ ############################################################################
71
+ # Modifiers
72
+ ############################################################################
73
+
74
+ ##
75
+ # Transform extracted values to lowercase for comparison
16
76
  #
17
- # Finalizes the Sorter instance and sorts the enumerable
77
+ # Only affects values that respond to #downcase (typically strings).
78
+ # Non-string values pass through unchanged.
18
79
  #
19
- # @return [Enumerable] The sorted enumerable
80
+ # @return [Sorter] Returns self for method chaining
20
81
  #
21
- def sort
22
- filter_steps = select_filter_steps
23
- transformation_steps = select_transformation_steps
24
-
25
- result =
26
- @enumerable.sort do |left, right|
27
- filter_steps.each do |step|
28
- left = step.perform(left)
29
- right = step.perform(right)
30
- end
31
-
32
- left_priority = type_priority(left)
33
- right_priority = type_priority(right)
34
-
35
- # Apply the transformation pipeline only for same-type comparisons
36
- if left_priority == right_priority
37
- left = apply_transformations(transformation_steps, left)
38
- right = apply_transformations(transformation_steps, right)
39
-
40
- left <=> right
41
- else
42
- left_priority <=> right_priority
43
- end
44
- end
45
-
46
- (@direction == :asc) ? result : result.reverse
82
+ # @example Case-insensitive string sorting
83
+ # names.sort_by.downcase.sort
84
+ #
85
+ # @example With hash extraction
86
+ # users.sort_by.dig(:name).downcase.sort
87
+ #
88
+ def downcase
89
+ @modifiers << {method: :downcase}
90
+ self
47
91
  end
48
92
 
93
+ ##
94
+ # Alias for #downcase - provides case-insensitive sorting
95
+ #
96
+ # @return [Sorter] Returns self for method chaining
97
+ #
98
+ alias_method :insensitive, :downcase
99
+
100
+ ##
101
+ # Transform extracted values to uppercase for comparison
49
102
  #
50
- # Adds a "filter" step to the sort pipeline.
51
- # Filter steps are used to get data from the current item being sorted
52
- # These are performed before transformation steps
103
+ # Only affects values that respond to #upcase (typically strings).
104
+ # Non-string values pass through unchanged.
53
105
  #
54
- # @param & [Proc] The block to execute
106
+ # @return [Sorter] Returns self for method chaining
55
107
  #
56
- # @return [Self] The sorter instance
108
+ # @example Uppercase sorting
109
+ # names.sort_by.upcase.sort
57
110
  #
58
- def add_filter(&)
59
- add_step(type: Step::FILTER, &)
111
+ def upcase
112
+ @modifiers << {method: :upcase}
113
+ self
60
114
  end
61
115
 
116
+ ############################################################################
117
+ # Ordering
118
+ ############################################################################
119
+
120
+ ##
121
+ # Sort in ascending order (default behavior)
62
122
  #
63
- # Adds a "transformation" step to the sort pipeline
64
- # Transformation steps are used to transform data.
65
- # These are performed after filter steps
66
- #
67
- # @param & [Proc] The block to execute
123
+ # This is typically unnecessary as ascending is the default,
124
+ # but can be useful for explicit clarity or resetting after desc.
68
125
  #
69
- # @return [Self] The sorter instance
126
+ # @return [Sorter] Returns self for method chaining
70
127
  #
71
- def add_transformation(&)
72
- add_step(type: Step::TRANSFORMATION, &)
128
+ def asc
129
+ @ordering << {method: :sort!}
130
+ self
73
131
  end
74
132
 
133
+ ##
134
+ # Sort in descending order
75
135
  #
76
- # Instructs the sorter to perform a fetch by key on the Hash being sorted
136
+ # Reverses the final sort order after all comparisons are complete.
77
137
  #
78
- # @param key [String, Symbol, Any] The hash key to fetch
138
+ # @return [Sorter] Returns self for method chaining
79
139
  #
80
- # @return [Self] The sorter instance
140
+ # @example Descending sort
141
+ # users.sort_by.dig(:age).desc.sort
81
142
  #
82
- def by_key(key)
83
- add_filter { |i| i&.fetch(key) }
143
+ def desc
144
+ @ordering << {method: :reverse!}
84
145
  self
85
146
  end
86
147
 
148
+ ############################################################################
149
+ # Terminators
150
+ ############################################################################
151
+
152
+ ##
153
+ # Execute the sort pipeline and return a new sorted array
87
154
  #
88
- # Instructs the sorter to perform a method call on the object being sorted
155
+ # Applies all chained extraction, transformation, and ordering steps
156
+ # to produce the final sorted result. The original collection is unchanged.
89
157
  #
90
- # @param method [String, Symbol] The method name to call
158
+ # @return [Array] A new array containing the sorted elements
91
159
  #
92
- # @return [Self] The sorter instance
160
+ # @example Basic termination
161
+ # sorted_users = users.sort_by.dig(:name).sort
93
162
  #
94
- def by_method(method)
95
- add_filter { |i| i&.public_send(method) }
163
+ def sort
164
+ # Apply all extraction and transformation steps during comparison
165
+ sorted = @input.sort do |item_a, item_b|
166
+ apply_steps(item_a, item_b)
167
+ end
168
+
169
+ # Apply any ordering transformations (like desc)
170
+ apply_ordering_steps(sorted)
96
171
  end
97
172
 
98
- alias_method :by_attribute, :by_method
173
+ ##
174
+ # Alias for #sort - returns a new sorted array
175
+ #
176
+ # @return [Array] A new array containing the sorted elements
177
+ #
178
+ # @see #sort
179
+ #
180
+ alias_method :to_a, :sort
99
181
 
182
+ ##
183
+ # Execute the sort pipeline and mutate the original array in place
184
+ #
185
+ # Same as #sort but modifies the original array instead of creating a new one.
186
+ # Returns the mutated array for chaining.
100
187
  #
101
- # Instructs the sorter to sort by a case insensitive value
102
- # This will prioritize capital letters first, followed by their lowercase counterparts
188
+ # @return [Array] The original array, now sorted
103
189
  #
104
- # @return [Self] The sorter instance
190
+ # @example In-place sorting
191
+ # users.sort_by.dig(:name).sort!
192
+ # # users array is now modified
105
193
  #
106
- def case_insensitive
107
- add_transformation do |item|
108
- case item
109
- when String
110
- item.chars.flat_map { |c| [c.downcase, c] }
111
- else
112
- item
113
- end
194
+ def sort!
195
+ # Sort the original array in place
196
+ @input.sort! do |item_a, item_b|
197
+ apply_steps(item_a, item_b)
114
198
  end
199
+
200
+ # Apply any ordering transformations
201
+ apply_ordering_steps(@input)
115
202
  end
116
203
 
204
+ ##
205
+ # Shorthand for adding desc and executing sort
117
206
  #
118
- # Controls which direction the array will be sorted
207
+ # Equivalent to calling .desc.sort but more concise.
119
208
  #
120
- # @return [Self] The sorter instance
209
+ # @return [Array] A new array sorted in descending order
121
210
  #
122
- def asc
123
- @direction = :asc
124
- self
211
+ # @example Reverse sorting
212
+ # users.sort_by.dig(:name).reverse
213
+ #
214
+ def reverse
215
+ desc.sort
125
216
  end
126
217
 
127
- alias_method :forward, :asc
128
-
218
+ ##
219
+ # Shorthand for adding desc and executing sort!
129
220
  #
130
- # Controls which direction the array will be sorted
221
+ # Equivalent to calling .desc.sort! but more concise.
131
222
  #
132
- # @return [Self] The sorter instance
223
+ # @return [Array] The original array, sorted in descending order
133
224
  #
134
- def desc
135
- @direction = :desc
136
- self
225
+ def reverse!
226
+ desc.sort!
137
227
  end
138
228
 
139
- alias_method :reverse, :desc
140
-
141
229
  private
142
230
 
143
- def add_step(type:, &block)
144
- @pipeline << Step.new(type:, block:)
145
- self
146
- end
231
+ ##
232
+ # Apply the complete pipeline of steps to two items for comparison
233
+ #
234
+ # Iterates through all extraction and transformation steps,
235
+ # applying each one to both items in sequence.
236
+ #
237
+ # @param item_a [Object] First item to compare
238
+ # @param item_b [Object] Second item to compare
239
+ # @return [Integer] Comparison result (-1, 0, 1)
240
+ #
241
+ def apply_steps(item_a, item_b)
242
+ @extractors.each do |step|
243
+ item_a, item_b = apply_step(step, item_a, item_b)
244
+ end
147
245
 
148
- def select_filter_steps
149
- @pipeline.select { |s| s.type == Step::FILTER }
150
- end
246
+ @modifiers.each do |step|
247
+ item_a, item_b = apply_step(step, item_a, item_b)
248
+ end
151
249
 
152
- def select_transformation_steps
153
- @pipeline.select { |s| s.type == Step::TRANSFORMATION }
250
+ # Final comparison using Ruby's spaceship operator
251
+ item_a <=> item_b
154
252
  end
155
253
 
156
- def type_priority(value)
157
- case value
158
- when NilClass then 0
159
- when Numeric then 1
160
- when String then 2
161
- when Array then 3
162
- when Hash then 4
163
- else
164
- 5
254
+ ##
255
+ # Apply ordering transformations to the sorted array
256
+ #
257
+ # Executes any ordering steps (like desc) that affect the final
258
+ # arrangement of the sorted results.
259
+ #
260
+ # @param sorted [Array] The array to apply ordering to
261
+ # @return [Array] The array with ordering applied
262
+ #
263
+ def apply_ordering_steps(sorted)
264
+ @ordering.each do |step|
265
+ sorted.public_send(step[:method])
165
266
  end
267
+
268
+ sorted
166
269
  end
167
270
 
168
- def apply_transformations(steps, value)
169
- result = value
271
+ ##
272
+ # Apply a single step to both items in the comparison
273
+ #
274
+ # Handles different step types and safely manages method calls,
275
+ # falling back to string conversion for non-responsive objects.
276
+ #
277
+ # @param step [Hash] Step configuration containing method and arguments
278
+ # @param item_a [Object] First item to transform
279
+ # @param item_b [Object] Second item to transform
280
+ # @return [Array<Object, Object>] Transformed items
281
+ #
282
+ def apply_step(step, item_a, item_b)
283
+ method = step[:method]
284
+ positional = step[:positional] || []
285
+ indifferent = step[:indifferent] || false
170
286
 
171
- steps.each do |step|
172
- result = step.perform(result)
287
+ # For indifferent key access, normalize all positional args to symbols
288
+ if indifferent
289
+ positional = positional.map { |i| i.respond_to?(:to_sym) ? i.to_sym : i }
173
290
  end
174
291
 
175
- result
292
+ item_a = extract_value_from(item_a, method, positional, indifferent)
293
+ item_b = extract_value_from(item_b, method, positional, indifferent)
294
+
295
+ [item_a, item_b]
296
+ end
297
+
298
+ ##
299
+ # Extracts a value from an object using the specified method and parameters.
300
+ #
301
+ # @param item [Object] the object to extract a value from
302
+ # @param method [Symbol, String] the method name to call on the object
303
+ # @param positional [Array] positional arguments to pass to the method
304
+ # @param indifferent [Boolean] whether to normalize hash keys to symbols for indifferent access
305
+ #
306
+ # @return [Object] the extracted value, or the string representation of the item
307
+ #
308
+ def extract_value_from(item, method, positional, indifferent)
309
+ return item.to_s unless item.respond_to?(method)
310
+
311
+ # For hash objects with indifferent access, normalize keys to symbols
312
+ item = item.transform_keys(&:to_sym) if indifferent
313
+
314
+ item.public_send(method, *positional)
176
315
  end
177
316
  end
178
317
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sortsmith
4
- VERSION = "0.2.0"
4
+ #
5
+ # The current version of the Sortsmith gem
6
+ #
7
+ VERSION = "0.9.0"
5
8
  end
data/lib/sortsmith.rb CHANGED
@@ -1,9 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "sortsmith/sorter"
4
- require_relative "sortsmith/step"
5
3
  require_relative "sortsmith/version"
4
+ require_relative "sortsmith/core_ext/enumerable"
5
+ require_relative "sortsmith/sorter"
6
6
 
7
+ ##
8
+ # Sortsmith provides a flexible, chainable API for sorting Ruby collections.
9
+ #
10
+ # The gem extends Ruby's built-in {Enumerable} module to provide an intuitive
11
+ # sorting interface that reads like natural language and supports complex
12
+ # sorting operations through method chaining.
13
+ #
14
+ # @example Basic usage
15
+ # users = ["Charlie", "alice", "Bob"]
16
+ # users.sort_by.downcase.sort
17
+ # # => ["alice", "Bob", "Charlie"]
18
+ #
19
+ # @example Hash sorting
20
+ # users = [
21
+ # { name: "Charlie", age: 25 },
22
+ # { name: "alice", age: 30 },
23
+ # { name: "Bob", age: 20 }
24
+ # ]
25
+ # users.sort_by.dig(:name).insensitive.sort
26
+ # # => sorted by name, case-insensitive
27
+ #
28
+ # @example Complex chaining
29
+ # users.sort_by.dig(:name, indifferent: true).downcase.desc.sort
30
+ # # => Extract name (works with both string/symbol keys), downcase, descending
31
+ #
32
+ # @see Sortsmith::Sorter The main sorting interface
33
+ # @see Enumerable#sort_by The extended sort_by method
34
+ #
35
+ # @author Bryan "itsthedevman"
36
+ # @since 0.1.0
37
+ #
7
38
  module Sortsmith
39
+ ##
40
+ # Base error class for all Sortsmith-related exceptions.
41
+ #
42
+ # This provides a namespace for any custom errors that may be raised
43
+ # during sorting operations, making it easier to rescue Sortsmith-specific
44
+ # issues without catching unrelated StandardError instances.
45
+ #
46
+ # @example Rescuing Sortsmith errors
47
+ # begin
48
+ # complex_sort_operation
49
+ # rescue Sortsmith::Error => e
50
+ # handle_sorting_error(e)
51
+ # end
52
+ #
53
+ # @since 0.1.0
54
+ #
8
55
  class Error < StandardError; end
9
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sortsmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-17 00:00:00.000000000 Z
11
+ date: 2025-07-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Sortsmith provides a flexible, chainable API for sorting Ruby collections.
14
14
  It supports sorting by object keys, methods, case sensitivity, and custom transformations.
@@ -25,17 +25,12 @@ files:
25
25
  - LICENSE.txt
26
26
  - README.md
27
27
  - Rakefile
28
- - Steepfile
29
28
  - flake.lock
30
29
  - flake.nix
31
30
  - lib/sortsmith.rb
31
+ - lib/sortsmith/core_ext/enumerable.rb
32
32
  - lib/sortsmith/sorter.rb
33
- - lib/sortsmith/step.rb
34
33
  - lib/sortsmith/version.rb
35
- - sig/sortsmith.rbs
36
- - sig/sortsmith/sorter.rbs
37
- - sig/sortsmith/step.rbs
38
- - sig/sortsmith/version.rbs
39
34
  homepage: https://github.com/itsthedevman/sortsmith
40
35
  licenses:
41
36
  - MIT
@@ -53,7 +48,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
48
  requirements:
54
49
  - - ">="
55
50
  - !ruby/object:Gem::Version
56
- version: '3.2'
51
+ version: '3.0'
57
52
  required_rubygems_version: !ruby/object:Gem::Requirement
58
53
  requirements:
59
54
  - - ">="
data/Steepfile DELETED
@@ -1,8 +0,0 @@
1
- # D = Steep::Diagnostic
2
-
3
- target :lib do
4
- signature "sig"
5
- ignore_signature "sig/test"
6
-
7
- check "lib"
8
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sortsmith
4
- #
5
- # Represents a step in the sorting process
6
- #
7
- class Step < Data.define(:type, :block)
8
- TYPES = [
9
- TRANSFORMATION = :transformation,
10
- FILTER = :filter
11
- ].freeze
12
-
13
- def perform(item)
14
- block.call(item)
15
- end
16
- end
17
- end
@@ -1,107 +0,0 @@
1
- module Sortsmith
2
- interface _Sortable
3
- def to_s: () -> String
4
- def <=>: (self) -> Integer
5
- end
6
-
7
- class Sorter[Elem < _Sortable]
8
- @enumerable: Enumerable[Elem]
9
-
10
- @pipeline: Array[Step]
11
-
12
- @direction: Symbol
13
-
14
- #
15
- # Creates a Sorter builder instance
16
- #
17
- # @param enumerable [Enumerable] The enumerable (Array, Hash) to sort
18
- #
19
- def initialize: (Enumerable[Elem] enumerable) -> void
20
-
21
- #
22
- # Finalizes the Sorter instance and sorts the enumerable
23
- #
24
- # @return [Enumerable] The sorted enumerable
25
- #
26
- def sort: () -> Enumerable[Elem]
27
-
28
- #
29
- # Adds a "filter" step to the sort pipeline.
30
- # Filter steps are used to get data from the current item being sorted
31
- # These are performed before transformation steps
32
- #
33
- # @param & [Proc] The block to execute
34
- #
35
- # @return [Self] The sorter instance
36
- #
37
- def add_filter: () { (untyped) -> untyped } -> self
38
-
39
- #
40
- # Adds a "transformation" step to the sort pipeline
41
- # Transformation steps are used to transform data.
42
- # These are performed after filter steps
43
- #
44
- # @param & [Proc] The block to execute
45
- #
46
- # @return [Self] The sorter instance
47
- #
48
- def add_transformation: () { (untyped) -> untyped } -> self
49
-
50
- #
51
- # Instructs the sorter to perform a fetch by key on the Hash being sorted
52
- #
53
- # @param key [String, Symbol, Any] The hash key to fetch
54
- #
55
- # @return [Self] The sorter instance
56
- #
57
- def by_key: ((String | Symbol | untyped) key) -> self
58
-
59
- #
60
- # Instructs the sorter to perform a method call on the object being sorted
61
- #
62
- # @param method [String, Symbol] The method name to call
63
- #
64
- # @return [Self] The sorter instance
65
- #
66
- def by_method: ((String | Symbol) method) -> self
67
-
68
- alias by_attribute by_method
69
-
70
- #
71
- # Instructs the sorter to sort by a case insensitive value
72
- #
73
- # @return [Self] The sorter instance
74
- #
75
- def case_insensitive: () -> self
76
-
77
- #
78
- # Controls which direction the array will be sorted
79
- #
80
- # @return [Self] The sorter instance
81
- #
82
- def asc: () -> self
83
-
84
- alias forward asc
85
-
86
- #
87
- # Controls which direction the array will be sorted
88
- #
89
- # @return [Self] The sorter instance
90
- #
91
- def desc: () -> self
92
-
93
- alias reverse desc
94
-
95
- private
96
-
97
- def add_step: (type: Symbol) { (untyped) -> untyped } -> self
98
-
99
- def select_filter_steps: () -> Array[Step]
100
-
101
- def select_transformation_steps: () -> Array[Step]
102
-
103
- def type_priority: (untyped value) -> Integer
104
-
105
- def apply_transformations: (Array[Step] steps, untyped value) -> untyped
106
- end
107
- end
@@ -1,28 +0,0 @@
1
- module Sortsmith
2
- #
3
- # Represents a step in the sorting process
4
- #
5
- class Step < ::Data
6
- TYPES: ::Array[Symbol]
7
-
8
- FILTER: Symbol
9
-
10
- TRANSFORMATION: Symbol
11
-
12
- attr_reader type: Symbol
13
-
14
- attr_reader block: ^(untyped) -> untyped
15
-
16
- def self.new: (Symbol type, untyped block) -> instance
17
- | (type: Symbol, block: untyped) -> instance
18
-
19
- def self.[]: (Symbol type, untyped block) -> instance
20
- | (type: Symbol, block: untyped) -> instance
21
-
22
- def self.members: () -> [ :type, :block ]
23
-
24
- def members: () -> [ :type, :block ]
25
-
26
- def perform: (untyped item) -> untyped
27
- end
28
- end
@@ -1,3 +0,0 @@
1
- module Sortsmith
2
- VERSION: String
3
- end
data/sig/sortsmith.rbs DELETED
@@ -1,4 +0,0 @@
1
- module Sortsmith
2
- class Error < StandardError
3
- end
4
- end