evil-seed 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +86 -0
- data/.github/workflows/test.yml +27 -9
- data/CHANGELOG.md +142 -0
- data/Gemfile +7 -1
- data/README.md +40 -2
- data/evil-seed.gemspec +1 -1
- data/lib/evil_seed/configuration/root.rb +119 -12
- data/lib/evil_seed/configuration.rb +7 -3
- data/lib/evil_seed/dumper.rb +2 -1
- data/lib/evil_seed/record_dumper.rb +21 -10
- data/lib/evil_seed/relation_dumper.rb +92 -29
- data/lib/evil_seed/root_dumper.rb +7 -3
- data/lib/evil_seed/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 757a2cee2b774b29a9d248270642db41faa431fdfd1b738aed0b8ea275fa358a
|
4
|
+
data.tar.gz: 8dc1887a03f1169aa332f9ef345ea666d5582e578ec6c8c97c6041b6f281dff4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aea3be4c8d73fc5ec6e04457d6322306d12a70d44a4792eb1dc855162b28b3b325617815a8a5898733cdc31067e8c51fa3714909c123fe890d54e21123121f7f
|
7
|
+
data.tar.gz: 98ed722479590c6615d87dfa3a0262d7012f008f0c9be2c4ddb3066a01ef2959febaf3ecea60c25e362417e9eba986f7ada50fad9ce06355fb8b5dfbc75e664c
|
@@ -0,0 +1,86 @@
|
|
1
|
+
name: Release gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- v*
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
release:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: write
|
13
|
+
id-token: write
|
14
|
+
packages: write
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v4
|
17
|
+
with:
|
18
|
+
fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290
|
19
|
+
- uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: "3.3"
|
22
|
+
- name: "Extract data from tag: version, message, body"
|
23
|
+
id: tag
|
24
|
+
run: |
|
25
|
+
git fetch --tags --force # Really fetch annotated tag. See https://github.com/actions/checkout/issues/290#issuecomment-680260080
|
26
|
+
echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
|
27
|
+
echo ::set-output name=subject::$(git for-each-ref $GITHUB_REF --format='%(contents:subject)')
|
28
|
+
BODY="$(git for-each-ref $GITHUB_REF --format='%(contents:body)')"
|
29
|
+
# Extract changelog entries between this and previous version headers
|
30
|
+
escaped_version=$(echo ${GITHUB_REF#refs/tags/v} | sed -e 's/[]\/$*.^[]/\\&/g')
|
31
|
+
changelog=$(awk "BEGIN{inrelease=0} /## \[${escaped_version}\]/{inrelease=1;next} /## \[[0-9]+\.[0-9]+\.[0-9]+.*?\]/{inrelease=0;exit} {if (inrelease) print}" CHANGELOG.md)
|
32
|
+
# Multiline body for release. See https://github.community/t/set-output-truncates-multiline-strings/16852/5
|
33
|
+
BODY="${BODY}"$'\n'"${changelog}"
|
34
|
+
BODY="${BODY//'%'/'%25'}"
|
35
|
+
BODY="${BODY//$'\n'/'%0A'}"
|
36
|
+
BODY="${BODY//$'\r'/'%0D'}"
|
37
|
+
echo "::set-output name=body::$BODY"
|
38
|
+
# Add pre-release option if tag name has any suffix after vMAJOR.MINOR.PATCH
|
39
|
+
if [[ ${GITHUB_REF#refs/tags/} =~ ^v[0-9]+\.[0-9]+\.[0-9]+.+ ]]; then
|
40
|
+
echo ::set-output name=prerelease::true
|
41
|
+
fi
|
42
|
+
- name: Build gem
|
43
|
+
run: gem build
|
44
|
+
- name: Calculate checksums
|
45
|
+
run: sha256sum evil-seed-${{ steps.tag.outputs.version }}.gem > SHA256SUM
|
46
|
+
- name: Check version
|
47
|
+
run: ls -l evil-seed-${{ steps.tag.outputs.version }}.gem
|
48
|
+
- name: Create Release
|
49
|
+
id: create_release
|
50
|
+
uses: actions/create-release@v1
|
51
|
+
env:
|
52
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
53
|
+
with:
|
54
|
+
tag_name: ${{ github.ref }}
|
55
|
+
release_name: ${{ steps.tag.outputs.subject }}
|
56
|
+
body: ${{ steps.tag.outputs.body }}
|
57
|
+
draft: false
|
58
|
+
prerelease: ${{ steps.tag.outputs.prerelease }}
|
59
|
+
- name: Upload built gem as release asset
|
60
|
+
uses: actions/upload-release-asset@v1
|
61
|
+
env:
|
62
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
63
|
+
with:
|
64
|
+
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
65
|
+
asset_path: evil-seed-${{ steps.tag.outputs.version }}.gem
|
66
|
+
asset_name: evil-seed-${{ steps.tag.outputs.version }}.gem
|
67
|
+
asset_content_type: application/x-tar
|
68
|
+
- name: Upload checksums as release asset
|
69
|
+
uses: actions/upload-release-asset@v1
|
70
|
+
env:
|
71
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
72
|
+
with:
|
73
|
+
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
74
|
+
asset_path: SHA256SUM
|
75
|
+
asset_name: SHA256SUM
|
76
|
+
asset_content_type: text/plain
|
77
|
+
- name: Publish to GitHub packages
|
78
|
+
env:
|
79
|
+
GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }}
|
80
|
+
run: |
|
81
|
+
gem push evil-seed-${{ steps.tag.outputs.version }}.gem --host https://rubygems.pkg.github.com/${{ github.repository_owner }}
|
82
|
+
- name: Configure RubyGems Credentials
|
83
|
+
uses: rubygems/configure-rubygems-credentials@main
|
84
|
+
- name: Publish to RubyGems
|
85
|
+
run: |
|
86
|
+
gem push evil-seed-${{ steps.tag.outputs.version }}.gem
|
data/.github/workflows/test.yml
CHANGED
@@ -19,30 +19,48 @@ jobs:
|
|
19
19
|
- ruby: "head"
|
20
20
|
activerecord: "head"
|
21
21
|
database: sqlite
|
22
|
+
- ruby: "3.4"
|
23
|
+
activerecord: "8.0"
|
24
|
+
database: postgresql
|
25
|
+
- ruby: "3.4"
|
26
|
+
activerecord: "8.0"
|
27
|
+
database: mysql
|
28
|
+
- ruby: "3.4"
|
29
|
+
activerecord: "8.0"
|
30
|
+
database: sqlite
|
31
|
+
- ruby: "3.3"
|
32
|
+
activerecord: "7.2"
|
33
|
+
database: postgresql
|
34
|
+
- ruby: "3.3"
|
35
|
+
activerecord: "7.2"
|
36
|
+
database: mysql
|
37
|
+
- ruby: "3.3"
|
38
|
+
activerecord: "7.2"
|
39
|
+
database: sqlite
|
22
40
|
- ruby: "3.2"
|
23
|
-
activerecord: "7.
|
41
|
+
activerecord: "7.1"
|
24
42
|
database: postgresql
|
25
43
|
- ruby: "3.2"
|
26
|
-
activerecord: "7.
|
44
|
+
activerecord: "7.1"
|
27
45
|
database: mysql
|
28
46
|
- ruby: "3.2"
|
29
|
-
activerecord: "7.
|
47
|
+
activerecord: "7.1"
|
30
48
|
database: sqlite
|
31
49
|
- ruby: "3.1"
|
32
|
-
activerecord: "
|
50
|
+
activerecord: "7.0"
|
33
51
|
database: sqlite
|
34
52
|
- ruby: "3.0"
|
35
|
-
activerecord: "6.
|
53
|
+
activerecord: "6.1"
|
36
54
|
database: sqlite
|
37
55
|
- ruby: "2.7"
|
38
|
-
activerecord: "
|
56
|
+
activerecord: "6.0"
|
39
57
|
database: sqlite
|
40
58
|
|
41
59
|
runs-on: ubuntu-latest
|
42
60
|
|
43
61
|
services:
|
44
62
|
postgres:
|
45
|
-
image: postgres:
|
63
|
+
image: ${{ (matrix.database == 'postgresql') && 'postgres:17' || '' }}
|
46
64
|
env:
|
47
65
|
POSTGRES_USER: postgres
|
48
66
|
POSTGRES_PASSWORD: postgres
|
@@ -54,7 +72,7 @@ jobs:
|
|
54
72
|
--health-timeout 5s
|
55
73
|
--health-retries 5
|
56
74
|
mysql:
|
57
|
-
image: mysql:
|
75
|
+
image: ${{ (matrix.database == 'mysql') && 'mysql:9' || '' }}
|
58
76
|
env:
|
59
77
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
60
78
|
MYSQL_DATABASE: evil_seed_test
|
@@ -70,7 +88,7 @@ jobs:
|
|
70
88
|
POSTGRES_PASSWORD: postgres
|
71
89
|
|
72
90
|
steps:
|
73
|
-
- uses: actions/checkout@
|
91
|
+
- uses: actions/checkout@v4
|
74
92
|
- uses: ruby/setup-ruby@v1
|
75
93
|
with:
|
76
94
|
ruby-version: ${{ matrix.ruby }}
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,142 @@
|
|
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
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [0.7.0] - 2025-02-07
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Options to exclude all `has_many` and `has_one` or optional `belongs_to` associations by default. [@Envek]
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
root.exclude_has_relations
|
18
|
+
root.exclude_optional_belongs_to
|
19
|
+
```
|
20
|
+
|
21
|
+
Excluded associations can be re-included by `include` with matching pattern.
|
22
|
+
|
23
|
+
- Exclusion and inclusion patterns can be specified as hashes and/or arrays. [@Envek]
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
config.root('Forum', featured: true) do |forum|
|
27
|
+
forum.include(parent: {questions: %i[answers votes]})
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Which is equivalent to:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
config.root('Forum', featured: true) do |forum|
|
35
|
+
forum.include(/\Aforum(\.parent(\.questions(\.answers))?)?)?\z/)
|
36
|
+
forum.include(/\Aforum(\.parent(\.questions(\.votes))?)?)?\z/)
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
- Association limits also can be specified as hashes and/or arrays. [@Envek]
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
config.root('Forum', featured: true) do |forum|
|
44
|
+
forum.limit_associations_size(15, questions: %i[answers votes])
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Which is equivalent to:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
config.root('Forum', featured: true) do |forum|
|
52
|
+
forum.limit_associations_size(15, 'forum.questions.answers')
|
53
|
+
forum.limit_associations_size(15, 'forum.questions.votes')
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
|
58
|
+
- Print reason of association exclusion or inclusion in verbose mode. [@Envek]
|
59
|
+
|
60
|
+
- Allow to apply custom scoping to included associations. [@Envek]
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
config.root('Forum', featured: true) do |forum|
|
64
|
+
forum.include('questions.answers') do
|
65
|
+
order(created_at: :desc)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
### Fixed
|
71
|
+
|
72
|
+
- Error when dumping single-column tables. [@lsantobuono]
|
73
|
+
- Bug with null foreign key to back to auxiliary `has_one` association with not matching names. E.g. user has many profiles and has one default profile, profile belongs to user.
|
74
|
+
- Ignored columns handling.
|
75
|
+
- Dump relations for records that were dumped earlier with relations excluded.
|
76
|
+
|
77
|
+
## [0.6.0] - 2024-06-18
|
78
|
+
|
79
|
+
### Added
|
80
|
+
|
81
|
+
- Association inclusion option. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
82
|
+
- Option to limit association depth. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
83
|
+
- Option to ignore `default_scope` in models. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
84
|
+
- Option to disable nullifying of foreign keys. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
85
|
+
|
86
|
+
## [0.5.0] - 2023-02-16
|
87
|
+
|
88
|
+
### Added
|
89
|
+
|
90
|
+
- Option to ignore columns from a given model. [@nhocki] ([#17](https://github.com/evilmartians/evil-seed/pull/17))
|
91
|
+
|
92
|
+
## [0.4.0] - 2022-12-07
|
93
|
+
|
94
|
+
### Fixed
|
95
|
+
|
96
|
+
- Ignore generated database columns. [@cmer] ([#16](https://github.com/evilmartians/evil-seed/pull/16))
|
97
|
+
|
98
|
+
## [0.3.0] - 2022-03-14
|
99
|
+
|
100
|
+
### Added
|
101
|
+
|
102
|
+
- Passing attribute value to anonymizer block (to partially modify it). [@Envek]
|
103
|
+
|
104
|
+
## [0.2.0] - 2022-03-10
|
105
|
+
|
106
|
+
### Fixed
|
107
|
+
|
108
|
+
- Ignore virtual ActiveRecord attributes. [@Envek]
|
109
|
+
|
110
|
+
### Removed
|
111
|
+
|
112
|
+
- Support for ActiveRecord 4.2
|
113
|
+
|
114
|
+
## [0.1.3] - 2021-09-02
|
115
|
+
|
116
|
+
### Fixed
|
117
|
+
|
118
|
+
- Compatibility with Ruby 3.0 and ActiveRecord 6.x.
|
119
|
+
|
120
|
+
## [0.1.2] - 2018-03-27
|
121
|
+
|
122
|
+
### Fixed
|
123
|
+
|
124
|
+
- Bug with unwanted pseudo columns in dump when dumping HABTM join table without one side.
|
125
|
+
|
126
|
+
## [0.1.1] - 2017-05-15
|
127
|
+
|
128
|
+
### Fixed
|
129
|
+
|
130
|
+
- ActiveRecord 4.2 support by backporting of `ActiveRecord::Relation#in_batches`
|
131
|
+
- Dumping of the whole model without constraints
|
132
|
+
|
133
|
+
## [0.1.0] - 2017-05-09
|
134
|
+
|
135
|
+
Initial release. [@palkan], [@Envek]
|
136
|
+
|
137
|
+
[@Envek]: https://github.com/Envek "Andrey Novikov"
|
138
|
+
[@palkan]: https://github.com/palkan "Vladimir Dementyev"
|
139
|
+
[@cmer]: https://github.com/cmer "Carl Mercier"
|
140
|
+
[@nhocki]: https://github.com/nhocki "Nicolás Hock-Isaza"
|
141
|
+
[@gazay]: https://github.com/gazay "Alex Gaziev"
|
142
|
+
[@lsantobuono]: https://github.com/lsantobuono ""
|
data/Gemfile
CHANGED
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
|
|
5
5
|
# Specify your gem's dependencies in evil-seed.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
activerecord_version = ENV.fetch("ACTIVERECORD_VERSION", "~>
|
8
|
+
activerecord_version = ENV.fetch("ACTIVERECORD_VERSION", "~> 8.0")
|
9
9
|
case activerecord_version.upcase
|
10
10
|
when "HEAD"
|
11
11
|
git "https://github.com/rails/rails.git" do
|
@@ -15,4 +15,10 @@ when "HEAD"
|
|
15
15
|
else
|
16
16
|
activerecord_version = "~> #{activerecord_version}.0" if activerecord_version.match?(/^\d+\.\d+$/)
|
17
17
|
gem "activerecord", activerecord_version
|
18
|
+
if Gem::Version.new("7.2") > Gem::Version.new(activerecord_version.scan(/\d+\.\d+/).first)
|
19
|
+
gem "sqlite3", "~> 1.4"
|
20
|
+
gem "concurrent-ruby", "< 1.3.5"
|
21
|
+
end
|
18
22
|
end
|
23
|
+
|
24
|
+
gem "debug"
|
data/README.md
CHANGED
@@ -48,18 +48,42 @@ EvilSeed.configure do |config|
|
|
48
48
|
# First, you should specify +root models+ and their +constraints+ to limit the number of dumped records:
|
49
49
|
# This is like Forum.where(featured: true).all
|
50
50
|
config.root('Forum', featured: true) do |root|
|
51
|
+
# You can limit number of records to be dumped
|
52
|
+
root.limit(100)
|
53
|
+
# Specify order for records to be selected for dump
|
54
|
+
root.order(created_at: :desc)
|
55
|
+
|
51
56
|
# It's possible to remove some associations from dumping with pattern of association path to exclude
|
52
57
|
#
|
53
58
|
# Association path is a dot-delimited string of association chain starting from model itself:
|
54
59
|
# example: "forum.users.questions"
|
55
|
-
root.exclude(/\btracking_pixels\b/, 'forum.popular_questions')
|
60
|
+
root.exclude(/\btracking_pixels\b/, 'forum.popular_questions', /\Aforum\.parent\b/)
|
61
|
+
|
62
|
+
# Include back only certain association chains
|
63
|
+
root.include(parent: {questions: %i[answers votes]})
|
64
|
+
# which is the same as
|
65
|
+
root.include(/\Aforum(\.parent(\.questions(\.(answers|votes))?)?)?\z/)
|
66
|
+
|
67
|
+
# You can also specify custom scoping for associations
|
68
|
+
root.include(questions: { answers: :reactions }) do
|
69
|
+
order(created_at: :desc) # Any ActiveRecord query method is allowed
|
70
|
+
end
|
56
71
|
|
57
72
|
# It's possible to limit the number of included into dump has_many and has_one records for every association
|
58
73
|
# Note that belongs_to records for all not excluded associations are always dumped to keep referential integrity.
|
59
74
|
root.limit_associations_size(100)
|
60
75
|
|
61
76
|
# Or for certain association only
|
62
|
-
root.limit_associations_size(
|
77
|
+
root.limit_associations_size(5, 'forum.questions')
|
78
|
+
root.limit_associations_size(15, 'forum.questions.answers')
|
79
|
+
# or
|
80
|
+
root.limit_associations_size(5, :questions)
|
81
|
+
root.limit_associations_size(15, questions: :answers)
|
82
|
+
|
83
|
+
# Limit the depth of associations to be dumped from the root level
|
84
|
+
# All traverses through has_many, belongs_to, etc are counted
|
85
|
+
# So forum.subforums.subforums.questions.answers will be 5 levels deep
|
86
|
+
root.limit_deep(10)
|
63
87
|
end
|
64
88
|
|
65
89
|
# Everything you can pass to +where+ method will work as constraints:
|
@@ -95,6 +119,20 @@ EvilSeed.configure do |config|
|
|
95
119
|
# This will remove the columns even if the model is not a root node and is
|
96
120
|
# dumped via an association.
|
97
121
|
config.ignore_columns("Profile", :name)
|
122
|
+
|
123
|
+
# Disable foreign key nullification for records that are not included in the dump
|
124
|
+
# By default, EvilSeed will nullify foreign keys for records that are not included in the dump
|
125
|
+
config.dont_nullify = true
|
126
|
+
|
127
|
+
# Unscope relations to include soft-deleted records etc
|
128
|
+
# This is useful when you want to include all records, including those that are hidden by default
|
129
|
+
# By default, EvilSeed will abide default scope of models
|
130
|
+
config.unscoped = true
|
131
|
+
|
132
|
+
# Verbose mode will print out the progress of the dump to the console along with writing the file
|
133
|
+
# By default, verbose mode is off
|
134
|
+
config.verbose = true
|
135
|
+
config.verbose_sql = true
|
98
136
|
end
|
99
137
|
```
|
100
138
|
|
data/evil-seed.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
|
30
30
|
spec.add_dependency 'activerecord', '>= 5.0'
|
31
31
|
|
32
|
-
spec.add_development_dependency 'rake', '~>
|
32
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
33
33
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
34
34
|
spec.add_development_dependency 'pg', '>= 0.20'
|
35
35
|
spec.add_development_dependency 'mysql2'
|
@@ -4,38 +4,145 @@ module EvilSeed
|
|
4
4
|
class Configuration
|
5
5
|
# Configuration for dumping some root model and its associations
|
6
6
|
class Root
|
7
|
-
attr_reader :model, :constraints
|
8
|
-
attr_reader :total_limit, :association_limits
|
9
|
-
attr_reader :exclusions
|
7
|
+
attr_reader :model, :constraints, :limit, :order
|
8
|
+
attr_reader :total_limit, :association_limits, :deep_limit, :dont_nullify
|
9
|
+
attr_reader :exclusions, :inclusions
|
10
10
|
|
11
11
|
# @param model [String] Name of the model class to dump
|
12
12
|
# @param constraints [String, Hash] Everything you can feed into +where+ to limit number of records
|
13
|
-
def initialize(model, *constraints)
|
13
|
+
def initialize(model, dont_nullify, *constraints)
|
14
14
|
@model = model
|
15
15
|
@constraints = constraints
|
16
16
|
@exclusions = []
|
17
|
+
@inclusions = {}
|
17
18
|
@association_limits = {}
|
19
|
+
@deep_limit = nil
|
20
|
+
@dont_nullify = dont_nullify
|
18
21
|
end
|
19
22
|
|
20
23
|
# Exclude some of associations from the dump
|
21
24
|
# @param association_patterns Array<String, Regex> Patterns to exclude associated models from dump
|
22
25
|
def exclude(*association_patterns)
|
23
|
-
|
26
|
+
association_patterns.each do |pattern|
|
27
|
+
case pattern
|
28
|
+
when String, Regexp
|
29
|
+
@exclusions << pattern
|
30
|
+
else
|
31
|
+
path_prefix = model.constantize.model_name.singular
|
32
|
+
@exclusions += compile_patterns(pattern, prefix: path_prefix).map { |p| Regexp.new(/\A#{p}\z/) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Include some excluded associations back to the dump
|
38
|
+
# @param association_patterns Array<String, Regex> Patterns to exclude associated models from dump
|
39
|
+
def include(*association_patterns, &block)
|
40
|
+
association_patterns.each do |pattern|
|
41
|
+
case pattern
|
42
|
+
when String, Regexp
|
43
|
+
@inclusions[pattern] = block
|
44
|
+
else
|
45
|
+
path_prefix = model.constantize.model_name.singular
|
46
|
+
compile_patterns(pattern, prefix: path_prefix).map do |p|
|
47
|
+
@inclusions[Regexp.new(/\A#{p}\z/)] = block
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def exclude_has_relations
|
54
|
+
@excluded_has_relations = :exclude_has_relations
|
55
|
+
end
|
56
|
+
|
57
|
+
def exclude_optional_belongs_to
|
58
|
+
@excluded_optional_belongs_to = :exclude_optional_belongs_to
|
59
|
+
end
|
60
|
+
|
61
|
+
def limit(limit = nil)
|
62
|
+
return @limit if limit.nil?
|
63
|
+
|
64
|
+
@limit = limit
|
65
|
+
end
|
66
|
+
|
67
|
+
def order(order = nil)
|
68
|
+
return @order if order.nil?
|
69
|
+
|
70
|
+
@order = order
|
24
71
|
end
|
25
72
|
|
26
73
|
# Limit number of records in all (if pattern is not provided) or given associations to include into dump
|
27
74
|
# @param limit [Integer] Maximum number of records in associations to include into dump
|
28
|
-
# @param association_pattern
|
29
|
-
def limit_associations_size(limit,
|
30
|
-
if
|
31
|
-
|
32
|
-
|
33
|
-
|
75
|
+
# @param association_pattern Array<String, Regex, Hash> Pattern to limit number of records for certain associated models
|
76
|
+
def limit_associations_size(limit, *association_patterns)
|
77
|
+
return @total_limit = limit if association_patterns.empty?
|
78
|
+
|
79
|
+
association_patterns.each do |pattern|
|
80
|
+
case pattern
|
81
|
+
when String, Regexp
|
82
|
+
@association_limits[pattern] = limit
|
83
|
+
else
|
84
|
+
path_prefix = model.constantize.model_name.singular
|
85
|
+
compile_patterns(pattern, prefix: path_prefix, partial: false).map do |p|
|
86
|
+
@association_limits[Regexp.new(/\A#{p}\z/)] = limit
|
87
|
+
end
|
88
|
+
end
|
34
89
|
end
|
35
90
|
end
|
36
91
|
|
92
|
+
# Limit deepenes of associations to include into dump
|
93
|
+
# @param limit [Integer] Maximum level to recursively dive into associations
|
94
|
+
def limit_deep(limit)
|
95
|
+
@deep_limit = limit
|
96
|
+
end
|
97
|
+
|
98
|
+
def do_not_nullify(nullify_flag)
|
99
|
+
@dont_nullify = nullify_flag
|
100
|
+
end
|
101
|
+
|
37
102
|
def excluded?(association_path)
|
38
|
-
exclusions.
|
103
|
+
exclusions.find { |exclusion| association_path.match(exclusion) } #.match(association_path) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def included?(association_path)
|
107
|
+
inclusions.find { |inclusion, _block| association_path.match(inclusion) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def excluded_has_relations?
|
111
|
+
@excluded_has_relations
|
112
|
+
end
|
113
|
+
|
114
|
+
def excluded_optional_belongs_to?
|
115
|
+
@excluded_optional_belongs_to
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def compile_patterns(pattern, prefix: "", partial: true)
|
121
|
+
wrap = -> (p) { partial ? "(?:\\.#{p})?" : "\\.#{p}" }
|
122
|
+
case pattern
|
123
|
+
when String, Symbol
|
124
|
+
["#{prefix}#{wrap.(pattern.to_s)}"]
|
125
|
+
when Regexp
|
126
|
+
["#{prefix}#{wrap.("(?:#{pattern.source})")}"]
|
127
|
+
when Array
|
128
|
+
pattern.map { |p| compile_patterns(p, prefix: prefix, partial: partial) }.flatten
|
129
|
+
when Hash
|
130
|
+
pattern.map do |k, v|
|
131
|
+
next nil unless v
|
132
|
+
subpatterns = compile_patterns(v, partial: partial)
|
133
|
+
next "#{prefix}#{wrap.(k)}" if subpatterns.empty?
|
134
|
+
|
135
|
+
subpatterns.map do |p|
|
136
|
+
"#{prefix}#{wrap.("#{k}#{p}")}"
|
137
|
+
end
|
138
|
+
end.compact.flatten
|
139
|
+
when false, nil
|
140
|
+
nil
|
141
|
+
when true
|
142
|
+
[prefix]
|
143
|
+
else
|
144
|
+
raise ArgumentError, "Unknown pattern type: #{pattern.class} for #{pattern.inspect}"
|
145
|
+
end
|
39
146
|
end
|
40
147
|
end
|
41
148
|
end
|
@@ -7,10 +7,14 @@ require_relative 'anonymizer'
|
|
7
7
|
module EvilSeed
|
8
8
|
# This module holds configuration for creating dump: which models and their constraints
|
9
9
|
class Configuration
|
10
|
-
attr_accessor :record_dumper_class
|
10
|
+
attr_accessor :record_dumper_class, :verbose, :verbose_sql, :unscoped, :dont_nullify
|
11
11
|
|
12
12
|
def initialize
|
13
13
|
@record_dumper_class = RecordDumper
|
14
|
+
@verbose = false
|
15
|
+
@verbose_sql = false
|
16
|
+
@unscoped = false
|
17
|
+
@dont_nullify = false
|
14
18
|
@ignored_columns = Hash.new { |h, k| h[k] = [] }
|
15
19
|
end
|
16
20
|
|
@@ -19,7 +23,7 @@ module EvilSeed
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def root(model, *constraints)
|
22
|
-
new_root = Root.new(model, *constraints)
|
26
|
+
new_root = Root.new(model, dont_nullify, *constraints)
|
23
27
|
yield new_root if block_given?
|
24
28
|
roots << new_root
|
25
29
|
end
|
@@ -35,7 +39,7 @@ module EvilSeed
|
|
35
39
|
end
|
36
40
|
|
37
41
|
def ignore_columns(model_class, *columns)
|
38
|
-
@ignored_columns[model_class] += columns
|
42
|
+
@ignored_columns[model_class.to_s] += columns.map(&:to_s)
|
39
43
|
end
|
40
44
|
|
41
45
|
# Customizer objects for every model
|
data/lib/evil_seed/dumper.rb
CHANGED
@@ -7,7 +7,7 @@ module EvilSeed
|
|
7
7
|
# This class initiates dump creation for every root model of configuration
|
8
8
|
# and then concatenates dumps from all roots into one single IO.
|
9
9
|
class Dumper
|
10
|
-
attr_reader :configuration, :loaded_map
|
10
|
+
attr_reader :configuration, :loaded_map, :to_load_map
|
11
11
|
|
12
12
|
# @param configuration [Configuration]
|
13
13
|
def initialize(configuration)
|
@@ -18,6 +18,7 @@ module EvilSeed
|
|
18
18
|
# @param output [IO] Stream to write SQL dump into
|
19
19
|
def call(output)
|
20
20
|
@loaded_map = Hash.new { |h, k| h[k] = Set.new } # stores primary keys of already dumped records for every table
|
21
|
+
@to_load_map = Hash.new { |h, k| h[k] = Set.new } # stores primary keys of records we're going to dump to avoid cycles
|
21
22
|
@output = output
|
22
23
|
configuration.roots.each do |root|
|
23
24
|
table_outputs = RootDumper.new(root, self).call
|
@@ -9,7 +9,7 @@ module EvilSeed
|
|
9
9
|
|
10
10
|
attr_reader :model_class, :configuration, :relation_dumper
|
11
11
|
|
12
|
-
delegate :loaded_map, to: :relation_dumper
|
12
|
+
delegate :loaded_map, :to_load_map, to: :relation_dumper
|
13
13
|
|
14
14
|
def initialize(model_class, configuration, relation_dumper)
|
15
15
|
@model_class = model_class
|
@@ -38,8 +38,10 @@ module EvilSeed
|
|
38
38
|
|
39
39
|
def loaded!(attributes)
|
40
40
|
id = model_class.primary_key && attributes[model_class.primary_key] || attributes
|
41
|
-
|
42
|
-
|
41
|
+
!loaded_map[model_class.table_name].include?(id).tap do
|
42
|
+
loaded_map[model_class.table_name] << id
|
43
|
+
to_load_map[model_class.table_name].delete(id)
|
44
|
+
end
|
43
45
|
end
|
44
46
|
|
45
47
|
def transform_and_anonymize(attributes)
|
@@ -51,9 +53,10 @@ module EvilSeed
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def insertable_column_names
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
@insertable_column_names ||=
|
57
|
+
model_class.columns_hash.reject do |_k, v|
|
58
|
+
v.respond_to?(:virtual?) ? v.virtual? : false
|
59
|
+
end.keys - configuration.ignored_columns_for(model_class.to_s)
|
57
60
|
end
|
58
61
|
|
59
62
|
def insert_statement
|
@@ -65,26 +68,34 @@ module EvilSeed
|
|
65
68
|
|
66
69
|
def write!(attributes)
|
67
70
|
# Remove non-insertable columns from attributes
|
68
|
-
attributes = attributes.slice(*insertable_column_names)
|
71
|
+
attributes = prepare(attributes.slice(*insertable_column_names))
|
72
|
+
|
73
|
+
if configuration.verbose_sql
|
74
|
+
puts("-- #{relation_dumper.association_path}\n")
|
75
|
+
puts(@tuples_written.zero? ? insert_statement : ",\n")
|
76
|
+
puts(" (#{attributes.join(', ')})")
|
77
|
+
end
|
69
78
|
|
70
79
|
@output.write("-- #{relation_dumper.association_path}\n") && @header_written = true unless @header_written
|
71
80
|
@output.write(@tuples_written.zero? ? insert_statement : ",\n")
|
72
|
-
@output.write(" (#{
|
81
|
+
@output.write(" (#{attributes.join(', ')})")
|
73
82
|
@tuples_written += 1
|
74
83
|
@output.write(";\n") && @tuples_written = 0 if @tuples_written == MAX_TUPLES_PER_INSERT_STMT
|
75
84
|
end
|
76
85
|
|
77
86
|
def finalize!
|
78
|
-
return
|
87
|
+
return true if @finalized
|
88
|
+
return false unless @header_written && @tuples_written > 0
|
79
89
|
@output.write(";\n\n")
|
80
90
|
@tuples_written = 0
|
91
|
+
@finalized = true
|
81
92
|
end
|
82
93
|
|
83
94
|
def prepare(attributes)
|
84
95
|
attributes.map do |key, value|
|
85
96
|
type = model_class.attribute_types[key]
|
86
97
|
model_class.connection.quote(type.serialize(value))
|
87
|
-
end
|
98
|
+
end.flatten.compact
|
88
99
|
end
|
89
100
|
end
|
90
101
|
end
|
@@ -24,37 +24,51 @@ module EvilSeed
|
|
24
24
|
MAX_IDENTIFIERS_IN_IN_STMT = 1_000
|
25
25
|
|
26
26
|
attr_reader :relation, :root_dumper, :model_class, :association_path, :search_key, :identifiers, :nullify_columns,
|
27
|
-
:belongs_to_reflections, :has_many_reflections, :foreign_keys, :loaded_ids, :
|
28
|
-
:record_dumper, :inverse_reflection, :table_names, :options
|
27
|
+
:belongs_to_reflections, :has_many_reflections, :foreign_keys, :loaded_ids, :local_load_map,
|
28
|
+
:records, :record_dumper, :inverse_reflection, :table_names, :options,
|
29
|
+
:current_deep, :verbose, :custom_scope
|
29
30
|
|
30
|
-
delegate :root, :configuration, :total_limit, :loaded_map, to: :root_dumper
|
31
|
+
delegate :root, :configuration, :dont_nullify, :total_limit, :deep_limit, :loaded_map, :to_load_map, to: :root_dumper
|
31
32
|
|
32
33
|
def initialize(relation, root_dumper, association_path, **options)
|
34
|
+
puts("- #{association_path}") if root_dumper.configuration.verbose
|
35
|
+
|
33
36
|
@relation = relation
|
34
37
|
@root_dumper = root_dumper
|
38
|
+
@verbose = configuration.verbose
|
35
39
|
@identifiers = options[:identifiers]
|
36
|
-
@
|
40
|
+
@local_load_map = Hash.new { |h, k| h[k] = [] }
|
37
41
|
@foreign_keys = Hash.new { |h, k| h[k] = [] }
|
38
42
|
@loaded_ids = []
|
39
43
|
@model_class = relation.klass
|
40
44
|
@search_key = options[:search_key] || model_class.primary_key
|
41
45
|
@association_path = association_path
|
42
46
|
@inverse_reflection = options[:inverse_of]
|
47
|
+
@records = []
|
43
48
|
@record_dumper = configuration.record_dumper_class.new(model_class, configuration, self)
|
44
49
|
@nullify_columns = []
|
45
50
|
@table_names = {}
|
46
51
|
@belongs_to_reflections = setup_belongs_to_reflections
|
47
52
|
@has_many_reflections = setup_has_many_reflections
|
48
53
|
@options = options
|
54
|
+
@current_deep = association_path.split('.').size
|
55
|
+
@dont_nullify = dont_nullify
|
56
|
+
@custom_scope = options[:custom_scope]
|
49
57
|
end
|
50
58
|
|
51
59
|
# Generate dump and write it into +io+
|
52
60
|
# @return [Array<IO>] List of dump IOs for separate tables in order of dependencies (belongs_to are first)
|
53
61
|
def call
|
54
62
|
dump!
|
55
|
-
|
56
|
-
|
57
|
-
|
63
|
+
if deep_limit and current_deep > deep_limit
|
64
|
+
[dump_records!].flatten.compact
|
65
|
+
else
|
66
|
+
[
|
67
|
+
dump_belongs_to_associations!,
|
68
|
+
dump_records!,
|
69
|
+
dump_has_many_associations!,
|
70
|
+
].flatten.compact
|
71
|
+
end
|
58
72
|
end
|
59
73
|
|
60
74
|
private
|
@@ -63,17 +77,31 @@ module EvilSeed
|
|
63
77
|
original_ignored_columns = model_class.ignored_columns
|
64
78
|
model_class.ignored_columns += Array(configuration.ignored_columns_for(model_class.sti_name))
|
65
79
|
model_class.send(:reload_schema_from_cache) if ActiveRecord.version < Gem::Version.new("6.1.0.rc1") # See https://github.com/rails/rails/pull/37581
|
66
|
-
if
|
80
|
+
if custom_scope
|
81
|
+
puts(" # #{search_key} (with scope)") if verbose
|
82
|
+
attrs = fetch_attributes(relation)
|
83
|
+
puts(" -- dumped #{attrs.size}") if verbose
|
84
|
+
attrs.each do |attributes|
|
85
|
+
next unless check_limits!
|
86
|
+
dump_record!(attributes)
|
87
|
+
end
|
88
|
+
elsif identifiers.present?
|
89
|
+
puts(" # #{search_key} => #{identifiers}") if verbose
|
67
90
|
# Don't use AR::Base#find_each as we will get error on Oracle if we will have more than 1000 ids in IN statement
|
68
91
|
identifiers.in_groups_of(MAX_IDENTIFIERS_IN_IN_STMT).each do |ids|
|
69
|
-
fetch_attributes(relation.where(search_key => ids.compact))
|
92
|
+
attrs = fetch_attributes(relation.where(search_key => ids.compact))
|
93
|
+
puts(" -- dumped #{attrs.size}") if verbose
|
94
|
+
attrs.each do |attributes|
|
70
95
|
next unless check_limits!
|
71
96
|
dump_record!(attributes)
|
72
97
|
end
|
73
98
|
end
|
74
99
|
else
|
100
|
+
puts(" # #{relation.count}") if verbose
|
75
101
|
relation.in_batches do |relation|
|
76
|
-
fetch_attributes(relation)
|
102
|
+
attrs = fetch_attributes(relation)
|
103
|
+
puts(" -- dumped #{attrs.size}") if verbose
|
104
|
+
attrs.each do |attributes|
|
77
105
|
next unless check_limits!
|
78
106
|
dump_record!(attributes)
|
79
107
|
end
|
@@ -84,43 +112,54 @@ module EvilSeed
|
|
84
112
|
end
|
85
113
|
|
86
114
|
def dump_record!(attributes)
|
87
|
-
|
88
|
-
|
115
|
+
unless dont_nullify
|
116
|
+
nullify_columns.each do |nullify_column|
|
117
|
+
attributes[nullify_column] = nil
|
118
|
+
end
|
89
119
|
end
|
90
|
-
|
120
|
+
records << attributes
|
91
121
|
foreign_keys.each do |reflection_name, fk_column|
|
92
122
|
foreign_key = attributes[fk_column]
|
93
|
-
next if foreign_key.nil? || loaded_map[table_names[reflection_name]].include?(foreign_key)
|
94
|
-
|
123
|
+
next if foreign_key.nil? || loaded_map[table_names[reflection_name]].include?(foreign_key) || to_load_map[table_names[reflection_name]].include?(foreign_key)
|
124
|
+
local_load_map[reflection_name] << foreign_key
|
125
|
+
to_load_map[table_names[reflection_name]] << foreign_key
|
95
126
|
end
|
96
127
|
loaded_ids << attributes[model_class.primary_key]
|
97
128
|
end
|
98
129
|
|
130
|
+
def dump_records!
|
131
|
+
records.each do |attributes|
|
132
|
+
record_dumper.call(attributes)
|
133
|
+
end
|
134
|
+
record_dumper.result
|
135
|
+
end
|
136
|
+
|
99
137
|
def dump_belongs_to_associations!
|
100
138
|
belongs_to_reflections.map do |reflection|
|
101
|
-
next if
|
139
|
+
next if local_load_map[reflection.name].empty?
|
102
140
|
RelationDumper.new(
|
103
141
|
build_relation(reflection),
|
104
142
|
root_dumper,
|
105
143
|
"#{association_path}.#{reflection.name}",
|
106
144
|
search_key: reflection.association_primary_key,
|
107
|
-
identifiers:
|
145
|
+
identifiers: local_load_map[reflection.name],
|
108
146
|
limitable: false,
|
109
147
|
).call
|
110
148
|
end
|
111
149
|
end
|
112
150
|
|
113
151
|
def dump_has_many_associations!
|
114
|
-
has_many_reflections.map do |reflection|
|
152
|
+
has_many_reflections.map do |reflection, custom_scope|
|
115
153
|
next if loaded_ids.empty? || total_limit.try(:zero?)
|
116
154
|
RelationDumper.new(
|
117
|
-
build_relation(reflection),
|
155
|
+
build_relation(reflection, custom_scope),
|
118
156
|
root_dumper,
|
119
157
|
"#{association_path}.#{reflection.name}",
|
120
158
|
search_key: reflection.foreign_key,
|
121
|
-
identifiers: loaded_ids,
|
159
|
+
identifiers: loaded_ids - local_load_map[reflection.name],
|
122
160
|
inverse_of: reflection.inverse_of.try(:name),
|
123
161
|
limitable: true,
|
162
|
+
custom_scope: custom_scope,
|
124
163
|
).call
|
125
164
|
end
|
126
165
|
end
|
@@ -130,7 +169,7 @@ module EvilSeed
|
|
130
169
|
# @return [Array<Hash{String => String, Integer, Float, Boolean, nil}>]
|
131
170
|
def fetch_attributes(relation)
|
132
171
|
relation.pluck(*model_class.column_names).map do |row|
|
133
|
-
Hash[model_class.column_names.zip(row)]
|
172
|
+
Hash[model_class.column_names.zip(Array(row))]
|
134
173
|
end
|
135
174
|
end
|
136
175
|
|
@@ -139,9 +178,14 @@ module EvilSeed
|
|
139
178
|
root_dumper.check_limits!(association_path)
|
140
179
|
end
|
141
180
|
|
142
|
-
def build_relation(reflection)
|
143
|
-
|
181
|
+
def build_relation(reflection, custom_scope = nil)
|
182
|
+
if configuration.unscoped
|
183
|
+
relation = reflection.klass.unscoped
|
184
|
+
else
|
185
|
+
relation = reflection.klass.all
|
186
|
+
end
|
144
187
|
relation = relation.instance_eval(&reflection.scope) if reflection.scope
|
188
|
+
relation = relation.instance_eval(&custom_scope) if custom_scope
|
145
189
|
relation = relation.where(reflection.type => model_class.to_s) if reflection.options[:as] # polymorphic
|
146
190
|
relation
|
147
191
|
end
|
@@ -149,24 +193,43 @@ module EvilSeed
|
|
149
193
|
def setup_belongs_to_reflections
|
150
194
|
model_class.reflect_on_all_associations(:belongs_to).reject do |reflection|
|
151
195
|
next false if reflection.options[:polymorphic] # TODO: Add support for polymorphic belongs_to
|
152
|
-
|
153
|
-
|
154
|
-
|
196
|
+
included = root.included?("#{association_path}.#{reflection.name}")
|
197
|
+
excluded = reflection.options[:optional] && root.excluded_optional_belongs_to?
|
198
|
+
excluded ||= root.excluded?("#{association_path}.#{reflection.name}")
|
199
|
+
inverse = reflection.name == inverse_reflection
|
200
|
+
puts " -- belongs_to #{reflection.name} #{"excluded by #{excluded}" if excluded} #{"re-included by #{included}" if included}" if verbose
|
201
|
+
if excluded and not included
|
202
|
+
if model_class.column_names.include?(reflection.foreign_key)
|
203
|
+
puts(" -- excluded #{reflection.foreign_key}") if verbose
|
204
|
+
nullify_columns << reflection.foreign_key
|
205
|
+
end
|
155
206
|
else
|
156
207
|
foreign_keys[reflection.name] = reflection.foreign_key
|
157
208
|
table_names[reflection.name] = reflection.table_name
|
158
209
|
end
|
159
|
-
excluded
|
210
|
+
excluded and not included or inverse
|
160
211
|
end
|
161
212
|
end
|
162
213
|
|
163
214
|
# This method returns only direct has_one and has_many reflections. For HABTM it returns intermediate has_many
|
164
215
|
def setup_has_many_reflections
|
216
|
+
puts(" -- reflections #{model_class._reflections.keys}") if verbose
|
165
217
|
model_class._reflections.select do |_reflection_name, reflection|
|
218
|
+
next false unless %i[has_one has_many].include?(reflection.macro)
|
219
|
+
|
166
220
|
next false if model_class.primary_key.nil?
|
221
|
+
|
167
222
|
next false if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
168
|
-
|
169
|
-
|
223
|
+
|
224
|
+
included = root.included?("#{association_path}.#{reflection.name}")
|
225
|
+
excluded = :inverse if reflection.name == inverse_reflection
|
226
|
+
excluded ||= root.excluded_has_relations?
|
227
|
+
excluded ||= root.excluded?("#{association_path}.#{reflection.name}")
|
228
|
+
puts " -- #{reflection.macro} #{reflection.name} #{"excluded by #{excluded}" if excluded} #{"re-included by #{included}" if included}" if verbose
|
229
|
+
!(excluded and not included)
|
230
|
+
end.map do |_reflection_name, reflection|
|
231
|
+
[reflection, root.included?("#{association_path}.#{reflection.name}")&.last]
|
232
|
+
end
|
170
233
|
end
|
171
234
|
end
|
172
235
|
end
|
@@ -5,15 +5,16 @@ require_relative 'relation_dumper'
|
|
5
5
|
module EvilSeed
|
6
6
|
# This module collects dumps generation for root and all it's dependencies
|
7
7
|
class RootDumper
|
8
|
-
attr_reader :root, :dumper, :model_class, :total_limit, :association_limits
|
8
|
+
attr_reader :root, :dumper, :model_class, :total_limit, :deep_limit, :dont_nullify, :association_limits
|
9
9
|
|
10
|
-
delegate :loaded_map, :configuration, to: :dumper
|
10
|
+
delegate :loaded_map, :to_load_map, :configuration, to: :dumper
|
11
11
|
|
12
12
|
def initialize(root, dumper)
|
13
13
|
@root = root
|
14
14
|
@dumper = dumper
|
15
|
-
@to_load_map = {}
|
16
15
|
@total_limit = root.total_limit
|
16
|
+
@deep_limit = root.deep_limit
|
17
|
+
@dont_nullify = root.dont_nullify
|
17
18
|
@association_limits = root.association_limits.dup
|
18
19
|
|
19
20
|
@model_class = root.model.constantize
|
@@ -24,7 +25,10 @@ module EvilSeed
|
|
24
25
|
def call
|
25
26
|
association_path = model_class.model_name.singular
|
26
27
|
relation = model_class.all
|
28
|
+
relation = relation.unscoped if configuration.unscoped
|
27
29
|
relation = relation.where(*root.constraints) if root.constraints.any? # without arguments returns not a relation
|
30
|
+
relation = relation.limit(root.limit) if root.limit
|
31
|
+
relation = relation.order(root.order) if root.order
|
28
32
|
RelationDumper.new(relation, self, association_path).call
|
29
33
|
end
|
30
34
|
|
data/lib/evil_seed/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: evil-seed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Novikov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2025-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '13.0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '13.0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: minitest
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,9 +146,11 @@ executables: []
|
|
146
146
|
extensions: []
|
147
147
|
extra_rdoc_files: []
|
148
148
|
files:
|
149
|
+
- ".github/workflows/release.yml"
|
149
150
|
- ".github/workflows/test.yml"
|
150
151
|
- ".gitignore"
|
151
152
|
- ".rubocop.yml"
|
153
|
+
- CHANGELOG.md
|
152
154
|
- Gemfile
|
153
155
|
- LICENSE.txt
|
154
156
|
- README.md
|
@@ -188,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
190
|
- !ruby/object:Gem::Version
|
189
191
|
version: '0'
|
190
192
|
requirements: []
|
191
|
-
rubygems_version: 3.
|
193
|
+
rubygems_version: 3.5.22
|
192
194
|
signing_key:
|
193
195
|
specification_version: 4
|
194
196
|
summary: Create partial and anonymized production database dumps for use in development
|