seed_builder 1.2.1 → 1.3.1
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/CHANGELOG.md +24 -0
- data/README.md +116 -19
- data/bin/seed +36 -0
- data/lib/seed_builder/config.rb +8 -6
- data/lib/seed_builder/loader.rb +172 -34
- data/lib/seed_builder/railtie.rb +15 -0
- data/lib/seed_builder.rb +22 -2
- data/lib/tasks/seed.rake +22 -0
- data/seed_builder.gemspec +25 -18
- data/sig/seed_builder/config.rbs +22 -0
- data/sig/seed_builder/loader.rbs +8 -0
- data/sig/seed_builder/railtie.rbs +5 -0
- data/sig/seed_builder.rbs +7 -0
- metadata +164 -71
- data/.editorconfig +0 -14
- data/.github/dependabot.yml +0 -27
- data/.github/workflows/_trunk_check.yml +0 -15
- data/.github/workflows/test.yml +0 -42
- data/.gitignore +0 -24
- data/.ruby-version +0 -1
- data/.trunk/.gitignore +0 -9
- data/.trunk/configs/.markdownlint.yaml +0 -2
- data/.trunk/configs/.shellcheckrc +0 -7
- data/.trunk/configs/.yamllint.yaml +0 -7
- data/.trunk/trunk.yaml +0 -39
- data/.vscode/extensions.json +0 -18
- data/.vscode/settings.json +0 -7
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -260
- data/lib/rails_config.rb +0 -3
- data/spec/fixtures/schema.rb +0 -9
- data/spec/fixtures/seeds/20241206200111_create_users.rb +0 -5
- data/spec/fixtures/seeds.rb +0 -1
- data/spec/generator_spec.rb +0 -50
- data/spec/seed_builder/config_spec.rb +0 -33
- data/spec/seed_builder/loader_spec.rb +0 -35
- data/spec/seed_builder_spec.rb +0 -25
- data/spec/spec_helper.rb +0 -23
- data/spec/support/junit_formatter.rb +0 -6
- data/spec/support/rails_helpers.rb +0 -41
- data/usr/bin/release.sh +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e17c7224d3dede956d465e3d7c81d45fadfae50481fad4f68fc0d5aee8d2936
|
|
4
|
+
data.tar.gz: a0bea568dc1e39af05473f5614ecd7f7337328ab191ce86de7f7ae1632e51615
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7cd7abeb732f00b1a226caa1cc2793c9bb403e416ca85886ee72232b70eac6d33c81f3b1e7df88a3dc8eeecc798c0e0497254e3a5e9ab85eb3aa2d97199dc3b1
|
|
7
|
+
data.tar.gz: 4ea91653aae9ab4216311fbf9da32a649595ced5e128632ed095e7dcd8c8ff1d0b19a79ab0e7fe23354abb2740f9864886fc90e3bc92d970f0fead5216e5ceb3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.3.1 (2026-11-03)
|
|
4
|
+
|
|
5
|
+
- Use updated seed paths after configuration has already been read.
|
|
6
|
+
- Load the application's Rails environment when running the seed command.
|
|
7
|
+
- Exclude development tools and audit records from the installed gem.
|
|
8
|
+
- Cancel release preparation when confirmation input is closed.
|
|
9
|
+
- Run timestamped seed files in numeric timestamp order.
|
|
10
|
+
- Fail `bin/seed` and `bin/rails seed:run` when the seed is missing, ambiguous, invalid, or outside the seeds directory.
|
|
11
|
+
- Reject an invalid or missing seed name before opening a database connection.
|
|
12
|
+
- Warn that the seed directory is missing only when directory loading is enabled.
|
|
13
|
+
- Keep `bin/rails db:seed` on the Rails seed file when `SeedBuilder.config.use_seed_loader` is false.
|
|
14
|
+
|
|
15
|
+
## 1.3.0 (2025-11-04)
|
|
16
|
+
|
|
17
|
+
- Replace `puts` statements with `Rails.logger` for proper logging integration
|
|
18
|
+
- Add tagged logging with `seed` tag for all seed-related log messages
|
|
19
|
+
- Add `logger` configuration option to allow custom logger setup via `SeedBuilder.config.logger = custom_logger`
|
|
20
|
+
- Fix seed file loading to use `load` instead of `require` to ensure files are re-executed on each seed run
|
|
21
|
+
- Logger automatically detects if `Rails.logger` already supports tagging to avoid double-wrapping
|
|
22
|
+
- Add ability to run specific seed files on demand using `bin/rails seed:run[SEED_NAME]`
|
|
23
|
+
- Add `load_seed_file` method to `SeedBuilder::Loader` for loading individual seeds
|
|
24
|
+
- Add ambiguous match detection: when multiple seed files match the same name, the loader lists the matches and the command needs the full name with the timestamp
|
|
25
|
+
- Patch `Rails.application.load_seed` to use SeedBuilder loader via Railtie integration
|
|
26
|
+
|
|
3
27
|
## 1.2.1
|
|
4
28
|
|
|
5
29
|
- Fix seed files to be loaded as `.rb` files
|
data/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# seed_builder.rb
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/seed_builder) [](https://github.com/amkisko/seed_builder.rb/actions/workflows/test.yml) [](https://codecov.io/
|
|
3
|
+
[](https://badge.fury.io/rb/seed_builder) [](https://github.com/amkisko/seed_builder.rb/actions/workflows/test.yml) [](https://app.codecov.io/github/amkisko/seed_builder.rb)
|
|
4
4
|
|
|
5
|
-
Seed
|
|
6
|
-
|
|
7
|
-
Sponsored by [Kisko Labs](https://www.kiskolabs.com).
|
|
5
|
+
Seed loader and generator for ActiveRecord.
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
10
8
|
|
|
@@ -30,10 +28,31 @@ gem "seed_builder"
|
|
|
30
28
|
|
|
31
29
|
### Run seeds
|
|
32
30
|
|
|
31
|
+
Run all seeds:
|
|
32
|
+
|
|
33
33
|
```sh
|
|
34
34
|
bin/rails db:seed
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Or in code:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
Rails.application.load_seed
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Run a specific seed file:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
bin/rails seed:run[create_users]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You can specify the seed name in different formats:
|
|
50
|
+
- Class name: `create_users` (matches `20241206200111_create_users.rb`)
|
|
51
|
+
- Full name: `20241206200111_create_users`
|
|
52
|
+
- Timestamp: `20241206200111`
|
|
53
|
+
|
|
54
|
+
If several seed files match the same name, the command writes the matching names to the log and exits with status 1. Run it again with the timestamped name, for example `20241206200111_create_users`. `bin/seed` uses the same names and the same failed status.
|
|
55
|
+
|
|
37
56
|
### Generate seed file
|
|
38
57
|
|
|
39
58
|
```sh
|
|
@@ -46,7 +65,7 @@ bin/rails g seed create_users
|
|
|
46
65
|
|
|
47
66
|
By default seed file `db/seeds.rb` is loaded.
|
|
48
67
|
|
|
49
|
-
To
|
|
68
|
+
To stop loading `db/seeds.rb`:
|
|
50
69
|
|
|
51
70
|
```ruby
|
|
52
71
|
SeedBuilder.config.load_default_seeds = false
|
|
@@ -54,7 +73,7 @@ SeedBuilder.config.load_default_seeds = false
|
|
|
54
73
|
|
|
55
74
|
### Set seed directory
|
|
56
75
|
|
|
57
|
-
|
|
76
|
+
A relative `seeds_path` is joined to `Rails.root`. An absolute `seeds_path` is used as given. Set `seeds_full_path` to select the directory directly.
|
|
58
77
|
|
|
59
78
|
```ruby
|
|
60
79
|
SeedBuilder.config.seeds_path = "db/seeds"
|
|
@@ -72,45 +91,123 @@ SeedBuilder.config.generate_spec = false
|
|
|
72
91
|
SeedBuilder.config.use_seed_loader = false
|
|
73
92
|
```
|
|
74
93
|
|
|
94
|
+
### Set custom logger
|
|
95
|
+
|
|
96
|
+
By default, SeedBuilder uses `Rails.logger` (or falls back to `Logger.new($stdout)` if Rails is not available). All seed-related log messages are tagged with `[seed]`.
|
|
97
|
+
|
|
98
|
+
To use a custom logger:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
SeedBuilder.config.logger = MyCustomLogger.new
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
SeedBuilder wraps that logger with `ActiveSupport::TaggedLogging` when the logger has no `tagged` method, so the `[seed]` tag is present.
|
|
105
|
+
|
|
75
106
|
## Specification checklist
|
|
76
107
|
|
|
77
108
|
- [x] User can generate seed file under `db/seeds` directory with common format
|
|
78
109
|
- [x] User can generate seed file with test script included
|
|
79
110
|
- [x] User can run all seeds
|
|
80
|
-
- [
|
|
111
|
+
- [x] User can run specific seed file
|
|
112
|
+
|
|
113
|
+
## Limitations
|
|
114
|
+
|
|
115
|
+
- `bin/rails db:seed` calls `ActiveRecord::Tasks::DatabaseTasks.load_seed`, which calls `Rails.application.load_seed`. The railtie replaces `load_seed` while `use_seed_loader` is on. Set `use_seed_loader = false` in an initializer, before the application finishes booting, to keep the Rails seed file.
|
|
116
|
+
- The ActiveRecord migration generator writes the seed files.
|
|
117
|
+
- A seed run is not reversed. Write seed files so they can run more than once, for example by checking that a record is already present.
|
|
118
|
+
- A seed file is not a migration.
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Install dependencies
|
|
124
|
+
bundle install
|
|
125
|
+
bundle exec appraisal generate
|
|
81
126
|
|
|
82
|
-
|
|
127
|
+
# Run tests for current Rails version
|
|
128
|
+
bundle exec polyrun parallel-rspec --workers 5 --merge-failures
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
130
|
+
# Run tests for the configured Rails versions (7.2 and 8.1)
|
|
131
|
+
bin/appraisals
|
|
132
|
+
|
|
133
|
+
# Run tests for Rails 7.2
|
|
134
|
+
bin/appraisals rails72
|
|
135
|
+
|
|
136
|
+
# Run tests for Rails 7.2 and Rails 8.1 on Ruby 3.4
|
|
137
|
+
bin/appraisals rails72 rails8ruby34
|
|
138
|
+
|
|
139
|
+
# Or use appraisal directly
|
|
140
|
+
bundle exec appraisal rails72 rspec
|
|
141
|
+
|
|
142
|
+
# Run linter
|
|
143
|
+
bundle exec standardrb --fix
|
|
144
|
+
|
|
145
|
+
# Check type signatures
|
|
146
|
+
bundle exec rbs validate
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Development: Using from Local Repository
|
|
150
|
+
|
|
151
|
+
When developing the gem or testing changes in your application, you can point your Gemfile to a local path:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
# In your application's Gemfile
|
|
155
|
+
gem "seed_builder", path: "../seed_builder.rb"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Then run:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
bundle install
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
With `path:` in the Gemfile, Bundler loads this checkout. Edits in the checkout are what the application runs, without a rebuild or reinstall.
|
|
88
165
|
|
|
89
166
|
## Contributing
|
|
90
167
|
|
|
91
168
|
Bug reports and pull requests are welcome on GitHub at <https://github.com/amkisko/seed_builder.rb>
|
|
92
169
|
|
|
93
170
|
Contribution policy:
|
|
94
|
-
|
|
95
171
|
- New features are not necessarily added to the gem
|
|
96
172
|
- Pull request should have test coverage for affected parts
|
|
97
173
|
- Pull request should have changelog entry
|
|
174
|
+
|
|
175
|
+
Review policy:
|
|
98
176
|
- It might take up to 2 calendar weeks to review and merge critical fixes
|
|
99
177
|
- It might take up to 6 calendar months to review and merge pull request
|
|
100
178
|
- It might take up to 1 calendar year to review an issue
|
|
101
179
|
|
|
102
180
|
## Publishing
|
|
103
181
|
|
|
104
|
-
|
|
182
|
+
From a clean checkout, run:
|
|
105
183
|
|
|
106
184
|
```sh
|
|
107
|
-
|
|
108
|
-
rm seed_builder-*.gem
|
|
109
|
-
gem build seed_builder.gemspec
|
|
110
|
-
gem push seed_builder-$GEM_VERSION.gem
|
|
111
|
-
git tag $GEM_VERSION && git push --tags
|
|
185
|
+
ruby usr/bin/release.rb
|
|
112
186
|
```
|
|
113
187
|
|
|
188
|
+
The script installs dependencies, runs the test suite, requires a clean worktree, checks the changelog heading, and asks before publishing. On confirmation it pushes the gem, tags that version, and opens a GitHub release.
|
|
189
|
+
|
|
190
|
+
## Links
|
|
191
|
+
|
|
192
|
+
- [GitHub](https://github.com/amkisko/seed_builder.rb)
|
|
193
|
+
- [GitLab](https://gitlab.com/amkisko/seed_builder.rb)
|
|
194
|
+
- [RubyGems](https://rubygems.org/gems/seed_builder)
|
|
195
|
+
- [Versions Atom](https://rubygems.org/gems/seed_builder/versions.atom) (feed id `8140233409492`)
|
|
196
|
+
- [libraries.io](https://libraries.io/rubygems/seed_builder)
|
|
197
|
+
- [Deps.dev](https://deps.dev/rubygems/seed_builder)
|
|
198
|
+
- [SonarCloud](https://sonarcloud.io/project/overview?id=amkisko_seed_builder.rb)
|
|
199
|
+
- [Snyk](https://snyk.io/test/github/amkisko/seed_builder.rb)
|
|
200
|
+
- [Codecov](https://app.codecov.io/github/amkisko/seed_builder.rb)
|
|
201
|
+
- [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/amkisko/seed_builder.rb)
|
|
202
|
+
|
|
114
203
|
## License
|
|
115
204
|
|
|
116
205
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
206
|
+
|
|
207
|
+
## Sponsors
|
|
208
|
+
|
|
209
|
+
Sponsored by [Kisko Labs](https://www.kiskolabs.com).
|
|
210
|
+
|
|
211
|
+
<a href="https://www.kiskolabs.com">
|
|
212
|
+
<img src="kisko.svg" width="200" alt="Sponsored by Kisko Labs" />
|
|
213
|
+
</a>
|
data/bin/seed
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
environment = File.expand_path("config/environment.rb", Dir.pwd)
|
|
4
|
+
require environment if File.file?(environment)
|
|
5
|
+
|
|
6
|
+
unless defined?(Rails)
|
|
7
|
+
puts "Error: Rails environment not loaded. Please run this from a Rails application root."
|
|
8
|
+
puts "Alternatively, use: bin/rails seed:run[SEED_NAME]"
|
|
9
|
+
exit 1
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "seed_builder"
|
|
13
|
+
|
|
14
|
+
if ARGV.empty?
|
|
15
|
+
puts "Usage: bin/seed SEED_NAME"
|
|
16
|
+
puts ""
|
|
17
|
+
puts "Run a specific seed file by name."
|
|
18
|
+
puts ""
|
|
19
|
+
puts "Examples:"
|
|
20
|
+
puts " bin/seed create_users"
|
|
21
|
+
puts " bin/seed 20241206200111_create_users"
|
|
22
|
+
puts " bin/seed 20241206200111"
|
|
23
|
+
puts ""
|
|
24
|
+
puts "Note: This script must be run from a Rails application root."
|
|
25
|
+
puts " If multiple seed files match the name, use the full name with"
|
|
26
|
+
puts " timestamp to avoid ambiguity (e.g., 20241206200111_create_users)."
|
|
27
|
+
puts ""
|
|
28
|
+
puts "Alternatively, use: bin/rails seed:run[SEED_NAME]"
|
|
29
|
+
exit 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
seed_name = ARGV[0]
|
|
33
|
+
|
|
34
|
+
loader = SeedBuilder::Loader.new
|
|
35
|
+
exit 1 unless loader.load_seed_file(seed_name)
|
|
36
|
+
|
data/lib/seed_builder/config.rb
CHANGED
|
@@ -7,7 +7,7 @@ module SeedBuilder
|
|
|
7
7
|
|
|
8
8
|
attr_writer :default_seeds_full_path
|
|
9
9
|
def default_seeds_full_path
|
|
10
|
-
@default_seeds_full_path
|
|
10
|
+
@default_seeds_full_path || Rails.root.join(default_seeds_path)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
attr_writer :seeds_path
|
|
@@ -17,7 +17,7 @@ module SeedBuilder
|
|
|
17
17
|
|
|
18
18
|
attr_writer :seeds_full_path
|
|
19
19
|
def seeds_full_path
|
|
20
|
-
@seeds_full_path
|
|
20
|
+
@seeds_full_path || Rails.root.join(seeds_path)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def seeds_path_glob
|
|
@@ -26,22 +26,24 @@ module SeedBuilder
|
|
|
26
26
|
|
|
27
27
|
attr_writer :load_default_seeds
|
|
28
28
|
def load_default_seeds?
|
|
29
|
-
@load_default_seeds.nil?
|
|
29
|
+
@load_default_seeds.nil? || @load_default_seeds
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
attr_writer :load_seeds
|
|
33
33
|
def load_seeds?
|
|
34
|
-
@load_seeds.nil?
|
|
34
|
+
@load_seeds.nil? || @load_seeds
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
attr_writer :generate_spec
|
|
38
38
|
def generate_spec?
|
|
39
|
-
@generate_spec.nil?
|
|
39
|
+
@generate_spec.nil? || @generate_spec
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
attr_writer :use_seed_loader
|
|
43
43
|
def use_seed_loader?
|
|
44
|
-
@use_seed_loader.nil?
|
|
44
|
+
@use_seed_loader.nil? || @use_seed_loader
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
attr_accessor :logger
|
|
46
48
|
end
|
|
47
49
|
end
|
data/lib/seed_builder/loader.rb
CHANGED
|
@@ -1,50 +1,188 @@
|
|
|
1
1
|
module SeedBuilder
|
|
2
2
|
class Loader
|
|
3
|
+
SEED_NAME = /\A[A-Za-z0-9_]+\z/
|
|
4
|
+
private_constant :SEED_NAME
|
|
5
|
+
|
|
3
6
|
def load_seed
|
|
4
|
-
|
|
7
|
+
default_path = SeedBuilder.config.default_seeds_full_path
|
|
8
|
+
base_path = SeedBuilder.config.seeds_full_path
|
|
9
|
+
load_default = SeedBuilder.config.load_default_seeds? && File.file?(default_path.to_s)
|
|
10
|
+
if SeedBuilder.config.load_seeds? && !File.directory?(base_path.to_s)
|
|
11
|
+
warn_seed("Seed directory #{base_path} does not exist")
|
|
12
|
+
end
|
|
13
|
+
load_directory = SeedBuilder.config.load_seeds? && File.directory?(base_path.to_s)
|
|
14
|
+
return unless load_default || load_directory
|
|
15
|
+
|
|
16
|
+
prepare_active_record
|
|
17
|
+
load_default_seed(default_path) if load_default
|
|
18
|
+
load_directory_seeds(base_path) if load_directory
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load_seed_file(seed_name)
|
|
22
|
+
base_path = SeedBuilder.config.seeds_full_path
|
|
23
|
+
unless seed_name.is_a?(String) && SEED_NAME.match?(seed_name)
|
|
24
|
+
warn_seed("Seed name '#{seed_name}' is invalid")
|
|
25
|
+
return false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unless File.directory?(base_path.to_s)
|
|
29
|
+
warn_seed("Seed directory #{base_path} does not exist")
|
|
30
|
+
return false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
result = locate_seed_file(base_path, seed_name)
|
|
34
|
+
if result.nil?
|
|
35
|
+
warn_seed("Seed file '#{seed_name}' not found in #{base_path}")
|
|
36
|
+
return false
|
|
37
|
+
elsif result.is_a?(Array) && result.first == :ambiguous
|
|
38
|
+
matches = result.last
|
|
39
|
+
logger.tagged("seed") do
|
|
40
|
+
logger.warn "Multiple seed files match '#{seed_name}':"
|
|
41
|
+
matches.each do |file|
|
|
42
|
+
logger.warn " - #{File.basename(file, ".*")}"
|
|
43
|
+
end
|
|
44
|
+
logger.warn "Please be more specific using the full name with timestamp (e.g., 20241206200111_create_users)"
|
|
45
|
+
end
|
|
46
|
+
return false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
seed_file = result
|
|
50
|
+
seed_path = File.basename(seed_file, ".*")
|
|
51
|
+
timestamp, klass_name = split_seed_basename(seed_path)
|
|
52
|
+
if klass_name.blank?
|
|
53
|
+
warn_seed("Invalid seed file format: #{seed_path}. Expected format: TIMESTAMP_CLASS_NAME.rb")
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
prepare_active_record
|
|
58
|
+
load seed_file
|
|
59
|
+
execute_seed_class(klass_name, timestamp, seed_name)
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def logger
|
|
66
|
+
SeedBuilder.logger
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def warn_seed(message)
|
|
70
|
+
logger.tagged("seed") do
|
|
71
|
+
logger.warn(message)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def load_default_seed(default_path)
|
|
76
|
+
started_at = Time.current
|
|
77
|
+
logger.tagged("seed") do
|
|
78
|
+
logger.info "== #{SeedBuilder.config.default_seeds_path}: seeding"
|
|
79
|
+
end
|
|
80
|
+
load default_path
|
|
81
|
+
logger.tagged("seed") do
|
|
82
|
+
logger.info "== #{SeedBuilder.config.default_seeds_path}: seeded (#{(Time.current - started_at).round(4)}s)"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
5
85
|
|
|
6
|
-
|
|
7
|
-
|
|
86
|
+
def load_directory_seeds(base_path)
|
|
87
|
+
seed_basenames(base_path).each do |seed_path|
|
|
88
|
+
full_path = File.join(base_path, "#{seed_path}.rb")
|
|
89
|
+
unless inside_directory?(base_path, full_path)
|
|
90
|
+
warn_seed("Seed file #{seed_path} was not loaded from #{base_path}")
|
|
91
|
+
next
|
|
92
|
+
end
|
|
8
93
|
|
|
94
|
+
load full_path
|
|
95
|
+
timestamp, klass_name = split_seed_basename(seed_path)
|
|
96
|
+
next if klass_name.blank?
|
|
97
|
+
|
|
98
|
+
execute_seed_class(klass_name, timestamp)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def prepare_active_record
|
|
9
103
|
ActiveRecord::Base.connection.schema_cache.clear!
|
|
10
104
|
ActiveRecord::Base.descendants.each(&:reset_column_information)
|
|
105
|
+
end
|
|
11
106
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
107
|
+
def execute_seed_class(klass_name, timestamp, seed_name = nil)
|
|
108
|
+
klass = klass_name.camelize.constantize
|
|
109
|
+
klass_name_display = klass.name
|
|
110
|
+
logger.tagged("seed") do
|
|
111
|
+
logger.info "== #{timestamp} #{klass_name_display}: seeding"
|
|
112
|
+
end
|
|
113
|
+
started_at = Time.current
|
|
114
|
+
seed_instance = klass.new
|
|
115
|
+
if seed_instance.respond_to?(:change)
|
|
116
|
+
seed_instance.change
|
|
117
|
+
else
|
|
118
|
+
raise "Seed #{klass_name_display} does not respond to :change"
|
|
18
119
|
end
|
|
120
|
+
logger.tagged("seed") do
|
|
121
|
+
logger.info "== #{timestamp} #{klass_name_display}: seeded (#{(Time.current - started_at).round(4)}s)"
|
|
122
|
+
end
|
|
123
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
124
|
+
klass_name_display = defined?(klass) ? klass.name : (klass_name&.camelize || seed_name || klass_name)
|
|
125
|
+
logger.tagged("seed") do
|
|
126
|
+
logger.error "Seeding #{klass_name_display} failed: #{e.record.errors.full_messages}"
|
|
127
|
+
end
|
|
128
|
+
raise e
|
|
129
|
+
rescue => e
|
|
130
|
+
logger.tagged("seed") do
|
|
131
|
+
logger.error "Error loading seed: #{e.message}"
|
|
132
|
+
end
|
|
133
|
+
raise e
|
|
134
|
+
end
|
|
19
135
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
puts "== #{timestamp} #{klass.name}: seeded (#{(Time.current - started_at).round(4)}s)"
|
|
39
|
-
rescue ActiveRecord::RecordInvalid => e
|
|
40
|
-
puts "Seeding #{klass.name} failed: #{e.record.errors.full_messages}"
|
|
41
|
-
raise e
|
|
42
|
-
end
|
|
136
|
+
def locate_seed_file(base_path, seed_name)
|
|
137
|
+
exact_path = File.join(base_path, "#{seed_name}.rb")
|
|
138
|
+
if File.file?(exact_path)
|
|
139
|
+
return inside_directory?(base_path, exact_path) ? exact_path : nil
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
matches = seed_basenames(base_path).filter_map { |basename|
|
|
143
|
+
next unless basename == seed_name || basename.end_with?("_#{seed_name}") || basename.start_with?("#{seed_name}_")
|
|
144
|
+
|
|
145
|
+
path = File.join(base_path, "#{basename}.rb")
|
|
146
|
+
path if inside_directory?(base_path, path)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case matches.length
|
|
150
|
+
when 0
|
|
151
|
+
nil
|
|
152
|
+
when 1
|
|
153
|
+
matches.first
|
|
43
154
|
else
|
|
44
|
-
|
|
155
|
+
[:ambiguous, matches]
|
|
45
156
|
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def seed_basenames(base_path)
|
|
160
|
+
Dir.children(base_path.to_s).filter_map { |entry|
|
|
161
|
+
next if entry.start_with?(".")
|
|
162
|
+
next unless entry.end_with?(".rb")
|
|
163
|
+
|
|
164
|
+
File.basename(entry, ".rb")
|
|
165
|
+
}.sort_by { |name| seed_sort_key(name) }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def seed_sort_key(name)
|
|
169
|
+
match = name.match(/\A([0-9]+)_/)
|
|
170
|
+
match ? [0, match[1].to_i, name] : [1, 0, name]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def split_seed_basename(seed_path)
|
|
174
|
+
match = seed_path.match(/\A([0-9]+)_(.+)\z/)
|
|
175
|
+
return [nil, nil] unless match
|
|
176
|
+
|
|
177
|
+
[match[1], match[2]]
|
|
178
|
+
end
|
|
46
179
|
|
|
47
|
-
|
|
180
|
+
def inside_directory?(base_path, file_path)
|
|
181
|
+
root = File.realpath(base_path.to_s)
|
|
182
|
+
resolved = File.realpath(file_path)
|
|
183
|
+
resolved.start_with?(root + File::SEPARATOR)
|
|
184
|
+
rescue Errno::ENOENT
|
|
185
|
+
false
|
|
48
186
|
end
|
|
49
187
|
end
|
|
50
188
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SeedBuilder
|
|
2
|
+
class Railtie < ::Rails::Railtie
|
|
3
|
+
config.to_prepare do
|
|
4
|
+
if SeedBuilder.config.use_seed_loader?
|
|
5
|
+
# Patch Rails.application.load_seed to use our custom loader
|
|
6
|
+
Rails.application.class.class_eval do
|
|
7
|
+
define_method(:load_seed) do
|
|
8
|
+
loader = SeedBuilder::Loader.new
|
|
9
|
+
loader.load_seed
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/seed_builder.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
require "active_support/tagged_logging"
|
|
1
2
|
require "seed_builder/config"
|
|
2
3
|
require "seed_builder/loader"
|
|
3
4
|
|
|
4
5
|
module SeedBuilder
|
|
5
|
-
VERSION = "1.
|
|
6
|
+
VERSION = "1.3.1".freeze
|
|
6
7
|
|
|
7
8
|
module_function
|
|
8
9
|
|
|
@@ -13,6 +14,25 @@ module SeedBuilder
|
|
|
13
14
|
def configure
|
|
14
15
|
yield config
|
|
15
16
|
end
|
|
17
|
+
|
|
18
|
+
def logger
|
|
19
|
+
# Use configured logger if set
|
|
20
|
+
base_logger = if config.logger
|
|
21
|
+
config.logger
|
|
22
|
+
elsif defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
|
23
|
+
Rails.logger
|
|
24
|
+
else
|
|
25
|
+
Logger.new($stdout)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# If the logger already supports tagging (e.g., Rails.logger is already TaggedLogging),
|
|
29
|
+
# use it directly to avoid double-wrapping and ensure tags propagate properly
|
|
30
|
+
if base_logger.respond_to?(:tagged)
|
|
31
|
+
base_logger
|
|
32
|
+
else
|
|
33
|
+
ActiveSupport::TaggedLogging.new(base_logger)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
16
36
|
end
|
|
17
37
|
|
|
18
|
-
require "
|
|
38
|
+
require "seed_builder/railtie" if defined?(Rails::Railtie)
|
data/lib/tasks/seed.rake
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
namespace :seed do
|
|
2
|
+
desc "Run a specific seed file by name"
|
|
3
|
+
task :run, [:seed_name] => :environment do |_task, args|
|
|
4
|
+
if args[:seed_name].blank?
|
|
5
|
+
puts "Usage: bin/rails seed:run[SEED_NAME]"
|
|
6
|
+
puts ""
|
|
7
|
+
puts "Run a specific seed file by name."
|
|
8
|
+
puts ""
|
|
9
|
+
puts "Examples:"
|
|
10
|
+
puts " bin/rails seed:run[create_users]"
|
|
11
|
+
puts " bin/rails seed:run[20241206200111_create_users]"
|
|
12
|
+
puts " bin/rails seed:run[20241206200111]"
|
|
13
|
+
puts ""
|
|
14
|
+
puts "Note: If multiple seed files match the name, use the full name with"
|
|
15
|
+
puts " timestamp to avoid ambiguity (e.g., 20241206200111_create_users)."
|
|
16
|
+
exit 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
loader = SeedBuilder::Loader.new
|
|
20
|
+
exit 1 unless loader.load_seed_file(args[:seed_name])
|
|
21
|
+
end
|
|
22
|
+
end
|