dirty_seed 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a892d4bb8402a4a51882d9b1b0917903a5a2a1ecfced0a29c05c4c68d9b47ada
4
- data.tar.gz: 65d335ed1f25ac5ef2ef71c92be56186a85ffb7bedff6512ad5541816879ed05
3
+ metadata.gz: 8eac8a564716c1859fb212114a00596eb82d80f335204e224feca8356d34901b
4
+ data.tar.gz: 6211c5a287f117be9078c1ad4e88f7a8a2bd4d781f8921bcff840dcc5a033a0a
5
5
  SHA512:
6
- metadata.gz: 52d3672671bba02cdbc421b6c0033c6732d52c05cb66ccf35fc79573b964b8762e13c12f965d10c2ab25eaeedc2a1b3405171eb7fc7ef77490002125289557ec
7
- data.tar.gz: cefb338d2283c36e005b8b7b45352d4cfad5bfd940bd83b44e8678e5936d597cb54d8120156b0400837c7f370ed08ac113d88366b8a5153fe41270697f0ec59b
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, in order to quickly test application.
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 try to create instances for each of your models inheriting from `ApplicationRecord`.
29
+ This will create records for each model inheriting from `ApplicationRecord`.
17
30
 
18
- If the instance cannot be saved, it is simply ignored.
31
+ Instance that cannot be saved ar simply ignored.
19
32
 
20
- Number of instances created and recurrent errors will be outputed.
33
+ For each model, the number of created created records and the recurrent errors are logged:
21
34
 
22
- ## Installation
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
- Add this line to your application's Gemfile:
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
- gem 'dirty_seed', git: 'https://github.com/BigBigDoudou/dirty_seed'
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
- And then execute:
31
- ```bash
32
- $ bundle
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
- * Use instance errors to adjust values.
44
- * Add a configuration system to define how to seed: number of instances, models to skip, default values, etc.
275
+ * Detect more meanings and extend it to other formats.
276
+ * Use instance errors to adjust values and eventually match custom validations.
277
+
278
+
@@ -2,7 +2,7 @@
2
2
 
3
3
  module DirtySeed
4
4
  module Assigners
5
- # Draws a boolean value matching validators
5
+ # Draws a value matching validators
6
6
  class DirtyBoolean < DirtyAssigner
7
7
  # Returns a boolean
8
8
  # @return [Boolean]
@@ -2,7 +2,7 @@
2
2
 
3
3
  module DirtySeed
4
4
  module Assigners
5
- # Draws a Date matching validators
5
+ # Draws a value matching validators
6
6
  class DirtyDate < DirtyAssigner
7
7
  # Returns a date matching all validators
8
8
  # @return [Date]
@@ -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 float matching all validators
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 an Integer matching all validators
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 Float between 0 and 1
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 integer matching all validators
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 integer
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 integer representing the minimal acceptable value
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 integer representing the maximal acceptable value
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
- SPECIFIC_ATTRIBUTES = {
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
- SPECIFIC_ATTRIBUTES[dirty_attribute.name]&.call || default
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