activerecord-data_integrity 0.1.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 +7 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.travis.yml +18 -0
- data/Appraisals +13 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +80 -0
- data/Rakefile +10 -0
- data/activerecord-data_integrity.gemspec +48 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dip.yml +46 -0
- data/docker-compose.yml +28 -0
- data/exe/data_integrity +7 -0
- data/lib/active_record/data_integrity/cli.rb +38 -0
- data/lib/active_record/data_integrity/collector.rb +41 -0
- data/lib/active_record/data_integrity/cop/accordance/primary_key.rb +33 -0
- data/lib/active_record/data_integrity/cop/accordance/table_presence.rb +19 -0
- data/lib/active_record/data_integrity/cop/belongs_to/foreign_key.rb +50 -0
- data/lib/active_record/data_integrity/cop/cop.rb +27 -0
- data/lib/active_record/data_integrity/cop/has_many/foreign_key.rb +50 -0
- data/lib/active_record/data_integrity/cop/validation/inclusion.rb +48 -0
- data/lib/active_record/data_integrity/cop/validation/presence.rb +46 -0
- data/lib/active_record/data_integrity/version.rb +7 -0
- data/lib/active_record/data_integrity.rb +22 -0
- data/lib/activerecord/data_integrity.rb +3 -0
- metadata +228 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 15651ea16d6fc6051e45163a15ff512472c440ee173e4c8c9acd9cf3e45e010f
|
|
4
|
+
data.tar.gz: 4badc1574b0c1c1329d1f0fdd14e10016a7097113cc6bf098751062d53f68cb3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 698d90f68b8f72a07f2cbc3e5910d60418a3ac4a2aa881007ebccf5e656d3ee85c52d2140218a42396025cb79d782e6a36406b666ed22dfd6cacd4bbc1df0192
|
|
7
|
+
data.tar.gz: 41e5fd22054feeb45b1f53ce9df8d00a4d070dc944690d1e79b4c7f3a8e9c797956bb6e35c75c5bd26c5b0be44683a8da43680fc77516e41f49e4f19db46d819
|
data/.gitignore
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/spec/examples.txt
|
|
9
|
+
spec/internal/log/
|
|
10
|
+
/test/tmp/
|
|
11
|
+
/test/version_tmp/
|
|
12
|
+
/tmp/
|
|
13
|
+
Gemfile.lock
|
|
14
|
+
|
|
15
|
+
# Used by dotenv library to load environment variables.
|
|
16
|
+
# .env
|
|
17
|
+
|
|
18
|
+
## Specific to RubyMotion:
|
|
19
|
+
.dat*
|
|
20
|
+
.repl_history
|
|
21
|
+
build/
|
|
22
|
+
*.bridgesupport
|
|
23
|
+
build-iPhoneOS/
|
|
24
|
+
build-iPhoneSimulator/
|
|
25
|
+
|
|
26
|
+
## Specific to RubyMotion (use of CocoaPods):
|
|
27
|
+
#
|
|
28
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
|
29
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
|
30
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
|
31
|
+
#
|
|
32
|
+
# vendor/Pods/
|
|
33
|
+
|
|
34
|
+
## Documentation cache and generated files:
|
|
35
|
+
/.yardoc/
|
|
36
|
+
/_yardoc/
|
|
37
|
+
/doc/
|
|
38
|
+
/rdoc/
|
|
39
|
+
|
|
40
|
+
## Environment normalization:
|
|
41
|
+
/.bundle/
|
|
42
|
+
/vendor/bundle
|
|
43
|
+
/lib/bundler/man/
|
|
44
|
+
|
|
45
|
+
# for a library or gem, you might want to ignore these files since the code is
|
|
46
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
47
|
+
# Gemfile.lock
|
|
48
|
+
# .ruby-version
|
|
49
|
+
# .ruby-gemset
|
|
50
|
+
|
|
51
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
52
|
+
.rvmrc
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require: rubocop-rspec
|
|
2
|
+
|
|
3
|
+
AllCops:
|
|
4
|
+
TargetRubyVersion: 2.5
|
|
5
|
+
TargetRailsVersion: 4.0
|
|
6
|
+
Exclude:
|
|
7
|
+
- 'activerecord-data_integrity.gemspec'
|
|
8
|
+
- 'spec/support/*'
|
|
9
|
+
- 'tmp/**/*'
|
|
10
|
+
- 'gemfiles/*'
|
|
11
|
+
- 'spec/internal/**/*'
|
|
12
|
+
|
|
13
|
+
Metrics/LineLength:
|
|
14
|
+
Max: 110
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
sudo: require
|
|
2
|
+
services:
|
|
3
|
+
- docker
|
|
4
|
+
install:
|
|
5
|
+
- gem install dip
|
|
6
|
+
before_script:
|
|
7
|
+
- unset BUNDLE_GEMFILE
|
|
8
|
+
- dip provision
|
|
9
|
+
script:
|
|
10
|
+
- dip rspec
|
|
11
|
+
- dip rubocop
|
|
12
|
+
deploy:
|
|
13
|
+
provider: rubygems
|
|
14
|
+
api_key:
|
|
15
|
+
secure: ILU6srwid/tAV00OmpcR4hpyTHO8TWK4C1wIYxmt+v0f/g8BuzsaZ/iXLM8aXWu7uRGeyp9jV0kA8Dg4KTCq4FlTazo3u2al7wm840h2KhB/mj/QxN8GBIr6hySQaouwRfzVRpVr7KBcHp10ElBd9GXhVm5v5V/j14RYW9wELqwQSyBnAjv2AI8InHJZE+tGnioqqSQHv7bM3fEnOMyd7BGwnvmVIQS2RbW+65i1r8iTFOrR+cpL1lg+q1Kbk0tr1ciEyg0kkNvZ/DUu8AxNMIlZlSdcNL73BN5a/CtJaAclSHbwEr2yGg3fFT61SR20/RmhE45v+crE8sKP+xGxhqkg+GL+LQQ5ouDuHi6r2tLOTMyeVUs402AWBth8dWyd1Xcvbho7owSLlUirYD8clD4Kh+XyfKMC1p4cjJiR4Id1F0089o12GcrwQebaYphnbrCYEtcoC8gts9p1fDiiY9ekrMfh8GcpYIHDmKbmpx79bCRk4Z45qdyOP206df2/pDEqLCEs5YoCP/rNpTi0ft1nRomlBPoX6ZeSrle9f3wbmp49Jrn/j3jENth24jkDpdE7D0Tr8nyGARTR/RG3K3XuXTTkCWQRynSM/CbitnLaRFv4+1ngQnpmiYOzHzcwSpMul9tLnQj3GeMXUawBs/OYD61NgG+FBczIQ295JO8=
|
|
16
|
+
gem: activerecord-data_integrity
|
|
17
|
+
on:
|
|
18
|
+
tags: true
|
data/Appraisals
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Salahutdinov Dmitry
|
|
4
|
+
|
|
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:
|
|
11
|
+
|
|
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/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Salahutdinov Dmitry
|
|
4
|
+
|
|
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:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/activerecord-data_integrity)
|
|
2
|
+
[](https://travis-ci.org/dsalahutdinov/activerecord-data_integrity.svg?branch=master)
|
|
3
|
+
|
|
4
|
+
# ActiveRecord::DataIntegrity
|
|
5
|
+
|
|
6
|
+
Checks your ActiveRecord models to match data integrity principles and rules.
|
|
7
|
+
Out of the box it enforces many issues such as the lack of foreign keys.
|
|
8
|
+
|
|
9
|
+
## Intallation
|
|
10
|
+
```ruby
|
|
11
|
+
group :development do
|
|
12
|
+
...
|
|
13
|
+
gem 'activerecord-data_integrity', require: false
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
## Quickstart
|
|
17
|
+
|
|
18
|
+
Run `data_integrity` CLI-tool in your Rails project's folder:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd ~/amplifr
|
|
22
|
+
bundle exec data_integrity
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It will load the Rails application, check the data integrity issues and give the similar output:
|
|
26
|
+
```
|
|
27
|
+
BelongsTo/ForeignKey: Label belongs_to project but has no foreign key to projects.id
|
|
28
|
+
Accordance/TablePresence: Stat::Hourly has no underlying table hourly_stats
|
|
29
|
+
...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Supported Issues
|
|
33
|
+
|
|
34
|
+
For now tool checks the following issues:
|
|
35
|
+
- [x] The lack of database foreign keys for belongs_to/has_many associations
|
|
36
|
+
- [x] The lack of not-null constraint for the columns with presence validation
|
|
37
|
+
- [x] Inclusion validated colums should have `enum` data type
|
|
38
|
+
|
|
39
|
+
## Roadmap (TODO & Help Wanted)
|
|
40
|
+
|
|
41
|
+
1) Support extra database issues, such as:
|
|
42
|
+
- [ ] presence of `dependend` option set for association and not confliction with underlying `ON DELETE` option of foreign key contraint
|
|
43
|
+
- [ ] check for foreign keys to have bigint data type
|
|
44
|
+
- [ ] presence of index for the foreign keys search
|
|
45
|
+
- [ ] checks for paranoia models and indexes excludion "removed" rows
|
|
46
|
+
|
|
47
|
+
2) Config for exluding some rules for the specific models (rubocop like)
|
|
48
|
+
|
|
49
|
+
3) Autofix for the fixing the issue, mostly by generating safe migrations
|
|
50
|
+
|
|
51
|
+
4) Possibility to run with checking only specific rule
|
|
52
|
+
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/activerecord-data_integrity.
|
|
56
|
+
|
|
57
|
+
## Run tests
|
|
58
|
+
|
|
59
|
+
The easiest way to tests up and running is to use handy [dip](https://github.com/bibendi/dip) gem with Docker and Docker Compose:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
gem install dip
|
|
63
|
+
git checkout git@github.com:dsalahutdinov/activerecord-data_integrity.git
|
|
64
|
+
cd activerecord-data_integrity
|
|
65
|
+
dip provision
|
|
66
|
+
dip rspec
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Otherwise (without Docker) set up environment manually:
|
|
70
|
+
```bash
|
|
71
|
+
git checkout git@github.com:dsalahutdinov/activerecord-data_integrity.git
|
|
72
|
+
cd activerecord-data_integrity
|
|
73
|
+
bundle install
|
|
74
|
+
bundle appraisal
|
|
75
|
+
DB_HOST=localhost DB_NAME=testdb DB_USERNAME=postgres bundle appraisal rspec
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'active_record/data_integrity/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'activerecord-data_integrity'
|
|
9
|
+
spec.version = ActiveRecord::DataIntegrity::VERSION
|
|
10
|
+
spec.authors = ['Salahutdinov Dmitry']
|
|
11
|
+
spec.email = ['dsalahutdinov@gmail.com']
|
|
12
|
+
|
|
13
|
+
spec.summary = 'Checks your ActiveRecord models to match data integrity principles and rules'
|
|
14
|
+
spec.description = 'CLI-tool to check data integrity of the Rails-application project'
|
|
15
|
+
spec.homepage = 'https://github.com/dsalahutdinov/activerecord-data_integrity'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
|
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
20
|
+
if spec.respond_to?(:metadata)
|
|
21
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
22
|
+
else
|
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
|
24
|
+
'public gem pushes.'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
28
|
+
f.match(%r{^(test|spec|features)/})
|
|
29
|
+
end
|
|
30
|
+
spec.bindir = 'exe'
|
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
32
|
+
spec.require_paths = ['lib']
|
|
33
|
+
|
|
34
|
+
spec.add_runtime_dependency 'pg'
|
|
35
|
+
spec.add_runtime_dependency 'rails'
|
|
36
|
+
spec.add_runtime_dependency 'rainbow'
|
|
37
|
+
|
|
38
|
+
spec.add_development_dependency 'appraisal', '~> 2.2'
|
|
39
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
40
|
+
spec.add_development_dependency 'combustion', '~> 1.0'
|
|
41
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
42
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
43
|
+
|
|
44
|
+
spec.add_development_dependency 'rubocop'
|
|
45
|
+
spec.add_development_dependency 'rubocop-rspec'
|
|
46
|
+
|
|
47
|
+
spec.add_development_dependency 'byebug'
|
|
48
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'activerecord-data_integrity'
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require 'irb'
|
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/dip.yml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
version: '2'
|
|
2
|
+
|
|
3
|
+
environment:
|
|
4
|
+
BUNDLE_GEMFILE: /app/Gemfile
|
|
5
|
+
|
|
6
|
+
compose:
|
|
7
|
+
files:
|
|
8
|
+
- docker-compose.yml
|
|
9
|
+
|
|
10
|
+
interaction:
|
|
11
|
+
bash:
|
|
12
|
+
service: app
|
|
13
|
+
compose_run_options: [no-deps]
|
|
14
|
+
|
|
15
|
+
app:
|
|
16
|
+
service: app
|
|
17
|
+
subcommands:
|
|
18
|
+
console:
|
|
19
|
+
command: ./bin/console
|
|
20
|
+
clean:
|
|
21
|
+
command: rm -rf Gemfile.lock gemfiles/*.gemfile.*
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
bundle:
|
|
25
|
+
service: app
|
|
26
|
+
command: bundle
|
|
27
|
+
compose_run_options: [no-deps]
|
|
28
|
+
|
|
29
|
+
appraisal:
|
|
30
|
+
service: app
|
|
31
|
+
command: bundle exec appraisal
|
|
32
|
+
compose_run_options: [no-deps]
|
|
33
|
+
|
|
34
|
+
rspec:
|
|
35
|
+
service: app
|
|
36
|
+
command: bundle exec appraisal bundle exec rspec
|
|
37
|
+
|
|
38
|
+
rubocop:
|
|
39
|
+
service: app
|
|
40
|
+
command: bundle exec rubocop
|
|
41
|
+
compose_run_options: [no-deps]
|
|
42
|
+
|
|
43
|
+
provision:
|
|
44
|
+
- dip app clean
|
|
45
|
+
- dip bundle install
|
|
46
|
+
- dip appraisal install
|
data/docker-compose.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: '3.4'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
app:
|
|
5
|
+
image: ruby:2.5.1
|
|
6
|
+
environment:
|
|
7
|
+
- BUNDLE_PATH=/bundle
|
|
8
|
+
- BUNDLE_CONFIG=/app/.bundle/config
|
|
9
|
+
- DB_HOST=db
|
|
10
|
+
- DB_NAME=docker
|
|
11
|
+
- DB_USERNAME=postgres
|
|
12
|
+
command: bash
|
|
13
|
+
working_dir: /app
|
|
14
|
+
volumes:
|
|
15
|
+
- .:/app:cached
|
|
16
|
+
- bundler_data:/bundle
|
|
17
|
+
tmpfs:
|
|
18
|
+
- /tmp
|
|
19
|
+
depends_on:
|
|
20
|
+
- db
|
|
21
|
+
|
|
22
|
+
db:
|
|
23
|
+
image: postgres:10
|
|
24
|
+
environment:
|
|
25
|
+
- POSTGRES_DB=docker
|
|
26
|
+
|
|
27
|
+
volumes:
|
|
28
|
+
bundler_data:
|
data/exe/data_integrity
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module DataIntegrity
|
|
5
|
+
# CLI application class
|
|
6
|
+
class CLI
|
|
7
|
+
def initialize; end
|
|
8
|
+
|
|
9
|
+
def run
|
|
10
|
+
require_rails
|
|
11
|
+
|
|
12
|
+
results = cops.map do |cop_class|
|
|
13
|
+
ActiveRecord::Base.descendants.each do |model|
|
|
14
|
+
cop_class.new(model).call
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ActiveRecord::DataIntegrity::Collector.render
|
|
19
|
+
|
|
20
|
+
exit(1) if results.include?(false)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def cops
|
|
26
|
+
@cops ||= ActiveRecord::DataIntegrity::Cop.descendants
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def require_rails
|
|
30
|
+
# Rails load ugly hack :)
|
|
31
|
+
require File.expand_path('config/environment', Dir.pwd)
|
|
32
|
+
Kernel.const_set(:APP_PATH, File.expand_path('config/application', Dir.pwd))
|
|
33
|
+
Rails.application.eager_load!
|
|
34
|
+
Rails.logger.level = 0
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module DataIntegrity
|
|
5
|
+
# collects result info for rendering
|
|
6
|
+
class Collector
|
|
7
|
+
class << self
|
|
8
|
+
def log(cop, message)
|
|
9
|
+
data.push(cop: cop, message: message)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def progress(_cop, char)
|
|
13
|
+
print char
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render
|
|
17
|
+
group_data_by_cop_name.each do |cop_name, items|
|
|
18
|
+
items.each do |item|
|
|
19
|
+
puts "#{Rainbow(cop_name).red}:"\
|
|
20
|
+
" #{Rainbow(item[:cop].model.name).yellow}"\
|
|
21
|
+
" #{item[:message]}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def data
|
|
29
|
+
@data ||= []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def group_data_by_cop_name
|
|
33
|
+
data.each_with_object({}) do |item, hash|
|
|
34
|
+
hash[item[:cop].name] ||= []
|
|
35
|
+
hash[item[:cop].name] << item
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module Accordance
|
|
8
|
+
# Checks the primary key integer has 8 bytes length
|
|
9
|
+
class PrimaryKey < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
log('has short integer primary key') unless valid?
|
|
12
|
+
progress(valid?, 'P')
|
|
13
|
+
valid?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def valid?
|
|
19
|
+
return true unless connection.table_exists?(model.table_name)
|
|
20
|
+
|
|
21
|
+
@valid ||= !connection.primary_keys(model.table_name).map! do |pk|
|
|
22
|
+
column_valid?(pk)
|
|
23
|
+
end.include?(false)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def column_valid?(name)
|
|
27
|
+
column = connection.columns(model.table_name).find { |c| c.name == name }
|
|
28
|
+
column.type == :integer && column.limit.present? && column.limit >= 8
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module Accordance
|
|
8
|
+
# Check the presence of the underlying table for the model
|
|
9
|
+
class TablePresence < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
connection.table_exists?(model.table_name).tap do |result|
|
|
12
|
+
log("has no underlying table #{model.table_name}") unless result
|
|
13
|
+
progress(result, 'T')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module BelongsTo
|
|
8
|
+
# Checks foreign key presence to the parent table of belongs_to association
|
|
9
|
+
class ForeignKey < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
results = associations.map do |association|
|
|
12
|
+
valid?(association)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
results.none?(&:!)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def valid?(association)
|
|
21
|
+
success = foreign_key?(association)
|
|
22
|
+
unless success
|
|
23
|
+
to_table = association.class_name.constantize.table_name
|
|
24
|
+
log("belongs_to #{association.name} but has no foreign key to #{to_table}.id")
|
|
25
|
+
end
|
|
26
|
+
progress(success, 'F')
|
|
27
|
+
|
|
28
|
+
success
|
|
29
|
+
rescue NameError
|
|
30
|
+
log("Error processing #{model.name}.#{association.name}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def associations
|
|
34
|
+
model
|
|
35
|
+
._reflections
|
|
36
|
+
.values
|
|
37
|
+
.select { |association| association.is_a?(ActiveRecord::Reflection::BelongsToReflection) }
|
|
38
|
+
.reject(&:polymorphic?)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def foreign_key?(association)
|
|
42
|
+
to_table = association.class_name.constantize.table_name
|
|
43
|
+
connection.foreign_keys(model.table_name).any? do |foreign_key|
|
|
44
|
+
foreign_key.to_table == to_table && foreign_key.options.fetch(:primary_key) == 'id'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module DataIntegrity
|
|
5
|
+
# Checking cop base class
|
|
6
|
+
class Cop
|
|
7
|
+
attr_reader :model
|
|
8
|
+
delegate :connection, to: :model
|
|
9
|
+
|
|
10
|
+
def initialize(model)
|
|
11
|
+
@model = model
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def name
|
|
15
|
+
self.class.name.gsub('ActiveRecord::DataIntegrity::', '').gsub('::', '/')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def log(msg)
|
|
19
|
+
ActiveRecord::DataIntegrity::Collector.log(self, msg)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def progress(subresult, false_char = 'E')
|
|
23
|
+
ActiveRecord::DataIntegrity::Collector.progress(self, subresult ? '.' : false_char)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module HasMany
|
|
8
|
+
# Checks foreign key presence to the parent table of belongs_to association
|
|
9
|
+
class ForeignKey < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
results = associations.map do |association|
|
|
12
|
+
valid?(association)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
results.none?(&:!)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def valid?(association)
|
|
21
|
+
success = foreign_key?(association)
|
|
22
|
+
unless success
|
|
23
|
+
from_table = association.class_name.constantize.table_name
|
|
24
|
+
log("has_many #{association.name} but has no foreign key from #{from_table}.id")
|
|
25
|
+
end
|
|
26
|
+
progress(success, 'F')
|
|
27
|
+
|
|
28
|
+
success
|
|
29
|
+
rescue NameError
|
|
30
|
+
log("Error processing #{model.name}.#{association.name}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def associations
|
|
34
|
+
model
|
|
35
|
+
._reflections
|
|
36
|
+
.values
|
|
37
|
+
.select { |association| association.is_a?(ActiveRecord::Reflection::HasManyReflection) }
|
|
38
|
+
.reject(&:polymorphic?)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def foreign_key?(association)
|
|
42
|
+
to_table = model.table_name
|
|
43
|
+
connection.foreign_keys(association.class_name.constantize.table_name).any? do |foreign_key|
|
|
44
|
+
foreign_key.to_table == to_table && foreign_key.options.fetch(:primary_key) == 'id'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module Validation
|
|
8
|
+
# Checks foreign key presence to the parent table of belongs_to association
|
|
9
|
+
class Inclusion < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
results = validators.map do |validator|
|
|
12
|
+
validator.attributes.map do |attribute|
|
|
13
|
+
valid?(attribute)
|
|
14
|
+
end
|
|
15
|
+
end.flatten
|
|
16
|
+
|
|
17
|
+
results.none?(&:!)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def valid?(attribute)
|
|
23
|
+
column = find_column(attribute)
|
|
24
|
+
return true if column.nil?
|
|
25
|
+
|
|
26
|
+
result = (column.type == :enum)
|
|
27
|
+
progress(result, 'D')
|
|
28
|
+
!column && log("has column #{attribute} with inclusion which is not enum") unless result
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validators
|
|
33
|
+
model
|
|
34
|
+
.validators
|
|
35
|
+
.select { |v| v.is_a?(ActiveModel::Validations::InclusionValidator) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find_column(attribute)
|
|
39
|
+
return nil unless connection.table_exists?(model.table_name)
|
|
40
|
+
|
|
41
|
+
connection
|
|
42
|
+
.columns(model.table_name)
|
|
43
|
+
.find { |col| col.name == attribute.to_s }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cop'
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module DataIntegrity
|
|
7
|
+
module Validation
|
|
8
|
+
# Checks foreign key presence to the parent table of belongs_to association
|
|
9
|
+
class Presence < ActiveRecord::DataIntegrity::Cop
|
|
10
|
+
def call
|
|
11
|
+
results = validators.map do |validator|
|
|
12
|
+
validator.attributes.map do |attribute|
|
|
13
|
+
valid?(attribute)
|
|
14
|
+
end
|
|
15
|
+
end.flatten
|
|
16
|
+
|
|
17
|
+
results.none?(&:!)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def valid?(attribute)
|
|
23
|
+
column = find_column(attribute)
|
|
24
|
+
return true if column.nil?
|
|
25
|
+
|
|
26
|
+
result = !column.null
|
|
27
|
+
progress(result, 'D')
|
|
28
|
+
!column && log("has nullable column #{attribute} with presence validation") unless result
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validators
|
|
33
|
+
model
|
|
34
|
+
.validators
|
|
35
|
+
.select { |v| v.is_a?(ActiveRecord::Validations::PresenceValidator) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find_column(attribute)
|
|
39
|
+
connection
|
|
40
|
+
.columns(model.table_name)
|
|
41
|
+
.find { |col| col.name == attribute.to_s }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_record/data_integrity/version'
|
|
4
|
+
|
|
5
|
+
require 'rails/all'
|
|
6
|
+
require 'active_record/data_integrity/cli'
|
|
7
|
+
require 'rainbow'
|
|
8
|
+
|
|
9
|
+
require 'active_record/data_integrity/collector'
|
|
10
|
+
|
|
11
|
+
require 'active_record/data_integrity/cop/accordance/table_presence'
|
|
12
|
+
require 'active_record/data_integrity/cop/accordance/primary_key'
|
|
13
|
+
require 'active_record/data_integrity/cop/belongs_to/foreign_key'
|
|
14
|
+
require 'active_record/data_integrity/cop/validation/presence'
|
|
15
|
+
require 'active_record/data_integrity/cop/validation/inclusion'
|
|
16
|
+
require 'active_record/data_integrity/cop/has_many/foreign_key'
|
|
17
|
+
|
|
18
|
+
module ActiveRecord
|
|
19
|
+
# Base module for the gem
|
|
20
|
+
module DataIntegrity
|
|
21
|
+
end
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activerecord-data_integrity
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Salahutdinov Dmitry
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-11-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: pg
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rails
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rainbow
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: appraisal
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.2'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.2'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: bundler
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.16'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.16'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: combustion
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rake
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '10.0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '10.0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rspec
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '3.0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: rubocop
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: rubocop-rspec
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: byebug
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
description: CLI-tool to check data integrity of the Rails-application project
|
|
168
|
+
email:
|
|
169
|
+
- dsalahutdinov@gmail.com
|
|
170
|
+
executables:
|
|
171
|
+
- data_integrity
|
|
172
|
+
extensions: []
|
|
173
|
+
extra_rdoc_files: []
|
|
174
|
+
files:
|
|
175
|
+
- ".gitignore"
|
|
176
|
+
- ".rspec"
|
|
177
|
+
- ".rubocop.yml"
|
|
178
|
+
- ".travis.yml"
|
|
179
|
+
- Appraisals
|
|
180
|
+
- Gemfile
|
|
181
|
+
- LICENSE
|
|
182
|
+
- LICENSE.txt
|
|
183
|
+
- README.md
|
|
184
|
+
- Rakefile
|
|
185
|
+
- activerecord-data_integrity.gemspec
|
|
186
|
+
- bin/console
|
|
187
|
+
- bin/setup
|
|
188
|
+
- dip.yml
|
|
189
|
+
- docker-compose.yml
|
|
190
|
+
- exe/data_integrity
|
|
191
|
+
- lib/active_record/data_integrity.rb
|
|
192
|
+
- lib/active_record/data_integrity/cli.rb
|
|
193
|
+
- lib/active_record/data_integrity/collector.rb
|
|
194
|
+
- lib/active_record/data_integrity/cop/accordance/primary_key.rb
|
|
195
|
+
- lib/active_record/data_integrity/cop/accordance/table_presence.rb
|
|
196
|
+
- lib/active_record/data_integrity/cop/belongs_to/foreign_key.rb
|
|
197
|
+
- lib/active_record/data_integrity/cop/cop.rb
|
|
198
|
+
- lib/active_record/data_integrity/cop/has_many/foreign_key.rb
|
|
199
|
+
- lib/active_record/data_integrity/cop/validation/inclusion.rb
|
|
200
|
+
- lib/active_record/data_integrity/cop/validation/presence.rb
|
|
201
|
+
- lib/active_record/data_integrity/version.rb
|
|
202
|
+
- lib/activerecord/data_integrity.rb
|
|
203
|
+
homepage: https://github.com/dsalahutdinov/activerecord-data_integrity
|
|
204
|
+
licenses:
|
|
205
|
+
- MIT
|
|
206
|
+
metadata:
|
|
207
|
+
allowed_push_host: https://rubygems.org
|
|
208
|
+
post_install_message:
|
|
209
|
+
rdoc_options: []
|
|
210
|
+
require_paths:
|
|
211
|
+
- lib
|
|
212
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
|
+
requirements:
|
|
214
|
+
- - ">="
|
|
215
|
+
- !ruby/object:Gem::Version
|
|
216
|
+
version: '0'
|
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - ">="
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: '0'
|
|
222
|
+
requirements: []
|
|
223
|
+
rubyforge_project:
|
|
224
|
+
rubygems_version: 2.7.7
|
|
225
|
+
signing_key:
|
|
226
|
+
specification_version: 4
|
|
227
|
+
summary: Checks your ActiveRecord models to match data integrity principles and rules
|
|
228
|
+
test_files: []
|