dirty_seed 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -12
- data/lib/dirty_seed/data_model.rb +2 -9
- data/lib/dirty_seed/logger.rb +27 -43
- data/lib/dirty_seed/seeder.rb +70 -42
- data/lib/dirty_seed/version.rb +1 -1
- data/spec/dummy/app/models/alfa.rb +12 -2
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1 -0
- data/spec/dummy/log/test.log +41982 -0
- data/spec/lib/dirty_seed/data_model_spec.rb +7 -5
- data/spec/lib/dirty_seed/seeder_spec.rb +22 -19
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98dffcdecbd3fa5934529401fbac06a25cb883f853896cb6f93c13cf3cfce9f5
|
4
|
+
data.tar.gz: bd502b06f80ea7d2f7003f13dc260138e2bad1b5dc64f318c043ced8e9ed4fc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685799e510fd74e13965b1ba4da70b2db9a4b170e4cbf4b6e411e757877593bafedd87153257645dc7084f198d3c226ac9bce0e131359ec1b90e5c85ecd4c2c6
|
7
|
+
data.tar.gz: f3285cdea256685d2b1d163f2277dd33434d60b4e9bb2695c157e01717a253085cf60d517ec98aa0b13732f36f87ac5d3fe59f7e3b155f0966ec6fe2aa81ed50
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'dirty_seed', '~> 0.2.
|
10
|
+
gem 'dirty_seed', '~> 0.2.1'
|
11
11
|
```
|
12
12
|
|
13
13
|
And then execute:
|
@@ -35,7 +35,7 @@ $ rake dirty_seed:seed COUNT=42
|
|
35
35
|
|
36
36
|
Instance that cannot be saved are simply ignored.
|
37
37
|
|
38
|
-
For each model, the number of created
|
38
|
+
For each model, the number of created records and the recurrent errors are printed out:
|
39
39
|
|
40
40
|
```bash
|
41
41
|
rake dirty_seed:seed COUNT=15
|
@@ -76,7 +76,7 @@ end
|
|
76
76
|
|
77
77
|
A value is assigned for each attribute, depending on its type.
|
78
78
|
|
79
|
-
Special types
|
79
|
+
Special types like `json`, `jsonb` and `array` are treated.
|
80
80
|
|
81
81
|
For instance, given the following schema:
|
82
82
|
|
@@ -93,6 +93,8 @@ create_table 'things' do |t|
|
|
93
93
|
t.string 'a_string'
|
94
94
|
t.text 'a_text'
|
95
95
|
t.time 'a_time'
|
96
|
+
t.json 'a_json'
|
97
|
+
t.text[] 'an_array'
|
96
98
|
end
|
97
99
|
```
|
98
100
|
|
@@ -100,7 +102,6 @@ end
|
|
100
102
|
|
101
103
|
```ruby
|
102
104
|
{
|
103
|
-
id: 1,
|
104
105
|
a_binary: '13',
|
105
106
|
a_boolean: false,
|
106
107
|
a_date: Wed, '02 Dec 2020',
|
@@ -110,7 +111,9 @@ end
|
|
110
111
|
a_float: 28.825997012616263,
|
111
112
|
a_string: 'Maxime eum ratione ab quod nihil.',
|
112
113
|
a_text: 'Autem non in est dolore.',
|
113
|
-
a_time: 'Sat, 01 Jan 2000 09:31:22 UTC +00:00'
|
114
|
+
a_time: 'Sat, 01 Jan 2000 09:31:22 UTC +00:00',
|
115
|
+
a_json: { 'Autem': 'Dolore', 'Lorem': 'Nihil' },
|
116
|
+
an_array: ['Autem', 'Dolore', 'Nihil'],
|
114
117
|
}
|
115
118
|
```
|
116
119
|
|
@@ -169,7 +172,6 @@ end
|
|
169
172
|
|
170
173
|
```ruby
|
171
174
|
{
|
172
|
-
:id => 1,
|
173
175
|
:thing_id => 42,
|
174
176
|
:notifiable_type => 'User',
|
175
177
|
:notifiable_id => 6,
|
@@ -187,14 +189,21 @@ For attributes requiring validations, assigned value is adapted.
|
|
187
189
|
|
188
190
|
Currently, the following validations are treated:
|
189
191
|
|
190
|
-
- `uniqueness`
|
191
192
|
- `absence`
|
193
|
+
- `format: { with: regex }`
|
192
194
|
- `inclusion: { in: [x, y] }`
|
195
|
+
- `length: { minimum: x }`
|
196
|
+
- `length: { maximum: x }`
|
197
|
+
- `length: { in: x..y }`
|
198
|
+
- `length: { is: x }`
|
193
199
|
- `numericality: { greater_than: x }`
|
194
200
|
- `numericality: { greater_than_or_equal_to: x }`
|
195
201
|
- `numericality: { lesser_than: x }`
|
196
202
|
- `numericality: { lesser_than_or_equal_to: x }`
|
197
203
|
- `numericality: { in: x..y }`
|
204
|
+
- `uniqueness`
|
205
|
+
|
206
|
+
Attribute with an `enum` are treated too.
|
198
207
|
|
199
208
|
Custom validations are not inspected.
|
200
209
|
|
@@ -226,7 +235,6 @@ end
|
|
226
235
|
|
227
236
|
```ruby
|
228
237
|
{
|
229
|
-
id: 1,
|
230
238
|
first_name: 'Emory',
|
231
239
|
last_name: 'Franecki',
|
232
240
|
address: '843 Schneider Squares, Port Olenmouth, TN 12657',
|
@@ -270,16 +278,14 @@ The current attribute names treated this way are:
|
|
270
278
|
- `username`
|
271
279
|
- `uuid`
|
272
280
|
|
273
|
-
More meanings will be added soon and will extend the mechanism to other formats (e.g. an `age` integer).
|
274
|
-
|
275
281
|
## License
|
276
282
|
|
277
283
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
278
284
|
|
279
285
|
## Next features and improvements
|
280
286
|
|
281
|
-
* Add specs to validate specific errors rescue.
|
287
|
+
* Add specs to validate all specific errors rescue.
|
282
288
|
* Manage validations on dates and times.
|
283
289
|
* Detect more meaningful attributes.
|
284
|
-
* Detect more protected attributes (to ignore).
|
290
|
+
* Detect more protected attributes (attributes to ignore).
|
285
291
|
* Add a configuration system to define how to seed: excluded models, default values, faker method...
|
@@ -24,10 +24,10 @@ module DirtySeed
|
|
24
24
|
# @param verbose [Boolean] true if logs should be outputed
|
25
25
|
# @return [void]
|
26
26
|
def seed(count, verbose: false)
|
27
|
-
logger.verbose
|
27
|
+
logger.verbose = verbose
|
28
28
|
# check if ApplicationRecord is defined first (or raise error)
|
29
29
|
::ApplicationRecord && seeders.each { |seeder| seeder.seed(count) }
|
30
|
-
|
30
|
+
logger.summary(seeders)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Creates and returns a seeder for each model
|
@@ -53,12 +53,5 @@ module DirtySeed
|
|
53
53
|
def active_record_models
|
54
54
|
::ApplicationRecord.descendants.reject(&:abstract_class)
|
55
55
|
end
|
56
|
-
|
57
|
-
# Prints logs in the console
|
58
|
-
# @return [void]
|
59
|
-
def print_logs
|
60
|
-
logger.break_line
|
61
|
-
seeders.each { |seeder| logger.seeder_data(seeder) }
|
62
|
-
end
|
63
56
|
end
|
64
57
|
end
|
data/lib/dirty_seed/logger.rb
CHANGED
@@ -6,73 +6,57 @@ module DirtySeed
|
|
6
6
|
class Logger
|
7
7
|
include Singleton
|
8
8
|
|
9
|
-
|
10
|
-
# @return [void]
|
11
|
-
def verbose!
|
12
|
-
@verbose = true
|
13
|
-
end
|
9
|
+
attr_accessor :verbose
|
14
10
|
|
15
11
|
# Outputs before seeding a model
|
16
12
|
# @param model [DirtySeed::Model]
|
17
13
|
# @return [void]
|
18
|
-
def
|
19
|
-
|
14
|
+
def seed_model_message(model)
|
15
|
+
return unless verbose
|
16
|
+
|
17
|
+
puts('')
|
18
|
+
puts("Seeding #{model.name.underscore.pluralize}")
|
19
|
+
print('> ')
|
20
20
|
end
|
21
21
|
|
22
|
-
#
|
23
|
-
# @
|
24
|
-
|
25
|
-
|
22
|
+
# Cleans the message before output (remove linebreak and truncate)
|
23
|
+
# @param message [String]
|
24
|
+
# @return string
|
25
|
+
def clean(message)
|
26
|
+
base = message.tr("\n", "\t")
|
27
|
+
# #truncate is defined in ActiveSupport and then could be undefined
|
28
|
+
base.respond_to?(:truncate) ? base.truncate(150) : base.slice(150)
|
26
29
|
end
|
27
30
|
|
28
31
|
# Outputs a success message
|
29
32
|
# @return [void]
|
30
33
|
def success
|
31
|
-
verbose && print("\e[
|
34
|
+
verbose && print("\e[32m.\e[0m")
|
32
35
|
end
|
33
36
|
|
34
37
|
# Outputs a fail message
|
35
38
|
# @return [void]
|
36
|
-
def
|
37
|
-
verbose && print("\e[
|
38
|
-
end
|
39
|
-
|
40
|
-
# Outputs an error message
|
41
|
-
# @return [void]
|
42
|
-
def error
|
43
|
-
verbose && print("\e[#{red}m!\e[0m")
|
39
|
+
def failure
|
40
|
+
verbose && print("\e[31mx\e[0m")
|
44
41
|
end
|
45
42
|
|
46
|
-
# Outputs
|
43
|
+
# Outputs an abort message
|
47
44
|
# @return [void]
|
48
|
-
def
|
49
|
-
verbose &&
|
45
|
+
def abort
|
46
|
+
verbose && print("\e[31m | too many errors -> abort\e[0m")
|
50
47
|
end
|
51
48
|
|
52
49
|
# Outputs seeder data
|
53
50
|
# @return [void]
|
54
|
-
def
|
51
|
+
def summary(seeders)
|
55
52
|
return unless verbose
|
56
53
|
|
57
|
-
puts
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
attr_reader :verbose
|
65
|
-
|
66
|
-
# Returns color code for green
|
67
|
-
# @return [Integer]
|
68
|
-
def green
|
69
|
-
32
|
70
|
-
end
|
71
|
-
|
72
|
-
# Returns color code for red
|
73
|
-
# @return [Integer]
|
74
|
-
def red
|
75
|
-
31
|
54
|
+
puts ''
|
55
|
+
seeders.each do |seeder|
|
56
|
+
puts seeder.model.name
|
57
|
+
puts " \e[32mseeded: #{seeder.records.count}\e[0m"
|
58
|
+
puts " \e[31merrors: #{seeder.errors.join(', ')}\e[0m" if seeder.errors.any?
|
59
|
+
end
|
76
60
|
end
|
77
61
|
end
|
78
62
|
end
|
data/lib/dirty_seed/seeder.rb
CHANGED
@@ -3,42 +3,32 @@
|
|
3
3
|
module DirtySeed
|
4
4
|
# Represents an Active Record model
|
5
5
|
class Seeder
|
6
|
-
attr_reader :model, :
|
6
|
+
attr_reader :model, :records, :errors
|
7
7
|
|
8
|
-
# Initializes an
|
8
|
+
# Initializes an record
|
9
9
|
# @param model [DirtySeed::Model]
|
10
10
|
# @return [DirtySeed::Seeder]
|
11
11
|
def initialize(model)
|
12
12
|
@model = model
|
13
|
-
@
|
13
|
+
@records = []
|
14
14
|
@errors = []
|
15
15
|
end
|
16
16
|
|
17
17
|
# Creates records
|
18
18
|
# @param count [Integer]
|
19
19
|
# @return [Array<Object>]
|
20
|
-
def seed(
|
21
|
-
|
22
|
-
|
20
|
+
def seed(qty = 1)
|
21
|
+
self.quantity = qty
|
22
|
+
logger.seed_model_message(model)
|
23
23
|
create_records
|
24
|
-
|
25
|
-
instances
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the number of successfully seeded records
|
29
|
-
# @return [Integer]
|
30
|
-
def score
|
31
|
-
instances.count
|
32
|
-
end
|
33
|
-
|
34
|
-
# Returns the errors as list
|
35
|
-
# @return [String]
|
36
|
-
def error_list
|
37
|
-
errors.join(', ')
|
24
|
+
records
|
38
25
|
end
|
39
26
|
|
40
27
|
private
|
41
28
|
|
29
|
+
attr_accessor :successive_errors, :quantity
|
30
|
+
attr_writer :errors
|
31
|
+
|
42
32
|
# Returns the logger
|
43
33
|
# @return [DirtySeed::Logger]
|
44
34
|
def logger
|
@@ -47,42 +37,80 @@ module DirtySeed
|
|
47
37
|
|
48
38
|
# Creates records
|
49
39
|
# @return [void]
|
40
|
+
# @note To take advantage of the faker uniqueness system, all params needs to be defined
|
41
|
+
# Then all records can be created with these params
|
42
|
+
# In other words: do not justcreate record one after the other
|
50
43
|
def create_records
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
rescue StandardError => e
|
58
|
-
@errors << e.message
|
59
|
-
logger.error
|
44
|
+
self.successive_errors = 0
|
45
|
+
params_collection.each do |params|
|
46
|
+
break if exceeded_successive_errors?
|
47
|
+
|
48
|
+
record = initialize_record(params)
|
49
|
+
record && save(record)
|
60
50
|
end
|
61
|
-
logger.break_line
|
62
51
|
end
|
63
52
|
|
64
|
-
#
|
65
|
-
#
|
53
|
+
# Is the successive errors maximum reached?
|
54
|
+
# @return [Boolean]
|
55
|
+
# @note The purpose is to stop trying to seed if to many errors happen
|
56
|
+
def exceeded_successive_errors?
|
57
|
+
return false if successive_errors < 3
|
58
|
+
|
59
|
+
logger.abort
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Initialize a record
|
64
|
+
# @param params [Hash] params to pass to #new
|
65
|
+
# @return [Object] instance of model
|
66
|
+
def initialize_record(params)
|
67
|
+
model.new(params)
|
68
|
+
# rescue from errors on initialize
|
69
|
+
rescue StandardError => e
|
70
|
+
add_standard_error(e)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Tries to save record in database
|
74
|
+
# Populates records and errors and log message
|
66
75
|
# @return [void]
|
67
|
-
def save(
|
68
|
-
if
|
76
|
+
def save(record)
|
77
|
+
if record.save
|
78
|
+
records << record
|
79
|
+
self.successive_errors = 0
|
69
80
|
logger.success
|
70
|
-
@instances << instance
|
71
81
|
else
|
72
|
-
|
73
|
-
@errors.concat(instance.errors.full_messages)
|
82
|
+
add_record_errors(record)
|
74
83
|
end
|
75
84
|
# rescue from errors on save
|
76
85
|
rescue StandardError => e
|
77
|
-
|
78
|
-
|
86
|
+
add_standard_error(e)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adds record errors
|
90
|
+
# @param record [Object] instance of model
|
91
|
+
# @return [void]
|
92
|
+
def add_record_errors(record)
|
93
|
+
self.errors |= record.errors.full_messages
|
94
|
+
self.successive_errors = successive_errors + 1
|
95
|
+
logger.failure
|
96
|
+
end
|
97
|
+
|
98
|
+
# Adds standard error
|
99
|
+
# @param error [Error] error inheriting from StandardError
|
100
|
+
# @return [void]
|
101
|
+
def add_standard_error(error)
|
102
|
+
self.errors |= [logger.clean(error.message)]
|
103
|
+
self.successive_errors = successive_errors + 1
|
104
|
+
logger.failure
|
79
105
|
end
|
80
106
|
|
81
107
|
# Generate records params
|
82
108
|
# @return [Array<Hash>] where Hash is params for one record
|
109
|
+
# @note If model has no attributes and no associations, return empty hashes
|
83
110
|
def params_collection
|
84
111
|
data = Hash[attributes_collection + associations_collection]
|
85
|
-
data.values.transpose.map { |vs| data.keys.zip(vs).to_h }
|
112
|
+
data = data.values.transpose.map { |vs| data.keys.zip(vs).to_h }
|
113
|
+
data.any? ? data : Array.new(quantity, {})
|
86
114
|
end
|
87
115
|
|
88
116
|
# Generate attributes params
|
@@ -96,7 +124,7 @@ module DirtySeed
|
|
96
124
|
def attributes_collection
|
97
125
|
model.attributes.map do |attribute|
|
98
126
|
Faker::UniqueGenerator.clear
|
99
|
-
[attribute.name, Array.new(
|
127
|
+
[attribute.name, Array.new(quantity) { attribute.value }]
|
100
128
|
end
|
101
129
|
end
|
102
130
|
|
@@ -110,7 +138,7 @@ module DirtySeed
|
|
110
138
|
# ]
|
111
139
|
def associations_collection
|
112
140
|
model.associations.map do |association|
|
113
|
-
[association.name, Array.new(
|
141
|
+
[association.name, Array.new(quantity) { association.value }]
|
114
142
|
end
|
115
143
|
end
|
116
144
|
end
|
data/lib/dirty_seed/version.rb
CHANGED
@@ -4,10 +4,20 @@ class Alfa < ApplicationRecord
|
|
4
4
|
has_many :julietts
|
5
5
|
|
6
6
|
after_initialize do |alfa|
|
7
|
-
raise StandardError if alfa.a_string == '39763e57-f8a0-483a-8b26-cc670de8cbfd'
|
7
|
+
raise StandardError, dirty_message('initialize') if alfa.a_string == '39763e57-f8a0-483a-8b26-cc670de8cbfd'
|
8
8
|
end
|
9
9
|
|
10
10
|
before_save do |alfa|
|
11
|
-
raise StandardError if alfa.a_string == '5ecb793c-e0fd-4315-b60d-66f34c1c17e3'
|
11
|
+
raise StandardError, dirty_message('save') if alfa.a_string == '5ecb793c-e0fd-4315-b60d-66f34c1c17e3'
|
12
|
+
end
|
13
|
+
|
14
|
+
def dirty_message(action)
|
15
|
+
<<~DIRTY
|
16
|
+
a dirty message on #{action}
|
17
|
+
taking several
|
18
|
+
lines and with a lot of
|
19
|
+
characters
|
20
|
+
#{Faker::Lorem.paragraph_by_chars(number: 500, supplemental: false)}
|
21
|
+
DIRTY
|
12
22
|
end
|
13
23
|
end
|