planter 0.0.12 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37c19399e25198fe0545690d5b5568c5bd91b4736258af3d8664c36abe882889
4
- data.tar.gz: a399b54a606d7c0e98d49f0bad5abff1fc4726d96eab3573c82d1dc649b83b1e
3
+ metadata.gz: b346415e35c538e46222ced2131792a27f778fafe169143116c6908ddca88aac
4
+ data.tar.gz: ed3952a7fd92566b2acb31e0fee881549675f40aa579ebe9115db2f86f302eb0
5
5
  SHA512:
6
- metadata.gz: 6275a47f463227b86374e327781edd92bbd9cdf9399bafa68c6c4cb3316ae812cf081509a26e81e04101f9cd4af43396f2c008477741f00f475ea09436353ba8
7
- data.tar.gz: 1a5a27d9253b78ae0c1c0526c1f8dcb977a428ec5c62cb4239f5542413e1f5020c68d8ee0d356ded4c1f3094c7f449f03fefb72180250388de81f8c44c2c6a63
6
+ metadata.gz: d07501dd0544476e04b768277d870f035626f6c00218a9f248e6fe6f7dcae833757162b688a269066bc8daa5dae1d2fb6f20db7c45273245da60f900cde80f3f
7
+ data.tar.gz: '09e7ba1054d15f7aa68aedaac7915fdda444eac1bff9630402e5f5999f872283e978b5e60b9581310cdd2bbc9ea4688d73b3fd7d6c8781b802b8bb21cee7fb8c'
data/README.md CHANGED
@@ -60,7 +60,7 @@ Planter.configure do |config|
60
60
  ]
61
61
 
62
62
  ##
63
- # The directory where the seeder files are kept.
63
+ # The directory where the seeders are kept.
64
64
  # config.seeders_directory = 'db/seeds'
65
65
 
66
66
  ##
@@ -68,7 +68,12 @@ Planter.configure do |config|
68
68
  # config.csv_files_directory = 'db/seed_files'
69
69
 
70
70
  ##
71
- # The default trim mode for ERB.
71
+ # The default trim mode for ERB. Valid modes are:
72
+ # '%' enables Ruby code processing for lines beginning with %
73
+ # '<>' omit newline for lines starting with <% and ending in %>
74
+ # '>' omit newline for lines ending in %>
75
+ # '-' omit blank lines ending in -%>
76
+ # I recommend reading the help documentation for ERB::new()
72
77
  # config.erb_trim_mode = nil
73
78
  end
74
79
  ```
@@ -202,13 +207,16 @@ Running `rails planter:seed` should now seed your `users` table.
202
207
 
203
208
  You can also seed children records for every existing record of a parent model.
204
209
  For example, to seed an address for every user, you'd need to create an
205
- `AddressesSeeder` that uses the `parent_model` option, as seen below.
210
+ `AddressesSeeder` that uses the `parent` option, as seen below. This option
211
+ should be the name of the `belongs_to` association in your model. The primary
212
+ key, foreign key, and model name of the parent will all be determined by
213
+ reflecting on the association.
206
214
 
207
215
  ```ruby
208
216
  require 'faker'
209
217
 
210
218
  class AddressesSeeder < Planter::Seeder
211
- seeding_method :data_array, parent_model: 'User'
219
+ seeding_method :data_array, parent: :user
212
220
 
213
221
  def data
