planter 0.0.13 → 0.1.2

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: 23a4116a1fe8a8d499e8522e5d4da4dadd76cb95ba917d54855f36adeaec519f
4
- data.tar.gz: 523fcef8dd453614e4c1045aafd1ae57b54e7b6043c4fac3b3638ef35c577862
3
+ metadata.gz: 39fe6b05679379fdcf8c619b1db28fdac6a6f89eec521b3e8cf3f3667a471c49
4
+ data.tar.gz: 42693cdccc9239a069996c74455edc658b339a894eff5b984ec00bba47a88797
5
5
  SHA512:
6
- metadata.gz: eb5d69e3807c0595c4a149cc0eeb487712a18381d86cad82a9e459ca28a19286c77e35b92820932b9be3ced37563632f7d8a27d1a85bf15c55621895bc5f923b
7
- data.tar.gz: 9d32d66773a92b393fdac8a90a67ca410251588d61270a35ba8a21f8d1afe74963c5ef81702fffc33ffb59674b7fb953029f19d887f17a26bd7a5b7ae3522df3
6
+ metadata.gz: c3ffd760187e67c1f57b7daf7a1bba7f019470f1324ddaf72d1412c34930ea520eea449aebd0cea43b6b48de948f93ba80f47bafe6de782f550c35daf0bb8508
7
+ data.tar.gz: cfd8daff8e7d45a9d72411c195b31be7d1ddde9b562a03397b43f0848360c701d569289ab32e3260bf03f765752cd81fa123f3b597d9b08cc69ac9a6cab7bb6f
data/README.md CHANGED
@@ -144,8 +144,8 @@ 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`.
149
149
 
150
150
  ```
151
151
  participant_id,name
@@ -207,13 +207,16 @@ Running `rails planter:seed` should now seed your `users` table.
207
207
 
208
208
  You can also seed children records for every existing record of a parent model.
209
209
  For example, to seed an address for every user, you'd need to create an
210
- `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.
211
214
 
212
215
  ```ruby
213
216
  require 'faker'
214
217
 
215
218
  class AddressesSeeder < Planter::Seeder
216
- seeding_method :data_array, parent_model: 'User'
219
+ seeding_method :data_array, parent: :user
217
220
 
218
221
  def data
