equalizer 0.0.11 → 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 +5 -5
- data/.mutant.yml +24 -0
- data/.rubocop.yml +28 -0
- data/.yardopts +4 -0
- data/.yardstick.yml +32 -0
- data/CHANGELOG.md +148 -0
- data/LICENSE +18 -18
- data/README.md +300 -58
- data/Rakefile +53 -4
- data/Steepfile +10 -0
- data/lib/equalizer.rb +152 -91
- data/sig/equalizer.rbs +64 -0
- metadata +30 -63
- data/.gitignore +0 -39
- data/.rspec +0 -6
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -21
- data/Gemfile +0 -9
- data/config/devtools.yml +0 -2
- data/config/flay.yml +0 -4
- data/config/flog.yml +0 -3
- data/config/mutant.yml +0 -2
- data/config/reek.yml +0 -105
- data/config/rubocop.yml +0 -115
- data/config/yardstick.yml +0 -34
- data/equalizer.gemspec +0 -23
- data/lib/equalizer/version.rb +0 -6
- data/spec/spec_helper.rb +0 -31
- data/spec/support/config_alias.rb +0 -5
- data/spec/unit/equalizer/included_spec.rb +0 -50
- data/spec/unit/equalizer/methods/eql_predicate_spec.rb +0 -65
- data/spec/unit/equalizer/methods/equality_operator_spec.rb +0 -128
- data/spec/unit/equalizer/universal_spec.rb +0 -157
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 152edf98048a51f0c55c1f028dc3b4a6fe6322910eb397a3f9d7a9f0e37c6b1b
|
|
4
|
+
data.tar.gz: 42ba9d636346bf0fdbfe8bdf216b1a56c5f25847c3afdf91afbaea3740e97b1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c599000f12a99e99fcf8f55a64dbf6075032c5e74ab3af5ab02dc0bc2152cfb3b7148c81ea43cd3850250eb4e4f7b3e2ccd32fc61c66e8faf2f52da6bac183a
|
|
7
|
+
data.tar.gz: 3a5919fb70d6b203eb574913abd78d65e943d2cfa7a5ea4d1582cfb87a99bca4876effc233f477b804d561338236050de18648ea65b10a369a5cdbe558f8eecb
|
data/.mutant.yml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
usage: opensource
|
|
2
|
+
integration:
|
|
3
|
+
name: minitest
|
|
4
|
+
includes:
|
|
5
|
+
- lib
|
|
6
|
+
- test
|
|
7
|
+
requires:
|
|
8
|
+
- equalizer
|
|
9
|
+
- test_helper
|
|
10
|
+
- equalizer_test
|
|
11
|
+
matcher:
|
|
12
|
+
subjects:
|
|
13
|
+
- "Equalizer*"
|
|
14
|
+
mutation:
|
|
15
|
+
operators: full
|
|
16
|
+
timeout: 10.0
|
|
17
|
+
ignore_patterns:
|
|
18
|
+
# module_eval's __LINE__ + 1 argument only affects debugging metadata
|
|
19
|
+
- send{selector=module_eval}
|
|
20
|
+
coverage_criteria:
|
|
21
|
+
test_result: true
|
|
22
|
+
process_abort: true
|
|
23
|
+
environment_variables:
|
|
24
|
+
MUTANT: "true"
|
data/.rubocop.yml
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
inherit_gem:
|
|
2
|
+
standard: config/base.yml
|
|
3
|
+
standard-performance: config/base.yml
|
|
4
|
+
|
|
5
|
+
plugins:
|
|
6
|
+
- rubocop-minitest
|
|
7
|
+
- rubocop-performance
|
|
8
|
+
- rubocop-rake
|
|
9
|
+
|
|
10
|
+
AllCops:
|
|
11
|
+
TargetRubyVersion: 3.3
|
|
12
|
+
NewCops: enable
|
|
13
|
+
|
|
14
|
+
Layout/LineLength:
|
|
15
|
+
Max: 80
|
|
16
|
+
|
|
17
|
+
Style/StringLiterals:
|
|
18
|
+
EnforcedStyle: double_quotes
|
|
19
|
+
|
|
20
|
+
Style/StringLiteralsInInterpolation:
|
|
21
|
+
EnforcedStyle: double_quotes
|
|
22
|
+
|
|
23
|
+
Style/Documentation:
|
|
24
|
+
Enabled: true
|
|
25
|
+
|
|
26
|
+
Naming/MethodParameterName:
|
|
27
|
+
Exclude:
|
|
28
|
+
- "test/**/*"
|
data/.yardopts
ADDED
data/.yardstick.yml
CHANGED
|
@@ -1,2 +1,34 @@
|
|
|
1
1
|
---
|
|
2
2
|
threshold: 100
|
|
3
|
+
require_exact_threshold: false
|
|
4
|
+
rules:
|
|
5
|
+
ApiTag::Presence:
|
|
6
|
+
enabled: true
|
|
7
|
+
exclude: []
|
|
8
|
+
ApiTag::Inclusion:
|
|
9
|
+
enabled: true
|
|
10
|
+
exclude: []
|
|
11
|
+
ApiTag::ProtectedMethod:
|
|
12
|
+
enabled: true
|
|
13
|
+
exclude: []
|
|
14
|
+
ApiTag::PrivateMethod:
|
|
15
|
+
enabled: true
|
|
16
|
+
exclude: []
|
|
17
|
+
ExampleTag:
|
|
18
|
+
enabled: false
|
|
19
|
+
exclude: []
|
|
20
|
+
ReturnTag:
|
|
21
|
+
enabled: true
|
|
22
|
+
exclude: []
|
|
23
|
+
Summary::Presence:
|
|
24
|
+
enabled: true
|
|
25
|
+
exclude: []
|
|
26
|
+
Summary::Length:
|
|
27
|
+
enabled: true
|
|
28
|
+
exclude: []
|
|
29
|
+
Summary::Delimiter:
|
|
30
|
+
enabled: true
|
|
31
|
+
exclude: []
|
|
32
|
+
Summary::SingleLine:
|
|
33
|
+
enabled: true
|
|
34
|
+
exclude: []
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-03-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Pattern matching support via `deconstruct` and `deconstruct_keys`
|
|
13
|
+
- Pretty print support via `pretty_print` and `pretty_print_instance_variables`
|
|
14
|
+
- Input validation requiring at least one Symbol attribute
|
|
15
|
+
- RBS type signatures
|
|
16
|
+
- Steep type checking
|
|
17
|
+
- Descriptive module names in ancestor chain (e.g., `Equalizer(x, y)`)
|
|
18
|
+
- Sigstore gem signing on release
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Rewritten from a `Module` subclass to a plain `module` with `Equalizer.new`
|
|
23
|
+
returning anonymous modules
|
|
24
|
+
- Minimum Ruby version raised to 3.3
|
|
25
|
+
- `hash` method now uses `[self.class, *deconstruct].hash` for improved clarity
|
|
26
|
+
- `inspect` output format uses `Object#to_s` base with `@attr=value` pairs
|
|
27
|
+
- Uses `public_send` instead of `__send__` for attribute access
|
|
28
|
+
- Switched test framework from RSpec to Minitest
|
|
29
|
+
- Replaced `Equalizer::Methods` module with `Equalizer::InstanceMethods`
|
|
30
|
+
- Inlined `VERSION` constant (removed separate `version.rb` file)
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
|
|
34
|
+
- `coerce` support in `==` method
|
|
35
|
+
- `cmp?` private method (replaced with direct attribute comparisons)
|
|
36
|
+
- `Equalizer::Methods` module (replaced by `Equalizer::InstanceMethods`)
|
|
37
|
+
- devtools dependency
|
|
38
|
+
|
|
39
|
+
## [0.0.11] - 2015-03-23
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Kill all surviving mutations in Equalizer
|
|
44
|
+
- Added mbj/devtools gem dependency
|
|
45
|
+
- Added bundler caching and container-based CI builds
|
|
46
|
+
- Fixed rspec deprecated API usage
|
|
47
|
+
|
|
48
|
+
## [0.0.10] - 2015-03-20
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Support for Ruby 2.1 and 2.2
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Fixed use of coerce method
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- Updated RSpec dependency to ~> 3.0
|
|
61
|
+
- Updated RuboCop dependency to >= 0.25
|
|
62
|
+
- Updated SimpleCov dependency to >= 0.9
|
|
63
|
+
|
|
64
|
+
### Removed
|
|
65
|
+
|
|
66
|
+
- Dropped support for Ruby 1.9.2
|
|
67
|
+
|
|
68
|
+
## [0.0.9] - 2013-12-23
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- Changed `#hash` method to use `Array#hash` for better uniformity
|
|
73
|
+
- Refactored block to use a Method object
|
|
74
|
+
- Removed devtools as a development dependency
|
|
75
|
+
- Added supported Ruby versions section to README
|
|
76
|
+
|
|
77
|
+
## [0.0.8] - 2013-12-02
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- Fixed mutation in equality and equivalency methods
|
|
82
|
+
- Fixed mutation in comparison method
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
|
|
86
|
+
- Refactored explicit method to be an included hook method
|
|
87
|
+
- Added spec for `Equalizer#included`
|
|
88
|
+
- Changed spec to verify private attributes can be compared
|
|
89
|
+
|
|
90
|
+
## [0.0.7] - 2013-08-22
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- Removed adamantium dependency
|
|
95
|
+
- Removed unused backports dependency
|
|
96
|
+
- Kill mutations in `Equalizer#define_cmp_method`
|
|
97
|
+
- Added license to gemspec
|
|
98
|
+
- Added mutant config and enabled mutation testing
|
|
99
|
+
|
|
100
|
+
### Removed
|
|
101
|
+
|
|
102
|
+
- Stopped testing Ruby 1.8
|
|
103
|
+
|
|
104
|
+
## [0.0.5] - 2013-03-01
|
|
105
|
+
|
|
106
|
+
### Changed
|
|
107
|
+
|
|
108
|
+
- Bumped backports dependency to ~> 3.0, >= 3.0.3
|
|
109
|
+
- Bumped yard dependency to ~> 0.8.5
|
|
110
|
+
|
|
111
|
+
## [0.0.4] - 2013-02-16
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- Added encoding to all Ruby source files
|
|
116
|
+
- Upgraded gem dependencies
|
|
117
|
+
|
|
118
|
+
## [0.0.3] - 2013-01-20
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- Fixed `#hash` and `#inspect` methods to have the expected arity
|
|
123
|
+
|
|
124
|
+
## [0.0.2] - 2012-12-25
|
|
125
|
+
|
|
126
|
+
### Changed
|
|
127
|
+
|
|
128
|
+
- Changed `#==` to have stricter behavior for subtypes
|
|
129
|
+
|
|
130
|
+
## [0.0.1] - 2012-11-22
|
|
131
|
+
|
|
132
|
+
### Added
|
|
133
|
+
|
|
134
|
+
- Initial release, extracted from veritas
|
|
135
|
+
- Define equality (`==`), equivalence (`eql?`), hashing (`hash`), and
|
|
136
|
+
inspection (`inspect`) methods based on specified attributes
|
|
137
|
+
|
|
138
|
+
[1.0.0]: https://github.com/dkubb/equalizer/compare/v0.0.11...v1.0.0
|
|
139
|
+
[0.0.11]: https://github.com/dkubb/equalizer/compare/v0.0.10...v0.0.11
|
|
140
|
+
[0.0.10]: https://github.com/dkubb/equalizer/compare/v0.0.9...v0.0.10
|
|
141
|
+
[0.0.9]: https://github.com/dkubb/equalizer/compare/v0.0.8...v0.0.9
|
|
142
|
+
[0.0.8]: https://github.com/dkubb/equalizer/compare/v0.0.7...v0.0.8
|
|
143
|
+
[0.0.7]: https://github.com/dkubb/equalizer/compare/v0.0.5...v0.0.7
|
|
144
|
+
[0.0.5]: https://github.com/dkubb/equalizer/compare/v0.0.4...v0.0.5
|
|
145
|
+
[0.0.4]: https://github.com/dkubb/equalizer/compare/v0.0.3...v0.0.4
|
|
146
|
+
[0.0.3]: https://github.com/dkubb/equalizer/compare/v0.0.2...v0.0.3
|
|
147
|
+
[0.0.2]: https://github.com/dkubb/equalizer/compare/v0.0.1...v0.0.2
|
|
148
|
+
[0.0.1]: https://github.com/dkubb/equalizer/releases/tag/v0.0.1
|
data/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
Copyright (c) 2012 Markus Schirp (packaging)
|
|
1
|
+
MIT License
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
a copy of this software and associated documentation files (the
|
|
6
|
-
"Software"), to deal in the Software without restriction, including
|
|
7
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
-
the following conditions:
|
|
3
|
+
Copyright (c) 2009-2026 Dan Kubb, Markus Schirp, Piotr Solnica, Erik Berlin
|
|
11
4
|
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,69 +1,316 @@
|
|
|
1
|
-
|
|
2
|
-
=========
|
|
1
|
+
# Equalizer
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://rubygems.org/gems/equalizer)
|
|
4
|
+
[](https://github.com/dkubb/equalizer/actions/workflows/test.yml)
|
|
5
|
+
[](https://github.com/dkubb/equalizer/actions/workflows/quality.yml)
|
|
6
|
+
[](https://github.com/dkubb/equalizer/actions/workflows/yard.yml)
|
|
7
|
+
[](https://github.com/dkubb/equalizer/actions/workflows/mutant.yml)
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
[][gemnasium]
|
|
9
|
-
[][codeclimate]
|
|
10
|
-
[][coveralls]
|
|
9
|
+
Equalizer provides equality, equivalence, hashing, pattern matching, and
|
|
10
|
+
inspection methods for Ruby objects based on explicitly specified attributes.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
[codeclimate]: https://codeclimate.com/github/dkubb/equalizer
|
|
16
|
-
[coveralls]: https://coveralls.io/r/dkubb/equalizer
|
|
12
|
+
Unlike approaches that automatically use all `attr_reader` attributes,
|
|
13
|
+
Equalizer requires explicit specification of which attributes affect equality,
|
|
14
|
+
giving you full control over comparison behavior.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
--------
|
|
16
|
+
## Installation
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
Add this line to your application's Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "equalizer"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install it directly:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gem install equalizer
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
class Point
|
|
34
|
+
include Equalizer.new(:x, :y)
|
|
35
|
+
|
|
36
|
+
attr_reader :x, :y
|
|
37
|
+
|
|
38
|
+
def initialize(x, y)
|
|
39
|
+
@x = x
|
|
40
|
+
@y = y
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
p1 = Point.new(1, 2)
|
|
45
|
+
p2 = Point.new(1, 2)
|
|
46
|
+
|
|
47
|
+
p1 == p2 # => true
|
|
48
|
+
p1.eql?(p2) # => true
|
|
49
|
+
p1.hash == p2.hash # => true
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
### Selective Attribute Comparison
|
|
55
|
+
|
|
56
|
+
Only the attributes you specify are used for equality. Other instance variables
|
|
57
|
+
are ignored:
|
|
58
|
+
|
|
59
|
+
> [!TIP]
|
|
60
|
+
> This is useful when you have attributes that shouldn't affect equality, like
|
|
61
|
+
> timestamps, cached values, or display names.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
22
64
|
class GeoLocation
|
|
23
65
|
include Equalizer.new(:latitude, :longitude)
|
|
24
66
|
|
|
25
|
-
attr_reader :latitude, :longitude
|
|
67
|
+
attr_reader :latitude, :longitude, :name
|
|
68
|
+
|
|
69
|
+
def initialize(latitude, longitude, name = nil)
|
|
70
|
+
@latitude = latitude
|
|
71
|
+
@longitude = longitude
|
|
72
|
+
@name = name
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
home = GeoLocation.new(37.7786, -122.4407, "Home")
|
|
77
|
+
work = GeoLocation.new(37.7786, -122.4407, "Work")
|
|
78
|
+
|
|
79
|
+
home == work # => true (name is not part of equality)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Equality vs Equivalence
|
|
26
83
|
|
|
27
|
-
|
|
28
|
-
|
|
84
|
+
Equalizer provides two comparison methods with different semantics:
|
|
85
|
+
|
|
86
|
+
#### `==` (Equality)
|
|
87
|
+
|
|
88
|
+
Returns `true` if the other object is an instance of the same class **or a
|
|
89
|
+
subclass**, and all specified attributes are equal using `==`:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
class ColoredPoint < Point
|
|
93
|
+
attr_reader :color
|
|
94
|
+
|
|
95
|
+
def initialize(x, y, color)
|
|
96
|
+
super(x, y)
|
|
97
|
+
@color = color
|
|
29
98
|
end
|
|
30
99
|
end
|
|
31
100
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
101
|
+
point = Point.new(1, 2)
|
|
102
|
+
colored = ColoredPoint.new(1, 2, "red")
|
|
103
|
+
|
|
104
|
+
point == colored # => true (ColoredPoint is a subclass of Point)
|
|
105
|
+
colored == point # => false (Point is not a subclass of ColoredPoint)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> [!IMPORTANT]
|
|
109
|
+
> In Ruby, the `==` operator is asymmetric when comparing across class
|
|
110
|
+
> hierarchies. A parent class instance can equal a subclass instance, but not
|
|
111
|
+
> vice versa.
|
|
35
112
|
|
|
36
|
-
|
|
113
|
+
#### `eql?` (Equivalence)
|
|
37
114
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
point_a.eql?(point_b) # => true
|
|
41
|
-
point_a.equal?(point_b) # => false
|
|
115
|
+
Returns `true` only if both objects are instances of the **exact same class**,
|
|
116
|
+
and all specified attributes are equal using `eql?`:
|
|
42
117
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
118
|
+
```ruby
|
|
119
|
+
point = Point.new(1, 2)
|
|
120
|
+
colored = ColoredPoint.new(1, 2, "red")
|
|
121
|
+
|
|
122
|
+
point.eql?(colored) # => false (different classes)
|
|
123
|
+
colored.eql?(point) # => false (different classes)
|
|
124
|
+
|
|
125
|
+
point.eql?(Point.new(1, 2)) # => true (same class, same values)
|
|
47
126
|
```
|
|
48
127
|
|
|
49
|
-
|
|
50
|
-
-----------------------
|
|
128
|
+
### Hashing
|
|
51
129
|
|
|
52
|
-
|
|
53
|
-
|
|
130
|
+
Objects that are `eql?` will have the same hash code, making them safe for use
|
|
131
|
+
as Hash keys and in Sets:
|
|
132
|
+
|
|
133
|
+
> [!NOTE]
|
|
134
|
+
> Ruby's `Hash` and `Set` use `eql?` and `hash` together. Equalizer ensures
|
|
135
|
+
> these methods stay consistent—objects that are `eql?` always have matching
|
|
136
|
+
> hash codes.
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
require "set"
|
|
140
|
+
|
|
141
|
+
p1 = Point.new(1, 2)
|
|
142
|
+
p2 = Point.new(1, 2)
|
|
143
|
+
|
|
144
|
+
# As Hash keys
|
|
145
|
+
locations = {}
|
|
146
|
+
locations[p1] = "first"
|
|
147
|
+
locations[p2] = "second"
|
|
148
|
+
locations.size # => 1 (p1 and p2 are the same key)
|
|
149
|
+
|
|
150
|
+
# In Sets
|
|
151
|
+
set = Set.new
|
|
152
|
+
set << p1
|
|
153
|
+
set << p2
|
|
154
|
+
set.size # => 1
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Pattern Matching
|
|
158
|
+
|
|
159
|
+
Equalizer provides full support for Ruby's pattern matching syntax.
|
|
160
|
+
|
|
161
|
+
> [!TIP]
|
|
162
|
+
> Use array patterns `[x, y]` for positional matching when attribute order
|
|
163
|
+
> matters. Use hash patterns `{x:, y:}` for named matching when you want
|
|
164
|
+
> clarity or only need specific attributes.
|
|
165
|
+
|
|
166
|
+
#### Array Patterns
|
|
167
|
+
|
|
168
|
+
Use `deconstruct` for array-style pattern matching:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
point = Point.new(3, 4)
|
|
172
|
+
|
|
173
|
+
case point
|
|
174
|
+
in [0, 0]
|
|
175
|
+
puts "origin"
|
|
176
|
+
in [x, 0]
|
|
177
|
+
puts "on x-axis at #{x}"
|
|
178
|
+
in [0, y]
|
|
179
|
+
puts "on y-axis at #{y}"
|
|
180
|
+
in [x, y]
|
|
181
|
+
puts "at (#{x}, #{y})"
|
|
182
|
+
end
|
|
183
|
+
# => "at (3, 4)"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Hash Patterns
|
|
54
187
|
|
|
55
|
-
|
|
56
|
-
* Ruby 1.9.3
|
|
57
|
-
* Ruby 2.0.0
|
|
58
|
-
* Ruby 2.1
|
|
59
|
-
* Ruby 2.2
|
|
60
|
-
* [JRuby][]
|
|
61
|
-
* [Rubinius][]
|
|
62
|
-
* [Ruby Enterprise Edition][ree]
|
|
188
|
+
Use `deconstruct_keys` for hash-style pattern matching:
|
|
63
189
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
190
|
+
```ruby
|
|
191
|
+
point = Point.new(3, 4)
|
|
192
|
+
|
|
193
|
+
case point
|
|
194
|
+
in { x: 0, y: 0 }
|
|
195
|
+
puts "origin"
|
|
196
|
+
in { x:, y: } if x == y
|
|
197
|
+
puts "on diagonal at #{x}"
|
|
198
|
+
in { x:, y: }
|
|
199
|
+
puts "at (#{x}, #{y})"
|
|
200
|
+
end
|
|
201
|
+
# => "at (3, 4)"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Class Patterns
|
|
205
|
+
|
|
206
|
+
Combine with class checks:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
case point
|
|
210
|
+
in Point(x: 0, y: 0)
|
|
211
|
+
puts "origin point"
|
|
212
|
+
in Point(x:, y:)
|
|
213
|
+
puts "point at (#{x}, #{y})"
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Clean Inspect Output
|
|
218
|
+
|
|
219
|
+
Equalizer customizes `inspect` to show only the attributes used for equality:
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
class User
|
|
223
|
+
include Equalizer.new(:id)
|
|
224
|
+
|
|
225
|
+
attr_reader :id, :name, :email, :created_at
|
|
226
|
+
|
|
227
|
+
def initialize(id, name, email)
|
|
228
|
+
@id = id
|
|
229
|
+
@name = name
|
|
230
|
+
@email = email
|
|
231
|
+
@created_at = Time.now
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
user = User.new(42, "Alice", "alice@example.com")
|
|
236
|
+
user.inspect
|
|
237
|
+
# => "#<User:0x00007f... @id=42>"
|
|
238
|
+
# Note: name, email, and created_at are not shown
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
> [!NOTE]
|
|
242
|
+
> When debugging, remember that `inspect` only shows equality attributes. Use
|
|
243
|
+
> `instance_variables` to see all instance variables if needed.
|
|
244
|
+
|
|
245
|
+
To keep the original `inspect` and `pretty_print` methods, pass `inspect: false`:
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
class Person < Struct.new(:id, :name)
|
|
249
|
+
include Equalizer.new(:id, inspect: false)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
amy = Person.new(1, "Amy")
|
|
253
|
+
amy.inspect
|
|
254
|
+
# => "#<struct Person id=1, name=\"Amy\">"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Clean Ancestor Chain
|
|
258
|
+
|
|
259
|
+
The included module has a descriptive name in the ancestor chain:
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
Point.ancestors
|
|
263
|
+
# => [Point, Equalizer(x, y), Object, Kernel, BasicObject]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Nested Equalizer Objects
|
|
267
|
+
|
|
268
|
+
Equalizer objects can be nested and will compare correctly:
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
class Line
|
|
272
|
+
include Equalizer.new(:start_point, :end_point)
|
|
273
|
+
|
|
274
|
+
attr_reader :start_point, :end_point
|
|
275
|
+
|
|
276
|
+
def initialize(start_point, end_point)
|
|
277
|
+
@start_point = start_point
|
|
278
|
+
@end_point = end_point
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
line1 = Line.new(Point.new(0, 0), Point.new(1, 1))
|
|
283
|
+
line2 = Line.new(Point.new(0, 0), Point.new(1, 1))
|
|
284
|
+
|
|
285
|
+
line1 == line2 # => true
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Error Handling
|
|
289
|
+
|
|
290
|
+
> [!CAUTION]
|
|
291
|
+
> Equalizer validates arguments at include time. Errors will be raised
|
|
292
|
+
> immediately if you pass invalid arguments.
|
|
293
|
+
|
|
294
|
+
Equalizer validates its arguments:
|
|
295
|
+
|
|
296
|
+
```ruby
|
|
297
|
+
# At least one attribute is required
|
|
298
|
+
Equalizer.new()
|
|
299
|
+
# => ArgumentError: at least one attribute is required
|
|
300
|
+
|
|
301
|
+
# Attributes must be Symbols
|
|
302
|
+
Equalizer.new("name")
|
|
303
|
+
# => ArgumentError: attribute must be a Symbol, got String
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Supported Ruby Versions
|
|
307
|
+
|
|
308
|
+
This library aims to support and is [tested against][test] the following Ruby
|
|
309
|
+
implementations:
|
|
310
|
+
|
|
311
|
+
* Ruby 3.3
|
|
312
|
+
* Ruby 3.4
|
|
313
|
+
* Ruby 4.0
|
|
67
314
|
|
|
68
315
|
If something doesn't work on one of these versions, it's a bug.
|
|
69
316
|
|
|
@@ -79,20 +326,15 @@ patches in a timely fashion. If critical issues for a particular implementation
|
|
|
79
326
|
exist at the time of a major release, support for that Ruby version may be
|
|
80
327
|
dropped.
|
|
81
328
|
|
|
82
|
-
|
|
83
|
-
|
|
329
|
+
[test]: https://github.com/dkubb/equalizer/actions/workflows/test.yml
|
|
330
|
+
|
|
331
|
+
## Credits
|
|
84
332
|
|
|
85
333
|
* Dan Kubb ([dkubb](https://github.com/dkubb))
|
|
86
|
-
* Piotr Solnica ([solnic](https://github.com/solnic))
|
|
87
334
|
* Markus Schirp ([mbj](https://github.com/mbj))
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
Contributing
|
|
91
|
-
-------------
|
|
92
|
-
|
|
93
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
335
|
+
* Piotr Solnica ([solnic](https://github.com/solnic))
|
|
336
|
+
* Erik Berlin ([sferik](https://github.com/sferik))
|
|
94
337
|
|
|
95
|
-
|
|
96
|
-
---------
|
|
338
|
+
## License
|
|
97
339
|
|
|
98
|
-
|
|
340
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|