find_duplicates 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -3
- data/lib/find_duplicates/version.rb +1 -1
- data/lib/find_duplicates.rb +34 -9
- data/spec/find_duplicates_spec.rb +90 -29
- data/spec/spec_helper.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcba9dbaac1d95a2a7db55acc525fe99044c7b15
|
4
|
+
data.tar.gz: 2bb1b972fbb79fbca75928e47c2e528fe1340eea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fc54d866ece1ca1602f08332fe841da89227f42abbf05aba28eece7728a28915075a69c6275c3e5d40a8a111bf3e181ddd9254feb795490cd190832f03c67ce
|
7
|
+
data.tar.gz: 487b12b8a2ff9a503352f230613049c1b4d3cadddd0d3237aeeb7b1382ad345afd7e400263bc4c0d6fdad7a4badba7a2d5e1fb5c94975c621a32f2da9bc04c7f
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# FindDuplicates
|
2
2
|
|
3
|
-
This
|
4
|
-
|
5
|
-
Use it to add methods to ActiveRecord models, that allow to find rows with duplicate values in certain columns. For example, find all users with the same first name, or find all items with the same price as a specific item.
|
3
|
+
This GEM allows you to find duplicate models with same value for a column, e.g.:
|
4
|
+
Find all users with the same first name.
|
6
5
|
|
7
6
|
Just add 'find_duplicates' to your Gemfile, and you automatically enhance your AR models functionality.
|
8
7
|
|
@@ -39,6 +38,14 @@ class methods:
|
|
39
38
|
> User.duplicates(:last_name) # => [@user1, @user2]
|
40
39
|
```
|
41
40
|
|
41
|
+
also works with multiple fields:
|
42
|
+
```ruby
|
43
|
+
> @user4 = User.create first_name: 'John', last_name: 'Major'
|
44
|
+
> @user5 = User.create first_name: 'John', last_name: 'Major'
|
45
|
+
> User.duplicates(:first_name, :last_name) # => [@user4, @user5]
|
46
|
+
> User.duplicates([:first_name, :last_name]) # => [@user4, @user5]
|
47
|
+
```
|
48
|
+
|
42
49
|
instance methods:
|
43
50
|
```ruby
|
44
51
|
> @user1.duplicate?(:first_name) # => true
|
@@ -46,6 +53,12 @@ instance methods:
|
|
46
53
|
> @user1.duplicates_with_self(:last_name) # => [@user1, @user2]
|
47
54
|
```
|
48
55
|
|
56
|
+
instance methods also work with multiple fields:
|
57
|
+
```ruby
|
58
|
+
> @user1.duplicates_with_self(:first_name, :last_name) # => []
|
59
|
+
> @user4.duplicates_with_self(:first_name, :last_name) # => [@user4, @user5]
|
60
|
+
```
|
61
|
+
|
49
62
|
## Contributing
|
50
63
|
|
51
64
|
1. Fork it
|
data/lib/find_duplicates.rb
CHANGED
@@ -4,23 +4,48 @@ require 'active_support/concern'
|
|
4
4
|
|
5
5
|
module FindDuplicates
|
6
6
|
extend ActiveSupport::Concern
|
7
|
-
def duplicate?(
|
8
|
-
|
7
|
+
def duplicate?(*args)
|
8
|
+
set_fields(args)
|
9
|
+
duplicates_with_self(@fields).count > 1
|
9
10
|
end
|
10
11
|
|
11
|
-
def duplicates(
|
12
|
-
|
12
|
+
def duplicates(*args)
|
13
|
+
set_fields(args)
|
14
|
+
duplicates_with_self(@fields).where('id <> ?', id)
|
13
15
|
end
|
14
16
|
|
15
|
-
def duplicates_with_self(
|
16
|
-
|
17
|
+
def duplicates_with_self(*args)
|
18
|
+
set_fields(args)
|
19
|
+
self.class.where(self.attributes.slice(*@fields))
|
17
20
|
end
|
18
21
|
module ClassMethods
|
19
|
-
def duplicates(
|
20
|
-
|
21
|
-
|
22
|
+
def duplicates(*args)
|
23
|
+
set_fields(args)
|
24
|
+
self.joins("join (
|
25
|
+
select #{fields_str}, count(*) as qty
|
26
|
+
from #{self.table_name}
|
27
|
+
group by #{fields_str}
|
28
|
+
having count(*) > 1
|
29
|
+
) t on #{fields_query}").all
|
22
30
|
end
|
31
|
+
private
|
32
|
+
def set_fields(args)
|
33
|
+
@fields = args.is_a?(Array) ? args : [args]
|
34
|
+
@fields = @fields.flatten.map &:to_s
|
35
|
+
end
|
36
|
+
def fields_str
|
37
|
+
@fields.join(',')
|
38
|
+
end
|
39
|
+
def fields_query
|
40
|
+
@fields.map {|f| "#{self.table_name}.#{f} = t.#{f}"}.join(' and ')
|
41
|
+
end
|
23
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def set_fields(args)
|
46
|
+
@fields = args.is_a?(Array) ? args : [args]
|
47
|
+
@fields = @fields.flatten.map &:to_s
|
48
|
+
end
|
24
49
|
end
|
25
50
|
|
26
51
|
class ActiveRecord::Base
|
@@ -5,43 +5,104 @@ describe FindDuplicates do
|
|
5
5
|
setup
|
6
6
|
end
|
7
7
|
|
8
|
-
context '
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
context 'single' do
|
9
|
+
context 'duplicate records' do
|
10
|
+
before do
|
11
|
+
@first = TestDuplicates.create!(title: 'first!')
|
12
|
+
@second = TestDuplicates.create!(title: 'second!')
|
13
|
+
@third = TestDuplicates.create!(title: 'first!')
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
it 'class duplicates' do
|
17
|
+
expect(TestDuplicates.duplicates(:title)).to match_array([@first, @third])
|
18
|
+
end
|
19
|
+
it 'instance duplicates' do
|
20
|
+
expect(@first.duplicates(:title)).to match_array([@third])
|
21
|
+
end
|
22
|
+
it 'duplicates_with_self' do
|
23
|
+
expect(@first.duplicates_with_self(:title)).to match_array([@first, @third])
|
24
|
+
end
|
25
|
+
it 'duplicate?' do
|
26
|
+
expect(@first.duplicate?(:title)).to be_truthy
|
27
|
+
end
|
23
28
|
end
|
24
|
-
|
25
|
-
|
29
|
+
context 'no duplicate records' do
|
30
|
+
before do
|
31
|
+
@first = TestDuplicates.create!(title: 'first!')
|
32
|
+
@second = TestDuplicates.create!(title: 'second!')
|
33
|
+
@third = TestDuplicates.create!(title: 'third!')
|
34
|
+
end
|
35
|
+
it 'duplicates' do
|
36
|
+
expect(TestDuplicates.duplicates(:title)).to be_empty
|
37
|
+
end
|
38
|
+
it 'instance duplicates' do
|
39
|
+
expect(@first.duplicates(:title)).to be_empty
|
40
|
+
end
|
41
|
+
it 'duplicates_with_self' do
|
42
|
+
expect(@first.duplicates_with_self(:title)).to match_array([@first])
|
43
|
+
end
|
44
|
+
it 'duplicate?' do
|
45
|
+
expect(@first.duplicate?(:title)).to be_falsey
|
46
|
+
end
|
26
47
|
end
|
27
48
|
end
|
28
|
-
context '
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
49
|
+
context 'multiple' do
|
50
|
+
context 'duplicate records' do
|
51
|
+
before do
|
52
|
+
@first = TestDuplicatesMultiple.create!(first_name: 'first!', last_name: 'first!')
|
53
|
+
@second = TestDuplicatesMultiple.create!(first_name: 'first!', last_name: 'first!')
|
54
|
+
@third = TestDuplicatesMultiple.create!(first_name: 'second!', last_name: 'first!')
|
55
|
+
@forth = TestDuplicatesMultiple.create!(first_name: 'first!', last_name: 'second!')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'class duplicates' do
|
59
|
+
expect(TestDuplicatesMultiple.duplicates(:first_name, :last_name)).to match_array([@first, @second])
|
60
|
+
end
|
61
|
+
it 'instance duplicates' do
|
62
|
+
expect(@first.duplicates(:first_name, :last_name)).to match_array([@second])
|
63
|
+
end
|
64
|
+
it 'duplicates_with_self' do
|
65
|
+
expect(@first.duplicates_with_self(:first_name, :last_name)).to match_array([@first, @second])
|
66
|
+
end
|
67
|
+
it 'duplicate?' do
|
68
|
+
expect(@first.duplicate?(:first_name, :last_name)).to be_truthy
|
69
|
+
end
|
33
70
|
end
|
34
|
-
|
35
|
-
|
71
|
+
context 'no duplicate records' do
|
72
|
+
before do
|
73
|
+
@first = TestDuplicatesMultiple.create!(first_name: 'first!', last_name: 'first!')
|
74
|
+
@second = TestDuplicatesMultiple.create!(first_name: 'first!', last_name: 'second!')
|
75
|
+
@third = TestDuplicatesMultiple.create!(first_name: 'second!', last_name: 'first!')
|
76
|
+
end
|
77
|
+
it 'duplicates' do
|
78
|
+
expect(TestDuplicatesMultiple.duplicates(:first_name, :last_name)).to be_empty
|
79
|
+
end
|
80
|
+
it 'instance duplicates' do
|
81
|
+
expect(@first.duplicates(:first_name, :last_name)).to be_empty
|
82
|
+
end
|
83
|
+
it 'duplicates_with_self' do
|
84
|
+
expect(@first.duplicates_with_self(:first_name, :last_name)).to match_array([@first])
|
85
|
+
end
|
86
|
+
it 'duplicate?' do
|
87
|
+
expect(@first.duplicate?(:first_name, :last_name)).to be_falsey
|
88
|
+
end
|
36
89
|
end
|
37
|
-
|
38
|
-
|
90
|
+
end
|
91
|
+
context 'params' do
|
92
|
+
it 'single param' do
|
93
|
+
td = TestDuplicatesMultiple.new
|
94
|
+
td.send(:set_fields, [:first_name])
|
95
|
+
expect(td.instance_variable_get(:@fields)).to eql(['first_name'])
|
39
96
|
end
|
40
|
-
it '
|
41
|
-
|
97
|
+
it 'multiple params' do
|
98
|
+
td = TestDuplicatesMultiple.new
|
99
|
+
td.send(:set_fields, [:first_name, :last_name])
|
100
|
+
expect(td.instance_variable_get(:@fields)).to eql(['first_name', 'last_name'])
|
42
101
|
end
|
43
|
-
it '
|
44
|
-
|
102
|
+
it 'params array' do
|
103
|
+
td = TestDuplicatesMultiple.new
|
104
|
+
td.send(:set_fields, [[:first_name, :last_name]])
|
105
|
+
expect(td.instance_variable_get(:@fields)).to eql(['first_name', 'last_name'])
|
45
106
|
end
|
46
107
|
end
|
47
108
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -28,7 +28,15 @@ def setup
|
|
28
28
|
t.column :title, :string
|
29
29
|
end
|
30
30
|
end
|
31
|
+
ActiveRecord::Schema.define do
|
32
|
+
create_table :test_duplicates_multiples, force: true do |t|
|
33
|
+
t.column :first_name, :string
|
34
|
+
t.column :last_name, :string
|
35
|
+
end
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
class TestDuplicates < ActiveRecord::Base
|
34
40
|
end
|
41
|
+
class TestDuplicatesMultiple < ActiveRecord::Base
|
42
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: find_duplicates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- adam klein
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-06-
|
12
|
+
date: 2014-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|