219
222
  [{
@@ -227,9 +230,7 @@ end
227
230
  ```
228
231
 
229
232
  Note that specifying `number_of_records` in this instance will create that many
230
- records *for each record of the parent model*. You can also specify the
231
- association if it's different from the table name, using the `:assocation`
232
- option.
233
+ records *for each record of the parent model*.
233
234
 
234
235
  ### Custom seeds
235
236
  To write your own custom seeds, just overload the `seed` method and do whatever
@@ -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
@@ -127,7 +128,7 @@ module Planter
127
128
  # class must set this attribute via +seeding_method+.
128
129
  #
129
130
  # @return [String]
130
- class_attribute :parent_model
131
+ class_attribute :parent
131
132
 
132
133
  ##
133
134
  # The number of records to create from each record in the +data+ array. If
@@ -137,116 +138,72 @@ module Planter
137
138
  # @return [Integer]
138
139
  class_attribute :number_of_records
139
140
 
140
- ##
141
- # When using +parent_model+, the association name. Your class can set this
142
- # attribute via +seeding_method+.
143
- #
144
- # @return [Symbol]
145
- class_attribute :association
146
-
147
141
  ##
148
142
  # The csv file corresponding to the model.
149
143
  #
150
144
  # @return [String]
151
- class_attribute :csv_file
145
+ class_attribute :csv_name
152
146
 
153
147
  ##
154
148
  # The seeding method specified.
155
149
  #
156
150
  # @return [Symbol]
157
- class_attribute :seeding_method
151
+ class_attribute :seed_method
158
152
 
159
153
  ##
160
- # Access the metaclass so we can define public and private class methods.
161
- class << self
162
- ##
163
- # If your class is going to use the inherited +seed+ method, you must tell
164
- # it which +seeding_method+ to use. The argument to this method must be
165
- # included in the +SEEDING_METHODS+ array.
166
- #
167
- # @param [Symbol] seeding_method
168
- #
169
- # @kwarg [Integer] number_of_records
170
- #
171
- # @kwarg [String] model
172
- #
173
- # @kwarg [String] parent_model
174
- #
175
- # @kwarg [Symbol, String] association
176
- #
177
- # @kwarg [Symbol, String] csv_name
178
- #
179
- # @kwarg [Symbol, String] unique_columns
180
- #
181
- # @kwarg [String] erb_trim_mode
182
- #
183
- # @example
184
- # require 'planter'
185
- # class UsersSeeder < Planter::Seeder
186
- # seeding_method :csv,
187
- # number_of_records: 2,
188
- # model: 'User'
189
- # parent_model: 'Person',
190
- # association: :users,
191
- # csv_name: :awesome_users,
192
- # unique_columns %i[username email],
193
- # erb_trim_mode: '<>'
194
- # end
195
- def seeding_method(
196
- method,
197
- number_of_records: 1,
198
- model: nil,
199
- parent_model: nil,
200
- association: nil,
201
- csv_name: nil,
202
- unique_columns: nil,
203
- erb_trim_mode: nil
204
- )
205
- if !SEEDING_METHODS.include?(method.intern)
206
- raise ArgumentError, "Method must be: #{SEEDING_METHODS.join(', ')}"
207
- elsif association && !parent_model
208
- raise ArgumentError, "Must specify :parent_model with :association"
209
- end
210
-
211
- self.seeding_method = method
212
- self.number_of_records = number_of_records
213
- self.model = model || to_s.delete_suffix('Seeder').singularize
214
- self.parent_model = parent_model
215
- self.association = parent_model && (association || determine_association)
216
- self.csv_file = determine_csv_filename(csv_name) if seeding_method == :csv
217
- self.erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
218
- self.unique_columns =
219
- case unique_columns
220
- when String, Symbol then [unique_columns.intern]
221
- when Array then unique_columns.map(&:intern)
222
- end
223
- end
224
-
225
- private
226
-
227
- def determine_association # :nodoc:
228
- associations =
229
- parent_model.constantize.reflect_on_all_associations.map(&:name)
230
- table = to_s.delete_suffix('Seeder').underscore.split('/').last
231
-
232
- [table, table.singularize].map(&:intern).each do |t|
233
- return t if associations.include?(t)
234
- end
235
-
236
- raise ArgumentError, "Couldn't determine association name"
154
+ # If your class is going to use the inherited +seed+ method, you must tell
155
+ # it which +seeding_method+ to use. The argument to this method must be
156
+ # included in the +SEEDING_METHODS+ array.
157
+ #
158
+ # @param [Symbol] seed_method
159
+ #
160
+ # @kwarg [Integer] number_of_records
161
+ #
162
+ # @kwarg [String] model
163
+ #
164
+ # @kwarg [Symbol, String] parent
165
+ #
166
+ # @kwarg [Symbol, String] csv_name
167
+ #
168
+ # @kwarg [Symbol, String] unique_columns
169
+ #
170
+ # @kwarg [String] erb_trim_mode
171
+ #
172
+ # @example
173
+ # require 'planter'
174
+ # class UsersSeeder < Planter::Seeder
175
+ # seeding_method :csv,
176
+ # number_of_records: 2,
177
+ # model: 'User'
178
+ # parent: :person,
179
+ # csv_name: :awesome_users,
180
+ # unique_columns %i[username email],
181
+ # erb_trim_mode: '<>'
182
+ # end
183
+ def self.seeding_method(
184
+ seed_method,
185
+ number_of_records: 1,
186
+ model: nil,
187
+ parent: nil,
188
+ csv_name: nil,
189
+ unique_columns: nil,
190
+ erb_trim_mode: nil
191
+ )
192
+ if !SEEDING_METHODS.include?(seed_method.intern)
193
+ raise ArgumentError, "Method must be: #{SEEDING_METHODS.join(', ')}"
237
194
  end
238
195
 
239
- def determine_csv_filename(csv_name) # :nodoc:
240
- file = (
241
- csv_name || "#{to_s.delete_suffix('Seeder').underscore}"
242
- ).to_s + '.csv'
243
- [file, "#{file}.erb"].each do |f|
244
- fname = Rails.root.join(Planter.config.csv_files_directory, f).to_s
245
- return fname if ::File.file?(fname)
196
+ self.seed_method = seed_method
197
+ self.number_of_records = number_of_records
198
+ self.model = model || to_s.delete_suffix('Seeder').singularize
199
+ self.parent = parent
200
+ self.csv_name = csv_name || to_s.delete_suffix('Seeder').underscore
201
+ self.erb_trim_mode = erb_trim_mode || Planter.config.erb_trim_mode
202
+ self.unique_columns =
203
+ case unique_columns
204
+ when String, Symbol then [unique_columns.intern]
205
+ when Array then unique_columns.map(&:intern)
246
206
  end
247
-
248
- raise ArgumentError, "Couldn't find csv for #{model}"
249
- end
250
207
  end
251
208
 
252
209
  ##
@@ -254,8 +211,9 @@ module Planter
254
211
  # valid +seeding_method+, and not implement its own +seed+ method.
255
212
  def seed
256
213
  validate_attributes
214
+ extract_data_from_csv if seed_method == :csv
257
215
 
258
- parent_model ? create_records_from_parent : create_records
216
+ parent ? create_records_from_parent : create_records
259
217
  end
260
218
 
261
219
  protected
@@ -263,69 +221,82 @@ module Planter
263
221
  ##
264
222
  # Creates records from the +data+ attribute.
265
223
  def create_records
266
- data.each do |rec|
267
- number_of_records.times do
268
- rec.transform_values { |value| value == 'NULL' ? nil : value }
269
- unique, attrs = split_record(rec)
270
- model.constantize.where(unique).first_or_create!(attrs)
271
- end
272
- end
224
+ data.each { |record| create_record(record) }
273
225
  end
274
226
 
275
227
  ##
276
- # Create records from the +data+ attribute for each record in the
277
- # +parent_table+, via the specified +association+.
228
+ # Create records from the +data+ attribute for each record in the +parent+.
278
229
  def create_records_from_parent
279
- parent_model.constantize.all.each do |assoc_rec|
280
- number_of_records.times do
281
- data.each { |rec| send(create_method, assoc_rec, association, rec) }
282
- end
230
+ parent_model.constantize.pluck(primary_key).each do |parent_id|
231
+ data.each { |record| create_record(record, parent_id: parent_id) }
283
232
  end
284
233
  end
285
234
 
286
- private
287
-
288
- def create_method # :nodoc:
289
- parent_model.constantize.reflect_on_association(
290
- association
291
- ).macro.to_s.include?('many') ? :create_has_many : :create_has_one
292
- end
293
-
294
- def create_has_many(assoc_rec, association, rec) # :nodoc:
295
- unique, attrs = split_record(rec)
296
- assoc_rec.public_send(association).where(unique).first_or_create!(attrs)
297
- end
298
-
299
- def create_has_one(assoc_rec, association, rec) # :nodoc:
300
- if assoc_rec.public_send(association)
301
- assoc_rec.public_send(association).update_attributes(rec)
302
- else
303
- assoc_rec.public_send("create_#{association}", rec)
235
+ def create_record(record, parent_id: nil)
236
+ number_of_records.times do
237
+ unique, attrs = split_record(record)
238
+ model.constantize.where(
239
+ unique.tap { |u| u[foreign_key] = parent_id if parent_id }
240
+ ).first_or_create!(attrs)
304
241
  end
305
242
  end
306
243
 
307
244
  def validate_attributes # :nodoc:
308
- case seeding_method.intern
245
+ case seed_method.intern
309
246
  when :csv
310
- contents = ::File.read(csv_file)
311
- if csv_file.end_with?('.erb')
312
- contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
313
- end
314
-
315
- @data ||= ::CSV.parse(
316
- contents, headers: true, header_converters: :symbol
317
- ).map(&:to_hash)
247
+ raise "Couldn't find csv for #{model}" unless full_csv_name
318
248
  when :data_array
319
- raise "Must define '@data'" if public_send(:data).nil?
249
+ raise 'data is not defined in the seeder' if public_send(:data).nil?
320
250
  else
321
- raise("Must set 'seeding_method'")
251
+ raise 'seeding_method not defined in the seeder'
322
252
  end
323
253
  end
324
254
 
325
255
  def split_record(rec) # :nodoc:
326
256
  return [rec, {}] unless unique_columns
257
+
327
258
  u = unique_columns.each_with_object({}) { |c, h| h[c] = rec.delete(c) }
328
259
  [u, rec]
329
260
  end
261
+
262
+ def association_options
263
+ @association_options ||=
264
+ model.constantize.reflect_on_association(parent).options
265
+ end
266
+
267
+ def primary_key
268
+ @primary_key ||=
269
+ association_options.fetch(:primary_key, :id)
270
+ end
271
+
272
+ def foreign_key
273
+ @foreign_key ||=
274
+ association_options.fetch(:foreign_key, "#{parent}_id")
275
+ end
276
+
277
+ def parent_model
278
+ @parent_model ||=
279
+ association_options.fetch(:class_name, parent.to_s.classify)
280
+ end
281
+
282
+ def full_csv_name
283
+ @full_csv_name ||=
284
+ %W[#{csv_name}.csv #{csv_name}.csv.erb #{csv_name}.erb.csv]
285
+ .map { |f| Rails.root.join(Planter.config.csv_files_directory, f).to_s }
286
+ .find { |f| ::File.file?(f) }
287
+ end
288
+
289
+ def extract_data_from_csv
290
+ contents = ::File.read(full_csv_name)
291
+ if full_csv_name.include?('.erb')
292
+ contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
293
+ end
294
+
295
+ @data ||= ::CSV.parse(
296
+ contents,
297
+ headers: true,
298
+ header_converters: :symbol
299
+ ).map(&:to_hash)
300
+ end
330
301
  end
331
302
  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 = 13
24
+ PATCH = 2
25
25
 
26
26
  ##
27
27
  # Version as +[MAJOR, MINOR, PATCH]+
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.13
4
+ version: 0.1.2
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-12-27 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: