planter 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/Rakefile +5 -45
- data/lib/generators/planter/initializer_generator.rb +1 -0
- data/lib/planter/adapters/active_record.rb +94 -3
- data/lib/planter/progress_bar.rb +16 -0
- data/lib/planter/seed_context.rb +13 -1
- data/lib/planter/seeder.rb +17 -1
- data/lib/planter/version.rb +2 -2
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b575630b7898231e26acae46bac6a745e46d9f60f596e3fe05af92adb6b36be
|
|
4
|
+
data.tar.gz: 3bac546364e78b3a9e84d40b29ead7ddf4b9453201ae4e9754f45227c6cf9963
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efe6a57b32e765863036c28eaad8c505812ee841aef607d39963f1430d6539c79c7942aa10f0014a711b92295150bc3a4bc41ff1343110e437f342f9137a4871
|
|
7
|
+
data.tar.gz: cae77c70d311dcbb8bb2618a7a14d06fcb393e6928f50cb378351ed81b972ddfa5250c9876633da398dac09af68f455793119884e164f829fc4064c6f5a50815
|
data/README.md
CHANGED
|
@@ -51,6 +51,7 @@ Planter.configure do |config|
|
|
|
51
51
|
# The adapter used to create records, discover parent records, and
|
|
52
52
|
# inspect database table names. Active Record is used by default.
|
|
53
53
|
# To use a custom adapter, replace this line with your own adapter.
|
|
54
|
+
# The Active Record adapter can be configured with a block.
|
|
54
55
|
config.adapter = Planter::Adapters::ActiveRecord.new
|
|
55
56
|
|
|
56
57
|
##
|
|
@@ -214,6 +215,34 @@ test1@example.com,test1
|
|
|
214
215
|
test2@example.com,test2
|
|
215
216
|
```
|
|
216
217
|
|
|
218
|
+
By default, the Active Record adapter raises when model validations prevent a
|
|
219
|
+
record from being created. To keep seeding after validation failures, configure
|
|
220
|
+
the adapter to warn instead.
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
Planter.configure do |config|
|
|
224
|
+
config.adapter = Planter::Adapters::ActiveRecord.new do |adapter_config|
|
|
225
|
+
adapter_config.validation_failure = :warn
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
In `:warn` mode, Planter prints a warning with the failed lookup attributes and
|
|
231
|
+
validation errors. This only applies to model-backed tables; model-less tables
|
|
232
|
+
are inserted directly and can still raise database errors. A seeder can override
|
|
233
|
+
the adapter default by defining `validation_failure`; Planter passes that value
|
|
234
|
+
to adapters through `context.adapter_options`.
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
class UsersSeeder < Planter::Seeder
|
|
238
|
+
seeding_method :csv, unique_columns: :email
|
|
239
|
+
|
|
240
|
+
def validation_failure
|
|
241
|
+
:raise
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
```
|
|
245
|
+
|
|
217
246
|
CSV seeders can also be useful for simple model-less tables, such as join
|
|
218
247
|
tables. With the default Active Record adapter, Planter will use the model when
|
|
219
248
|
one exists and fall back to direct table inserts when one does not.
|
|
@@ -454,6 +483,10 @@ class MyAdapter
|
|
|
454
483
|
end
|
|
455
484
|
```
|
|
456
485
|
|
|
486
|
+
Planter passes adapter-specific per-seeder settings through
|
|
487
|
+
`context.adapter_options`. Custom adapters can read options they support and
|
|
488
|
+
ignore the rest.
|
|
489
|
+
|
|
457
490
|
## License
|
|
458
491
|
The gem is available as open source under the terms of the [MIT
|
|
459
492
|
License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -2,8 +2,13 @@ require "bundler/setup"
|
|
|
2
2
|
require "bundler/gem_tasks"
|
|
3
3
|
require "rake/testtask"
|
|
4
4
|
require "rdoc/task"
|
|
5
|
+
require "semverve/task"
|
|
5
6
|
require "standard/rake"
|
|
6
7
|
|
|
8
|
+
Semverve::Task.new do |t|
|
|
9
|
+
t.bundle_lock = true
|
|
10
|
+
end
|
|
11
|
+
|
|
7
12
|
Rake::TestTask.new(:test) do |t|
|
|
8
13
|
t.libs << "test"
|
|
9
14
|
t.pattern = "test/**/*_test.rb"
|
|
@@ -17,48 +22,3 @@ RDoc::Task.new do |rdoc|
|
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
task default: :test
|
|
20
|
-
|
|
21
|
-
namespace :version do
|
|
22
|
-
desc "Print the current version from the version.rb file"
|
|
23
|
-
task :current do
|
|
24
|
-
puts Planter::VERSION
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
namespace :increment do
|
|
28
|
-
desc "Increment the version's PATCH level"
|
|
29
|
-
task :patch do
|
|
30
|
-
File.join(__dir__, "lib", "planter", "version.rb").then do |version_file|
|
|
31
|
-
File.write(
|
|
32
|
-
version_file,
|
|
33
|
-
File.read(version_file).sub(/(PATCH\s=\s)(\d+)/) { "#{$1}#{$2.next}" }
|
|
34
|
-
)
|
|
35
|
-
end
|
|
36
|
-
system("bundle lock")
|
|
37
|
-
end
|
|
38
|
-
desc "Increment the version's MINOR level"
|
|
39
|
-
task :minor do
|
|
40
|
-
File.join(__dir__, "lib", "planter", "version.rb").then do |version_file|
|
|
41
|
-
File.write(
|
|
42
|
-
version_file,
|
|
43
|
-
File.read(version_file)
|
|
44
|
-
.sub(/(PATCH\s=\s)(\d+)/) { "#{$1}0" }
|
|
45
|
-
.sub(/(MINOR\s=\s)(\d+)/) { "#{$1}#{$2.next}" }
|
|
46
|
-
)
|
|
47
|
-
end
|
|
48
|
-
system("bundle lock")
|
|
49
|
-
end
|
|
50
|
-
desc "Increment the version's MAJOR level"
|
|
51
|
-
task :major do
|
|
52
|
-
File.join(__dir__, "lib", "planter", "version.rb").then do |version_file|
|
|
53
|
-
File.write(
|
|
54
|
-
version_file,
|
|
55
|
-
File.read(version_file)
|
|
56
|
-
.sub(/(PATCH\s=\s)(\d+)/) { "#{$1}0" }
|
|
57
|
-
.sub(/(MINOR\s=\s)(\d+)/) { "#{$1}0" }
|
|
58
|
-
.sub(/(MAJOR\s=\s)(\d+)/) { "#{$1}#{$2.next}" }
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
system("bundle lock")
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -19,6 +19,7 @@ module Planter
|
|
|
19
19
|
# The adapter used to create records, discover parent records, and
|
|
20
20
|
# inspect database table names. Active Record is used by default.
|
|
21
21
|
# To use a custom adapter, replace this line with your own adapter.
|
|
22
|
+
# The Active Record adapter can be configured with a block.
|
|
22
23
|
config.adapter = Planter::Adapters::ActiveRecord.new
|
|
23
24
|
|
|
24
25
|
##
|
|
@@ -22,6 +22,57 @@ module Planter
|
|
|
22
22
|
# resolving those values into whatever persistence or reflection objects
|
|
23
23
|
# they need.
|
|
24
24
|
class ActiveRecord
|
|
25
|
+
##
|
|
26
|
+
# Validation failure actions supported by the Active Record adapter.
|
|
27
|
+
#
|
|
28
|
+
# @return [Array<Symbol>]
|
|
29
|
+
VALIDATION_FAILURE_ACTIONS = %i[raise warn].freeze
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Configuration for the Active Record adapter.
|
|
33
|
+
class Configuration
|
|
34
|
+
##
|
|
35
|
+
# How model validation failures should be handled when creating records.
|
|
36
|
+
#
|
|
37
|
+
# @return [Symbol]
|
|
38
|
+
attr_reader :validation_failure
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Create a new Active Record adapter configuration.
|
|
42
|
+
def initialize
|
|
43
|
+
@validation_failure = :raise
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Set how model validation failures should be handled.
|
|
48
|
+
#
|
|
49
|
+
# @param [String, Symbol] action either +:raise+ or +:warn+
|
|
50
|
+
def validation_failure=(action)
|
|
51
|
+
action = action.to_sym
|
|
52
|
+
unless VALIDATION_FAILURE_ACTIONS.include?(action)
|
|
53
|
+
raise ArgumentError, "validation_failure must be: #{VALIDATION_FAILURE_ACTIONS.join(", ")}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@validation_failure = action
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# The adapter configuration.
|
|
62
|
+
#
|
|
63
|
+
# @return [Planter::Adapters::ActiveRecord::Configuration]
|
|
64
|
+
attr_reader :configuration
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Create a new Active Record adapter.
|
|
68
|
+
#
|
|
69
|
+
# @yield [configuration] optional configuration block
|
|
70
|
+
# @yieldparam configuration [Planter::Adapters::ActiveRecord::Configuration]
|
|
71
|
+
def initialize
|
|
72
|
+
@configuration = Configuration.new
|
|
73
|
+
yield configuration if block_given?
|
|
74
|
+
end
|
|
75
|
+
|
|
25
76
|
##
|
|
26
77
|
# Create a record unless one already exists.
|
|
27
78
|
#
|
|
@@ -35,9 +86,7 @@ module Planter
|
|
|
35
86
|
# @return [Object]
|
|
36
87
|
def create_record(context:, lookup_attributes:, create_attributes:)
|
|
37
88
|
if (model = model(context))
|
|
38
|
-
model
|
|
39
|
-
.where(lookup_attributes)
|
|
40
|
-
.first_or_create!(create_attributes)
|
|
89
|
+
create_model_record(model, context, lookup_attributes, create_attributes)
|
|
41
90
|
else
|
|
42
91
|
create_table_record(context, lookup_attributes, create_attributes)
|
|
43
92
|
end
|
|
@@ -99,6 +148,48 @@ module Planter
|
|
|
99
148
|
end
|
|
100
149
|
end
|
|
101
150
|
|
|
151
|
+
def create_model_record(model, context, lookup_attributes, create_attributes)
|
|
152
|
+
relation = model.where(lookup_attributes)
|
|
153
|
+
case validation_failure(context)
|
|
154
|
+
when :raise
|
|
155
|
+
relation.first_or_create!(create_attributes)
|
|
156
|
+
when :warn
|
|
157
|
+
record = relation.first_or_create(create_attributes)
|
|
158
|
+
warn_validation_failure(context, lookup_attributes, record) if failed_create?(record)
|
|
159
|
+
record
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def validation_failure(context)
|
|
164
|
+
adapter_options = context.adapter_options if context.respond_to?(:adapter_options)
|
|
165
|
+
context_action = adapter_options&.[](:validation_failure)
|
|
166
|
+
action = context_action || configuration.validation_failure
|
|
167
|
+
action = action.to_sym
|
|
168
|
+
return action if VALIDATION_FAILURE_ACTIONS.include?(action)
|
|
169
|
+
|
|
170
|
+
raise ArgumentError, "validation_failure must be: #{VALIDATION_FAILURE_ACTIONS.join(", ")}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def failed_create?(record)
|
|
174
|
+
record == false || (record.respond_to?(:persisted?) && !record.persisted?)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def warn_validation_failure(context, lookup_attributes, record)
|
|
178
|
+
warn(
|
|
179
|
+
[
|
|
180
|
+
"WARNING: Planter could not create #{context.table_name} with lookup attributes",
|
|
181
|
+
lookup_attributes.inspect,
|
|
182
|
+
validation_errors(record)
|
|
183
|
+
].compact.join(" ")
|
|
184
|
+
)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def validation_errors(record)
|
|
188
|
+
return unless record.respond_to?(:errors) && record.errors.any?
|
|
189
|
+
|
|
190
|
+
"Errors: #{record.errors.full_messages.join(", ")}"
|
|
191
|
+
end
|
|
192
|
+
|
|
102
193
|
def association_options(context)
|
|
103
194
|
model!(context).reflect_on_association(context.parent).options
|
|
104
195
|
end
|
data/lib/planter/progress_bar.rb
CHANGED
|
@@ -6,13 +6,29 @@ module Planter
|
|
|
6
6
|
##
|
|
7
7
|
# Builds progress bars for seed runs.
|
|
8
8
|
class ProgressBar
|
|
9
|
+
##
|
|
10
|
+
# The format string used by visible progress bars.
|
|
11
|
+
#
|
|
12
|
+
# @return [String]
|
|
9
13
|
FORMAT = "%t |%B| %p%% %c/%C"
|
|
10
14
|
|
|
15
|
+
##
|
|
16
|
+
# Progress bar object used when visible progress bars are disabled.
|
|
11
17
|
class NullProgressBar
|
|
18
|
+
##
|
|
19
|
+
# No-op progress increment.
|
|
12
20
|
def increment
|
|
13
21
|
end
|
|
14
22
|
end
|
|
15
23
|
|
|
24
|
+
##
|
|
25
|
+
# Create a visible progress bar, or a null progress bar when disabled.
|
|
26
|
+
#
|
|
27
|
+
# @param [String] title progress bar title
|
|
28
|
+
# @param [Integer] total total number of records to seed
|
|
29
|
+
# @param [IO] output progress bar output stream
|
|
30
|
+
#
|
|
31
|
+
# @return [ProgressBar, NullProgressBar]
|
|
16
32
|
def self.create(title:, total:, output: $stdout)
|
|
17
33
|
return NullProgressBar.new unless enabled?(total)
|
|
18
34
|
|
data/lib/planter/seed_context.rb
CHANGED
|
@@ -46,6 +46,14 @@ module Planter
|
|
|
46
46
|
# @return [String, nil]
|
|
47
47
|
attr_reader :erb_trim_mode
|
|
48
48
|
|
|
49
|
+
##
|
|
50
|
+
# Adapter-specific options collected from the seeder.
|
|
51
|
+
#
|
|
52
|
+
# Adapters may ignore any options they do not support.
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash]
|
|
55
|
+
attr_reader :adapter_options
|
|
56
|
+
|
|
49
57
|
##
|
|
50
58
|
# Create a new seed context.
|
|
51
59
|
#
|
|
@@ -62,6 +70,8 @@ module Planter
|
|
|
62
70
|
# @param [Array<Symbol>, nil] unique_columns
|
|
63
71
|
#
|
|
64
72
|
# @param [String, nil] erb_trim_mode
|
|
73
|
+
#
|
|
74
|
+
# @param [Hash] adapter_options adapter-specific per-seeder options
|
|
65
75
|
def initialize(
|
|
66
76
|
table_name:,
|
|
67
77
|
seed_method:,
|
|
@@ -69,7 +79,8 @@ module Planter
|
|
|
69
79
|
parent:,
|
|
70
80
|
number_of_records:,
|
|
71
81
|
unique_columns:,
|
|
72
|
-
erb_trim_mode
|
|
82
|
+
erb_trim_mode:,
|
|
83
|
+
adapter_options: {}
|
|
73
84
|
)
|
|
74
85
|
@table_name = table_name.to_s
|
|
75
86
|
@seed_method = seed_method&.intern
|
|
@@ -78,6 +89,7 @@ module Planter
|
|
|
78
89
|
@number_of_records = number_of_records
|
|
79
90
|
@unique_columns = unique_columns
|
|
80
91
|
@erb_trim_mode = erb_trim_mode
|
|
92
|
+
@adapter_options = adapter_options.to_h.transform_keys(&:to_sym)
|
|
81
93
|
end
|
|
82
94
|
end
|
|
83
95
|
end
|
data/lib/planter/seeder.rb
CHANGED
|
@@ -89,6 +89,11 @@ module Planter
|
|
|
89
89
|
#
|
|
90
90
|
# If you need to seed a different way, put your own custom +seed+ method in
|
|
91
91
|
# your seeder class and do whatever needs to be done.
|
|
92
|
+
#
|
|
93
|
+
# Adapter-specific settings can be provided by defining methods on your
|
|
94
|
+
# seeder. For example, the Active Record adapter reads +validation_failure+
|
|
95
|
+
# into +SeedContext#adapter_options+ when deciding whether failed model
|
|
96
|
+
# validations should raise or warn.
|
|
92
97
|
class Seeder
|
|
93
98
|
##
|
|
94
99
|
# The allowed seeding methods.
|
|
@@ -326,10 +331,21 @@ module Planter
|
|
|
326
331
|
parent: parent,
|
|
327
332
|
number_of_records: number_of_records,
|
|
328
333
|
unique_columns: unique_columns,
|
|
329
|
-
erb_trim_mode: erb_trim_mode
|
|
334
|
+
erb_trim_mode: erb_trim_mode,
|
|
335
|
+
adapter_options: seeder_adapter_options
|
|
330
336
|
)
|
|
331
337
|
end
|
|
332
338
|
|
|
339
|
+
def seeder_adapter_options
|
|
340
|
+
{
|
|
341
|
+
validation_failure: seeder_validation_failure
|
|
342
|
+
}.compact
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def seeder_validation_failure
|
|
346
|
+
public_send(:validation_failure) if respond_to?(:validation_failure)
|
|
347
|
+
end
|
|
348
|
+
|
|
333
349
|
def csv_data_source
|
|
334
350
|
@csv_data_source ||= Planter::CsvDataSource.new(context: context, seeder: self)
|
|
335
351
|
end
|
data/lib/planter/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: planter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Gray
|
|
@@ -71,6 +71,20 @@ dependencies:
|
|
|
71
71
|
- - "<"
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
73
|
version: '2.0'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: semverve
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '0'
|
|
81
|
+
type: :development
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
74
88
|
- !ruby/object:Gem::Dependency
|
|
75
89
|
name: simplecov
|
|
76
90
|
requirement: !ruby/object:Gem::Requirement
|