evil-seed 0.5.0 → 0.6.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 +10 -10
- data/CHANGELOG.md +74 -0
- data/Gemfile +2 -1
- data/README.md +23 -1
- data/lib/evil_seed/configuration/root.rb +27 -4
- data/lib/evil_seed/configuration.rb +6 -2
- data/lib/evil_seed/record_dumper.rb +9 -3
- data/lib/evil_seed/relation_dumper.rb +45 -14
- data/lib/evil_seed/root_dumper.rb +4 -1
- data/lib/evil_seed/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfdedee7cac6da3803d6071ccbda5eaf19ee07bc07c67775b202160c304dd323
|
4
|
+
data.tar.gz: e04bc0672ccb683928651843c749ad05378af846c6d4b51d7e7c3d14eb43c179
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2973540b6fd16936419e9e76e389df4dd854313e3674ff7c73f7056c18c3e8979ca302d0c395a60e3a17abb7f9bc2b886ef4939711ad43c3df72cebc06cf944
|
7
|
+
data.tar.gz: cb2772174b471dc7977fcaa00bc79dc0bf45429f836a7e490487d37e625f4d8328583d1d103b13d2a8899e56736793acb24e35fd362c196aab1a60489f023159
|
@@ -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,12 +19,15 @@ jobs:
|
|
19
19
|
- ruby: "head"
|
20
20
|
activerecord: "head"
|
21
21
|
database: sqlite
|
22
|
-
- ruby: "3.
|
23
|
-
activerecord: "7.
|
22
|
+
- ruby: "3.3"
|
23
|
+
activerecord: "7.1"
|
24
24
|
database: postgresql
|
25
|
-
- ruby: "3.
|
26
|
-
activerecord: "7.
|
25
|
+
- ruby: "3.3"
|
26
|
+
activerecord: "7.1"
|
27
27
|
database: mysql
|
28
|
+
- ruby: "3.3"
|
29
|
+
activerecord: "7.1"
|
30
|
+
database: sqlite
|
28
31
|
- ruby: "3.2"
|
29
32
|
activerecord: "7.0"
|
30
33
|
database: sqlite
|
@@ -34,15 +37,12 @@ jobs:
|
|
34
37
|
- ruby: "3.0"
|
35
38
|
activerecord: "6.0"
|
36
39
|
database: sqlite
|
37
|
-
- ruby: "2.7"
|
38
|
-
activerecord: "5.2"
|
39
|
-
database: sqlite
|
40
40
|
|
41
41
|
runs-on: ubuntu-latest
|
42
42
|
|
43
43
|
services:
|
44
44
|
postgres:
|
45
|
-
image: postgres:
|
45
|
+
image: postgres:16
|
46
46
|
env:
|
47
47
|
POSTGRES_USER: postgres
|
48
48
|
POSTGRES_PASSWORD: postgres
|
@@ -54,7 +54,7 @@ jobs:
|
|
54
54
|
--health-timeout 5s
|
55
55
|
--health-retries 5
|
56
56
|
mysql:
|
57
|
-
image: mysql:8.
|
57
|
+
image: mysql:8.4
|
58
58
|
env:
|
59
59
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
60
60
|
MYSQL_DATABASE: evil_seed_test
|
@@ -70,7 +70,7 @@ jobs:
|
|
70
70
|
POSTGRES_PASSWORD: postgres
|
71
71
|
|
72
72
|
steps:
|
73
|
-
- uses: actions/checkout@
|
73
|
+
- uses: actions/checkout@v4
|
74
74
|
- uses: ruby/setup-ruby@v1
|
75
75
|
with:
|
76
76
|
ruby-version: ${{ matrix.ruby }}
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,74 @@
|
|
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.6.0] - 2024-06-18
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Association inclusion option. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
15
|
+
- Option to limit association depth. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
16
|
+
- Option to ignore `default_scope` in models. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
17
|
+
- Option to disable nullifying of foreign keys. [@gazay] ([#13](https://github.com/evilmartians/evil-seed/pull/13))
|
18
|
+
|
19
|
+
## [0.5.0] - 2023-02-16
|
20
|
+
|
21
|
+
### Added
|
22
|
+
|
23
|
+
- Option to ignore columns from a given model. [@nhocki] ([#17](https://github.com/evilmartians/evil-seed/pull/17))
|
24
|
+
|
25
|
+
## [0.4.0] - 2022-12-07
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- Ignore generated database columns. [@cmer] ([#16](https://github.com/evilmartians/evil-seed/pull/16))
|
30
|
+
|
31
|
+
## [0.3.0] - 2022-03-14
|
32
|
+
|
33
|
+
### Added
|
34
|
+
|
35
|
+
- Passing attribute value to anonymizer block (to partially modify it). [@Envek]
|
36
|
+
|
37
|
+
## [0.2.0] - 2022-03-10
|
38
|
+
|
39
|
+
### Fixed
|
40
|
+
|
41
|
+
- Ignore virtual ActiveRecord attributes. [@Envek]
|
42
|
+
|
43
|
+
### Removed
|
44
|
+
|
45
|
+
- Support for ActiveRecord 4.2
|
46
|
+
|
47
|
+
## [0.1.3] - 2021-09-02
|
48
|
+
|
49
|
+
### Fixed
|
50
|
+
|
51
|
+
- Compatibility with Ruby 3.0 and ActiveRecord 6.x.
|
52
|
+
|
53
|
+
## [0.1.2] - 2018-03-27
|
54
|
+
|
55
|
+
### Fixed
|
56
|
+
|
57
|
+
- Bug with unwanted pseudo columns in dump when dumping HABTM join table without one side.
|
58
|
+
|
59
|
+
## [0.1.1] - 2017-05-15
|
60
|
+
|
61
|
+
### Fixed
|
62
|
+
|
63
|
+
- ActiveRecord 4.2 support by backporting of `ActiveRecord::Relation#in_batches`
|
64
|
+
- Dumping of the whole model without constraints
|
65
|
+
|
66
|
+
## [0.1.0] - 2017-05-09
|
67
|
+
|
68
|
+
Initial release. [@palkan], [@Envek]
|
69
|
+
|
70
|
+
[@Envek]: https://github.com/Envek "Andrey Novikov"
|
71
|
+
[@palkan]: https://github.com/palkan "Vladimir Dementyev"
|
72
|
+
[@cmer]: https://github.com/cmer "Carl Mercier"
|
73
|
+
[@nhocki]: https://github.com/nhocki "Nicolás Hock-Isaza"
|
74
|
+
[@gazay]: https://github.com/gazay "Alex Gaziev"
|
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", "~> 7.
|
8
|
+
activerecord_version = ENV.fetch("ACTIVERECORD_VERSION", "~> 7.1")
|
9
9
|
case activerecord_version.upcase
|
10
10
|
when "HEAD"
|
11
11
|
git "https://github.com/rails/rails.git" do
|
@@ -15,4 +15,5 @@ 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
|
+
gem "sqlite3", "~> 1.4"
|
18
19
|
end
|
data/README.md
CHANGED
@@ -52,7 +52,10 @@ EvilSeed.configure do |config|
|
|
52
52
|
#
|
53
53
|
# Association path is a dot-delimited string of association chain starting from model itself:
|
54
54
|
# example: "forum.users.questions"
|
55
|
-
root.exclude(/\btracking_pixels\b/, 'forum.popular_questions')
|
55
|
+
root.exclude(/\btracking_pixels\b/, 'forum.popular_questions', /\Aforum\.parent\b/)
|
56
|
+
|
57
|
+
# Include back only certain associations
|
58
|
+
root.include(/\Aforum(\.parent(\.questions(\.answers)?)?)?\z/)
|
56
59
|
|
57
60
|
# It's possible to limit the number of included into dump has_many and has_one records for every association
|
58
61
|
# Note that belongs_to records for all not excluded associations are always dumped to keep referential integrity.
|
@@ -60,6 +63,11 @@ EvilSeed.configure do |config|
|
|
60
63
|
|
61
64
|
# Or for certain association only
|
62
65
|
root.limit_associations_size(10, 'forum.questions')
|
66
|
+
|
67
|
+
# Limit the depth of associations to be dumped from the root level
|
68
|
+
# All traverses through has_many, belongs_to, etc are counted
|
69
|
+
# So forum.subforums.subforums.questions.answers will be 5 levels deep
|
70
|
+
root.limit_deep(10)
|
63
71
|
end
|
64
72
|
|
65
73
|
# Everything you can pass to +where+ method will work as constraints:
|
@@ -95,6 +103,20 @@ EvilSeed.configure do |config|
|
|
95
103
|
# This will remove the columns even if the model is not a root node and is
|
96
104
|
# dumped via an association.
|
97
105
|
config.ignore_columns("Profile", :name)
|
106
|
+
|
107
|
+
# Disable foreign key nullification for records that are not included in the dump
|
108
|
+
# By default, EvilSeed will nullify foreign keys for records that are not included in the dump
|
109
|
+
config.dont_nullify = true
|
110
|
+
|
111
|
+
# Unscope relations to include soft-deleted records etc
|
112
|
+
# This is useful when you want to include all records, including those that are hidden by default
|
113
|
+
# By default, EvilSeed will abide default scope of models
|
114
|
+
config.unscoped = true
|
115
|
+
|
116
|
+
# Verbose mode will print out the progress of the dump to the console along with writing the file
|
117
|
+
# By default, verbose mode is off
|
118
|
+
config.verbose = true
|
119
|
+
config.verbose_sql = true
|
98
120
|
end
|
99
121
|
```
|
100
122
|
|
@@ -5,16 +5,19 @@ module EvilSeed
|
|
5
5
|
# Configuration for dumping some root model and its associations
|
6
6
|
class Root
|
7
7
|
attr_reader :model, :constraints
|
8
|
-
attr_reader :total_limit, :association_limits
|
9
|
-
attr_reader :exclusions
|
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
|
@@ -23,6 +26,12 @@ module EvilSeed
|
|
23
26
|
@exclusions += association_patterns
|
24
27
|
end
|
25
28
|
|
29
|
+
# Include some excluded associations back to the dump
|
30
|
+
# @param association_patterns Array<String, Regex> Patterns to exclude associated models from dump
|
31
|
+
def include(*association_patterns)
|
32
|
+
@inclusions += association_patterns
|
33
|
+
end
|
34
|
+
|
26
35
|
# Limit number of records in all (if pattern is not provided) or given associations to include into dump
|
27
36
|
# @param limit [Integer] Maximum number of records in associations to include into dump
|
28
37
|
# @param association_pattern [String, Regex] Pattern to limit number of records for certain associated models
|
@@ -34,8 +43,22 @@ module EvilSeed
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|
46
|
+
# Limit deepenes of associations to include into dump
|
47
|
+
# @param limit [Integer] Maximum level to recursively dive into associations
|
48
|
+
def limit_deep(limit)
|
49
|
+
@deep_limit = limit
|
50
|
+
end
|
51
|
+
|
52
|
+
def do_not_nullify(nullify_flag)
|
53
|
+
@dont_nullify = nullify_flag
|
54
|
+
end
|
55
|
+
|
37
56
|
def excluded?(association_path)
|
38
|
-
exclusions.any? { |exclusion|
|
57
|
+
exclusions.any? { |exclusion| association_path.match(exclusion) } #.match(association_path) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def included?(association_path)
|
61
|
+
inclusions.any? { |inclusion| association_path.match(inclusion) } #.match(association_path) }
|
39
62
|
end
|
40
63
|
end
|
41
64
|
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
|
@@ -65,11 +65,17 @@ module EvilSeed
|
|
65
65
|
|
66
66
|
def write!(attributes)
|
67
67
|
# Remove non-insertable columns from attributes
|
68
|
-
attributes = attributes.slice(*insertable_column_names)
|
68
|
+
attributes = prepare(attributes.slice(*insertable_column_names))
|
69
|
+
|
70
|
+
if configuration.verbose_sql
|
71
|
+
puts("-- #{relation_dumper.association_path}\n")
|
72
|
+
puts(@tuples_written.zero? ? insert_statement : ",\n")
|
73
|
+
puts(" (#{attributes.join(', ')})")
|
74
|
+
end
|
69
75
|
|
70
76
|
@output.write("-- #{relation_dumper.association_path}\n") && @header_written = true unless @header_written
|
71
77
|
@output.write(@tuples_written.zero? ? insert_statement : ",\n")
|
72
|
-
@output.write(" (#{
|
78
|
+
@output.write(" (#{attributes.join(', ')})")
|
73
79
|
@tuples_written += 1
|
74
80
|
@output.write(";\n") && @tuples_written = 0 if @tuples_written == MAX_TUPLES_PER_INSERT_STMT
|
75
81
|
end
|
@@ -84,7 +90,7 @@ module EvilSeed
|
|
84
90
|
attributes.map do |key, value|
|
85
91
|
type = model_class.attribute_types[key]
|
86
92
|
model_class.connection.quote(type.serialize(value))
|
87
|
-
end
|
93
|
+
end.flatten.compact
|
88
94
|
end
|
89
95
|
end
|
90
96
|
end
|
@@ -25,13 +25,15 @@ module EvilSeed
|
|
25
25
|
|
26
26
|
attr_reader :relation, :root_dumper, :model_class, :association_path, :search_key, :identifiers, :nullify_columns,
|
27
27
|
:belongs_to_reflections, :has_many_reflections, :foreign_keys, :loaded_ids, :to_load_map,
|
28
|
-
:record_dumper, :inverse_reflection, :table_names, :options
|
28
|
+
:record_dumper, :inverse_reflection, :table_names, :options,
|
29
|
+
:current_deep, :verbose
|
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: :root_dumper
|
31
32
|
|
32
33
|
def initialize(relation, root_dumper, association_path, **options)
|
33
34
|
@relation = relation
|
34
35
|
@root_dumper = root_dumper
|
36
|
+
@verbose = configuration.verbose
|
35
37
|
@identifiers = options[:identifiers]
|
36
38
|
@to_load_map = Hash.new { |h, k| h[k] = [] }
|
37
39
|
@foreign_keys = Hash.new { |h, k| h[k] = [] }
|
@@ -46,15 +48,23 @@ module EvilSeed
|
|
46
48
|
@belongs_to_reflections = setup_belongs_to_reflections
|
47
49
|
@has_many_reflections = setup_has_many_reflections
|
48
50
|
@options = options
|
51
|
+
@current_deep = association_path.split('.').size
|
52
|
+
@dont_nullify = dont_nullify
|
53
|
+
|
54
|
+
puts("- #{association_path}") if verbose
|
49
55
|
end
|
50
56
|
|
51
57
|
# Generate dump and write it into +io+
|
52
58
|
# @return [Array<IO>] List of dump IOs for separate tables in order of dependencies (belongs_to are first)
|
53
59
|
def call
|
54
60
|
dump!
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
if deep_limit and current_deep > deep_limit
|
62
|
+
[record_dumper.result].flatten.compact
|
63
|
+
else
|
64
|
+
belongs_to_dumps = dump_belongs_to_associations!
|
65
|
+
has_many_dumps = dump_has_many_associations!
|
66
|
+
[belongs_to_dumps, record_dumper.result, has_many_dumps].flatten.compact
|
67
|
+
end
|
58
68
|
end
|
59
69
|
|
60
70
|
private
|
@@ -64,16 +74,22 @@ module EvilSeed
|
|
64
74
|
model_class.ignored_columns += Array(configuration.ignored_columns_for(model_class.sti_name))
|
65
75
|
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
76
|
if identifiers.present?
|
77
|
+
puts(" # #{search_key} => #{identifiers}") if verbose
|
67
78
|
# 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
79
|
identifiers.in_groups_of(MAX_IDENTIFIERS_IN_IN_STMT).each do |ids|
|
69
|
-
fetch_attributes(relation.where(search_key => ids.compact))
|
80
|
+
attrs = fetch_attributes(relation.where(search_key => ids.compact))
|
81
|
+
puts(" -- dumped #{attrs.size}") if verbose
|
82
|
+
attrs.each do |attributes|
|
70
83
|
next unless check_limits!
|
71
84
|
dump_record!(attributes)
|
72
85
|
end
|
73
86
|
end
|
74
87
|
else
|
88
|
+
puts(" # #{relation.count}") if verbose
|
75
89
|
relation.in_batches do |relation|
|
76
|
-
fetch_attributes(relation)
|
90
|
+
attrs = fetch_attributes(relation)
|
91
|
+
puts(" -- dumped #{attrs.size}") if verbose
|
92
|
+
attrs.each do |attributes|
|
77
93
|
next unless check_limits!
|
78
94
|
dump_record!(attributes)
|
79
95
|
end
|
@@ -84,8 +100,10 @@ module EvilSeed
|
|
84
100
|
end
|
85
101
|
|
86
102
|
def dump_record!(attributes)
|
87
|
-
|
88
|
-
|
103
|
+
unless dont_nullify
|
104
|
+
nullify_columns.each do |nullify_column|
|
105
|
+
attributes[nullify_column] = nil
|
106
|
+
end
|
89
107
|
end
|
90
108
|
return unless record_dumper.call(attributes)
|
91
109
|
foreign_keys.each do |reflection_name, fk_column|
|
@@ -140,7 +158,11 @@ module EvilSeed
|
|
140
158
|
end
|
141
159
|
|
142
160
|
def build_relation(reflection)
|
143
|
-
|
161
|
+
if configuration.unscoped
|
162
|
+
relation = reflection.klass.unscoped
|
163
|
+
else
|
164
|
+
relation = reflection.klass.all
|
165
|
+
end
|
144
166
|
relation = relation.instance_eval(&reflection.scope) if reflection.scope
|
145
167
|
relation = relation.where(reflection.type => model_class.to_s) if reflection.options[:as] # polymorphic
|
146
168
|
relation
|
@@ -149,23 +171,32 @@ module EvilSeed
|
|
149
171
|
def setup_belongs_to_reflections
|
150
172
|
model_class.reflect_on_all_associations(:belongs_to).reject do |reflection|
|
151
173
|
next false if reflection.options[:polymorphic] # TODO: Add support for polymorphic belongs_to
|
174
|
+
included = root.included?("#{association_path}.#{reflection.name}")
|
152
175
|
excluded = root.excluded?("#{association_path}.#{reflection.name}") || reflection.name == inverse_reflection
|
153
|
-
if excluded
|
154
|
-
|
176
|
+
if excluded and not included
|
177
|
+
if model_class.column_names.include?(reflection.foreign_key)
|
178
|
+
puts(" -- excluded #{reflection.foreign_key}") if verbose
|
179
|
+
nullify_columns << reflection.foreign_key
|
180
|
+
end
|
155
181
|
else
|
156
182
|
foreign_keys[reflection.name] = reflection.foreign_key
|
157
183
|
table_names[reflection.name] = reflection.table_name
|
158
184
|
end
|
159
|
-
excluded
|
185
|
+
excluded and not included
|
160
186
|
end
|
161
187
|
end
|
162
188
|
|
163
189
|
# This method returns only direct has_one and has_many reflections. For HABTM it returns intermediate has_many
|
164
190
|
def setup_has_many_reflections
|
191
|
+
puts(" -- reflections #{model_class._reflections.keys}") if verbose
|
165
192
|
model_class._reflections.select do |_reflection_name, reflection|
|
166
193
|
next false if model_class.primary_key.nil?
|
194
|
+
|
167
195
|
next false if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
168
|
-
|
196
|
+
|
197
|
+
included = root.included?("#{association_path}.#{reflection.name}")
|
198
|
+
excluded = root.excluded?("#{association_path}.#{reflection.name}") || reflection.name == inverse_reflection
|
199
|
+
%i[has_one has_many].include?(reflection.macro) && !(excluded and not included)
|
169
200
|
end.map(&:second)
|
170
201
|
end
|
171
202
|
end
|
@@ -5,7 +5,7 @@ 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
10
|
delegate :loaded_map, :configuration, to: :dumper
|
11
11
|
|
@@ -14,6 +14,8 @@ module EvilSeed
|
|
14
14
|
@dumper = dumper
|
15
15
|
@to_load_map = {}
|
16
16
|
@total_limit = root.total_limit
|
17
|
+
@deep_limit = root.deep_limit
|
18
|
+
@dont_nullify = root.dont_nullify
|
17
19
|
@association_limits = root.association_limits.dup
|
18
20
|
|
19
21
|
@model_class = root.model.constantize
|
@@ -24,6 +26,7 @@ module EvilSeed
|
|
24
26
|
def call
|
25
27
|
association_path = model_class.model_name.singular
|
26
28
|
relation = model_class.all
|
29
|
+
relation = relation.unscoped if configuration.unscoped
|
27
30
|
relation = relation.where(*root.constraints) if root.constraints.any? # without arguments returns not a relation
|
28
31
|
RelationDumper.new(relation, self, association_path).call
|
29
32
|
end
|
data/lib/evil_seed/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: evil-seed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Novikov
|
8
8
|
- Vladimir Dementyev
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-06-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -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
|
@@ -173,7 +175,7 @@ homepage: https://github.com/palkan/evil-seed
|
|
173
175
|
licenses:
|
174
176
|
- MIT
|
175
177
|
metadata: {}
|
176
|
-
post_install_message:
|
178
|
+
post_install_message:
|
177
179
|
rdoc_options: []
|
178
180
|
require_paths:
|
179
181
|
- lib
|
@@ -188,8 +190,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
190
|
- !ruby/object:Gem::Version
|
189
191
|
version: '0'
|
190
192
|
requirements: []
|
191
|
-
rubygems_version: 3.
|
192
|
-
signing_key:
|
193
|
+
rubygems_version: 3.5.11
|
194
|
+
signing_key:
|
193
195
|
specification_version: 4
|
194
196
|
summary: Create partial and anonymized production database dumps for use in development
|
195
197
|
test_files: []
|