planter 0.0.2 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fevanthegrayt%2Fplanter%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/evanthegrayt/planter/goto?ref=master)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/planter.svg)](https://badge.fury.io/rb/planter)
|
4
|
+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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: []
|