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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ff36072911914eaea5004a422959980277fc11681483fd84d82cdb89d5677b9
4
- data.tar.gz: 2b0f743b67520ea7b5c535a4eaa55a349388bc01be02985d5e4a4a3bfe8996e8
3
+ metadata.gz: 98dffcdecbd3fa5934529401fbac06a25cb883f853896cb6f93c13cf3cfce9f5
4
+ data.tar.gz: bd502b06f80ea7d2f7003f13dc260138e2bad1b5dc64f318c043ced8e9ed4fc9
5
5
  SHA512:
6
- metadata.gz: 7b5c5620e630a0803ea2337482e2bf9579164c39976602493373f01eab4285418533b3ec226bcb5611b0acfb2f44ce4fab012d30d205876851911b28d31d3095
7
- data.tar.gz: 00a6422b95a9e8b0712d070c618f00dffa217d26704944d35c1250db3c54b0b05e262f69f8f77d282d39ef4419ae65d84d06b02ef9772ac8aa5fc87c00788d47
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.0'
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 created records and the recurrent errors are printed out:
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 (`hstore`, `json`, `jsonb`, `array`...) are currently ignored (no value assignment).
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! if 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
- print_logs
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
@@ -6,73 +6,57 @@ module DirtySeed
6
6
  class Logger
7
7
  include Singleton
8
8
 
9
- # Sets verbose to true
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 seed(model)
19
- verbose && puts("Seeding #{model.name.underscore.pluralize}")
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
- # Outputs the start of a new line
23
- # @return [void]
24
- def start_line
25
- verbose && print('> ')
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[#{green}m.\e[0m")
34
+ verbose && print("\e[32m.\e[0m")
32
35
  end
33
36
 
34
37
  # Outputs a fail message
35
38
  # @return [void]
36
- def fail
37
- verbose && print("\e[#{red}mx\e[0m")
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 a line break
43
+ # Outputs an abort message
47
44
  # @return [void]
48
- def break_line
49
- verbose && puts('')
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 seeder_data(seeder)
51
+ def summary(seeders)
55
52
  return unless verbose
56
53
 
57
- puts seeder.model.name
58
- puts " \e[#{green}mseeded: #{seeder.score}\e[0m"
59
- puts " \e[#{red}merrors: #{seeder.error_list}\e[0m" if seeder.errors.any?
60
- end
61
-
62
- private
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
@@ -3,42 +3,32 @@
3
3
  module DirtySeed
4
4
  # Represents an Active Record model
5
5
  class Seeder
6
- attr_reader :model, :instances, :errors
6
+ attr_reader :model, :records, :errors
7
7
 
8
- # Initializes an instance
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
- @instances = []
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(count)
21
- logger.seed(model)
22
- @count = count
20
+ def seed(qty = 1)
21
+ self.quantity = qty
22
+ logger.seed_model_message(model)
23
23
  create_records
24
- @errors.uniq!
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
- logger.start_line
52
- data = params_collection
53
- @count.times do |i|
54
- instance = model.new(data[i])
55
- save(instance)
56
- # rescue from errors on initialize
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
- # Tries to save instance in database
65
- # Populates instances and errors and log message
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(instance)
68
- if instance.save
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
- logger.fail
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
- @errors << e.message
78
- logger.error
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(@count) { attribute.value }]
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(@count) { association.value }]
141
+ [association.name, Array.new(quantity) { association.value }]
114
142
  end
115
143
  end
116
144
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DirtySeed
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.1'
5
5
  public_constant :VERSION
6
6
  end
@@ -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