planter 0.4.1 → 0.5.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 +111 -35
- data/lib/generators/planter/adapter_generator.rb +26 -11
- data/lib/generators/planter/initializer_generator.rb +7 -1
- data/lib/generators/planter/seeder_generator.rb +109 -9
- data/lib/planter/adapters/active_record.rb +112 -26
- data/lib/planter/config.rb +2 -2
- data/lib/planter/csv_data_source.rb +54 -0
- data/lib/planter/record_attributes.rb +113 -0
- data/lib/planter/seed_context.rb +83 -0
- data/lib/planter/seeder.rb +71 -85
- data/lib/planter/version.rb +2 -2
- data/lib/planter.rb +5 -2
- metadata +4 -1
data/lib/planter/seeder.rb
CHANGED
|
@@ -4,30 +4,35 @@ module Planter
|
|
|
4
4
|
##
|
|
5
5
|
# Class that seeders should inherit from. Seeders should be in +db/seeds+,
|
|
6
6
|
# and named +TABLE_seeder.rb+, where +TABLE+ is the name of the table being
|
|
7
|
-
# seeded (
|
|
8
|
-
# the table, you'll need to specify the table with the +
|
|
7
|
+
# seeded (for example, +users_seeder.rb+). If your seeder is named differently
|
|
8
|
+
# than the table, you'll need to specify the table with the +table+ option. The
|
|
9
9
|
# seeder's class name should be the same as the file name, but camelized. So,
|
|
10
10
|
# +UsersSeeder+. The directory where the seeder files are located can be
|
|
11
11
|
# changed via an initializer.
|
|
12
12
|
#
|
|
13
|
+
# To generate a seeder with a specific style, pass +--seeding-method=csv+,
|
|
14
|
+
# +--seeding-method=data-array+, or +--seeding-method=custom+ to
|
|
15
|
+
# +rails generate planter:seeder+. When the +csv+ method is used, the generator
|
|
16
|
+
# also creates a CSV file with headers pulled from the table.
|
|
17
|
+
#
|
|
13
18
|
# The most basic way to seed is to have a CSV file with the same name as the
|
|
14
19
|
# table in +db/seed_files/+. So, +users.csv+. This CSV should have the
|
|
15
|
-
# table's column names as
|
|
16
|
-
# should look like the following. Note that +:csv_name+ and +:
|
|
17
|
-
# required if your seeder or
|
|
20
|
+
# table's column names as headers. To seed using this method, your class
|
|
21
|
+
# should look like the following. Note that +:csv_name+ and +:table+ are only
|
|
22
|
+
# required if your seeder or CSV are named differently than the table being
|
|
18
23
|
# seeded. The directory where the seed files are kept can be changed via an
|
|
19
24
|
# initializer.
|
|
20
25
|
# # db/seeds/users_seeder.rb
|
|
21
26
|
# require 'planter'
|
|
22
27
|
# class UsersSeeder < Planter::Seeder
|
|
23
|
-
# seeding_method :csv, csv_name: :users,
|
|
28
|
+
# seeding_method :csv, csv_name: :users, table: :users
|
|
24
29
|
# end
|
|
25
30
|
#
|
|
26
31
|
# Another way to seed is to create records from a data array. To do this,
|
|
27
32
|
# your class must implement a +data+ attribute or method, which is an array
|
|
28
33
|
# of hashes. Note that this class already provides the +attr_reader+ for this
|
|
29
34
|
# attribute, so the most you have to do is create instance variables in your
|
|
30
|
-
# constructor. If
|
|
35
|
+
# constructor. If you want your data to be different for each new record
|
|
31
36
|
# (via Faker, +Array#sample+, etc.), you'll probably want to supply a method
|
|
32
37
|
# called data that returns an array of new data each time.
|
|
33
38
|
# require 'planter'
|
|
@@ -39,13 +44,12 @@ module Planter
|
|
|
39
44
|
# end
|
|
40
45
|
#
|
|
41
46
|
# In both of the above methods, you can specify a +parent+ association, which
|
|
42
|
-
# is
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# using +Faker+ or a similar library.
|
|
47
|
+
# is interpreted by the configured adapter. With the default Active Record
|
|
48
|
+
# adapter, +parent+ is the +belongs_to+ association name on a model-backed
|
|
49
|
+
# table. When specified, records will be created for each record in the parent
|
|
50
|
+
# table. Note that nothing is automatically done to prevent any validation
|
|
51
|
+
# errors; you must do this on your own, most likely using +Faker+ or a similar
|
|
52
|
+
# library.
|
|
49
53
|
# require 'planter'
|
|
50
54
|
# class UsersSeeder < Planter::Seeder
|
|
51
55
|
# seeding_method :data_array, parent: :person
|
|
@@ -55,7 +59,7 @@ module Planter
|
|
|
55
59
|
# end
|
|
56
60
|
#
|
|
57
61
|
# You can also set +number_of_records+ to determine how many times each
|
|
58
|
-
# record in the +data+ array will
|
|
62
|
+
# record in the +data+ array will be created. The default is 1. Note that if
|
|
59
63
|
# this attribute is set alongside +parent+, +number_of_records+ will be how
|
|
60
64
|
# many records will be created for each record in the parent table.
|
|
61
65
|
# require 'planter'
|
|
@@ -68,7 +72,7 @@ module Planter
|
|
|
68
72
|
#
|
|
69
73
|
# By default, all fields are used to look up the record. If it already
|
|
70
74
|
# exists, it is not re-created. If you have specific fields that a record
|
|
71
|
-
# should be looked
|
|
75
|
+
# should be looked up by, you can pass the +unique_columns+ option. This will
|
|
72
76
|
# attempt to look up the record by those fields only, and if one doesn't
|
|
73
77
|
# exist, one will be created with the rest of the attributes. An example of
|
|
74
78
|
# when this would be useful is with Devise; you can't pass +password+ in the
|
|
@@ -97,14 +101,14 @@ module Planter
|
|
|
97
101
|
# attribute when using +data_array+ seeding method, although it's probably
|
|
98
102
|
# more likely that you'll want to define a method that returns a new set of
|
|
99
103
|
# data each time (via +Faker+, +Array#sample+, etc.). When using +csv+,
|
|
100
|
-
# +data+ will be set to the data within the
|
|
104
|
+
# +data+ will be set to the data within the CSV. You can override this.
|
|
101
105
|
#
|
|
102
106
|
# @return [Array]
|
|
103
107
|
attr_reader :data
|
|
104
108
|
|
|
105
109
|
##
|
|
106
110
|
# A hash of user-defined column names and procs to be run on values. This
|
|
107
|
-
# is most useful for when seeding from
|
|
111
|
+
# is most useful for when seeding from CSV, and you need to transform, say,
|
|
108
112
|
# 'true' (String) into true (Boolean). The user may define this as an
|
|
109
113
|
# instance variable, or define a method that returns the hash.
|
|
110
114
|
#
|
|
@@ -145,16 +149,15 @@ module Planter
|
|
|
145
149
|
class_attribute :unique_columns
|
|
146
150
|
|
|
147
151
|
##
|
|
148
|
-
# The
|
|
149
|
-
#
|
|
152
|
+
# The table being seeded. If the table name you need is different, change
|
|
153
|
+
# via +seeding_method+.
|
|
150
154
|
#
|
|
151
155
|
# @return [String]
|
|
152
|
-
class_attribute :
|
|
156
|
+
class_attribute :table_name
|
|
153
157
|
|
|
154
158
|
##
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
# class must set this attribute via +seeding_method+.
|
|
159
|
+
# Adapter-defined parent relation. Records in the +data+ array will be
|
|
160
|
+
# created for each record in the parent relation.
|
|
158
161
|
#
|
|
159
162
|
# @return [String]
|
|
160
163
|
class_attribute :parent
|
|
@@ -168,7 +171,7 @@ module Planter
|
|
|
168
171
|
class_attribute :number_of_records
|
|
169
172
|
|
|
170
173
|
##
|
|
171
|
-
# The
|
|
174
|
+
# The CSV file corresponding to the table.
|
|
172
175
|
#
|
|
173
176
|
# @return [String]
|
|
174
177
|
class_attribute :csv_name
|
|
@@ -188,7 +191,7 @@ module Planter
|
|
|
188
191
|
#
|
|
189
192
|
# @kwarg [Integer] number_of_records
|
|
190
193
|
#
|
|
191
|
-
# @kwarg [String]
|
|
194
|
+
# @kwarg [String, Symbol] table
|
|
192
195
|
#
|
|
193
196
|
# @kwarg [Symbol, String] parent
|
|
194
197
|
#
|
|
@@ -203,7 +206,7 @@ module Planter
|
|
|
203
206
|
# class UsersSeeder < Planter::Seeder
|
|
204
207
|
# seeding_method :csv,
|
|
205
208
|
# number_of_records: 2,
|
|
206
|
-
#
|
|
209
|
+
# table: :users
|
|
207
210
|
# parent: :person,
|
|
208
211
|
# csv_name: :awesome_users,
|
|
209
212
|
# unique_columns %i[username email],
|
|
@@ -212,7 +215,7 @@ module Planter
|
|
|
212
215
|
def self.seeding_method(
|
|
213
216
|
seed_method,
|
|
214
217
|
number_of_records: 1,
|
|
215
|
-
|
|
218
|
+
table: nil,
|
|
216
219
|
parent: nil,
|
|
217
220
|
csv_name: nil,
|
|
218
221
|
unique_columns: nil,
|
|
@@ -224,9 +227,9 @@ module Planter
|
|
|
224
227
|
|
|
225
228
|
self.seed_method = seed_method
|
|
226
229
|
self.number_of_records = number_of_records
|
|
227
|
-
self.
|
|
230
|
+
self.table_name = (table || default_table_name).to_s
|
|
228
231
|
self.parent = parent
|
|
229
|
-
self.csv_name = csv_name ||
|
|
232
|
+
self.csv_name = (csv_name || table_name).to_s
|
|
230
233
|
self.erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
|
|
231
234
|
self.unique_columns =
|
|
232
235
|
case unique_columns
|
|
@@ -235,14 +238,22 @@ module Planter
|
|
|
235
238
|
end
|
|
236
239
|
end
|
|
237
240
|
|
|
241
|
+
##
|
|
242
|
+
# Return the table name inferred from the seeder class name.
|
|
243
|
+
#
|
|
244
|
+
# @return [String]
|
|
245
|
+
def self.default_table_name
|
|
246
|
+
to_s.delete_suffix("Seeder").underscore
|
|
247
|
+
end
|
|
248
|
+
|
|
238
249
|
##
|
|
239
250
|
# The default seed method. To use this method, your class must provide a
|
|
240
251
|
# valid +seeding_method+, and not implement its own +seed+ method.
|
|
241
252
|
def seed
|
|
242
253
|
validate_attributes
|
|
243
|
-
extract_data_from_csv if seed_method == :csv
|
|
254
|
+
extract_data_from_csv if context.seed_method == :csv
|
|
244
255
|
|
|
245
|
-
parent ? create_records_from_parent : create_records
|
|
256
|
+
context.parent ? create_records_from_parent : create_records
|
|
246
257
|
end
|
|
247
258
|
|
|
248
259
|
private
|
|
@@ -250,7 +261,7 @@ module Planter
|
|
|
250
261
|
##
|
|
251
262
|
# Creates records from the +data+ attribute.
|
|
252
263
|
def create_records
|
|
253
|
-
number_of_records.times do
|
|
264
|
+
context.number_of_records.times do
|
|
254
265
|
data.each { |record| create_record(record) }
|
|
255
266
|
end
|
|
256
267
|
end
|
|
@@ -258,47 +269,30 @@ module Planter
|
|
|
258
269
|
##
|
|
259
270
|
# Create records from the +data+ attribute for each record in the +parent+.
|
|
260
271
|
def create_records_from_parent
|
|
261
|
-
adapter.parent_ids(
|
|
262
|
-
number_of_records.times do
|
|
272
|
+
adapter.parent_ids(context: context).each do |parent_id|
|
|
273
|
+
context.number_of_records.times do
|
|
263
274
|
data.each { |record| create_record(record, parent_id: parent_id) }
|
|
264
275
|
end
|
|
265
276
|
end
|
|
266
277
|
end
|
|
267
278
|
|
|
268
279
|
def create_record(record, parent_id: nil)
|
|
269
|
-
|
|
270
|
-
unique = unique.merge(foreign_key => parent_id) if parent_id
|
|
280
|
+
lookup_attributes, create_attributes = record_attributes.prepare(record, parent_id: parent_id)
|
|
271
281
|
adapter.create_record(
|
|
272
|
-
|
|
273
|
-
lookup_attributes:
|
|
274
|
-
create_attributes:
|
|
282
|
+
context: context,
|
|
283
|
+
lookup_attributes: lookup_attributes,
|
|
284
|
+
create_attributes: create_attributes
|
|
275
285
|
)
|
|
276
286
|
end
|
|
277
287
|
|
|
278
|
-
def full_csv_name
|
|
279
|
-
@full_csv_name ||=
|
|
280
|
-
%W[#{csv_name}.csv #{csv_name}.csv.erb #{csv_name}.erb.csv]
|
|
281
|
-
.map { |f| Rails.root.join(Planter.config.csv_files_directory, f).to_s }
|
|
282
|
-
.find { |f| ::File.file?(f) }
|
|
283
|
-
end
|
|
284
|
-
|
|
285
288
|
def extract_data_from_csv
|
|
286
|
-
|
|
287
|
-
if full_csv_name.include?(".erb")
|
|
288
|
-
contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
@data ||= ::CSV.parse(
|
|
292
|
-
contents,
|
|
293
|
-
headers: true,
|
|
294
|
-
header_converters: :symbol
|
|
295
|
-
).map(&:to_hash)
|
|
289
|
+
@data ||= csv_data_source.data
|
|
296
290
|
end
|
|
297
291
|
|
|
298
292
|
def validate_attributes # :nodoc:
|
|
299
|
-
case seed_method
|
|
293
|
+
case context.seed_method
|
|
300
294
|
when :csv
|
|
301
|
-
raise "Couldn't find csv for #{
|
|
295
|
+
raise "Couldn't find csv for #{context.table_name}" unless csv_data_source.path
|
|
302
296
|
when :data_array
|
|
303
297
|
raise "data is not defined in the seeder" if public_send(:data).nil?
|
|
304
298
|
else
|
|
@@ -306,36 +300,28 @@ module Planter
|
|
|
306
300
|
end
|
|
307
301
|
end
|
|
308
302
|
|
|
309
|
-
def
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
]
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def transform(field, value, record)
|
|
323
|
-
case transformations[field].arity
|
|
324
|
-
when 0 then transformations[field].call
|
|
325
|
-
when 1 then transformations[field].call(value)
|
|
326
|
-
when 2 then transformations[field].call(value, record)
|
|
327
|
-
end
|
|
303
|
+
def context
|
|
304
|
+
@context ||= Planter::SeedContext.new(
|
|
305
|
+
table_name: table_name,
|
|
306
|
+
seed_method: seed_method,
|
|
307
|
+
csv_name: csv_name,
|
|
308
|
+
parent: parent,
|
|
309
|
+
number_of_records: number_of_records,
|
|
310
|
+
unique_columns: unique_columns,
|
|
311
|
+
erb_trim_mode: erb_trim_mode
|
|
312
|
+
)
|
|
328
313
|
end
|
|
329
314
|
|
|
330
|
-
def
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
u = unique_columns.each_with_object({}) { |c, h| h[c] = rec[c] }
|
|
334
|
-
[u, rec.except(*unique_columns)]
|
|
315
|
+
def csv_data_source
|
|
316
|
+
@csv_data_source ||= Planter::CsvDataSource.new(context: context, seeder: self)
|
|
335
317
|
end
|
|
336
318
|
|
|
337
|
-
def
|
|
338
|
-
|
|
319
|
+
def record_attributes
|
|
320
|
+
@record_attributes ||= Planter::RecordAttributes.new(
|
|
321
|
+
context: context,
|
|
322
|
+
adapter: adapter,
|
|
323
|
+
transformations_provider: -> { public_send(:transformations) }
|
|
324
|
+
)
|
|
339
325
|
end
|
|
340
326
|
|
|
341
327
|
def adapter
|
data/lib/planter/version.rb
CHANGED
data/lib/planter.rb
CHANGED
|
@@ -5,6 +5,9 @@ require "erb"
|
|
|
5
5
|
require "planter/version"
|
|
6
6
|
require "planter/railtie"
|
|
7
7
|
require "planter/config"
|
|
8
|
+
require "planter/seed_context"
|
|
9
|
+
require "planter/csv_data_source"
|
|
10
|
+
require "planter/record_attributes"
|
|
8
11
|
require "planter/seeder"
|
|
9
12
|
|
|
10
13
|
##
|
|
@@ -59,9 +62,9 @@ module Planter
|
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
##
|
|
62
|
-
# This is the method to call from your +db/seeds.rb+. It
|
|
65
|
+
# This is the method to call from your +db/seeds.rb+. It calls the seeders
|
|
63
66
|
# listed in +Planter.config.seeders+. To call specific seeders at runtime,
|
|
64
|
-
# you can set the +SEEDERS+
|
|
67
|
+
# you can set the +SEEDERS+ environment variable to a comma-separated list
|
|
65
68
|
# of seeders, like +rails db:seed SEEDERS=users,accounts+.
|
|
66
69
|
#
|
|
67
70
|
# @example
|
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: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Gray
|
|
@@ -102,7 +102,10 @@ files:
|
|
|
102
102
|
- lib/planter.rb
|
|
103
103
|
- lib/planter/adapters/active_record.rb
|
|
104
104
|
- lib/planter/config.rb
|
|
105
|
+
- lib/planter/csv_data_source.rb
|
|
105
106
|
- lib/planter/railtie.rb
|
|
107
|
+
- lib/planter/record_attributes.rb
|
|
108
|
+
- lib/planter/seed_context.rb
|
|
106
109
|
- lib/planter/seeder.rb
|
|
107
110
|
- lib/planter/version.rb
|
|
108
111
|
- lib/tasks/planter_tasks.rake
|