dirty_seed 0.1.5 → 0.1.6
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 +246 -12
- data/lib/dirty_seed/assigners/dirty_boolean.rb +1 -1
- data/lib/dirty_seed/assigners/dirty_date.rb +1 -1
- data/lib/dirty_seed/assigners/dirty_float.rb +3 -3
- data/lib/dirty_seed/assigners/dirty_integer.rb +4 -4
- data/lib/dirty_seed/assigners/dirty_string.rb +33 -28
- data/lib/dirty_seed/assigners/dirty_time.rb +2 -2
- data/lib/dirty_seed/assigners/fakers.yml +93 -0
- data/lib/dirty_seed/data_model.rb +15 -13
- data/lib/dirty_seed/dirty_attribute.rb +10 -4
- data/lib/dirty_seed/dirty_model.rb +4 -10
- data/lib/dirty_seed/version.rb +1 -1
- data/spec/dummy/app/models/bravo.rb +4 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +3 -0
- data/spec/dummy/log/test.log +15456 -0
- data/spec/lib/dirty_seed/assigners/dirty_integer_spec.rb +0 -2
- data/spec/lib/dirty_seed/assigners/dirty_string_spec.rb +10 -0
- data/spec/lib/dirty_seed/data_model_spec.rb +3 -3
- data/spec/lib/dirty_seed/dirty_model_spec.rb +5 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8eac8a564716c1859fb212114a00596eb82d80f335204e224feca8356d34901b
|
4
|
+
data.tar.gz: 6211c5a287f117be9078c1ad4e88f7a8a2bd4d781f8921bcff840dcc5a033a0a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dab9f9eae8cd32e59142cada451d8ba38dfec944c06726b7da82f968c39a6a3e77521879633b8496fe7086cba6acf32c626e0784aa4fe007842aae3f5c6b8373
|
7
|
+
data.tar.gz: 36504d9781746ee02493ef3a4092356eee9a93295fc5ddef9624e99a0b30d8a47fceee18593099d5a09994e33fe4d3ce9499b24c0ea07fa6a3d679eece304da3
|
data/README.md
CHANGED
@@ -2,7 +2,20 @@
|
|
2
2
|
|
3
3
|
# DirtySeed
|
4
4
|
|
5
|
-
Populate the database with records matching associations and validations
|
5
|
+
:seedling: Populate the database with records matching associations and validations in order to quickly test the application rendering.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'dirty_seed', '~> 0.1.6'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
```bash
|
17
|
+
$ bundle
|
18
|
+
```
|
6
19
|
|
7
20
|
## Usage
|
8
21
|
|
@@ -13,32 +26,253 @@ To seed dirty data, run:
|
|
13
26
|
$ rake dirty_seed:seed
|
14
27
|
```
|
15
28
|
|
16
|
-
This will
|
29
|
+
This will create records for each model inheriting from `ApplicationRecord`.
|
17
30
|
|
18
|
-
|
31
|
+
Instance that cannot be saved ar simply ignored.
|
19
32
|
|
20
|
-
|
33
|
+
For each model, the number of created created records and the recurrent errors are logged:
|
21
34
|
|
22
|
-
|
35
|
+
```bash
|
36
|
+
rake dirty_seed:seed
|
37
|
+
User
|
38
|
+
created: 15
|
39
|
+
Article
|
40
|
+
created: 9
|
41
|
+
errors: Title should match a specific regex
|
42
|
+
```
|
23
43
|
|
24
|
-
|
44
|
+
### Database population
|
45
|
+
|
46
|
+
For each model inheriting from `ApplicationRecord`, 15 records are created.
|
47
|
+
|
48
|
+
Models are sorted by their dependency to each others (through a `belongs_to` association) to ensure that some records exist before seeding an instance that requires one.
|
49
|
+
|
50
|
+
For instance, given the following models, the order of seeding will be `User`, `Article` and `Notification`:
|
25
51
|
|
26
52
|
```ruby
|
27
|
-
|
53
|
+
# app/models/article
|
54
|
+
class Article
|
55
|
+
belongs_to :user
|
56
|
+
has_many :notifications, as: :notifiable
|
57
|
+
end
|
58
|
+
|
59
|
+
class User
|
60
|
+
has_many :articles
|
61
|
+
has_many :notifications, as: :notifiable
|
62
|
+
end
|
63
|
+
|
64
|
+
class Notification
|
65
|
+
belongs_to :notifiable, polymorphic: true
|
66
|
+
end
|
28
67
|
```
|
29
68
|
|
30
|
-
|
31
|
-
|
32
|
-
|
69
|
+
### Value assignment
|
70
|
+
|
71
|
+
A value is assigned for each attribute, depending on its type.
|
72
|
+
|
73
|
+
Special types (`hstore`, `json`, `jsonb`, `array`...) are currently ignored (no value assignment).
|
74
|
+
|
75
|
+
For instance, given the following schema:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# db/schema.rb
|
79
|
+
create_table 'things' do |t|
|
80
|
+
t.binary 'a_binary'
|
81
|
+
t.boolean 'a_boolean'
|
82
|
+
t.date 'a_date'
|
83
|
+
t.datetime 'a_datetime'
|
84
|
+
t.decimal 'a_decimal'
|
85
|
+
t.integer 'an_integer'
|
86
|
+
t.float 'a_float'
|
87
|
+
t.string 'a_string'
|
88
|
+
t.text 'a_text'
|
89
|
+
t.time 'a_time'
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
...then a dirty seeded `thing` looks like:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
{
|
97
|
+
id: 1,
|
98
|
+
a_binary: '13',
|
99
|
+
a_boolean: false,
|
100
|
+
a_date: Wed, '02 Dec 2020',
|
101
|
+
a_datetime: 'Sun, 08 Nov 2020 03:01:34 UTC +00:00',
|
102
|
+
a_decimal: 19.8812490973183,
|
103
|
+
an_integer: 6,
|
104
|
+
a_float: 28.825997012616263,
|
105
|
+
a_string: 'Maxime eum ratione ab quod nihil.',
|
106
|
+
a_text: 'Autem non in est dolore.',
|
107
|
+
a_time: 'Sat, 01 Jan 2000 09:31:22 UTC +00:00'
|
108
|
+
}
|
109
|
+
```
|
110
|
+
|
111
|
+
### Ignored attributes
|
112
|
+
|
113
|
+
Some "special" attributes are not getting a value because:
|
114
|
+
- Rails assigns it (STI type...);
|
115
|
+
- or the RDBMS (PostgreSQL, SQLite...) assigns it;
|
116
|
+
- or it is used in specific cases (authentication...).
|
117
|
+
|
118
|
+
These attributes are currently:
|
119
|
+
|
120
|
+
- `id`
|
121
|
+
- `created_at`
|
122
|
+
- `updated_at`
|
123
|
+
- `type` (for STI)
|
124
|
+
- `encrypted_password` (authentication usage)
|
125
|
+
- `reset_password_token` (authentication usage)
|
126
|
+
- `reset_password_sent_at` (authentication usage)
|
127
|
+
- `remember_created_at` (authentication usage)
|
128
|
+
|
129
|
+
### Associations
|
130
|
+
|
131
|
+
For attributes related to an association, an instance matching the `belongs_to` is assigned.
|
132
|
+
|
133
|
+
Polymorphic associations work too and only an instance of a model that `has_one` or `has_many` of this association can be assigned.
|
134
|
+
|
135
|
+
For instance, given the following schema:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# schema.rb
|
139
|
+
create_table :notifications do |t|
|
140
|
+
t.references :thing
|
141
|
+
t.references :notifiable, polymorphic: true, null: false
|
142
|
+
t.references :foo, null: false, foreign_key: { to_table: :things }
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
...and the models:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
# app/models/notification.rb
|
150
|
+
class Notification < ApplicationRecord
|
151
|
+
belongs_to :notifiable, polymorphic: true
|
152
|
+
belongs_to :thing
|
153
|
+
belongs_to :bar, class_name: 'Thing', foreign_key: :foo_id
|
154
|
+
end
|
155
|
+
|
156
|
+
# app/models/user.rb
|
157
|
+
class User < ApplicationRecord
|
158
|
+
has_many :notifications, as: :notifiable
|
159
|
+
end
|
33
160
|
```
|
34
161
|
|
162
|
+
...then a dirty seeded `notification` looks like:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
{
|
166
|
+
:id => 1,
|
167
|
+
:thing_id => 42,
|
168
|
+
:notifiable_type => 'User',
|
169
|
+
:notifiable_id => 6,
|
170
|
+
:foo_id => 75
|
171
|
+
}
|
172
|
+
|
173
|
+
notification.notifiable.class # User
|
174
|
+
notification.thing.class # Thing
|
175
|
+
notification.bar.class # Thing
|
176
|
+
```
|
177
|
+
|
178
|
+
### Validations
|
179
|
+
|
180
|
+
For attributes requiring validations, assigned value is adapted.
|
181
|
+
|
182
|
+
Currently, only the following validations are treated:
|
183
|
+
|
184
|
+
- `numericality: { greater_than: x }`
|
185
|
+
- `numericality: { greater_than_or_equal_to: x }`
|
186
|
+
- `numericality: { lesser_than: x }`
|
187
|
+
- `numericality: { lesser_than_or_equal_to: x }`
|
188
|
+
- `numericality: { in: x..y }`
|
189
|
+
|
190
|
+
Much more validations will be treated soon: `uniqueness`, `absence`, `inclusion`, `exclusion`, `length`...
|
191
|
+
|
192
|
+
Custom validations are not inspected.
|
193
|
+
|
194
|
+
### Meaning detection
|
195
|
+
|
196
|
+
Some string attribute meanings are guessed by name: `email`, `first_name`, `latitude`...
|
197
|
+
|
198
|
+
Values are then built with the `faker` gem.
|
199
|
+
|
200
|
+
For instance, given the following schema:
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
# db/schema.rb
|
204
|
+
create_table "users" do |t|
|
205
|
+
t.string "first_name"
|
206
|
+
t.string "last_name"
|
207
|
+
t.string "address"
|
208
|
+
t.string "city"
|
209
|
+
t.string "country"
|
210
|
+
t.string "email"
|
211
|
+
t.string "password"
|
212
|
+
t.string "phone"
|
213
|
+
t.string "username"
|
214
|
+
# ...
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
...then a dirty seeded `user` looks like:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
{
|
222
|
+
id: 1,
|
223
|
+
first_name: 'Emory',
|
224
|
+
last_name: 'Franecki',
|
225
|
+
address: '843 Schneider Squares, Port Olenmouth, TN 12657',
|
226
|
+
city: 'Torpshire',
|
227
|
+
country: 'United Arab Emirates',
|
228
|
+
email: 'scottie_friesen@example.net',
|
229
|
+
password: 'ZkUtNtFg4L',
|
230
|
+
phone: '(814) 382-6102 x036',
|
231
|
+
username: 'ernestine_rau',
|
232
|
+
# ...
|
233
|
+
}
|
234
|
+
```
|
235
|
+
|
236
|
+
The current attribute names treated this way are:
|
237
|
+
|
238
|
+
- `address`
|
239
|
+
- `city`
|
240
|
+
- `country`
|
241
|
+
- `description`
|
242
|
+
- `email`
|
243
|
+
- `first_name`
|
244
|
+
- `firstname`
|
245
|
+
- `last_name`
|
246
|
+
- `lastname`
|
247
|
+
- `lat`
|
248
|
+
- `latitute`
|
249
|
+
- `lng`
|
250
|
+
- `locale`
|
251
|
+
- `longitude`
|
252
|
+
- `middlename`
|
253
|
+
- `middle_name`
|
254
|
+
- `password`
|
255
|
+
- `phone`
|
256
|
+
- `phone_number`
|
257
|
+
- `reference`
|
258
|
+
- `title`
|
259
|
+
- `user_name`
|
260
|
+
- `username`
|
261
|
+
- `uuid`
|
262
|
+
|
263
|
+
More meanings will be added and will extend the mechanism to other formats (e.g. an `age` integer).
|
264
|
+
|
35
265
|
## License
|
36
266
|
|
37
267
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
38
268
|
|
39
269
|
## Next features
|
40
270
|
|
271
|
+
* Add a configuration system to define how to seed: number of instances, models to skip, default values, etc.
|
41
272
|
* Assign values for more data types (json, array, etc.).
|
273
|
+
* Detect more "special" attributes to ignore.
|
42
274
|
* Integrate more validations (uniqueness, inclusion, length, absence, etc.).
|
43
|
-
*
|
44
|
-
*
|
275
|
+
* Detect more meanings and extend it to other formats.
|
276
|
+
* Use instance errors to adjust values and eventually match custom validations.
|
277
|
+
|
278
|
+
|
@@ -4,7 +4,7 @@ module DirtySeed
|
|
4
4
|
module Assigners
|
5
5
|
# Draws an Float matching validators
|
6
6
|
class DirtyFloat < DirtyAssigner
|
7
|
-
# Returns a
|
7
|
+
# Returns a value matching all validators
|
8
8
|
# @return [Float]
|
9
9
|
def value
|
10
10
|
integer + decimals
|
@@ -12,13 +12,13 @@ module DirtySeed
|
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
-
# Returns
|
15
|
+
# Returns a value matching all validators
|
16
16
|
# @return [Integer]
|
17
17
|
def integer
|
18
18
|
DirtySeed::Assigners::DirtyInteger.new(dirty_attribute, 0).value
|
19
19
|
end
|
20
20
|
|
21
|
-
# Returns a
|
21
|
+
# Returns a value between 0 and 1
|
22
22
|
# @return [Float]
|
23
23
|
def decimals
|
24
24
|
rand(0..Float(1))
|
@@ -6,7 +6,7 @@ module DirtySeed
|
|
6
6
|
class DirtyInteger < DirtyAssigner
|
7
7
|
attr_reader :min, :max
|
8
8
|
|
9
|
-
# Returns an
|
9
|
+
# Returns an value matching all validators
|
10
10
|
# @return [Integer]
|
11
11
|
def value
|
12
12
|
define_min_and_max
|
@@ -31,7 +31,7 @@ module DirtySeed
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
# Returns a random
|
34
|
+
# Returns a random value
|
35
35
|
# @return [Integer]
|
36
36
|
def random
|
37
37
|
rand(sequence..42)
|
@@ -64,7 +64,7 @@ module DirtySeed
|
|
64
64
|
@max = max_for(validator) if @max.nil? || max_for(validator) < @max
|
65
65
|
end
|
66
66
|
|
67
|
-
# Returns an
|
67
|
+
# Returns an value representing the minimal acceptable value
|
68
68
|
# @param validator [ActiveModel::Validations::EachValidator]
|
69
69
|
# @return [Integer]
|
70
70
|
def min_for(validator)
|
@@ -73,7 +73,7 @@ module DirtySeed
|
|
73
73
|
validator.options[:in]&.min
|
74
74
|
end
|
75
75
|
|
76
|
-
# Returns an
|
76
|
+
# Returns an value representing the maximal acceptable value
|
77
77
|
# @param validator [ActiveModel::Validations::EachValidator]
|
78
78
|
# @return [Integer]
|
79
79
|
def max_for(validator)
|
@@ -4,41 +4,46 @@ module DirtySeed
|
|
4
4
|
module Assigners
|
5
5
|
# Draws a String matching validators
|
6
6
|
class DirtyString < DirtyAssigner
|
7
|
-
|
8
|
-
address: -> { Faker::Address.unique.full_address },
|
9
|
-
city: -> { Faker::Address.unique.city },
|
10
|
-
country: -> { Faker::Address.unique.country },
|
11
|
-
description: -> { Faker::Lorem.unique.paragraph(sentence_count: 2, random_sentences_to_add: 4) },
|
12
|
-
email: -> { Faker::Internet.unique.email(domain: 'example') },
|
13
|
-
first_name: -> { ::Faker::Name.unique.first_name },
|
14
|
-
firstname: -> { ::Faker::Name.unique.first_name },
|
15
|
-
last_name: -> { ::Faker::Name.unique.last_name },
|
16
|
-
lastname: -> { ::Faker::Name.unique.last_name },
|
17
|
-
lat: -> { Faker::Address.unique.latitude },
|
18
|
-
latitutde: -> { Faker::Address.unique.latitude },
|
19
|
-
lng: -> { Faker::Address.unique.longitude },
|
20
|
-
locale: -> { Faker::Address.unique.country_code },
|
21
|
-
longitude: -> { Faker::Address.unique.longitude },
|
22
|
-
middlename: -> { ::Faker::Name.unique.middle_name },
|
23
|
-
middle_name: -> { ::Faker::Name.unique.middle_name },
|
24
|
-
password: -> { ::Faker::Internet.unique.password },
|
25
|
-
phone: -> { Faker::PhoneNumber.unique.phone_number },
|
26
|
-
phone_number: -> { Faker::PhoneNumber.unique.phone_number },
|
27
|
-
reference: -> { Faker::Internet.unique.uuid },
|
28
|
-
title: -> { Faker::Lorem.unique.sentence(word_count: 3, random_words_to_add: 4) },
|
29
|
-
username: -> { Faker::Internet.unique.username }
|
30
|
-
}.freeze
|
31
|
-
private_constant :SPECIFIC_ATTRIBUTES
|
32
|
-
|
33
|
-
# Returns a string matching all validators
|
7
|
+
# Returns a value matching all validators
|
34
8
|
# @return [String]
|
35
9
|
# @note First try to guess attribute meaning by its name and use Faker to return a coherent value
|
36
10
|
def value
|
37
|
-
|
11
|
+
specific = specific_attributes[dirty_attribute.name]
|
12
|
+
return faker_value(specific) if specific
|
13
|
+
|
14
|
+
default
|
38
15
|
end
|
39
16
|
|
40
17
|
private
|
41
18
|
|
19
|
+
# Returns specific attributes
|
20
|
+
# @return [Hash]
|
21
|
+
# @example
|
22
|
+
# { address: { category: 'Address', method: 'full_address' } }
|
23
|
+
def specific_attributes
|
24
|
+
YAML.safe_load(
|
25
|
+
File.read(
|
26
|
+
DirtySeed::Engine.root.join('lib', 'dirty_seed', 'assigners', 'fakers.yml')
|
27
|
+
)
|
28
|
+
).deep_symbolize_keys
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a value matching the requirements
|
32
|
+
# @param category [Symbol] fake category
|
33
|
+
# @param method [Symbol] fake method
|
34
|
+
# @param unique [Boolean] should the value be unique
|
35
|
+
# @param options [Hash] options used by faker
|
36
|
+
# @return [String]
|
37
|
+
def faker_value(category:, method:, unique: false, options: nil)
|
38
|
+
action = "::Faker::#{category}".constantize
|
39
|
+
action = action.unique if unique
|
40
|
+
if options
|
41
|
+
action.public_send(method, options)
|
42
|
+
else
|
43
|
+
action.public_send(method)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
42
47
|
# Returns a standard string
|
43
48
|
# @return [String]
|
44
49
|
def default
|