planter 0.0.6 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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.