dry-equalizer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +6 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +22 -0
- data/.yardstick.yml +2 -0
- data/CHANGELOG.md +9 -0
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +9 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/Rakefile +5 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +4 -0
- data/config/flog.yml +3 -0
- data/config/mutant.yml +2 -0
- data/config/reek.yml +105 -0
- data/config/rubocop.yml +115 -0
- data/config/yardstick.yml +34 -0
- data/dry-equalizer.gemspec +23 -0
- data/lib/dry-equalizer.rb +1 -0
- data/lib/dry/equalizer.rb +129 -0
- data/lib/dry/equalizer/version.rb +8 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/config_alias.rb +5 -0
- data/spec/unit/equalizer/included_spec.rb +50 -0
- data/spec/unit/equalizer/methods/eql_predicate_spec.rb +65 -0
- data/spec/unit/equalizer/methods/equality_operator_spec.rb +88 -0
- data/spec/unit/equalizer/universal_spec.rb +158 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 331c7852ce35d03c2bf636b387de9f0dfda2ff0f
|
4
|
+
data.tar.gz: 34c7ecb5014f00f1134b8217b70931f38787d384
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf4777f0013c80f11093c59b9efe78b1b3b7a877f0bf4c0dc7fe2542b435a6756712ece41ea76a3d677ab1c6e97741c10e3680b85ebc1b445a055b704097d8f6
|
7
|
+
data.tar.gz: de3f710c92e3c9a849ec37f4c7ef32d015d1634a0794140894647b74068a11dca02de706a2c07dab615902f3f8230bdfa305d70716635e48f7b52bcf9b276d4f
|
data/.gitignore
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## Rubinius
|
17
|
+
*.rbc
|
18
|
+
.rbx
|
19
|
+
|
20
|
+
## PROJECT::GENERAL
|
21
|
+
*.gem
|
22
|
+
coverage
|
23
|
+
profiling
|
24
|
+
turbulence
|
25
|
+
rdoc
|
26
|
+
pkg
|
27
|
+
tmp
|
28
|
+
doc
|
29
|
+
log
|
30
|
+
.yardoc
|
31
|
+
measurements
|
32
|
+
|
33
|
+
## BUNDLER
|
34
|
+
.bundle
|
35
|
+
Gemfile.lock
|
36
|
+
bin
|
37
|
+
bundle
|
38
|
+
|
39
|
+
## PROJECT::SPECIFIC
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
File without changes
|
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
language: ruby
|
2
|
+
bundler_args: --without yard guard benchmarks
|
3
|
+
script: "bundle exec rake ci"
|
4
|
+
cache: bundler
|
5
|
+
sudo: false
|
6
|
+
rvm:
|
7
|
+
- 2.1
|
8
|
+
- 2.2
|
9
|
+
- ruby-head
|
10
|
+
- rbx-2
|
11
|
+
matrix:
|
12
|
+
include:
|
13
|
+
- rvm: jruby-9000
|
14
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug --2.0" # for simplecov
|
15
|
+
- rvm: jruby-head
|
16
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug --2.0" # for simplecov
|
17
|
+
allow_failures:
|
18
|
+
- rvm: jruby-9000
|
19
|
+
- rvm: jruby-head
|
20
|
+
- rvm: rbx-2
|
21
|
+
- rvm: ruby-head
|
22
|
+
fast_finish: true
|
data/.yardstick.yml
ADDED
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Contributing
|
2
|
+
------------
|
3
|
+
|
4
|
+
* If you want your code merged into the mainline, please discuss the proposed changes with me before doing any work on it. This library is still in early development, and the direction it is going may not always be clear. Some features may not be appropriate yet, may need to be deferred until later when the foundation for them is laid, or may be more applicable in a plugin.
|
5
|
+
* Fork the project.
|
6
|
+
* Make your feature addition or bug fix.
|
7
|
+
* Follow this [style guide](https://github.com/dkubb/styleguide).
|
8
|
+
* Add specs for it. This is important so I don't break it in a future version unintentionally. Tests must cover all branches within the code, and code must be fully covered.
|
9
|
+
* Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
10
|
+
* Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged.
|
11
|
+
* Send me a pull request. Bonus points for topic branches.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2009-2013 Dan Kubb
|
2
|
+
Copyright (c) 2012 Markus Schirp (packaging)
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
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:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
dry-equalizer
|
2
|
+
=========
|
3
|
+
|
4
|
+
Module to define equality, equivalence and inspection methods
|
5
|
+
|
6
|
+
[![Gem Version](http://img.shields.io/gem/v/equalizer.svg)][gem]
|
7
|
+
[![Build Status](http://img.shields.io/travis/dryrb/dry-equalizer.svg)][travis]
|
8
|
+
[![Dependency Status](http://img.shields.io/gemnasium/dryrb/dry-equalizer.svg)][gemnasium]
|
9
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/dryrb/dry-equalizer.svg)][codeclimate]
|
10
|
+
|
11
|
+
[gem]: https://rubygems.org/gems/equalizer
|
12
|
+
[travis]: https://travis-ci.org/dryrb/dry-equalizer
|
13
|
+
[gemnasium]: https://gemnasium.com/dryrb/dry-equalizer
|
14
|
+
[codeclimate]: https://codeclimate.com/github/dryrb/dry-equalizer
|
15
|
+
|
16
|
+
Examples
|
17
|
+
--------
|
18
|
+
|
19
|
+
``` ruby
|
20
|
+
class GeoLocation
|
21
|
+
include Equalizer.new(:latitude, :longitude)
|
22
|
+
|
23
|
+
attr_reader :latitude, :longitude
|
24
|
+
|
25
|
+
def initialize(latitude, longitude)
|
26
|
+
@latitude, @longitude = latitude, longitude
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
point_a = GeoLocation.new(1, 2)
|
31
|
+
point_b = GeoLocation.new(1, 2)
|
32
|
+
point_c = GeoLocation.new(2, 2)
|
33
|
+
|
34
|
+
point_a.inspect # => "#<GeoLocation latitude=1 longitude=2>"
|
35
|
+
|
36
|
+
point_a == point_b # => true
|
37
|
+
point_a.hash == point_b.hash # => true
|
38
|
+
point_a.eql?(point_b) # => true
|
39
|
+
point_a.equal?(point_b) # => false
|
40
|
+
|
41
|
+
point_a == point_c # => false
|
42
|
+
point_a.hash == point_c.hash # => false
|
43
|
+
point_a.eql?(point_c) # => false
|
44
|
+
point_a.equal?(point_c) # => false
|
45
|
+
```
|
46
|
+
|
47
|
+
Supported Ruby Versions
|
48
|
+
-----------------------
|
49
|
+
|
50
|
+
This library aims to support and is [tested against][travis] the following Ruby
|
51
|
+
implementations:
|
52
|
+
|
53
|
+
* Ruby 2.1
|
54
|
+
* Ruby 2.2
|
55
|
+
|
56
|
+
If something doesn't work on one of these versions, it's a bug.
|
57
|
+
|
58
|
+
This library may inadvertently work (or seem to work) on other Ruby versions or
|
59
|
+
implementations, however support will only be provided for the implementations
|
60
|
+
listed above.
|
61
|
+
|
62
|
+
If you would like this library to support another Ruby version or
|
63
|
+
implementation, you may volunteer to be a maintainer. Being a maintainer
|
64
|
+
entails making sure all tests run and pass on that implementation. When
|
65
|
+
something breaks on your implementation, you will be responsible for providing
|
66
|
+
patches in a timely fashion. If critical issues for a particular implementation
|
67
|
+
exist at the time of a major release, support for that Ruby version may be
|
68
|
+
dropped.
|
69
|
+
|
70
|
+
Credits
|
71
|
+
-------
|
72
|
+
|
73
|
+
This is a fork of the original [equalizer](https://github.com/dkubb/equalizer).
|
74
|
+
|
75
|
+
* Dan Kubb ([dkubb](https://github.com/dkubb))
|
76
|
+
* Piotr Solnica ([solnic](https://github.com/solnic))
|
77
|
+
* Markus Schirp ([mbj](https://github.com/mbj))
|
78
|
+
* Erik Michaels-Ober ([sferik](https://github.com/sferik))
|
79
|
+
|
80
|
+
Contributing
|
81
|
+
-------------
|
82
|
+
|
83
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
84
|
+
|
85
|
+
Copyright
|
86
|
+
---------
|
87
|
+
|
88
|
+
Copyright © 2009-2013 Dan Kubb. See LICENSE for details.
|
data/Rakefile
ADDED
data/config/devtools.yml
ADDED
data/config/flay.yml
ADDED
data/config/flog.yml
ADDED
data/config/mutant.yml
ADDED
data/config/reek.yml
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
---
|
2
|
+
Attribute:
|
3
|
+
enabled: false
|
4
|
+
exclude: []
|
5
|
+
BooleanParameter:
|
6
|
+
enabled: true
|
7
|
+
exclude: []
|
8
|
+
ClassVariable:
|
9
|
+
enabled: true
|
10
|
+
exclude: []
|
11
|
+
ControlParameter:
|
12
|
+
enabled: true
|
13
|
+
exclude: []
|
14
|
+
DataClump:
|
15
|
+
enabled: true
|
16
|
+
exclude: []
|
17
|
+
max_copies: 2
|
18
|
+
min_clump_size: 2
|
19
|
+
DuplicateMethodCall:
|
20
|
+
enabled: true
|
21
|
+
exclude: []
|
22
|
+
max_calls: 1
|
23
|
+
allow_calls: []
|
24
|
+
FeatureEnvy:
|
25
|
+
enabled: true
|
26
|
+
exclude: []
|
27
|
+
IrresponsibleModule:
|
28
|
+
enabled: true
|
29
|
+
exclude: []
|
30
|
+
LongParameterList:
|
31
|
+
enabled: true
|
32
|
+
exclude: []
|
33
|
+
max_params: 2
|
34
|
+
overrides:
|
35
|
+
initialize:
|
36
|
+
max_params: 3
|
37
|
+
LongYieldList:
|
38
|
+
enabled: true
|
39
|
+
exclude: []
|
40
|
+
max_params: 2
|
41
|
+
NestedIterators:
|
42
|
+
enabled: true
|
43
|
+
exclude:
|
44
|
+
- Equalizer#define_cmp_method
|
45
|
+
- Equalizer#define_inspect_method
|
46
|
+
max_allowed_nesting: 1
|
47
|
+
ignore_iterators: []
|
48
|
+
NilCheck:
|
49
|
+
enabled: true
|
50
|
+
exclude: []
|
51
|
+
RepeatedConditional:
|
52
|
+
enabled: true
|
53
|
+
exclude: []
|
54
|
+
max_ifs: 1
|
55
|
+
TooManyInstanceVariables:
|
56
|
+
enabled: true
|
57
|
+
exclude: []
|
58
|
+
max_instance_variables: 3
|
59
|
+
TooManyMethods:
|
60
|
+
enabled: true
|
61
|
+
exclude: []
|
62
|
+
max_methods: 6
|
63
|
+
TooManyStatements:
|
64
|
+
enabled: true
|
65
|
+
exclude:
|
66
|
+
- Equalizer#define_inspect_method
|
67
|
+
max_statements: 5
|
68
|
+
UncommunicativeMethodName:
|
69
|
+
enabled: true
|
70
|
+
exclude: []
|
71
|
+
reject:
|
72
|
+
- !ruby/regexp /^[a-z]$/
|
73
|
+
- !ruby/regexp /[0-9]$/
|
74
|
+
- !ruby/regexp /[A-Z]/
|
75
|
+
accept: []
|
76
|
+
UncommunicativeModuleName:
|
77
|
+
enabled: true
|
78
|
+
exclude: []
|
79
|
+
reject:
|
80
|
+
- !ruby/regexp /^.$/
|
81
|
+
- !ruby/regexp /[0-9]$/
|
82
|
+
accept: []
|
83
|
+
UncommunicativeParameterName:
|
84
|
+
enabled: true
|
85
|
+
exclude: []
|
86
|
+
reject:
|
87
|
+
- !ruby/regexp /^.$/
|
88
|
+
- !ruby/regexp /[0-9]$/
|
89
|
+
- !ruby/regexp /[A-Z]/
|
90
|
+
accept: []
|
91
|
+
UncommunicativeVariableName:
|
92
|
+
enabled: true
|
93
|
+
exclude: []
|
94
|
+
reject:
|
95
|
+
- !ruby/regexp /^.$/
|
96
|
+
- !ruby/regexp /[0-9]$/
|
97
|
+
- !ruby/regexp /[A-Z]/
|
98
|
+
accept: []
|
99
|
+
UnusedParameters:
|
100
|
+
enabled: true
|
101
|
+
exclude: []
|
102
|
+
UtilityFunction:
|
103
|
+
enabled: true
|
104
|
+
exclude: []
|
105
|
+
max_helper_calls: 0
|
data/config/rubocop.yml
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Avoid parameter lists longer than five parameters.
|
2
|
+
ParameterLists:
|
3
|
+
Max: 3
|
4
|
+
CountKeywordArgs: true
|
5
|
+
|
6
|
+
# Avoid more than `Max` levels of nesting.
|
7
|
+
BlockNesting:
|
8
|
+
Max: 3
|
9
|
+
|
10
|
+
# Align with the style guide.
|
11
|
+
CollectionMethods:
|
12
|
+
PreferredMethods:
|
13
|
+
collect: 'map'
|
14
|
+
inject: 'reduce'
|
15
|
+
find: 'detect'
|
16
|
+
find_all: 'select'
|
17
|
+
|
18
|
+
# Do not force public/protected/private keyword to be indented at the same
|
19
|
+
# level as the def keyword. My personal preference is to outdent these keywords
|
20
|
+
# because I think when scanning code it makes it easier to identify the
|
21
|
+
# sections of code and visually separate them. When the keyword is at the same
|
22
|
+
# level I think it sort of blends in with the def keywords and makes it harder
|
23
|
+
# to scan the code and see where the sections are.
|
24
|
+
AccessModifierIndentation:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
# Limit line length
|
28
|
+
LineLength:
|
29
|
+
Max: 106
|
30
|
+
|
31
|
+
# Disable documentation checking until a class needs to be documented once
|
32
|
+
Documentation:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
# Do not always use &&/|| instead of and/or.
|
36
|
+
AndOr:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
# Do not favor modifier if/unless usage when you have a single-line body
|
40
|
+
IfUnlessModifier:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
# Allow case equality operator (in limited use within the specs)
|
44
|
+
CaseEquality:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# Constants do not always have to use SCREAMING_SNAKE_CASE
|
48
|
+
ConstantName:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
# Not all trivial readers/writers can be defined with attr_* methods
|
52
|
+
TrivialAccessors:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
# Allow empty lines around class body
|
56
|
+
EmptyLinesAroundClassBody:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
# Allow empty lines around module body
|
60
|
+
EmptyLinesAroundModuleBody:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
# Allow empty lines around block body
|
64
|
+
EmptyLinesAroundBlockBody:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
# Allow multiple line operations to not require indentation
|
68
|
+
MultilineOperationIndentation:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
# Prefer String#% over Kernel#sprintf
|
72
|
+
FormatString:
|
73
|
+
Enabled: false
|
74
|
+
|
75
|
+
# Use square brackets for literal Array objects
|
76
|
+
PercentLiteralDelimiters:
|
77
|
+
PreferredDelimiters:
|
78
|
+
'%': '{}'
|
79
|
+
'%i': '[]'
|
80
|
+
'%q': ()
|
81
|
+
'%Q': ()
|
82
|
+
'%r': '{}'
|
83
|
+
'%s': ()
|
84
|
+
'%w': '[]'
|
85
|
+
'%W': '[]'
|
86
|
+
'%x': ()
|
87
|
+
|
88
|
+
# Use %i[...] for arrays of symbols
|
89
|
+
SymbolArray:
|
90
|
+
Enabled: true
|
91
|
+
|
92
|
+
# Align if/else blocks with the variable assignment
|
93
|
+
EndAlignment:
|
94
|
+
AlignWith: variable
|
95
|
+
|
96
|
+
# Do not always align parameters when it is easier to read
|
97
|
+
AlignParameters:
|
98
|
+
Exclude:
|
99
|
+
- spec/**/*_spec.rb
|
100
|
+
|
101
|
+
# Prefer #kind_of? over #is_a?
|
102
|
+
ClassCheck:
|
103
|
+
EnforcedStyle: kind_of?
|
104
|
+
|
105
|
+
# Do not prefer double quotes to be used when %q or %Q is more appropriate
|
106
|
+
UnneededPercentQ:
|
107
|
+
Enabled: false
|
108
|
+
|
109
|
+
# Allow a maximum ABC score
|
110
|
+
Metrics/AbcSize:
|
111
|
+
Max: 8.6
|
112
|
+
|
113
|
+
# Do not prefer lambda.call(...) over lambda.(...)
|
114
|
+
LambdaCall:
|
115
|
+
Enabled: false
|
@@ -0,0 +1,34 @@
|
|
1
|
+
---
|
2
|
+
path: 'lib/**/*.rb'
|
3
|
+
threshold: 100
|
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: true
|
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: []
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../lib/dry/equalizer/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'dry-equalizer'
|
7
|
+
gem.version = Dry::Equalizer::VERSION.dup
|
8
|
+
gem.authors = ['Dan Kubb', 'Markus Schirp']
|
9
|
+
gem.email = %w[dan.kubb@gmail.com mbj@schirp-dso.com]
|
10
|
+
gem.description = 'Module to define equality, equivalence and inspection methods'
|
11
|
+
gem.summary = gem.description
|
12
|
+
gem.homepage = 'https://github.com/dkubb/equalizer'
|
13
|
+
gem.licenses = 'MIT'
|
14
|
+
|
15
|
+
gem.require_paths = %w[lib]
|
16
|
+
gem.files = `git ls-files`.split("\n")
|
17
|
+
gem.test_files = `git ls-files -- spec/{unit,integration}`.split("\n")
|
18
|
+
gem.extra_rdoc_files = %w[LICENSE README.md CONTRIBUTING.md]
|
19
|
+
|
20
|
+
gem.required_ruby_version = '>= 2.1.0'
|
21
|
+
|
22
|
+
gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dry/equalizer'
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
# Build an equalizer module for the inclusion in other class
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
def self.Equalizer(*keys)
|
8
|
+
Dry::Equalizer.new(*keys)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Define equality, equivalence and inspection methods
|
12
|
+
class Equalizer < Module
|
13
|
+
# Initialize an Equalizer with the given keys
|
14
|
+
#
|
15
|
+
# Will use the keys with which it is initialized to define #cmp?,
|
16
|
+
# #hash, and #inspect
|
17
|
+
#
|
18
|
+
# @param [Array<Symbol>] keys
|
19
|
+
#
|
20
|
+
# @return [undefined]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def initialize(*keys)
|
24
|
+
@keys = keys
|
25
|
+
define_methods
|
26
|
+
freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Hook called when module is included
|
32
|
+
#
|
33
|
+
# @param [Module] descendant
|
34
|
+
# the module or class including Equalizer
|
35
|
+
#
|
36
|
+
# @return [self]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
def included(descendant)
|
40
|
+
super
|
41
|
+
descendant.include Methods
|
42
|
+
end
|
43
|
+
|
44
|
+
# Define the equalizer methods based on #keys
|
45
|
+
#
|
46
|
+
# @return [undefined]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
def define_methods
|
50
|
+
define_cmp_method
|
51
|
+
define_hash_method
|
52
|
+
define_inspect_method
|
53
|
+
end
|
54
|
+
|
55
|
+
# Define an #cmp? method based on the instance's values identified by #keys
|
56
|
+
#
|
57
|
+
# @return [undefined]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def define_cmp_method
|
61
|
+
keys = @keys
|
62
|
+
define_method(:cmp?) do |comparator, other|
|
63
|
+
keys.all? do |key|
|
64
|
+
__send__(key).public_send(comparator, other.__send__(key))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
private :cmp?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Define a #hash method based on the instance's values identified by #keys
|
71
|
+
#
|
72
|
+
# @return [undefined]
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
def define_hash_method
|
76
|
+
keys = @keys
|
77
|
+
define_method(:hash) do | |
|
78
|
+
keys.map(&method(:send)).push(self.class).hash
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Define an inspect method that reports the values of the instance's keys
|
83
|
+
#
|
84
|
+
# @return [undefined]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def define_inspect_method
|
88
|
+
keys = @keys
|
89
|
+
define_method(:inspect) do | |
|
90
|
+
klass = self.class
|
91
|
+
name = klass.name || klass.inspect
|
92
|
+
"#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# The comparison methods
|
97
|
+
module Methods
|
98
|
+
# Compare the object with other object for equality
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# object.eql?(other) # => true or false
|
102
|
+
#
|
103
|
+
# @param [Object] other
|
104
|
+
# the other object to compare with
|
105
|
+
#
|
106
|
+
# @return [Boolean]
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
def eql?(other)
|
110
|
+
instance_of?(other.class) && cmp?(__method__, other)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Compare the object with other object for equivalency
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# object == other # => true or false
|
117
|
+
#
|
118
|
+
# @param [Object] other
|
119
|
+
# the other object to compare with
|
120
|
+
#
|
121
|
+
# @return [Boolean]
|
122
|
+
#
|
123
|
+
# @api public
|
124
|
+
def ==(other)
|
125
|
+
other.kind_of?(self.class) && cmp?(__method__, other)
|
126
|
+
end
|
127
|
+
end # module Methods
|
128
|
+
end # class Equalizer
|
129
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
if ENV['COVERAGE'] == 'true'
|
4
|
+
require 'simplecov'
|
5
|
+
|
6
|
+
SimpleCov.start do
|
7
|
+
command_name 'spec:unit'
|
8
|
+
|
9
|
+
add_filter 'config'
|
10
|
+
add_filter 'spec'
|
11
|
+
add_filter 'vendor'
|
12
|
+
|
13
|
+
minimum_coverage 100
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'devtools/spec_helper'
|
18
|
+
require 'dry-equalizer'
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.raise_errors_for_deprecations!
|
22
|
+
|
23
|
+
config.expect_with :rspec do |expect_with|
|
24
|
+
expect_with.syntax = :expect
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dry::Equalizer, '#included' do
|
6
|
+
subject { descendant.instance_exec(object) { |mod| include mod } }
|
7
|
+
|
8
|
+
let(:object) { described_class.new }
|
9
|
+
let(:descendant) { Class.new }
|
10
|
+
let(:superclass) { described_class.superclass }
|
11
|
+
|
12
|
+
before do
|
13
|
+
# Prevent Module.included from being called through inheritance
|
14
|
+
allow(described_class::Methods).to receive(:included)
|
15
|
+
end
|
16
|
+
|
17
|
+
around do |example|
|
18
|
+
# Restore included method after each example
|
19
|
+
superclass.class_eval do
|
20
|
+
alias_method :original_included, :included
|
21
|
+
example.call
|
22
|
+
undef_method :included
|
23
|
+
alias_method :included, :original_included
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'delegates to the superclass #included method' do
|
28
|
+
# This is the most succinct approach I could think of to test whether the
|
29
|
+
# superclass#included method is called. All of the built-in rspec helpers
|
30
|
+
# did not seem to work for this.
|
31
|
+
included = false
|
32
|
+
|
33
|
+
superclass.class_eval do
|
34
|
+
define_method(:included) do |_|
|
35
|
+
# Only set the flag when an Dry::Equalizer instance is included.
|
36
|
+
# Otherwise, other module includes (which get triggered internally
|
37
|
+
# in RSpec when `change` is used for the first time, since it uses
|
38
|
+
# autoloading for its matchers) will wrongly set this flag.
|
39
|
+
included = true if self.kind_of?(Dry::Equalizer)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
expect { subject }.to change { included }.from(false).to(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'includes methods into the descendant' do
|
47
|
+
subject
|
48
|
+
expect(descendant.included_modules).to include(described_class::Methods)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dry::Equalizer::Methods, '#eql?' do
|
6
|
+
subject { object.eql?(other) }
|
7
|
+
|
8
|
+
let(:object) { described_class.new(true) }
|
9
|
+
|
10
|
+
let(:described_class) do
|
11
|
+
Class.new do
|
12
|
+
include Dry::Equalizer::Methods
|
13
|
+
|
14
|
+
attr_reader :boolean
|
15
|
+
|
16
|
+
def initialize(boolean)
|
17
|
+
@boolean = boolean
|
18
|
+
end
|
19
|
+
|
20
|
+
def cmp?(comparator, other)
|
21
|
+
boolean.send(comparator, other.boolean)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with the same object' do
|
27
|
+
let(:other) { object }
|
28
|
+
|
29
|
+
it { should be(true) }
|
30
|
+
|
31
|
+
it 'is symmetric' do
|
32
|
+
should eql(other.eql?(object))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with an equivalent object' do
|
37
|
+
let(:other) { object.dup }
|
38
|
+
|
39
|
+
it { should be(true) }
|
40
|
+
|
41
|
+
it 'is symmetric' do
|
42
|
+
should eql(other.eql?(object))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with an equivalent object of a subclass' do
|
47
|
+
let(:other) { Class.new(described_class).new(true) }
|
48
|
+
|
49
|
+
it { should be(false) }
|
50
|
+
|
51
|
+
it 'is symmetric' do
|
52
|
+
should eql(other.eql?(object))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with a different object' do
|
57
|
+
let(:other) { described_class.new(false) }
|
58
|
+
|
59
|
+
it { should be(false) }
|
60
|
+
|
61
|
+
it 'is symmetric' do
|
62
|
+
should eql(other.eql?(object))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dry::Equalizer::Methods, '#==' do
|
6
|
+
subject { object == other }
|
7
|
+
|
8
|
+
let(:object) { described_class.new(true) }
|
9
|
+
let(:described_class) { Class.new(super_class) }
|
10
|
+
|
11
|
+
let(:super_class) do
|
12
|
+
Class.new do
|
13
|
+
include Dry::Equalizer::Methods
|
14
|
+
|
15
|
+
attr_reader :boolean
|
16
|
+
|
17
|
+
def initialize(boolean)
|
18
|
+
@boolean = boolean
|
19
|
+
end
|
20
|
+
|
21
|
+
def cmp?(comparator, other)
|
22
|
+
boolean.send(comparator, other.boolean)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with the same object' do
|
28
|
+
let(:other) { object }
|
29
|
+
|
30
|
+
it { should be(true) }
|
31
|
+
|
32
|
+
it 'is symmetric' do
|
33
|
+
should eql(other == object)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with an equivalent object' do
|
38
|
+
let(:other) { object.dup }
|
39
|
+
|
40
|
+
it { should be(true) }
|
41
|
+
|
42
|
+
it 'is symmetric' do
|
43
|
+
should eql(other == object)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a subclass instance having equivalent obervable state' do
|
48
|
+
let(:other) { Class.new(described_class).new(true) }
|
49
|
+
|
50
|
+
it { should be(true) }
|
51
|
+
|
52
|
+
it 'is not symmetric' do
|
53
|
+
# the subclass instance should maintain substitutability with the object
|
54
|
+
# (in the LSP sense) the reverse is not true.
|
55
|
+
should_not eql(other == object)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with a superclass instance having equivalent observable state' do
|
60
|
+
let(:other) { super_class.new(true) }
|
61
|
+
|
62
|
+
it { should be(false) }
|
63
|
+
|
64
|
+
it 'is not symmetric' do
|
65
|
+
should_not eql(other == object)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with an object of another class' do
|
70
|
+
let(:other) { Class.new.new }
|
71
|
+
|
72
|
+
it { should be(false) }
|
73
|
+
|
74
|
+
it 'is symmetric' do
|
75
|
+
should eql(other == object)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'with a different object' do
|
80
|
+
let(:other) { described_class.new(false) }
|
81
|
+
|
82
|
+
it { should be(false) }
|
83
|
+
|
84
|
+
it 'is symmetric' do
|
85
|
+
should eql(other == object)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Dry::Equalizer do
|
6
|
+
let(:object) { described_class }
|
7
|
+
let(:name) { 'User' }
|
8
|
+
let(:klass) { ::Class.new }
|
9
|
+
|
10
|
+
context 'with no keys' do
|
11
|
+
subject { Dry::Equalizer() }
|
12
|
+
|
13
|
+
before do
|
14
|
+
# specify the class #name method
|
15
|
+
allow(klass).to receive(:name).and_return(name)
|
16
|
+
klass.send(:include, subject)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:instance) { klass.new }
|
20
|
+
|
21
|
+
it { should be_instance_of(object) }
|
22
|
+
|
23
|
+
it { should be_frozen }
|
24
|
+
|
25
|
+
it 'defines #hash and #inspect methods dynamically' do
|
26
|
+
expect(subject.public_instance_methods(false).map(&:to_s).sort)
|
27
|
+
.to eql(%w[hash inspect])
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#eql?' do
|
31
|
+
context 'when the objects are similar' do
|
32
|
+
let(:other) { instance.dup }
|
33
|
+
|
34
|
+
it { expect(instance.eql?(other)).to be(true) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the objects are different' do
|
38
|
+
let(:other) { double('other') }
|
39
|
+
|
40
|
+
it { expect(instance.eql?(other)).to be(false) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#==' do
|
45
|
+
context 'when the objects are similar' do
|
46
|
+
let(:other) { instance.dup }
|
47
|
+
|
48
|
+
it { expect(instance == other).to be(true) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when the objects are different' do
|
52
|
+
let(:other) { double('other') }
|
53
|
+
|
54
|
+
it { expect(instance == other).to be(false) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#hash' do
|
59
|
+
it 'has the expected arity' do
|
60
|
+
expect(klass.instance_method(:hash).arity).to be(0)
|
61
|
+
end
|
62
|
+
|
63
|
+
it { expect(instance.hash).to eql([klass].hash) }
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#inspect' do
|
67
|
+
it 'has the expected arity' do
|
68
|
+
expect(klass.instance_method(:inspect).arity).to be(0)
|
69
|
+
end
|
70
|
+
|
71
|
+
it { expect(instance.inspect).to eql('#<User>') }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'with keys' do
|
76
|
+
subject { Dry::Equalizer(*keys) }
|
77
|
+
|
78
|
+
let(:keys) { %i[firstname lastname].freeze }
|
79
|
+
let(:firstname) { 'John' }
|
80
|
+
let(:lastname) { 'Doe' }
|
81
|
+
let(:instance) { klass.new(firstname, lastname) }
|
82
|
+
|
83
|
+
let(:klass) do
|
84
|
+
::Class.new do
|
85
|
+
attr_reader :firstname, :lastname
|
86
|
+
private :firstname, :lastname
|
87
|
+
|
88
|
+
def initialize(firstname, lastname)
|
89
|
+
@firstname = firstname
|
90
|
+
@lastname = lastname
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
before do
|
96
|
+
# specify the class #inspect method
|
97
|
+
allow(klass).to receive_messages(name: nil, inspect: name)
|
98
|
+
klass.send(:include, subject)
|
99
|
+
end
|
100
|
+
|
101
|
+
it { should be_instance_of(object) }
|
102
|
+
|
103
|
+
it { should be_frozen }
|
104
|
+
|
105
|
+
it 'defines #hash and #inspect methods dynamically' do
|
106
|
+
expect(subject.public_instance_methods(false).map(&:to_s).sort)
|
107
|
+
.to eql(%w[hash inspect])
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#eql?' do
|
111
|
+
context 'when the objects are similar' do
|
112
|
+
let(:other) { instance.dup }
|
113
|
+
|
114
|
+
it { expect(instance.eql?(other)).to be(true) }
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when the objects are different' do
|
118
|
+
let(:other) { double('other') }
|
119
|
+
|
120
|
+
it { expect(instance.eql?(other)).to be(false) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#==' do
|
125
|
+
context 'when the objects are similar' do
|
126
|
+
let(:other) { instance.dup }
|
127
|
+
|
128
|
+
it { expect(instance == other).to be(true) }
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when the objects are different type' do
|
132
|
+
let(:other) { klass.new('Foo', 'Bar') }
|
133
|
+
|
134
|
+
it { expect(instance == other).to be(false) }
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'when the objects are from different type' do
|
138
|
+
let(:other) { double('other') }
|
139
|
+
|
140
|
+
it { expect(instance == other).to be(false) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#hash' do
|
145
|
+
it 'returns the expected hash' do
|
146
|
+
expect(instance.hash)
|
147
|
+
.to eql([firstname, lastname, klass].hash)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#inspect' do
|
152
|
+
it 'returns the expected string' do
|
153
|
+
expect(instance.inspect)
|
154
|
+
.to eql('#<User firstname="John" lastname="Doe">')
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dry-equalizer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Kubb
|
8
|
+
- Markus Schirp
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-11-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.3'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.5
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.3'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.5
|
34
|
+
description: Module to define equality, equivalence and inspection methods
|
35
|
+
email:
|
36
|
+
- dan.kubb@gmail.com
|
37
|
+
- mbj@schirp-dso.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
- CONTRIBUTING.md
|
44
|
+
files:
|
45
|
+
- ".gitignore"
|
46
|
+
- ".rspec"
|
47
|
+
- ".rubocop.yml"
|
48
|
+
- ".travis.yml"
|
49
|
+
- ".yardstick.yml"
|
50
|
+
- CHANGELOG.md
|
51
|
+
- CONTRIBUTING.md
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
- Rakefile
|
56
|
+
- config/devtools.yml
|
57
|
+
- config/flay.yml
|
58
|
+
- config/flog.yml
|
59
|
+
- config/mutant.yml
|
60
|
+
- config/reek.yml
|
61
|
+
- config/rubocop.yml
|
62
|
+
- config/yardstick.yml
|
63
|
+
- dry-equalizer.gemspec
|
64
|
+
- lib/dry-equalizer.rb
|
65
|
+
- lib/dry/equalizer.rb
|
66
|
+
- lib/dry/equalizer/version.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
- spec/support/config_alias.rb
|
69
|
+
- spec/unit/equalizer/included_spec.rb
|
70
|
+
- spec/unit/equalizer/methods/eql_predicate_spec.rb
|
71
|
+
- spec/unit/equalizer/methods/equality_operator_spec.rb
|
72
|
+
- spec/unit/equalizer/universal_spec.rb
|
73
|
+
homepage: https://github.com/dkubb/equalizer
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.1.0
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.4.5
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Module to define equality, equivalence and inspection methods
|
97
|
+
test_files:
|
98
|
+
- spec/unit/equalizer/included_spec.rb
|
99
|
+
- spec/unit/equalizer/methods/eql_predicate_spec.rb
|
100
|
+
- spec/unit/equalizer/methods/equality_operator_spec.rb
|
101
|
+
- spec/unit/equalizer/universal_spec.rb
|
102
|
+
has_rdoc:
|