evil-seed 0.5.0 → 0.6.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 +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: []
|