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 +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.
|