planter 0.0.6 → 0.0.10

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: 95ab85b49c50ea3b7ee077d4225083884558ae89c6fa5b2ea4e9410284f890eb
4
- data.tar.gz: 2fd36b19ab5064d681323eb5f7e73a95cf710b7af8a587031c84b7175610cfbe
3
+ metadata.gz: 259e8d195e480b9b5f16d67ee729d822f9c67131650997d2c95958f1356d76ab
4
+ data.tar.gz: f2722663f8f1628268208dd0e35da17507a62b339e0dc45004ba16c59f0879b5
5
5
  SHA512:
6
- metadata.gz: fa74216b4529bcb3d21ccd697574c175ad7dd641d477921108ff9949e1ab9a9537cf6f4847b6e2aaadeb91e8aecedc6afec243c72250d9b77cce1da0e4926580
7
- data.tar.gz: 9b9cb5dd5a29b8a66c8cd9a6c49d5192c1395a5d935a98628928f65babcd709c2a6480ed356bf2f575e0710856a214025efdf91942acf3fe034a39a1369f302a
6
+ metadata.gz: 3598c5503a8fef0db359b9ba6bcbd29e22ca3566e6f9ec0ebf0daa9a23961a227cad94478178b700d7b696b638fed51ea9bb61c5a71fb7a1874eb0765c6334ec
7
+ data.tar.gz: 92d6beec3c0c16fb9dd03d3b26fe6aadf94e28298f058795508c18863e6e41c0f3034c254eb995804d59f211c14bbdc1565848260026b0cf4c2e87f9bd1f7f6e
data/README.md CHANGED
@@ -7,23 +7,24 @@
7
7
 
8
8
  Seeds for Rails applications can get complicated fast, and Rails doesn't provide
9
9
  much for assisting with this process. This plugin seeks to rectify that by
10
- providing easy ways to seed specific tables by hooking into the existing `rails
11
- db:seed` task.
10
+ providing easy ways to seed specific tables.
12
11
 
13
12
  Features include:
14
13
 
15
14
  - Seed tables from CSV files, an array of hashes, or custom methods.
16
- - Call specific seeders with `rails db:seed SEEDERS=users,addresses`.
15
+ - Call specific seeders with `rails planter:seed SEEDERS=users,addresses`.
17
16
  - Control the number of records being created.
18
17
  - Seed associations.
19
18
 
