dirty_seed 0.2.0 → 0.2.1
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 +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
|