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 +4 -4
- data/README.md +73 -41
- data/lib/generators/planter/initializer_generator.rb +33 -0
- data/lib/generators/planter/seeder_generator.rb +40 -0
- data/lib/planter/railtie.rb +8 -0
- data/lib/planter/seeder.rb +187 -36
- data/lib/planter/version.rb +1 -1
- data/lib/planter.rb +28 -72
- data/lib/tasks/planter_tasks.rake +13 -4
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 259e8d195e480b9b5f16d67ee729d822f9c67131650997d2c95958f1356d76ab
|
4
|
+
data.tar.gz: f2722663f8f1628268208dd0e35da17507a62b339e0dc45004ba16c59f0879b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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
|
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,
|
45
|
-
|
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
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
`
|
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 :
|
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
|
90
|
-
option. Note that the value
|
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 :
|
134
|
+
seeding_method :csv, csv_name: :people
|
95
135
|
end
|
96
136
|
```
|
97
137
|
|
98
|
-
|
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
|
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
|
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 `
|
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
|
data/lib/planter/railtie.rb
CHANGED
data/lib/planter/seeder.rb
CHANGED
@@ -2,21 +2,101 @@
|
|
2
2
|
|
3
3
|
module Planter
|
4
4
|
##
|
5
|
-
#
|
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[
|
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 +
|
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
|
-
# +
|
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
|
-
# @
|
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 :
|
126
|
+
# seeding_method :csv,
|
127
|
+
# number_of_records: 2,
|
37
128
|
# model: 'User'
|
38
129
|
# parent_model: 'Person',
|
39
130
|
# association: :users,
|
40
|
-
#
|
131
|
+
# csv_name: :awesome_users,
|
132
|
+
# unique_columns %i[username email]
|
41
133
|
# end
|
42
|
-
def self.seeding_method(
|
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
|
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 =
|
51
|
-
@model =
|
52
|
-
@parent_model =
|
53
|
-
@association = @parent_model &&
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
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, '
|
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
|
-
|
149
|
-
|
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
|
-
|
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(
|
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 :
|
185
|
-
|
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.
|
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
|
data/lib/planter/version.rb
CHANGED
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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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
|
-
#
|
29
|
-
#
|
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
|
-
#
|
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 [
|
46
|
+
# @return [Planter::Config]
|
93
47
|
#
|
94
48
|
# @example
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
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
|
-
#
|
108
|
-
#
|
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
|
-
#
|
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
|
70
|
+
raise RuntimeError, 'No seeders specified' unless seeders&.any?
|
115
71
|
|
116
|
-
seeders.each do |
|
117
|
-
require Rails.root.join(config.seeders_directory, "#{
|
118
|
-
puts "Seeding #{
|
119
|
-
"#{
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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.
|
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-
|
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.
|
75
|
+
rubygems_version: 3.2.22
|
74
76
|
signing_key:
|
75
77
|
specification_version: 4
|
76
78
|
summary: Framework for seeding rails applications.
|