20
19
  You can view the documentation [here](https://evanthegrayt.github.io/planter/).
21
20
 
22
21
  ## Installation
23
- Add this line to your application's Gemfile:
22
+ Add the following line to your application's Gemfile. Because this plugin is
23
+ currently a pre-release version, it's recommended to lock it to a specific
24
+ version, as breaking changes may occur, even at the patch level.
24
25
 
25
26
  ```ruby
26
- gem 'planter'
27
+ gem 'planter', '0.0.10'
27
28
  ```
28
29
 
29
30
  And then execute:
@@ -41,30 +42,69 @@ $ gem install planter
41
42
  ## Usage
42
43
  Let's assume you'd like to seed your `users` table.
43
44
 
44
- To get started, simply add the following to your `db/seeds.rb` file. Note that
45
- the `config.seeders` should be an array of the seeders to use. They should be in
46
- the correct order to successfully seed the tables when considering associations.
45
+ To get started, run `rails generate planter:initializer`, which will create
46
+ `config/initializers/planter.rb` with the following contents.
47
47
 
48
48
  ```ruby
49
49
  require 'planter'
50
50
 
51
51
  Planter.configure do |config|
52
- config.seeders = %i[ users ]
52
+ ##
53
+ # The list of seeders. These files are stored in the
54
+ # config.seeders_directory, which can be changed below. When a new
55
+ # seeder is generated, it will be appended to the bottom of this
56
+ # list. If the order is incorrect, you'll need to adjust it.
57
+ # Just be sure to keep the ending bracket on its own line, or the
58
+ # generator won't know where to put new elements.
59
+ config.seeders = %i[
60
+ ]
61
+
62
+ ##
63
+ # The directory where the seeder files are kept.
64
+ # config.seeders_directory = 'db/seeds'
65
+
66
+ ##
67
+ # The directory where CSVs are kept.
68
+ # config.csv_files_directory = 'db/seed_files'
53
69
  end
70
+ ```
71
+
72
+ By default, a `planter:seed` task is provided for seeding your application. This
73
+ allows you to use `db:seed` for other purposes. If you want Planter to hook
74
+ into the existing `db:seed` task, simply add the following to `db/seeds.rb`.
54
75
 
76
+ ```ruby
55
77
  Planter.seed
56
78
  ```
57
79
 
58
- Then, create a directory called `db/seeds`, and create a file called
59
- `db/seeds/users_seeder.rb`. In that file, create the following class. Note the
60
- name of the seeder is the name of the table, plus `Seeder`, and it inherits from
61
- `Planter::Seeder`.
80
+ To create a users seeder, run `rails generate planter:seeder users`. Usually,
81
+ seeders seed a specific table, so it's recommended to name your seeders after
82
+ the table. If you don't, you'll need to manually specify a few things. More on
83
+ that later. This will create a file named `db/seeds/users_seeder.rb` (the
84
+ directory will be created if it doesn't exist) with the following contents.
62
85
 
63
86
  ```ruby
64
87
  class UsersSeeder < Planter::Seeder
88
+ # TODO: Choose a seeding_method. For example:
89
+ # seeding_method :csv
90
+
91
+ # For now, we overload the seed method so no exception will be raised.
92
+ def seed
93
+ end
65
94
  end
66
95
  ```
67
96
 
97
+ This also adds `users` to the `config.seeders` array in our initializer. A few
98
+ things to note.
99
+
100
+ - The seeder will always be appended at the end of the array. If this is not the
101
+ correct order, you'll need to adjust the array manually.
102
+ - When adjusting the array, always keep the closing bracket on its own line, or
103
+ the generator won't know where to put the new seeders.
104
+
105
+ If you want to generate a seeder for every table currently in your database, run
106
+ `rails generate planter:seeder ALL`.
107
+
68
108
  You then need to choose a seeding method, of which there are currently three.
69
109
 
70
110
  ### Seeding from CSV
@@ -72,7 +112,7 @@ To seed from CSV, you simply need to add the following to your seeder class.
72
112
 
73
113
  ```ruby
74
114
  class UsersSeeder < Planter::Seeder
75
- seeding_method :standard_csv
115
+ seeding_method :csv
76
116
  end
77
117
  ```
78
118
 
@@ -86,16 +126,28 @@ test1@example.com,test1
86
126
  test2@example.com,test2
87
127
  ```
88
128
 
89
- If the CSV files are not located in the project, you can specify a `:csv_file`
90
- option. Note that the value must be a full path.
129
+ If the CSV file is named differently than the seeder, you can specify the
130
+ `:csv_name` option. Note that the value should not include the file extension.
91
131
 
92
132
  ```ruby
93
133
  class UsersSeeder < Planter::Seeder
94
- seeding_method :standard_csv, csv_file: '/home/me/users.csv'
134
+ seeding_method :csv, csv_name: :people
95
135
  end
96
136
  ```
97
137
 
98
- Running `rails db:seed` will now seed your `users` table.
138
+ `ERB` can be used in the CSV files if you name it with `.erb` at the end of the
139
+ file name. For example, `users.csv.erb`. Note that lines starting with `<%` and
140
+ ending with `%>` will not be considered rows, so you can use `ERB` rows to set
141
+ values. For example:
142
+
143
+ ```
144
+ email,login_attempts
145
+ <% count = 1 %>
146
+ test2@example.com,<%= count += 1 %>
147
+ test2@example.com,<%= count += 1 %>
148
+ ```
149
+
150
+ Running `rails planter:seed` will now seed your `users` table.
99
151
 
100
152
  ## Seeding from a data array
101
153
  If you need dynamic seeds, you can add something similar to the following to
@@ -103,7 +155,7 @@ your seeder class. In this example, we'll use
103
155
  [faker](https://github.com/faker-ruby/faker).
104
156
 
105
157
  ```ruby
106
- require 'faker' # You should really just require this in `db/seeds.rb`.
158
+ require 'faker' # You could just require this in `db/seeds.rb`.
107
159
 
108
160
  class UsersSeeder < Planter::Seeder
109
161
  seeding_method :data_array, number_of_records: 10
@@ -123,7 +175,7 @@ ten elements to create ten records. It's also worth noting that setting an
123
175
  instance variable called `@data` from an `initialize` method would also work, as
124
176
  the `Planter::Seeder` parent class automatically provides `attr_reader :data`.
125
177
 
126
- Running `rails db:seed` should now seed your `users` table.
178
+ Running `rails planter:seed` should now seed your `users` table.
127
179
 
128
180
  You can also seed children records for every existing record of a parent model.
129
181
  For example, to seed an address for every user, you'd need to create an
@@ -148,7 +200,7 @@ end
148
200
 
149
201
  Note that specifying `number_of_records` in this instance will create that many
150
202
  records *for each record of the parent model*. You can also specify the
151
- association if it's different from the table name, using the `assocation:`
203
+ association if it's different from the table name, using the `:assocation`
152
204
  option.
153
205
 
154
206
  ### Custom seeds
@@ -168,26 +220,6 @@ class UsersSeeder < Planter::Seeder
168
220
  end
169
221
  ```
170
222
 
171
- ## Customization
172
- You can change the directories of both the seeder files and the csv files. In
173
- your `configure` block in `db/seeds.rb`, you can add the following. Note that,
174
- in both instances, the path should be relative to `Rails.root`.
175
-
176
- ```ruby
177
- require 'planter'
178
-
179
- Planter.configure do |config|
180
- config.seeders_directory = 'db/seeder_classes'
181
- config.csv_files_directory = 'db/csvs'
182
- config.seeders = %i[
183
- users
184
- addresses
185
- ]
186
- end
187
-
188
- Planter.seed
189
- ```
190
-
191
223
  ## License
192
224
  The gem is available as open source under the terms of the [MIT
193
225
  License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,33 @@
1
+ module Planter
2
+ module Generators
3
+ class InitializerGenerator < Rails::Generators::Base
4
+ desc 'Genrates an initializer for Planter at config/initializers/planter.rb'
5
+
6
+ def create_initializer_file
7
+ create_file 'config/initializers/planter.rb', <<~EOF
8
+ require 'planter'
9
+
10
+ Planter.configure do |config|
11
+ ##
12
+ # The list of seeders. These files are stored in the
13
+ # config.seeders_directory, which can be changed below. When a new
14
+ # seeder is generated, it will be appended to the bottom of this
15
+ # list. If the order is incorrect, you'll need to adjust it.
16
+ # Just be sure to keep the ending bracket on its own line, or the
17
+ # generator won't know where to put new elements.
18
+ config.seeders = %i[
19
+ ]
20
+
21
+ ##
22
+ # The directory where the seeder files are kept.
23
+ # config.seeders_directory = 'db/seeds'
24
+
25
+ ##
26
+ # The directory where CSVs are kept.
27
+ # config.csv_files_directory = 'db/seed_files'
28
+ end
29
+ EOF
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ module Planter
2
+ module Generators
3
+ class SeederGenerator < Rails::Generators::Base
4
+ argument :seeder, required: true
5
+
6
+ desc "This generator creates a seeder file at #{::Planter.config.seeders_directory}"
7
+
8
+ def generate_seeders
9
+ seeder == 'ALL' ? tables.each { |t| generate(t) } : generate(seeder)
10
+ end
11
+
12
+ private
13
+
14
+ def generate(seeder)
15
+ empty_directory ::Planter.config.seeders_directory
16
+
17
+ create_file "#{::Planter.config.seeders_directory}/#{seeder}_seeder.rb", <<~EOF
18
+ class #{seeder.camelize}Seeder < Planter::Seeder
19
+ # TODO: Choose a seeding_method. For example:
20
+ # seeding_method :csv
21
+
22
+ # For now, we overload the seed method so no exception will be raised.
23
+ def seed
24
+ end
25
+ end
26
+ EOF
27
+
28
+ inject_into_file 'config/initializers/planter.rb',
29
+ " #{seeder}\n",
30
+ before: /^\s*\]\s*$/
31
+ end
32
+
33
+ def tables
34
+ @tables ||= ActiveRecord::Base.connection.tables.reject do |table|
35
+ %w[ar_internal_metadata schema_migrations].include?(table)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,5 +2,13 @@
2
2
 
3
3
  module Planter
4
4
  class Railtie < ::Rails::Railtie # :nodoc:
5
+ rake_tasks do
6
+ load File.join(
7
+ File.expand_path(File.dirname(__FILE__)),
8
+ '..',
9
+ 'tasks',
10
+ 'planter_tasks.rake'
11
+ )
12
+ end
5
13
  end
6
14
  end
@@ -2,21 +2,101 @@
2
2
 
3
3
  module Planter
4
4
  ##
5
- # The class your seeder files should inherit from.
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.
12
+ #
13
+ # The most basic way to seed is to have a CSV file with the same name as the
14
+ # table in +db/seed_files/+. So, +users.csv+. This CSV should have the
15
+ # table's column names as header. To seed using this method, your class
16
+ # should look like the following. Note that +:csv_name+ and +:model+ are only
17
+ # required if your seeder or csv are named differently than the table being
18
+ # seeded. The directory where the seed files are kept can be changed via an
19
+ # initializer.
20
+ # # db/seeds/users_seeder.rb
21
+ # require 'planter'
22
+ # class UsersSeeder < Planter::Seeder
23
+ # seeding_method :csv, csv_name: :users, model: 'User'
24
+ # end
25
+ #
26
+ # Another way to seed is to create records from a data array. To do this,
27
+ # your class must implement a +data+ attribute or method, which is an array
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
30
+ # constructor. If if you want your data to be different for each new record
31
+ # (via Faker, +Array#sample+, etc.), you'll probably want to supply a method
32
+ # called data that returns an array of new data each time.
33
+ # require 'planter'
34
+ # class UsersSeeder < Planter::Seeder
35
+ # seeding_method :data_array
36
+ # def data
37
+ # [{foo: 'bar', baz: 'bar'}]
38
+ # end
39
+ # end
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+.
47
+ # require 'planter'
48
+ # class UsersSeeder < Planter::Seeder
49
+ # seeding_method :data_array, parent_model: 'Person', association: :users
50
+ # def data
51
+ # [{foo: 'bar', baz: 'bar'}]
52
+ # end
53
+ # end
54
+ #
55
+ # You can also set +number_of_records+ to determine how many times each
56
+ # 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.
60
+ # require 'planter'
61
+ # class UsersSeeder < Planter::Seeder
62
+ # seeding_method :data_array, number_of_records: 5
63
+ # def data
64
+ # [{foo: 'bar', baz: 'bar'}]
65
+ # end
66
+ # end
67
+ #
68
+ # By default, all fields are used to look up the record. If it already
69
+ # exists, it is not re-created. If you have specific fields that a record
70
+ # should be looked-up by, you can pass the +unique_columns+ option. This will
71
+ # attempt to look up the record by those fields only, and if one doesn't
72
+ # exist, one will be created with the rest of the attributes. An example of
73
+ # when this would be useful is with Devise; you can't pass +password+ in the
74
+ # create method, so specifying +unique_columns+ on everything except
75
+ # +password+ allows it to be passed as an attribute to the +first_or_create+
76
+ # call.
77
+ # require 'planter'
78
+ # class UsersSeeder < Planter::Seeder
79
+ # seeding_method :data_array, unique_columns: %i[username email]
80
+ # def data
81
+ # [{username: 'foo', email: 'bar', password: 'Example'}]
82
+ # end
83
+ # end
84
+ #
85
+ # If you need to seed a different way, put your own custom +seed+ method in
86
+ # your seeder class and do whatever needs to be done.
6
87
  class Seeder
7
88
  ##
8
89
  # The allowed seeding methods.
9
90
  #
10
91
  # @return [Array]
11
- SEEDING_METHODS = %i[standard_csv data_array].freeze
92
+ SEEDING_METHODS = %i[csv data_array].freeze
12
93
 
13
94
  ##
14
95
  # Array of hashes used to create records. Your class must set this
15
- # attribute when using +data_hash+ seeding method, although it's probably
96
+ # attribute when using +data_array+ seeding method, although it's probably
16
97
  # more likely that you'll want to define a method that returns a new set of
17
- # data each time (via +Faker+, +Array#sample+, etc.). When using
18
- # +standard_csv+, +data+ will be set to the data within the csv. You can
19
- # override this.
98
+ # data each time (via +Faker+, +Array#sample+, etc.). When using +csv+,
99
+ # +data+ will be set to the data within the csv. You can override this.
20
100
  #
21
101
  # @return [Array]
22
102
  attr_reader :data
@@ -28,40 +108,60 @@ module Planter
28
108
  #
29
109
  # @param [Symbol] seeding_method
30
110
  #
31
- # @param [Hash] options
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
32
122
  #
33
123
  # @example
34
124
  # require 'planter'
35
125
  # class UsersSeeder < Planter::Seeder
36
- # seeding_method :data_array,
126
+ # seeding_method :csv,
127
+ # number_of_records: 2,
37
128
  # model: 'User'
38
129
  # parent_model: 'Person',
39
130
  # association: :users,
40
- # number_of_records: 2
131
+ # csv_name: :awesome_users,
132
+ # unique_columns %i[username email]
41
133
  # end
42
- def self.seeding_method(method, **options)
134
+ def self.seeding_method(
135
+ method,
136
+ number_of_records: 1,
137
+ model: to_s.delete_suffix('Seeder').singularize,
138
+ parent_model: nil,
139
+ association: nil,
140
+ csv_name: nil,
141
+ erb_trim_mode: nil,
142
+ unique_columns: nil
143
+ )
43
144
  if !SEEDING_METHODS.include?(method.intern)
44
145
  raise ArgumentError, "Method must be one of #{SEEDING_METHODS.join(', ')}"
45
- elsif options[:association] && !options[:parent_model]
146
+ elsif association && !parent_model
46
147
  raise ArgumentError, "Must specify :parent_model with :association"
47
148
  end
48
149
 
49
150
  @seeding_method = method
50
- @number_of_records = options.fetch(:number_of_records, 1)
51
- @model = options.fetch(:model, to_s.delete_suffix('Seeder').singularize)
52
- @parent_model = options[:parent_model]
53
- @association = @parent_model && options.fetch(:association) do
54
- determine_association(options)
55
- end
56
- return unless @seeding_method == :standard_csv
57
-
58
- @csv_file = options.fetch(:csv_file, Rails.root.join(
59
- Planter.config.csv_files_directory,
60
- "#{to_s.delete_suffix('Seeder').underscore}.csv"
61
- ).to_s)
151
+ @number_of_records = number_of_records
152
+ @model = model
153
+ @parent_model = parent_model
154
+ @association = @parent_model && (association || determine_association)
155
+ @csv_file = determine_csv_filename(csv_name) if @seeding_method == :csv
156
+ @erb_trim_mode = erb_trim_mode
157
+ @unique_columns =
158
+ case unique_columns
159
+ when String, Symbol then [unique_columns.intern]
160
+ when Array then unique_columns.map(&:intern)
161
+ end
62
162
  end
63
163
 
64
- def self.determine_association(options) # :nodoc:
164
+ def self.determine_association # :nodoc:
65
165
  associations =
66
166
  @parent_model.constantize.reflect_on_all_associations.map(&:name)
67
167
  table = to_s.delete_suffix('Seeder').underscore.split('/').last
@@ -70,10 +170,23 @@ module Planter
70
170
  return t if associations.include?(t)
71
171
  end
72
172
 
73
- raise ArgumentError, 'Could not determine association name'
173
+ raise ArgumentError, "Couldn't determine association name"
74
174
  end
75
175
  private_class_method :determine_association
76
176
 
177
+ def self.determine_csv_filename(csv_name) # :nodoc:
178
+ file = (
179
+ csv_name || "#{to_s.delete_suffix('Seeder').underscore}"
180
+ ).to_s + '.csv'
181
+ [file, "#{file}.erb"].each do |f|
182
+ fname = Rails.root.join(Planter.config.csv_files_directory, f).to_s
183
+ return fname if ::File.file?(fname)
184
+ end
185
+
186
+ raise ArgumentError, "Couldn't find csv for #{@model}"
187
+ end
188
+ private_class_method :determine_csv_filename
189
+
77
190
  ##
78
191
  # The default seed method. To use this method, your class must provide a
79
192
  # valid +seeding_method+, and not implement its own +seed+ method.
@@ -140,14 +253,31 @@ module Planter
140
253
  @csv_file ||= self.class.instance_variable_get('@csv_file')
141
254
  end
142
255
 
256
+ ##
257
+ # When creating a record, the fields that will be used to look up the
258
+ # record. If it already exists, a new one will not be created.
259
+ #
260
+ # @return [Array]
261
+ def unique_columns
262
+ @unique_columns ||= self.class.instance_variable_get('@unique_columns')
263
+ end
264
+
265
+ ##
266
+ # Trim mode for ERB when parsing CSVs.
267
+ #
268
+ # @return [String, nil]
269
+ def erb_trim_mode
270
+ @erb_trim_mode ||= self.class.instance_variable_get('@erb_trim_mode')
271
+ end
272
+
143
273
  ##
144
274
  # Creates records from the +data+ attribute.
145
275
  def create_records
146
276
  data.each do |rec|
147
277
  number_of_records.times do
148
- model.constantize.where(
149
- rec.transform_values { |value| value == 'NULL' ? nil : value }
150
- ).first_or_create!
278
+ rec.transform_values { |value| value == 'NULL' ? nil : value }
279
+ unique, attrs = split_record(rec)
280
+ model.constantize.where(unique).first_or_create!(attrs)
151
281
  end
152
282
  end
153
283
  end
@@ -165,31 +295,52 @@ module Planter
165
295
 
166
296
  private
167
297
 
168
- def create_method
298
+ def create_method # :nodoc:
169
299
  parent_model.constantize.reflect_on_association(
170
300
  association
171
301
  ).macro.to_s.include?('many') ? :create_has_many : :create_has_one
172
302
  end
173
303
 
174
- def create_has_many(assoc_rec, association, rec)
175
- assoc_rec.public_send(association).where(rec).first_or_create!
304
+ def create_has_many(assoc_rec, association, rec) # :nodoc:
305
+ unique, attrs = split_record(rec)
306
+ assoc_rec.public_send(association).where(unique).first_or_create!(attrs)
176
307
  end
177
308
 
178
- def create_has_one(assoc_rec, association, rec)
179
- assoc_rec.public_send("create_#{association}", rec)
309
+ def create_has_one(assoc_rec, association, rec) # :nodoc:
310
+ if assoc_rec.public_send(association)
311
+ assoc_rec.public_send(association).update_attributes(rec)
312
+ else
313
+ assoc_rec.public_send("create_#{association}", rec)
314
+ end
180
315
  end
181
316
 
182
317
  def validate_attributes # :nodoc:
183
318
  case seeding_method.intern
184
- when :standard_csv
185
- raise "#{csv_file} does not exist" unless ::File.file?(csv_file)
319
+ when :csv
320
+ contents = ::File.read(csv_file)
321
+ if csv_file.end_with?('.erb')
322
+ contents = ERB.new(contents, trim_mode: erb_trim_mode).result(binding)
323
+ end
186
324
 
187
- @data ||= ::CSV.table(csv_file).map(&:to_hash)
325
+ @data ||= ::CSV.parse(
326
+ contents, headers: true, header_converters: :symbol
327
+ ).map(&:to_hash)
188
328
  when :data_array
189
329
  raise "Must define '@data'" if public_send(:data).nil?
190
330
  else
191
331
  raise("Must set 'seeding_method'")
192
332
  end
193
333
  end
334
+
335
+ def split_record(rec) # :nodoc:
336
+ u = {}
337
+ model.constantize.reflect_on_all_associations.map(&:name).each do |a|
338
+ u[a] = rec.delete(a) if rec.key?(a)
339
+ end
340
+ return [u, rec] unless unique_columns
341
+
342
+ unique_columns.each { |c, h| h[c] = rec.delete(c) }
343
+ [u, rec]
344
+ end
194
345
  end
195
346
  end
@@ -21,7 +21,7 @@ module Planter
21
21
  # Patch version.
22
22
  #
23
23
  # @return [Integer]
24
- PATCH = 6
24
+ PATCH = 10
25
25
 
26
26
  ##
27
27
  # Version as +[MAJOR, MINOR, PATCH]+
data/lib/planter.rb CHANGED
@@ -1,74 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'csv'
4
+ require 'erb'
4
5
  require 'planter/version'
5
6
  require 'planter/railtie'
6
7
  require 'planter/config'
7
8
  require 'planter/seeder'
8
9
 
9
10
  ##
10
- # Class that seeders should inherit from. Seeder files should be in +db/seeds+,
11
- # and named +TABLE_seeder.rb+, where +TABLE+ is the name of the table being
12
- # seeded (I.E. +users_seeder.rb+). The seeder's class name should be the same
13
- # as the file name, but camelized. So, +UsersSeeder+. The directory where the
14
- # seeder files are located can be changed via an initializer.
11
+ # The main module for the plugin. It nicely wraps the +Planter::Config+ class
12
+ # so that you can customize the plugin via an initializer or in the
13
+ # +db/seeds.rb+ file. This is how you'll specify your list of seeders to use,
14
+ # along with customizing the +seeders_directory+ and +csv_files_directory+.
15
15
  #
16
- # The most basic way to seed is to have a CSV file with the same name as the
17
- # table in +db/seed_files/+. So, +users.csv+. This CSV should have the table's
18
- # column names as header. To seed using this method, your class should look
19
- # like the following. Note that +:csv_file+ is not required; it defaults to the
20
- # table name with a +csv+ file extension. The directory where the seed files
21
- # are kept can be changed via an initializer.
22
- # # db/seeds/users_seeder.rb
23
- # require 'planter'
24
- # class UsersSeeder < Planter::Seeder
25
- # seeding_method :standard_csv, csv_file: '/home/me/users.csv'
16
+ # Planter.configure do |config|
17
+ # config.seeders = %i[users]
18
+ # config.seeders_directory = 'db/seeds'
19
+ # config.csv_files_directory = 'db/seed_files'
26
20
  # end
27
21
  #
28
- # Another way to seed is to create records from a data array. To do this, your
29
- # class must implement a +data+ attribute or method, which is an array of
30
- # hashes. Note that this class already provides the +attr_reader+ for this
31
- # attribute, so the most you have to do it create instance variables in your
32
- # constructor. If if you want your data to be different for each new record
33
- # (via Faker, +Array#sample+, etc.), you'll probably want to supply a method
34
- # called data that returns an array of new data each time.
35
- # require 'planter'
36
- # class UsersSeeder < Planter::Seeder
37
- # seeding_method :data_array
38
- # def data
39
- # [{foo: 'bar', baz: 'bar'}]
40
- # end
41
- # end
42
- #
43
- # In both of the above methods, you can specify +parent_model+ and
44
- # +association+. If specified, records will be created via that parent model's
45
- # association. If +association+ is not provided, it will be assumed to be the
46
- # model name, pluralized and snake-cased (implying a +has_many+ relationship).
47
- # For example, if we're seeding the users table, and the model is +User+, the
48
- # association will default to +users+.
49
- # require 'planter'
50
- # class UsersSeeder < Planter::Seeder
51
- # seeding_method :data_array, parent_model: 'Person', association: :users
52
- # def data
53
- # [{foo: 'bar', baz: 'bar'}]
54
- # end
55
- # end
22
+ # To then seed your application, simply call the +seed+ method from your
23
+ # +db/seeds.rb+ file (or wherever you need to call it from).
56
24
  #
57
- # You can also set +number_of_records+ to determine how many times each record
58
- # in the +data+ array will get created. The default is 1. Note that if this
59
- # attribute is set alongside +parent_model+ and +association+,
60
- # +number_of_records+ will be how many records will be created for each record
61
- # in the parent table.
62
- # require 'planter'
63
- # class UsersSeeder < Planter::Seeder
64
- # seeding_method :data_array, number_of_records: 5
65
- # def data
66
- # [{foo: 'bar', baz: 'bar'}]
67
- # end
68
- # end
69
- #
70
- # If you need to seed a different way, put your own custom +seed+ method in
71
- # your seeder class and do whatever needs to be done.
25
+ # Planter.seed
72
26
  module Planter
73
27
  ##
74
28
  # The seeder configuration.
@@ -89,13 +43,14 @@ module Planter
89
43
  ##
90
44
  # Quick way of configuring the directories via an initializer.
91
45
  #
92
- # @return [self]
46
+ # @return [Planter::Config]
93
47
  #
94
48
  # @example
95
- # Planter.configure do |app_seeder|
96
- # app_seeder.seeders = %i[users]
97
- # app_seeder.seeders_directory = 'db/seeds'
98
- # app_seeder.csv_files_directory = 'db/seed_files'
49
+ # require 'planter'
50
+ # Planter.configure do |config|
51
+ # config.seeders = %i[users]
52
+ # config.seeders_directory = 'db/seeds'
53
+ # config.csv_files_directory = 'db/seed_files'
99
54
  # end
100
55
  def self.configure
101
56
  config.tap { |c| yield c }
@@ -103,20 +58,21 @@ module Planter
103
58
 
104
59
  ##
105
60
  # This is the method to call from your +db/seeds.rb+. It callse the seeders
106
- # listed in +Planter.config.seeders+. To call specific seeders at
107
- # runtime, you can set the +SEEDERS+ environmental variable to a
108
- # comma-separated list of seeders.
61
+ # listed in +Planter.config.seeders+. To call specific seeders at runtime,
62
+ # you can set the +SEEDERS+ environmental variable to a comma-separated list
63
+ # of seeders, like +rails db:seed SEEDERS=users,accounts+.
109
64
  #
110
65
  # @example
111
- # rails db:seed SEEDERS=users,accounts
66
+ # # db/seeds.rb, assuming your +configure+ block is in an initializer.
67
+ # Planter.seed
112
68
  def self.seed
113
69
  seeders = ENV['SEEDERS']&.split(',') || config.seeders&.map(&:to_s)
114
- raise RuntimeError, 'No seeders specified; nothing to do' unless seeders&.any?
70
+ raise RuntimeError, 'No seeders specified' unless seeders&.any?
115
71
 
116
- seeders.each do |table|
117
- require Rails.root.join(config.seeders_directory, "#{table}_seeder.rb").to_s
118
- puts "Seeding #{table}" unless config.quiet
119
- "#{table.camelize}Seeder".constantize.new.seed
72
+ seeders.each do |s|
73
+ require Rails.root.join(config.seeders_directory, "#{s}_seeder.rb").to_s
74
+ puts "Seeding #{s}" unless config.quiet
75
+ "#{s.camelize}Seeder".constantize.new.seed
120
76
  end
121
77
  end
122
78
  end
@@ -1,4 +1,13 @@
1
- # desc "Explaining what the task does"
2
- # task :planter do
3
- # # Task goes here
4
- # end
1
+ require 'planter'
2
+
3
+ namespace :planter do
4
+ desc 'Seed application. Use this to keep planter separate from db:seed'
5
+ task seed: :environment do
6
+ Planter.configure do |config|
7
+ # NOTE: the seed method already looks for ENV['SEEDERS']
8
+ ENV['SEEDERS_DIRECTORY'] && config.seeders_directory = ENV['SEEDERS_DIRECTORY']
9
+ ENV['CSV_FILES_DIRECTORY'] && config.csv_files_directory = ENV['CSV_FILES_DIRECTORY']
10
+ end
11
+ Planter.seed
12
+ end
13
+ end
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.6
4
+ version: 0.0.10
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-05-23 00:00:00.000000000 Z
11
+ date: 2021-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -41,6 +41,8 @@ files:
41
41
  - LICENSE
42
42
  - README.md
43
43
  - Rakefile
44
+ - lib/generators/planter/initializer_generator.rb
45
+ - lib/generators/planter/seeder_generator.rb
44
46
  - lib/planter.rb
45
47
  - lib/planter/config.rb
46
48
  - lib/planter/railtie.rb
@@ -70,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
72
  - !ruby/object:Gem::Version
71
73
  version: '0'
72
74
  requirements: []
73
- rubygems_version: 3.2.3
75
+ rubygems_version: 3.2.22
74
76
  signing_key:
75
77
  specification_version: 4
76
78
  summary: Framework for seeding rails applications.