214
222
  [{
@@ -222,9 +230,7 @@ end
222
230
  ```
223
231
 
224
232
  Note that specifying `number_of_records` in this instance will create that many
225
- records *for each record of the parent model*. You can also specify the
226
- association if it's different from the table name, using the `:assocation`
227
- option.
233
+ records *for each record of the parent model*.
228
234
 
229
235
  ### Custom seeds
230
236
  To write your own custom seeds, just overload the `seed` method and do whatever
@@ -257,4 +263,4 @@ Request.
257
263
  I do these projects for fun, and I enjoy knowing that they're helpful to people.
258
264
  Consider starring [the repository](https://github.com/evanthegrayt/planter)
259
265
  if you like it! If you love it, follow me [on
260
- Github](https://github.com/evanthegrayt)!
266
+ GitHub](https://github.com/evanthegrayt)!
@@ -19,7 +19,7 @@ module Planter
19
19
  ]
20
20
 
21
21
  ##
22
- # The directory where the seeder files are kept.
22
+ # The directory where the seeders are kept.
23
23
  # config.seeders_directory = 'db/seeds'
24
24
 
25
25
  ##
@@ -27,7 +27,12 @@ module Planter
27
27
  # config.csv_files_directory = 'db/seed_files'
28
28
 
29
29
  ##
30
- # The default trim mode for ERB.
30
+ # The default trim mode for ERB. Valid modes are:
31
+ # '%' enables Ruby code processing for lines beginning with %
32
+ # '<>' omit newline for lines starting with <% and ending in %>
33
+ # '>' omit newline for lines ending in %>
34
+ # '-' omit blank lines ending in -%>
35
+ # I recommend reading the help documentation for ERB::new()
31
36
  # config.erb_trim_mode = nil
32
37
  end
33
38
  EOF
@@ -3,7 +3,7 @@ module Planter
3
3
  class SeederGenerator < Rails::Generators::Base
4
4
  argument :seeder, required: true
5
5
 
6
- desc "This generator creates a seeder file at #{::Planter.config.seeders_directory}"
6
+ desc "Creates a seeder file at #{::Planter.config.seeders_directory}"
7
7
 
8
8
  def generate_seeders
9
9
  seeder == 'ALL' ? tables.each { |t| generate(t) } : generate(seeder)
@@ -2,13 +2,13 @@
2
2
 
3
3
  module Planter
4
4
  ##
5
- # Class that seeders should inherit from. Seeder files should be in
6
- # +db/seeds+, and named +TABLE_seeder.rb+, where +TABLE+ is the name of the
7
- # table being seeded (I.E. +users_seeder.rb+). If your seeder is named
8
- # differently than the table, you'll need to specify the table with the
9
- # +model+ option. The seeder's class name should be the same as the file
10
- # name, but camelized. So, +UsersSeeder+. The directory where the seeder
11
- # files are located can be changed via an initializer.
5
+ # Class that seeders should inherit from. Seeders should be in +db/seeds+,
6
+ # and named +TABLE_seeder.rb+, where +TABLE+ is the name of the table being
7
+ # seeded (I.E. +users_seeder.rb+). If your seeder is named differently than
8
+ # the table, you'll need to specify the table with the +model+ option. The
9
+ # seeder's class name should be the same as the file name, but camelized. So,
10
+ # +UsersSeeder+. The directory where the seeder files are located can be
11
+ # changed via an initializer.
12
12
  #
13
13
  # The most basic way to seed is to have a CSV file with the same name as the
14
14
  # table in +db/seed_files/+. So, +users.csv+. This CSV should have the
@@ -26,7 +26,7 @@ module Planter
26
26
  # Another way to seed is to create records from a data array. To do this,
27
27
  # your class must implement a +data+ attribute or method, which is an array
28
28
  # of hashes. Note that this class already provides the +attr_reader+ for this
29
- # attribute, so the most you have to do it create instance variables in your
29
+ # attribute, so the most you have to do is create instance variables in your
30
30
  # constructor. If if you want your data to be different for each new record
31
31
  # (via Faker, +Array#sample+, etc.), you'll probably want to supply a method
32
32
  # called data that returns an array of new data each time.
@@ -38,15 +38,17 @@ module Planter
38
38
  # end
39
39
  # end
40
40
  #
41
- # In both of the above methods, you can specify +parent_model+ and
42
- # +association+. If specified, records will be created via that parent
43
- # model's association. If +association+ is not provided, it will be assumed
44
- # to be the model name, pluralized and snake-cased (implying a +has_many+
45
- # relationship). For example, if we're seeding the users table, and the
46
- # model is +User+, the association will default to +users+.
41
+ # In both of the above methods, you can specify a +parent+ association, which
42
+ # is the +belongs_to+ association name in your model, which, when specified,
43
+ # records will be created for each record in the parent table. For example,
44
+ # if we're seeding the users table, and the model is +User+, which belongs to
45
+ # +Person+, then doing the following will create a user record for each
46
+ # record in the Person table. Note that nothing is automatically done to
47
+ # prevent any validation errors; you must do this on your own, mostly likely
48
+ # using +Faker+ or a similar library.
47
49
  # require 'planter'
48
50
  # class UsersSeeder < Planter::Seeder
49
- # seeding_method :data_array, parent_model: 'Person', association: :users
51
+ # seeding_method :data_array, parent: :person
50
52
  # def data
51
53
  # [{foo: 'bar', baz: 'bar'}]
52
54
  # end
@@ -54,9 +56,8 @@ module Planter
54
56
  #
55
57
  # You can also set +number_of_records+ to determine how many times each
56
58
  # record in the +data+ array will get created. The default is 1. Note that if
57
- # this attribute is set alongside +parent_model+ and +association+,
58
- # +number_of_records+ will be how many records will be created for each
59
- # record in the parent table.
59
+ # this attribute is set alongside +parent+, +number_of_records+ will be how
60
+ # many records will be created for each record in the parent table.
60
61
  # require 'planter'
61
62
  # class UsersSeeder < Planter::Seeder
62
63
  # seeding_method :data_array, number_of_records: 5
@@ -102,121 +103,24 @@ module Planter
102
103
  attr_reader :data
103
104
 
104
105
  ##
105
- # If your class is going to use the inherited +seed+ method, you must tell
106
- # it which +seeding_method+ to use. The argument to this method must be
107
- # included in the +SEEDING_METHODS+ array.
108
- #
109
- # @param [Symbol] seeding_method
110
- #
111
- # @kwarg [Integer] number_of_records
112
- #
113
- # @kwarg [String] model
114
- #
115
- # @kwarg [String] parent_model
116
- #
117
- # @kwarg [Symbol, String] association
118
- #
119
- # @kwarg [Symbol, String] csv_name
120
- #
121
- # @kwarg [Symbol, String] unique_columns
122
- #
123
- # @kwarg [String] erb_trim_mode
106
+ # What trim mode should ERB use?
124
107
  #
125
- # @example
126
- # require 'planter'
127
- # class UsersSeeder < Planter::Seeder
128
- # seeding_method :csv,
129
- # number_of_records: 2,
130
- # model: 'User'
131
- # parent_model: 'Person',
132
- # association: :users,
133
- # csv_name: :awesome_users,
134
- # unique_columns %i[username email],
135
- # erb_trim_mode: '<>'
136
- # end
137
- def self.seeding_method(
138
- method,
139
- number_of_records: 1,
140
- model: nil,
141
- parent_model: nil,
142
- association: nil,
143
- csv_name: nil,
144
- unique_columns: nil,
145
- erb_trim_mode: nil
146
- )
147
- if !SEEDING_METHODS.include?(method.intern)
148
- raise ArgumentError, "Method must be one of #{SEEDING_METHODS.join(', ')}"
149
- elsif association && !parent_model
150
- raise ArgumentError, "Must specify :parent_model with :association"
151
- end
152
-
153
- @seeding_method = method
154
- @number_of_records = number_of_records
155
- @model = model || to_s.delete_suffix('Seeder').singularize
156
- @parent_model = parent_model
157
- @association = @parent_model && (association || determine_association)
158
- @csv_file = determine_csv_filename(csv_name) if @seeding_method == :csv
159
- @erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
160
- @unique_columns =
161
- case unique_columns
162
- when String, Symbol then [unique_columns.intern]
163
- when Array then unique_columns.map(&:intern)
164
- end
165
- end
166
-
167
- def self.determine_association # :nodoc:
168
- associations =
169
- @parent_model.constantize.reflect_on_all_associations.map(&:name)
170
- table = to_s.delete_suffix('Seeder').underscore.split('/').last
171
-
172
- [table, table.singularize].map(&:intern).each do |t|
173
- return t if associations.include?(t)
174
- end
175
-
176
- raise ArgumentError, "Couldn't determine association name"
177
- end
178
- private_class_method :determine_association
179
-
180
- def self.determine_csv_filename(csv_name) # :nodoc:
181
- file = (
182
- csv_name || "#{to_s.delete_suffix('Seeder').underscore}"
183
- ).to_s + '.csv'
184
- [file, "#{file}.erb"].each do |f|
185
- fname = Rails.root.join(Planter.config.csv_files_directory, f).to_s
186
- return fname if ::File.file?(fname)
187
- end
188
-
189
- raise ArgumentError, "Couldn't find csv for #{@model}"
190
- end
191
- private_class_method :determine_csv_filename
192
-
193
- ##
194
- # The default seed method. To use this method, your class must provide a
195
- # valid +seeding_method+, and not implement its own +seed+ method.
196
- def seed
197
- validate_attributes
198
-
199
- parent_model ? create_records_from_parent : create_records
200
- end
201
-
202
- protected
108
+ # @return [String]
109
+ class_attribute :erb_trim_mode
203
110
 
204
111
  ##
205
- # The seeding method specified.
112
+ # When creating a record, the fields that will be used to look up the
113
+ # record. If it already exists, a new one will not be created.
206
114
  #
207
- # @return [Symbol]
208
- def seeding_method
209
- @seeding_method ||= self.class.instance_variable_get('@seeding_method')
210
- end
115
+ # @return [Array]
116
+ class_attribute :unique_columns
211
117
 
212
118
  ##
213
119
  # The model for the table being seeded. If the model name you need is
214
120
  # different, change via +seeding_method+.
215
121
  #
216
122
  # @return [String]
217
- def model
218
- @model ||= self.class.instance_variable_get('@model')
219
- end
123
+ class_attribute :model
220
124
 
221
125
  ##
222
126
  # The model of the parent. When provided with +association+, records in the
@@ -224,18 +128,7 @@ module Planter
224
128
  # class must set this attribute via +seeding_method+.
225
129
  #
226
130
  # @return [String]
227
- def parent_model
228
- @parent_model ||= self.class.instance_variable_get('@parent_model')
229
- end
230
-
231
- ##
232
- # When using +parent_model+, the association name. Your class can set this
233
- # attribute via +seeding_method+.
234
- #
235
- # @return [Symbol]
236
- def association
237
- @association ||= self.class.instance_variable_get('@association')
238
- end
131
+ class_attribute :parent
239
132
 
240
133
  ##
241
134
  # The number of records to create from each record in the +data+ array. If
@@ -243,82 +136,132 @@ module Planter
243
136
  # +seeding_method+.
244
137
  #
245
138
  # @return [Integer]
246
- def number_of_records
247
- @number_of_records ||=
248
- self.class.instance_variable_get('@number_of_records')
249
- end
139
+ class_attribute :number_of_records
250
140
 
251
141
  ##
252
142
  # The csv file corresponding to the model.
253
143
  #
254
144
  # @return [String]
255
- def csv_file
256
- @csv_file ||= self.class.instance_variable_get('@csv_file')
257
- end
145
+ class_attribute :csv_file
258
146
 
259
147
  ##
260
- # When creating a record, the fields that will be used to look up the
261
- # record. If it already exists, a new one will not be created.
148
+ # The seeding method specified.
262
149
  #
263
- # @return [Array]
264
- def unique_columns
265
- @unique_columns ||= self.class.instance_variable_get('@unique_columns')
266
- end
150
+ # @return [Symbol]
151
+ class_attribute :seed_method
267
152
 
268
153
  ##
269
- # What trim mode should ERB use?
270
- #
271
- # @return [String]
272
- def erb_trim_mode
273
- @erb_trim_mode ||= self.class.instance_variable_get('@erb_trim_mode')
274
- end
154
+ # Access the metaclass so we can define public and private class methods.
155
+ class << self
156
+ ##
157
+ # If your class is going to use the inherited +seed+ method, you must tell
158
+ # it which +seeding_method+ to use. The argument to this method must be
159
+ # included in the +SEEDING_METHODS+ array.
160
+ #
161
+ # @param [Symbol] seed_method
162
+ #
163
+ # @kwarg [Integer] number_of_records
164
+ #
165
+ # @kwarg [String] model
166
+ #
167
+ # @kwarg [String] parent
168
+ #
169
+ # @kwarg [Symbol, String] parent
170
+ #
171
+ # @kwarg [Symbol, String] csv_name
172
+ #
173
+ # @kwarg [Symbol, String] unique_columns
174
+ #
175
+ # @kwarg [String] erb_trim_mode
176
+ #
177
+ # @example
178
+ # require 'planter'
179
+ # class UsersSeeder < Planter::Seeder
180
+ # seeding_method :csv,
181
+ # number_of_records: 2,
182
+ # model: 'User'
183
+ # parent: :person,
184
+ # csv_name: :awesome_users,
185
+ # unique_columns %i[username email],
186
+ # erb_trim_mode: '<>'
187
+ # end
188
+ def seeding_method(
189
+ seed_method,
190
+ number_of_records: 1,
191
+ model: nil,
192
+ parent: nil,
193
+ csv_name: nil,
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
275
200
 
276
- ##
277
- # Creates records from the +data+ attribute.
278
- def create_records
279
- data.each do |rec|
280
- number_of_records.times do
281
- rec.transform_values { |value| value == 'NULL' ? nil : value }
282
- unique, attrs = split_record(rec)
283
- model.constantize.where(unique).first_or_create!(attrs)
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
212
+ end
213
+
214
+ private
215
+
216
+ def determine_csv_filename(csv_name) # :nodoc:
217
+ file = (
218
+ csv_name || "#{to_s.delete_suffix('Seeder').underscore}"
219
+ ).to_s + '.csv'
220
+ [file, "#{file}.erb"].each do |f|
221
+ fname = Rails.root.join(Planter.config.csv_files_directory, f).to_s
222
+ return fname if ::File.file?(fname)
284
223
  end
224
+
225
+ raise ArgumentError, "Couldn't find csv for #{model}"
285
226
  end
286
227
  end
287
228
 
288
229
  ##
289
- # Create records from the +data+ attribute for each record in the
290
- # +parent_table+, via the specified +association+.
291
- def create_records_from_parent
292
- parent_model.constantize.all.each do |assoc_rec|
293
- number_of_records.times do
294
- data.each { |rec| send(create_method, assoc_rec, association, rec) }
295
- end
296
- end
230
+ # The default seed method. To use this method, your class must provide a
231
+ # valid +seeding_method+, and not implement its own +seed+ method.
232
+ def seed
233
+ validate_attributes
234
+
235
+ parent ? create_records_from_parent : create_records
297
236
  end
298
237
 
299
- private
238
+ protected
300
239
 
301
- def create_method # :nodoc:
302
- parent_model.constantize.reflect_on_association(
303
- association
304
- ).macro.to_s.include?('many') ? :create_has_many : :create_has_one
240
+ ##
241
+ # Creates records from the +data+ attribute.
242
+ def create_records
243
+ data.each { |record| create_record(record) }
305
244
  end
306
245
 
307
- def create_has_many(assoc_rec, association, rec) # :nodoc:
308
- unique, attrs = split_record(rec)
309
- assoc_rec.public_send(association).where(unique).first_or_create!(attrs)
246
+ ##
247
+ # Create records from the +data+ attribute for each record in the +parent+.
248
+ def create_records_from_parent
249
+ parent_model.constantize.pluck(primary_key).each do |parent_id|
250
+ data.each { |record| create_record(record, parent_id: parent_id) }
251
+ end
310
252
  end
311
253
 
312
- def create_has_one(assoc_rec, association, rec) # :nodoc:
313
- if assoc_rec.public_send(association)
314
- assoc_rec.public_send(association).update_attributes(rec)
315
- else
316
- assoc_rec.public_send("create_#{association}", rec)
254
+ def create_record(record, parent_id: nil)
255
+ number_of_records.times do
256
+ unique, attrs = split_record(record)
257
+ model.constantize.where(
258
+ unique.tap { |u| u[foreign_key] = parent_id if parent_id }
259
+ ).first_or_create!(attrs)
317
260
  end
318
261
  end
319
262
 
320
263
  def validate_attributes # :nodoc:
321
- case seeding_method.intern
264
+ case seed_method.intern
322
265
  when :csv
323
266
  contents = ::File.read(csv_file)
324
267
  if csv_file.end_with?('.erb')
@@ -337,8 +280,29 @@ module Planter
337
280
 
338
281
  def split_record(rec) # :nodoc:
339
282
  return [rec, {}] unless unique_columns
283
+
340
284
  u = unique_columns.each_with_object({}) { |c, h| h[c] = rec.delete(c) }
341
285
  [u, rec]
342
286
  end
287
+
288
+ def association_options
289
+ @association_options ||=
290
+ model.constantize.reflect_on_association(parent).options
291
+ end
292
+
293
+ def primary_key
294
+ @primary_key ||=
295
+ association_options.fetch(:primary_key, :id)
296
+ end
297
+
298
+ def foreign_key
299
+ @foreign_key ||=
300
+ association_options.fetch(:foreign_key, "#{parent}_id")
301
+ end
302
+
303
+ def parent_model
304
+ @parent_model ||=
305
+ association_options.fetch(:class_name, parent.to_s.classify)
306
+ end
343
307
  end
344
308
  end
@@ -15,13 +15,13 @@ module Planter
15
15
  # Minor version.
16
16
  #
17
17
  # @return [Integer]
18
- MINOR = 0
18
+ MINOR = 1
19
19
 
20
20
  ##
21
21
  # Patch version.
22
22
  #
23
23
  # @return [Integer]
24
- PATCH = 12
24
+ PATCH = 1
25
25
 
26
26
  ##
27
27
  # Version as +[MAJOR, MINOR, PATCH]+
data/lib/planter.rb CHANGED
@@ -67,7 +67,7 @@ module Planter
67
67
  # Planter.seed
68
68
  def self.seed
69
69
  seeders = ENV['SEEDERS']&.split(',') || config.seeders&.map(&:to_s)
70
- raise RuntimeError, 'No seeders specified' unless seeders&.any?
70
+ raise RuntimeError, 'No seeders specified' if seeders.blank?
71
71
 
72
72
  seeders.each do |s|
73
73
  require Rails.root.join(config.seeders_directory, "#{s}_seeder.rb").to_s
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.0.12
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Gray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-15 00:00:00.000000000 Z
11
+ date: 2021-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,20 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 6.1.3
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 6.1.3.1
19
+ version: 6.1.4.4
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: 6.1.3
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 6.1.3.1
26
+ version: 6.1.4.4
33
27
  description: Create a seeder for each table in your database, and easily seed from
34
28
  CSV or custom methods
35
29
  email: