planter 0.0.2 → 0.0.9
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 +77 -43
- data/Rakefile +1 -1
- data/lib/generators/planter/initializer_generator.rb +33 -0
- data/lib/generators/planter/seeder_generator.rb +40 -0
- data/lib/planter.rb +30 -74
- data/lib/planter/config.rb +5 -5
- data/lib/planter/railtie.rb +8 -0
- data/lib/planter/seeder.rb +172 -36
- data/lib/planter/version.rb +48 -1
- data/lib/tasks/planter_tasks.rake +13 -4
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61c2952a2b44b89c48245554cd6be4b19598891aa958b3f2f0dd3e3d2548a079
|
4
|
+
data.tar.gz: 91e099c652ce4bfb0bf87320f9efa3d1e0e0826ae031e17e967563beb8561f19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d70a187f4064ab1081a5a4b3df180f4ced5436601151029fca74b985cc0658e0422b56f338693b08a885a5dfc7d241357ef86144373825d8553117c3e7f6f30e
|
7
|
+
data.tar.gz: 923979e971e3cbc573a9eaffbc39481d8fc8a4930894de1ea8d8f4ef79c3f6d7bbaba0ab111d0099ee67298b336373a0836802126c5d5bba4c237eb41ab0c5d8
|
data/README.md
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
# Planter
|
2
|
+
[](https://actions-badge.atrox.dev/evanthegrayt/planter/goto?ref=master)
|
3
|
+
[](https://badge.fury.io/rb/planter)
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
5
|
+
|
2
6
|
> Pre-release version! Anything is subject to change in the near future!
|
3
7
|
|
4
8
|
Seeds for Rails applications can get complicated fast, and Rails doesn't provide
|
5
9
|
much for assisting with this process. This plugin seeks to rectify that by
|
6
|
-
providing easy ways to seed specific tables
|
7
|
-
db:seed` task.
|
10
|
+
providing easy ways to seed specific tables.
|
8
11
|
|
9
12
|
Features include:
|
10
13
|
|
11
14
|
- Seed tables from CSV files, an array of hashes, or custom methods.
|
12
|
-
-
|
15
|
+
- Call specific seeders with `rails planter:seed SEEDERS=users,addresses`.
|
13
16
|
- Control the number of records being created.
|
14
17
|
- Seed associations.
|
15
18
|
|
19
|
+
You can view the documentation [here](https://evanthegrayt.github.io/planter/).
|
20
|
+
|
16
21
|
## Installation
|
17
22
|
Add this line to your application's Gemfile:
|
18
23
|
|
@@ -35,30 +40,69 @@ $ gem install planter
|
|
35
40
|
## Usage
|
36
41
|
Let's assume you'd like to seed your `users` table.
|
37
42
|
|
38
|
-
To get started,
|
39
|
-
|
40
|
-
the correct order to successfully seed the tables when considering associations.
|
43
|
+
To get started, run `rails generate planter:initializer`, which will create
|
44
|
+
`config/initializers/planter.rb` with the following contents.
|
41
45
|
|
42
46
|
```ruby
|
43
47
|
require 'planter'
|
44
48
|
|
45
49
|
Planter.configure do |config|
|
46
|
-
|
50
|
+
##
|
51
|
+
# The list of seeders. These files are stored in the
|
52
|
+
# config.seeders_directory, which can be changed below. When a new
|
53
|
+
# seeder is generated, it will be appended to the bottom of this
|
54
|
+
# list. If the order is incorrect, you'll need to adjust the it.
|
55
|
+
# Just be sure to keep the ending bracket on its own line, or the
|
56
|
+
# generator won't know where to put new elements.
|
57
|
+
config.seeders = %i[
|
58
|
+
]
|
59
|
+
|
60
|
+
##
|
61
|
+
# The directory where the seeder files are kept.
|
62
|
+
# config.seeders_directory = 'db/seeds'
|
63
|
+
|
64
|
+
##
|
65
|
+
# The directory where CSVs are kept.
|
66
|
+
# config.csv_files_directory = 'db/seed_files'
|
47
67
|
end
|
68
|
+
```
|
69
|
+
|
70
|
+
By default, a `planter:seed` task is provided for seeding your application. This
|
71
|
+
allows you to use `db:seed` for other purposes. If you want Planter to hook
|
72
|
+
into the existing `db:seed` task, simply add the following to `db/seeds.rb`.
|
48
73
|
|
74
|
+
```ruby
|
49
75
|
Planter.seed
|
50
76
|
```
|
51
77
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
`
|
78
|
+
To create a users seeder, run `rails generate planter:seeder users`. Usually,
|
79
|
+
seeders seed a specific table, so it's recommended to name your seeders after
|
80
|
+
the table. If you don't, you'll need to manually specify a few things. More on
|
81
|
+
that later. This will create a file named `db/seeds/users_seeder.rb` (the
|
82
|
+
directory will be created if it doesn't exist) with the following contents.
|
56
83
|
|
57
84
|
```ruby
|
58
85
|
class UsersSeeder < Planter::Seeder
|
86
|
+
# TODO: Choose a seeding_method. For example:
|
87
|
+
# seeding_method :csv
|
88
|
+
|
89
|
+
# For now, we overload the seed method so no exception will be raised.
|
90
|
+
def seed
|
91
|
+
end
|
59
92
|
end
|
60
93
|
```
|
61
94
|
|
95
|
+
This also adds `users` to the `config.seeders` array in our initializer. A few
|
96
|
+
things to note.
|
97
|
+
|
98
|
+
- The seeder will always be appended at the end of the array. If this is not the
|
99
|
+
correct order, you'll need to adjust the array manually.
|
100
|
+
- When adjusting the array, always keep the closing bracket on its own line, or
|
101
|
+
the generator won't know where to put the new seeders.
|
102
|
+
|
103
|
+
If you want to generate a seeder for every table currently in your database, run
|
104
|
+
`rails generate planter:seeder ALL`.
|
105
|
+
|
62
106
|
You then need to choose a seeding method, of which there are currently three.
|
63
107
|
|
64
108
|
### Seeding from CSV
|
@@ -66,7 +110,7 @@ To seed from CSV, you simply need to add the following to your seeder class.
|
|
66
110
|
|
67
111
|
```ruby
|
68
112
|
class UsersSeeder < Planter::Seeder
|
69
|
-
seeding_method :
|
113
|
+
seeding_method :csv
|
70
114
|
end
|
71
115
|
```
|
72
116
|
|
@@ -80,16 +124,28 @@ test1@example.com,test1
|
|
80
124
|
test2@example.com,test2
|
81
125
|
```
|
82
126
|
|
83
|
-
If the CSV
|
84
|
-
option. Note that the value
|
127
|
+
If the CSV file is named differently than the seeder, you can specify the
|
128
|
+
`:csv_name` option. Note that the value should not include the file extension.
|
85
129
|
|
86
130
|
```ruby
|
87
131
|
class UsersSeeder < Planter::Seeder
|
88
|
-
seeding_method :
|
132
|
+
seeding_method :csv, csv_name: :people
|
89
133
|
end
|
90
134
|
```
|
91
135
|
|
92
|
-
|
136
|
+
`ERB` can be used in the CSV files if you name it with `.erb` at the end of the
|
137
|
+
file name. For example, `users.csv.erb`. Note that lines starting with `<%` and
|
138
|
+
ending with `%>` will not be considered rows, so you can use `ERB` rows to set
|
139
|
+
values. For example:
|
140
|
+
|
141
|
+
```csv.erb
|
142
|
+
email,login_attempts
|
143
|
+
<% count = 1 %>
|
144
|
+
test2@example.com,<%= count += 1 %>
|
145
|
+
test2@example.com,<%= count += 1 %>
|
146
|
+
```
|
147
|
+
|
148
|
+
Running `rails planter:seed` will now seed your `users` table.
|
93
149
|
|
94
150
|
## Seeding from a data array
|
95
151
|
If you need dynamic seeds, you can add something similar to the following to
|
@@ -97,7 +153,7 @@ your seeder class. In this example, we'll use
|
|
97
153
|
[faker](https://github.com/faker-ruby/faker).
|
98
154
|
|
99
155
|
```ruby
|
100
|
-
require 'faker' # You
|
156
|
+
require 'faker' # You could just require this in `db/seeds.rb`.
|
101
157
|
|
102
158
|
class UsersSeeder < Planter::Seeder
|
103
159
|
seeding_method :data_array, number_of_records: 10
|
@@ -117,7 +173,7 @@ ten elements to create ten records. It's also worth noting that setting an
|
|
117
173
|
instance variable called `@data` from an `initialize` method would also work, as
|
118
174
|
the `Planter::Seeder` parent class automatically provides `attr_reader :data`.
|
119
175
|
|
120
|
-
Running `rails
|
176
|
+
Running `rails planter:seed` should now seed your `users` table.
|
121
177
|
|
122
178
|
You can also seed children records for every existing record of a parent model.
|
123
179
|
For example, to seed an address for every user, you'd need to create an
|
@@ -142,10 +198,8 @@ end
|
|
142
198
|
|
143
199
|
Note that specifying `number_of_records` in this instance will create that many
|
144
200
|
records *for each record of the parent model*. You can also specify the
|
145
|
-
association if it's different from the table name, using the `
|
146
|
-
option.
|
147
|
-
association is plural by default. Any help with making this more heuristically
|
148
|
-
complete would be welcome.
|
201
|
+
association if it's different from the table name, using the `:assocation`
|
202
|
+
option.
|
149
203
|
|
150
204
|
### Custom seeds
|
151
205
|
To write your own custom seeds, just overload the `seed` method and do whatever
|
@@ -154,7 +208,7 @@ you need to do.
|
|
154
208
|
```ruby
|
155
209
|
class UsersSeeder < Planter::Seeder
|
156
210
|
USERS = {
|
157
|
-
'test1@example.com' => { username: 'John Smith' }
|
211
|
+
'test1@example.com' => { username: 'John Smith' },
|
158
212
|
'test2@example.com' => { username: 'Jane Smith' }
|
159
213
|
}
|
160
214
|
|
@@ -164,26 +218,6 @@ class UsersSeeder < Planter::Seeder
|
|
164
218
|
end
|
165
219
|
```
|
166
220
|
|
167
|
-
## Customization
|
168
|
-
You can change the directories of both the seeder files and the csv files. In
|
169
|
-
your `configure` block in `db/seeds.rb`, you can add the following. Note that,
|
170
|
-
in both instances, the path should be relative to `Rails.root`.
|
171
|
-
|
172
|
-
```ruby
|
173
|
-
require 'planter'
|
174
|
-
|
175
|
-
Planter.configure do |config|
|
176
|
-
config.seeders_directory = 'db/seeder_classes'
|
177
|
-
config.csv_files_directory = 'db/csvs'
|
178
|
-
config.tables = %i[
|
179
|
-
users
|
180
|
-
addresses
|
181
|
-
]
|
182
|
-
end
|
183
|
-
|
184
|
-
Planter.seed
|
185
|
-
```
|
186
|
-
|
187
221
|
## License
|
188
222
|
The gem is available as open source under the terms of the [MIT
|
189
223
|
License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -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 the 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.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,34 +43,36 @@ 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 }
|
102
57
|
end
|
103
58
|
|
104
59
|
##
|
105
|
-
# This is the method to call from your +db/seeds.rb+. It
|
106
|
-
# listed in +Planter.config.
|
107
|
-
#
|
108
|
-
#
|
60
|
+
# This is the method to call from your +db/seeds.rb+. It callse the 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
|
-
#
|
66
|
+
# # db/seeds.rb, assuming your +configure+ block is in an initializer.
|
67
|
+
# Planter.seed
|
112
68
|
def self.seed
|
113
|
-
|
114
|
-
raise RuntimeError, 'No
|
69
|
+
seeders = ENV['SEEDERS']&.split(',') || config.seeders&.map(&:to_s)
|
70
|
+
raise RuntimeError, 'No seeders specified' unless seeders&.any?
|
115
71
|
|
116
|
-
|
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
|
data/lib/planter/config.rb
CHANGED
@@ -5,7 +5,7 @@ module Planter
|
|
5
5
|
# Configure the application seeder.
|
6
6
|
#
|
7
7
|
# @example
|
8
|
-
# Planter.configure { |seeder| seeder.
|
8
|
+
# Planter.configure { |seeder| seeder.seeders = %i[users] }
|
9
9
|
class Config
|
10
10
|
##
|
11
11
|
# Tell the application where the seeder classes are kept. Must be a path
|
@@ -26,13 +26,13 @@ module Planter
|
|
26
26
|
attr_accessor :csv_files_directory
|
27
27
|
|
28
28
|
##
|
29
|
-
# Tell the application what
|
30
|
-
# order, and can be strings or symbols.
|
29
|
+
# Tell the application what seeders exist. Elements should be in the correct
|
30
|
+
# order to seed the tables successfully, and can be strings or symbols.
|
31
31
|
#
|
32
|
-
# @param [Array]
|
32
|
+
# @param [Array] seeders
|
33
33
|
#
|
34
34
|
# @return [Array]
|
35
|
-
attr_accessor :
|
35
|
+
attr_accessor :seeders
|
36
36
|
|
37
37
|
##
|
38
38
|
# When true, don't print output when seeding.
|
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,58 @@ 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
|
+
unique_columns: nil
|
142
|
+
)
|
43
143
|
if !SEEDING_METHODS.include?(method.intern)
|
44
144
|
raise ArgumentError, "Method must be one of #{SEEDING_METHODS.join(', ')}"
|
45
|
-
elsif
|
145
|
+
elsif association && !parent_model
|
46
146
|
raise ArgumentError, "Must specify :parent_model with :association"
|
47
147
|
end
|
48
148
|
|
49
149
|
@seeding_method = method
|
50
|
-
@number_of_records =
|
51
|
-
@model =
|
52
|
-
@parent_model =
|
53
|
-
@association = @parent_model &&
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
"#{to_s.delete_suffix('Seeder').underscore}.csv"
|
61
|
-
).to_s)
|
150
|
+
@number_of_records = number_of_records
|
151
|
+
@model = model
|
152
|
+
@parent_model = parent_model
|
153
|
+
@association = @parent_model && (association || determine_association)
|
154
|
+
@csv_file = determine_csv_filename(csv_name) if @seeding_method == :csv
|
155
|
+
@unique_columns =
|
156
|
+
case unique_columns
|
157
|
+
when String, Symbol then [unique_columns.intern]
|
158
|
+
when Array then unique_columns.map(&:intern)
|
159
|
+
end
|
62
160
|
end
|
63
161
|
|
64
|
-
def self.determine_association
|
162
|
+
def self.determine_association # :nodoc:
|
65
163
|
associations =
|
66
164
|
@parent_model.constantize.reflect_on_all_associations.map(&:name)
|
67
165
|
table = to_s.delete_suffix('Seeder').underscore.split('/').last
|
@@ -70,10 +168,23 @@ module Planter
|
|
70
168
|
return t if associations.include?(t)
|
71
169
|
end
|
72
170
|
|
73
|
-
raise ArgumentError, '
|
171
|
+
raise ArgumentError, "Couldn't determine association name"
|
74
172
|
end
|
75
173
|
private_class_method :determine_association
|
76
174
|
|
175
|
+
def self.determine_csv_filename(csv_name) # :nodoc:
|
176
|
+
file = (
|
177
|
+
csv_name || "#{to_s.delete_suffix('Seeder').underscore}"
|
178
|
+
).to_s + '.csv'
|
179
|
+
[file, "#{file}.erb"].each do |f|
|
180
|
+
fname = Rails.root.join(Planter.config.csv_files_directory, f).to_s
|
181
|
+
return fname if ::File.file?(fname)
|
182
|
+
end
|
183
|
+
|
184
|
+
raise ArgumentError, "Couldn't find csv for #{@model}"
|
185
|
+
end
|
186
|
+
private_class_method :determine_csv_filename
|
187
|
+
|
77
188
|
##
|
78
189
|
# The default seed method. To use this method, your class must provide a
|
79
190
|
# valid +seeding_method+, and not implement its own +seed+ method.
|
@@ -140,14 +251,23 @@ module Planter
|
|
140
251
|
@csv_file ||= self.class.instance_variable_get('@csv_file')
|
141
252
|
end
|
142
253
|
|
254
|
+
##
|
255
|
+
# When creating a record, the fields that will be used to look up the
|
256
|
+
# record. If it already exists, a new one will not be created.
|
257
|
+
#
|
258
|
+
# @return [Array]
|
259
|
+
def unique_columns
|
260
|
+
@unique_columns ||= self.class.instance_variable_get('@unique_columns')
|
261
|
+
end
|
262
|
+
|
143
263
|
##
|
144
264
|
# Creates records from the +data+ attribute.
|
145
265
|
def create_records
|
146
266
|
data.each do |rec|
|
147
267
|
number_of_records.times do
|
148
|
-
|
149
|
-
|
150
|
-
).first_or_create!
|
268
|
+
rec.transform_values { |value| value == 'NULL' ? nil : value }
|
269
|
+
unique, attrs = split_record(rec)
|
270
|
+
model.constantize.where(unique).first_or_create!(attrs)
|
151
271
|
end
|
152
272
|
end
|
153
273
|
end
|
@@ -165,31 +285,47 @@ module Planter
|
|
165
285
|
|
166
286
|
private
|
167
287
|
|
168
|
-
def create_method
|
288
|
+
def create_method # :nodoc:
|
169
289
|
parent_model.constantize.reflect_on_association(
|
170
290
|
association
|
171
291
|
).macro.to_s.include?('many') ? :create_has_many : :create_has_one
|
172
292
|
end
|
173
293
|
|
174
|
-
def create_has_many(assoc_rec, association, rec)
|
175
|
-
|
294
|
+
def create_has_many(assoc_rec, association, rec) # :nodoc:
|
295
|
+
unique, attrs = split_record(rec)
|
296
|
+
assoc_rec.public_send(association).where(unique).first_or_create!(attrs)
|
176
297
|
end
|
177
298
|
|
178
|
-
def create_has_one(assoc_rec, association, rec)
|
179
|
-
assoc_rec.public_send(
|
299
|
+
def create_has_one(assoc_rec, association, rec) # :nodoc:
|
300
|
+
if assoc_rec.public_send(association)
|
301
|
+
assoc_rec.public_send(association).update_attributes(rec)
|
302
|
+
else
|
303
|
+
assoc_rec.public_send("create_#{association}", rec)
|
304
|
+
end
|
180
305
|
end
|
181
306
|
|
182
307
|
def validate_attributes # :nodoc:
|
183
308
|
case seeding_method.intern
|
184
|
-
when :
|
185
|
-
|
309
|
+
when :csv
|
310
|
+
contents = ::File.read(csv_file)
|
311
|
+
if csv_file.end_with?('.erb')
|
312
|
+
contents = ERB.new(contents, trim_mode: '<>').result(binding)
|
313
|
+
end
|
186
314
|
|
187
|
-
@data ||= ::CSV.
|
315
|
+
@data ||= ::CSV.parse(
|
316
|
+
contents, headers: true, header_converters: :symbol
|
317
|
+
).map(&:to_hash)
|
188
318
|
when :data_array
|
189
319
|
raise "Must define '@data'" if public_send(:data).nil?
|
190
320
|
else
|
191
321
|
raise("Must set 'seeding_method'")
|
192
322
|
end
|
193
323
|
end
|
324
|
+
|
325
|
+
def split_record(rec) # :nodoc:
|
326
|
+
return [rec, {}] unless unique_columns
|
327
|
+
u = unique_columns.each_with_object({}) { |c, h| h[c] = rec.delete(c) }
|
328
|
+
[u, rec]
|
329
|
+
end
|
194
330
|
end
|
195
331
|
end
|
data/lib/planter/version.rb
CHANGED
@@ -1,7 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Planter
|
4
|
+
##
|
5
|
+
# Module that contains all gem version information. Follows semantic
|
6
|
+
# versioning. Read: https://semver.org/
|
7
|
+
module Version
|
8
|
+
##
|
9
|
+
# Major version.
|
10
|
+
#
|
11
|
+
# @return [Integer]
|
12
|
+
MAJOR = 0
|
13
|
+
|
14
|
+
##
|
15
|
+
# Minor version.
|
16
|
+
#
|
17
|
+
# @return [Integer]
|
18
|
+
MINOR = 0
|
19
|
+
|
20
|
+
##
|
21
|
+
# Patch version.
|
22
|
+
#
|
23
|
+
# @return [Integer]
|
24
|
+
PATCH = 9
|
25
|
+
|
26
|
+
##
|
27
|
+
# Version as +[MAJOR, MINOR, PATCH]+
|
28
|
+
#
|
29
|
+
# @return [Array]
|
30
|
+
def self.to_a
|
31
|
+
[MAJOR, MINOR, PATCH]
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Version as +MAJOR.MINOR.PATCH+
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
def self.to_s
|
39
|
+
to_a.join('.')
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Version as +{major: MAJOR, minor: MINOR, patch: PATCH}+
|
44
|
+
#
|
45
|
+
# @return [Hash]
|
46
|
+
def self.to_h
|
47
|
+
Hash[%i[major minor patch].zip(to_a)]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
4
51
|
##
|
5
52
|
# Gem version, semantic.
|
6
|
-
VERSION =
|
53
|
+
VERSION = Version.to_s.freeze
|
7
54
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Gray
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-11 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
|
@@ -54,7 +56,8 @@ metadata:
|
|
54
56
|
allowed_push_host: https://rubygems.org
|
55
57
|
homepage_uri: https://github.com/evanthegrayt/planter
|
56
58
|
source_code_uri: https://github.com/evanthegrayt/planter
|
57
|
-
|
59
|
+
documentation_uri: https://evanthegrayt.github.io/planter/
|
60
|
+
post_install_message:
|
58
61
|
rdoc_options: []
|
59
62
|
require_paths:
|
60
63
|
- lib
|
@@ -69,8 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
72
|
- !ruby/object:Gem::Version
|
70
73
|
version: '0'
|
71
74
|
requirements: []
|
72
|
-
rubygems_version: 3.2.
|
73
|
-
signing_key:
|
75
|
+
rubygems_version: 3.2.15
|
76
|
+
signing_key:
|
74
77
|
specification_version: 4
|
75
78
|
summary: Framework for seeding rails applications.
|
76
79
|
test_files: []
|