planter 0.1.1 → 0.2.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 +52 -9
- data/lib/planter/seeder.rb +127 -83
- data/lib/planter/version.rb +7 -5
- data/lib/planter.rb +6 -4
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85e12676f9e3c992f26939f709bb8ff9785a75cfbf13a5279075e2e84ae2adc9
|
4
|
+
data.tar.gz: 130e7dd566d20b09dcfb38c4d62c76d872b169b459f68eff0a2d8715c13f320a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 535f7b4909895f4f2ed6219919cbd620b3f283daecd693457ce52dfa7ec3d8281d49d7487dc530ced549a52d55719759e3f0975efa833763475fe03a1b863171
|
7
|
+
data.tar.gz: 36df7d4e59476de2f1526b66e1571d70221ff77204eac23e2d16408ddf7ca6bfcdacbfb351e0a9f4323ca2439c407afb5ecbd3a88725493d62ef8c0ac594f1f4
|
data/README.md
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# Planter
|
2
2
|
[](https://actions-badge.atrox.dev/evanthegrayt/planter/goto?ref=master)
|
3
3
|
[](https://badge.fury.io/rb/planter)
|
4
|
+

|
4
5
|
[](https://opensource.org/licenses/MIT)
|
5
6
|
|
6
|
-
> Pre-release version! Anything is subject to change in the near future!
|
7
|
-
|
8
7
|
Seeds for Rails applications can get complicated fast, and Rails doesn't provide
|
9
8
|
much for assisting with this process. This plugin seeks to rectify that by
|
10
9
|
providing easy ways to seed specific tables.
|
@@ -21,10 +20,10 @@ You can view the documentation [here](https://evanthegrayt.github.io/planter/).
|
|
21
20
|
## Installation
|
22
21
|
Add the following line to your application's Gemfile. Because this plugin is
|
23
22
|
currently a pre-release version, it's recommended to lock it to a specific
|
24
|
-
version, as breaking changes may occur, even at the
|
23
|
+
version, as breaking changes may occur, even at the minor level.
|
25
24
|
|
26
25
|
```ruby
|
27
|
-
gem 'planter', '0.0
|
26
|
+
gem 'planter', '0.2.0'
|
28
27
|
```
|
29
28
|
|
30
29
|
And then execute:
|
@@ -83,6 +82,7 @@ allows you to use `db:seed` for other purposes. If you want Planter to hook
|
|
83
82
|
into the existing `db:seed` task, simply add the following to `db/seeds.rb`.
|
84
83
|
|
85
84
|
```ruby
|
85
|
+
# db/seeds.rb
|
86
86
|
Planter.seed
|
87
87
|
```
|
88
88
|
|
@@ -144,13 +144,24 @@ class UsersSeeder < Planter::Seeder
|
|
144
144
|
end
|
145
145
|
```
|
146
146
|
|
147
|
-
`ERB` can be used in the CSV files if you end the file name with `.csv.erb
|
148
|
-
For example, `users.csv.erb`.
|
147
|
+
`ERB` can be used in the CSV files if you end the file name with `.csv.erb` or
|
148
|
+
`.erb.csv`. For example, `users.csv.erb`. When using ERB, instance variables set
|
149
|
+
in the seeder can be used in the CSV.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class UsersSeeder < Planter::Seeder
|
153
|
+
seeding_method :csv, csv_name: :people
|
154
|
+
|
155
|
+
def initialize
|
156
|
+
@name_prefix = 'Test User'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
149
160
|
|
150
161
|
```
|
151
162
|
participant_id,name
|
152
|
-
<%= Participant.find_by(email: 'test1@example.com').id
|
153
|
-
<%= Participant.find_by(email: 'test2@example.com').id
|
163
|
+
<%= Participant.find_by(email: 'test1@example.com').id %>,<%= @name_prefix %> 1
|
164
|
+
<%= Participant.find_by(email: 'test2@example.com').id %>,<%= @name_prefix %> 2
|
154
165
|
```
|
155
166
|
|
156
167
|
Note that, if you need to change the trim mode for ERB, you can set a default in
|
@@ -175,6 +186,38 @@ end
|
|
175
186
|
|
176
187
|
For help with `erb_trim_mode`, see the help documentation for `ERB::new`.
|
177
188
|
|
189
|
+
Lastly, it's worth mentioning `transformations` under the CSV section, as that's
|
190
|
+
usually the pace where they're needed most, but it will work with any method.
|
191
|
+
|
192
|
+
If you're seeding with a CSV, and it contains values that need to have code
|
193
|
+
executed on them before it's imported into the database, you can define an
|
194
|
+
instance variable called `@transformations`, or a method called
|
195
|
+
`transformations`, that returns a Hash of field names, and Procs to run on the
|
196
|
+
value. For example, if you have an `admin` column, and the CSV contains "true",
|
197
|
+
it will come through as a String, but you probably want it to be a Boolean. This
|
198
|
+
can be solved with the following.
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
class UsersSeeder < Planter::Seeder
|
202
|
+
seeding_method :csv
|
203
|
+
|
204
|
+
def transformations
|
205
|
+
{
|
206
|
+
admin: ->(value) { value == 'true' },
|
207
|
+
last_name: ->(value, row) { "#{value} #{row[:suffix]}".squish }
|
208
|
+
}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
When defining a Proc/Lambda, you can make it accept 0, 1, or 2 arguments.
|
214
|
+
- When `0`, the value is replaced by the result of the Lambda
|
215
|
+
- When `1`, the value is passed to the Lambda, and is subsequently replaced by
|
216
|
+
the result of the Lambda
|
217
|
+
- When `2`, the value is the first argument, and the entire row, as a Hash, is
|
218
|
+
the second argument. This allows for more complicated transformations that can
|
219
|
+
be dependent on other fields and values in the record.
|
220
|
+
|
178
221
|
Running `rails planter:seed` will now seed your `users` table.
|
179
222
|
|
180
223
|
## Seeding from a data array
|
@@ -244,7 +287,7 @@ class UsersSeeder < Planter::Seeder
|
|
244
287
|
}
|
245
288
|
|
246
289
|
def seed
|
247
|
-
USERS.each { |email, attrs| User.where(email).first_or_create!(attrs) }
|
290
|
+
USERS.each { |email, attrs| User.where(email: email).first_or_create!(attrs) }
|
248
291
|
end
|
249
292
|
end
|
250
293
|
```
|
data/lib/planter/seeder.rb
CHANGED
@@ -102,6 +102,35 @@ module Planter
|
|
102
102
|
# @return [Array]
|
103
103
|
attr_reader :data
|
104
104
|
|
105
|
+
##
|
106
|
+
# A hash of user-defined column names and procs to be run on values. This
|
107
|
+
# is most useful for when seeding from csv, and you need to transform, say,
|
108
|
+
# 'true' (String) into true (Boolean). The user may define this as an
|
109
|
+
# instance variable, or define a method that returns the hash.
|
110
|
+
#
|
111
|
+
# When defining a Proc/Lambda, you can make it accept 0, 1, or 2 arguments.
|
112
|
+
# - When 0, the value is replaced by the result of the Lambda.
|
113
|
+
# - When 1, the value is passed to the Lambda, and is subsequently
|
114
|
+
# replaced by the result of the Lambda.
|
115
|
+
# - When 2, the value is the first argument, and the entire row, as a
|
116
|
+
# Hash, is the second argument. This allows for more complicated
|
117
|
+
# transformations that can be dependent on other fields and values in the
|
118
|
+
# record.
|
119
|
+
#
|
120
|
+
# @return [Hash, nil]
|
121
|
+
#
|
122
|
+
# @example
|
123
|
+
# class UsersSeeder < Planter::Seeder
|
124
|
+
# seeding_method :csv
|
125
|
+
# def transformations
|
126
|
+
# {
|
127
|
+
# admin: ->(v) { v == 'true' },
|
128
|
+
# last_name: ->(value, row) { "#{value} #{row[:suffix]}".squish }
|
129
|
+
# }
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
attr_reader :transformations
|
133
|
+
|
105
134
|
##
|
106
135
|
# What trim mode should ERB use?
|
107
136
|
#
|
@@ -142,7 +171,7 @@ module Planter
|
|
142
171
|
# The csv file corresponding to the model.
|
143
172
|
#
|
144
173
|
# @return [String]
|
145
|
-
class_attribute :
|
174
|
+
class_attribute :csv_name
|
146
175
|
|
147
176
|
##
|
148
177
|
# The seeding method specified.
|
@@ -151,79 +180,59 @@ module Planter
|
|
151
180
|
class_attribute :seed_method
|
152
181
|
|
153
182
|
##
|
154
|
-
#
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
unique_columns: nil,
|
195
|
-
erb_trim_mode: nil
|
196
|
-
)
|
197
|
-
if !SEEDING_METHODS.include?(seed_method.intern)
|
198
|
-
raise ArgumentError, "Method must be: #{SEEDING_METHODS.join(', ')}"
|
199
|
-
end
|
200
|
-
|
201
|
-
self.seed_method = seed_method
|
202
|
-
self.number_of_records = number_of_records
|
203
|
-
self.model = model || to_s.delete_suffix('Seeder').singularize
|
204
|
-
self.parent = parent
|
205
|
-
self.csv_file = determine_csv_filename(csv_name) if seed_method == :csv
|
206
|
-
self.erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
|
207
|
-
self.unique_columns =
|
208
|
-
case unique_columns
|
209
|
-
when String, Symbol then [unique_columns.intern]
|
210
|
-
when Array then unique_columns.map(&:intern)
|
211
|
-
end
|
183
|
+
# If your class is going to use the inherited +seed+ method, you must tell
|
184
|
+
# it which +seeding_method+ to use. The argument to this method must be
|
185
|
+
# included in the +SEEDING_METHODS+ array.
|
186
|
+
#
|
187
|
+
# @param [Symbol] seed_method
|
188
|
+
#
|
189
|
+
# @kwarg [Integer] number_of_records
|
190
|
+
#
|
191
|
+
# @kwarg [String] model
|
192
|
+
#
|
193
|
+
# @kwarg [Symbol, String] parent
|
194
|
+
#
|
195
|
+
# @kwarg [Symbol, String] csv_name
|
196
|
+
#
|
197
|
+
# @kwarg [Symbol, String] unique_columns
|
198
|
+
#
|
199
|
+
# @kwarg [String] erb_trim_mode
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# require 'planter'
|
203
|
+
# class UsersSeeder < Planter::Seeder
|
204
|
+
# seeding_method :csv,
|
205
|
+
# number_of_records: 2,
|
206
|
+
# model: 'User'
|
207
|
+
# parent: :person,
|
208
|
+
# csv_name: :awesome_users,
|
209
|
+
# unique_columns %i[username email],
|
210
|
+
# erb_trim_mode: '<>'
|
211
|
+
# end
|
212
|
+
def self.seeding_method(
|
213
|
+
seed_method,
|
214
|
+
number_of_records: 1,
|
215
|
+
model: nil,
|
216
|
+
parent: nil,
|
217
|
+
csv_name: nil,
|
218
|
+
unique_columns: nil,
|
219
|
+
erb_trim_mode: nil
|
220
|
+
)
|
221
|
+
unless SEEDING_METHODS.include?(seed_method.intern)
|
222
|
+
raise ArgumentError, "Method must be: #{SEEDING_METHODS.join(', ')}"
|
212
223
|
end
|
213
224
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
225
|
+
self.seed_method = seed_method
|
226
|
+
self.number_of_records = number_of_records
|
227
|
+
self.model = model || to_s.delete_suffix('Seeder').singularize
|
228
|
+
self.parent = parent
|
229
|
+
self.csv_name = csv_name || to_s.delete_suffix('Seeder').underscore
|
230
|
+
self.erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
|
231
|
+
self.unique_columns =
|
232
|
+
case unique_columns
|
233
|
+
when String, Symbol then [unique_columns.intern]
|
234
|
+
when Array then unique_columns.map(&:intern)
|
223
235
|
end
|
224
|
-
|
225
|
-
raise ArgumentError, "Couldn't find csv for #{model}"
|
226
|
-
end
|
227
236
|
end
|
228
237
|
|
229
238
|
##
|
@@ -231,11 +240,12 @@ module Planter
|
|
231
240
|
# valid +seeding_method+, and not implement its own +seed+ method.
|
232
241
|
def seed
|
233
242
|
validate_attributes
|
243
|
+
extract_data_from_csv if seed_method == :csv
|
234
244
|
|
235
245
|
parent ? create_records_from_parent : create_records
|
236
246
|
end
|
237
247
|
|
238
|
-
|
248
|
+
private
|
239
249
|
|
240
250
|
##
|
241
251
|
# Creates records from the +data+ attribute.
|
@@ -253,7 +263,7 @@ module Planter
|
|
253
263
|
|
254
264
|
def create_record(record, parent_id: nil)
|
255
265
|
number_of_records.times do
|
256
|
-
unique, attrs = split_record(record)
|
266
|
+
unique, attrs = split_record(apply_transformations(record))
|
257
267
|
model.constantize.where(
|
258
268
|
unique.tap { |u| u[foreign_key] = parent_id if parent_id }
|
259
269
|
).first_or_create!(attrs)
|
@@ -263,18 +273,32 @@ module Planter
|
|
263
273
|
def validate_attributes # :nodoc:
|
264
274
|
case seed_method.intern
|
265
275
|
when :csv
|
266
|
-
|
267
|
-
if csv_file.end_with?('.erb')
|
268
|
-
contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
|
269
|
-
end
|
270
|
-
|
271
|
-
@data ||= ::CSV.parse(
|
272
|
-
contents, headers: true, header_converters: :symbol
|
273
|
-
).map(&:to_hash)
|
276
|
+
raise "Couldn't find csv for #{model}" unless full_csv_name
|
274
277
|
when :data_array
|
275
|
-
raise
|
278
|
+
raise 'data is not defined in the seeder' if public_send(:data).nil?
|
276
279
|
else
|
277
|
-
raise
|
280
|
+
raise 'seeding_method not defined in the seeder'
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def apply_transformations(record)
|
285
|
+
return record if public_send(:transformations).nil?
|
286
|
+
|
287
|
+
Hash[record.map { |field, value| map_record(field, value, record) }]
|
288
|
+
end
|
289
|
+
|
290
|
+
def map_record(field, value, record)
|
291
|
+
[
|
292
|
+
field,
|
293
|
+
transformations.key?(field) ? transform(field, value, record) : value
|
294
|
+
]
|
295
|
+
end
|
296
|
+
|
297
|
+
def transform(field, value, record)
|
298
|
+
case transformations[field].arity
|
299
|
+
when 0 then transformations[field].call
|
300
|
+
when 1 then transformations[field].call(value)
|
301
|
+
when 2 then transformations[field].call(value, record)
|
278
302
|
end
|
279
303
|
end
|
280
304
|
|
@@ -304,5 +328,25 @@ module Planter
|
|
304
328
|
@parent_model ||=
|
305
329
|
association_options.fetch(:class_name, parent.to_s.classify)
|
306
330
|
end
|
331
|
+
|
332
|
+
def full_csv_name
|
333
|
+
@full_csv_name ||=
|
334
|
+
%W[#{csv_name}.csv #{csv_name}.csv.erb #{csv_name}.erb.csv]
|
335
|
+
.map { |f| Rails.root.join(Planter.config.csv_files_directory, f).to_s }
|
336
|
+
.find { |f| ::File.file?(f) }
|
337
|
+
end
|
338
|
+
|
339
|
+
def extract_data_from_csv
|
340
|
+
contents = ::File.read(full_csv_name)
|
341
|
+
if full_csv_name.include?('.erb')
|
342
|
+
contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
|
343
|
+
end
|
344
|
+
|
345
|
+
@data ||= ::CSV.parse(
|
346
|
+
contents,
|
347
|
+
headers: true,
|
348
|
+
header_converters: :symbol
|
349
|
+
).map(&:to_hash)
|
350
|
+
end
|
307
351
|
end
|
308
352
|
end
|
data/lib/planter/version.rb
CHANGED
@@ -15,19 +15,21 @@ module Planter
|
|
15
15
|
# Minor version.
|
16
16
|
#
|
17
17
|
# @return [Integer]
|
18
|
-
MINOR =
|
18
|
+
MINOR = 2
|
19
19
|
|
20
20
|
##
|
21
21
|
# Patch version.
|
22
22
|
#
|
23
23
|
# @return [Integer]
|
24
|
-
PATCH =
|
24
|
+
PATCH = 0
|
25
|
+
|
26
|
+
module_function
|
25
27
|
|
26
28
|
##
|
27
29
|
# Version as +[MAJOR, MINOR, PATCH]+
|
28
30
|
#
|
29
31
|
# @return [Array]
|
30
|
-
def
|
32
|
+
def to_a
|
31
33
|
[MAJOR, MINOR, PATCH]
|
32
34
|
end
|
33
35
|
|
@@ -35,7 +37,7 @@ module Planter
|
|
35
37
|
# Version as +MAJOR.MINOR.PATCH+
|
36
38
|
#
|
37
39
|
# @return [String]
|
38
|
-
def
|
40
|
+
def to_s
|
39
41
|
to_a.join('.')
|
40
42
|
end
|
41
43
|
|
@@ -43,7 +45,7 @@ module Planter
|
|
43
45
|
# Version as +{major: MAJOR, minor: MINOR, patch: PATCH}+
|
44
46
|
#
|
45
47
|
# @return [Hash]
|
46
|
-
def
|
48
|
+
def to_h
|
47
49
|
Hash[%i[major minor patch].zip(to_a)]
|
48
50
|
end
|
49
51
|
end
|
data/lib/planter.rb
CHANGED
@@ -24,11 +24,13 @@ require 'planter/seeder'
|
|
24
24
|
#
|
25
25
|
# Planter.seed
|
26
26
|
module Planter
|
27
|
+
module_function
|
28
|
+
|
27
29
|
##
|
28
30
|
# The seeder configuration.
|
29
31
|
#
|
30
32
|
# @return [Planter::Config]
|
31
|
-
def
|
33
|
+
def config
|
32
34
|
@config ||= Planter::Config.new
|
33
35
|
end
|
34
36
|
|
@@ -36,7 +38,7 @@ module Planter
|
|
36
38
|
# Resets the config back to its initial state.
|
37
39
|
#
|
38
40
|
# @return [Planter::Config]
|
39
|
-
def
|
41
|
+
def reset_config
|
40
42
|
@config = Planter::Config.new
|
41
43
|
end
|
42
44
|
|
@@ -52,7 +54,7 @@ module Planter
|
|
52
54
|
# config.seeders_directory = 'db/seeds'
|
53
55
|
# config.csv_files_directory = 'db/seed_files'
|
54
56
|
# end
|
55
|
-
def
|
57
|
+
def configure
|
56
58
|
config.tap { |c| yield c }
|
57
59
|
end
|
58
60
|
|
@@ -65,7 +67,7 @@ module Planter
|
|
65
67
|
# @example
|
66
68
|
# # db/seeds.rb, assuming your +configure+ block is in an initializer.
|
67
69
|
# Planter.seed
|
68
|
-
def
|
70
|
+
def seed
|
69
71
|
seeders = ENV['SEEDERS']&.split(',') || config.seeders&.map(&:to_s)
|
70
72
|
raise RuntimeError, 'No seeders specified' if seeders.blank?
|
71
73
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: planter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Gray
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 7.0.2.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 7.0.2.3
|
27
27
|
description: Create a seeder for each table in your database, and easily seed from
|
28
28
|
CSV or custom methods
|
29
29
|
email:
|
@@ -66,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '0'
|
68
68
|
requirements: []
|
69
|
-
rubygems_version: 3.2.
|
69
|
+
rubygems_version: 3.2.33
|
70
70
|
signing_key:
|
71
71
|
specification_version: 4
|
72
72
|
summary: Framework for seeding rails applications.
|