auto-seeding 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/README.md +120 -0
- data/Rakefile +8 -0
- data/auto-seeding.gemspec +24 -0
- data/lib/auto-seeding.rb +2 -2
- data/lib/auto-seeding/data/basic.yml +47 -0
- data/lib/auto-seeding/data/faker.yml +75 -0
- data/lib/auto-seeding/data/ffaker.yml +69 -0
- data/lib/auto-seeding/seeder.rb +171 -0
- data/lib/auto-seeding/source.rb +114 -0
- data/lib/auto-seeding/utils.rb +25 -0
- data/test/conf.yml +8 -0
- data/test/test_basic.rb +63 -0
- data/test/test_faker.rb +45 -0
- data/test/test_ffaker.rb +45 -0
- data/test/test_globals.rb +27 -0
- data/test/test_options.rb +49 -0
- data/test/test_validators.rb +167 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 959a395df405f309c48515a254490e92c6f6b08c
|
4
|
+
data.tar.gz: e1f48a26d1d196ff4c5581a0e750ed240792c65c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc92862725d1ce097d198b8aed277eefe2816f293bf1ef0a59e4a9871649d18eb0757ed78fbf32f9074526cab6c4f0e68a1217dd5aba2a6b77719dd5663b3f8d
|
7
|
+
data.tar.gz: 3b884c46b754398e1a058eeb92436fc4e8aa57d090a7a48f4e759486f99add265253bddde82dd5a58bed25ebb953e8586f6be0c88a8fa33f759664f9cfe42345
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
_misc/
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Auto-seeding [![Gem Version](https://badge.fury.io/rb/auto-seeding.svg)](https://badge.fury.io/rb/auto-seeding) [![Build Status](https://travis-ci.org/blocknotes/auto-seeding.svg)](https://travis-ci.org/blocknotes/auto-seeding)
|
2
|
+
|
3
|
+
A component to auto generate seed data with ActiveRecord using a set of predefined or custom rules respecting models validations.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
- Add to Gemfile: `gem 'auto-seeding'` (better in *development* group)
|
8
|
+
- Edit the seed task:
|
9
|
+
|
10
|
+
```rb
|
11
|
+
auto_seeding = AutoSeeding::Seeder.new
|
12
|
+
3.times.each do
|
13
|
+
auto_seeding.update( Author.new ).save!
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
### Options
|
18
|
+
|
19
|
+
- **conf/seeder**: seeding source [*nil* | *:faker* | *:ffaker*] (*:faker* requires Faker gem, *:ffaker* requires FFaker gem)
|
20
|
+
- **conf/file**: load seed configuration from a local file
|
21
|
+
- **auto_create**: array of nested associations to create while seeding (useful for has_one associations), ex. [*:profile*]
|
22
|
+
- **ignore_attrs**: ignore some attributes, ex. [*:id*, *updated_at*]
|
23
|
+
- **skip_associations**: array of nested associations to skip while seeding, ex. [*:posts*]
|
24
|
+
- **sources**: configure sources rules for autoseed data
|
25
|
+
|
26
|
+
Conf file: see [data folder](https://github.com/blocknotes/auto-seeding/tree/master/lib/auto-seeding/data)
|
27
|
+
|
28
|
+
Global options (shared between instances):
|
29
|
+
|
30
|
+
```rb
|
31
|
+
AutoSeeding::Seeder.config({
|
32
|
+
skip_associations: [:versions],
|
33
|
+
conf: {
|
34
|
+
seeder: :ffaker,
|
35
|
+
},
|
36
|
+
})
|
37
|
+
```
|
38
|
+
|
39
|
+
Instance options:
|
40
|
+
|
41
|
+
```rb
|
42
|
+
autoseeding = AutoSeeding::Seeder.new({
|
43
|
+
auto_create: [:profile], # array of symbols
|
44
|
+
conf: {
|
45
|
+
file: 'test/conf.yml', # string
|
46
|
+
seeder: :faker, # symbol - :faker or :ffaker
|
47
|
+
},
|
48
|
+
ignore_attrs: [:id], # array of symbols - ignored attributes
|
49
|
+
skip_associations: [:author], # array of symbols - ignored nested associations
|
50
|
+
sources: { # hash - keys: types, fields
|
51
|
+
types: { # hash - override basic types rules
|
52
|
+
integer: {
|
53
|
+
source_model: 'Random',
|
54
|
+
source_method: 'rand',
|
55
|
+
source_args: '0..100',
|
56
|
+
}
|
57
|
+
},
|
58
|
+
fields: [ # array of hashes - override fields rules
|
59
|
+
{
|
60
|
+
in: ['name'],
|
61
|
+
source_model: 'Faker::StarWars',
|
62
|
+
source_method: 'character',
|
63
|
+
type: 'string'
|
64
|
+
},
|
65
|
+
{
|
66
|
+
regexp: '^(.+_|)title(|_.+)$',
|
67
|
+
source_model: 'Faker::Book',
|
68
|
+
source_method: 'title',
|
69
|
+
post_process: '->( val ) { val + " (seeding)" }',
|
70
|
+
type: 'string'
|
71
|
+
}
|
72
|
+
]
|
73
|
+
}
|
74
|
+
})
|
75
|
+
```
|
76
|
+
|
77
|
+
## Notes
|
78
|
+
|
79
|
+
Generated data can be manipulated easily before saving:
|
80
|
+
|
81
|
+
```rb
|
82
|
+
obj = auto_seeding.update( Author.new )
|
83
|
+
obj.name = 'John Doe'
|
84
|
+
obj.save!
|
85
|
+
```
|
86
|
+
|
87
|
+
Field names can be changed using *append* and *prepend* options - example using Carrierwave remote url property:
|
88
|
+
|
89
|
+
```rb
|
90
|
+
AutoSeeding::Seeder.new({
|
91
|
+
sources: {
|
92
|
+
fields: [
|
93
|
+
{
|
94
|
+
regexp: '^(.+_|)photo(|_.+)$|^(.+_|)image(|_.+)$',
|
95
|
+
source_model: 'Faker::Avatar',
|
96
|
+
source_method: 'image',
|
97
|
+
prepend: 'remote_',
|
98
|
+
append: '_url',
|
99
|
+
type: 'string'
|
100
|
+
}
|
101
|
+
]
|
102
|
+
}
|
103
|
+
}
|
104
|
+
```
|
105
|
+
|
106
|
+
To avoid problems with PaperTrail use:
|
107
|
+
|
108
|
+
`AutoSeeding::Seeder.config({ skip_associations: [:versions] })`
|
109
|
+
|
110
|
+
## Do you like it? Star it!
|
111
|
+
|
112
|
+
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
113
|
+
|
114
|
+
## Contributors
|
115
|
+
|
116
|
+
- [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
|
117
|
+
|
118
|
+
## License
|
119
|
+
|
120
|
+
[MIT](LICENSE.txt)
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
lib = File.expand_path( '../lib/', __FILE__ )
|
4
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?( lib )
|
5
|
+
require 'auto-seeding'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = AutoSeeding::NAME
|
9
|
+
spec.version = AutoSeeding::VERSION.join('.')
|
10
|
+
spec.date = AutoSeeding::DATE
|
11
|
+
spec.summary = AutoSeeding::INFO
|
12
|
+
spec.description = AutoSeeding::DESC
|
13
|
+
spec.authors = AutoSeeding::AUTHORS.map { |a| a[0] }.flatten
|
14
|
+
spec.email = AutoSeeding::AUTHORS.first[1]
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
spec.homepage = 'https://github.com/blocknotes/auto-seeding'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
spec.add_dependency 'activerecord', '~> 5'
|
20
|
+
spec.add_development_dependency 'rake', '~> 12'
|
21
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
22
|
+
spec.add_development_dependency 'faker', '~> 1'
|
23
|
+
spec.add_development_dependency 'ffaker', '~> 2'
|
24
|
+
end
|
data/lib/auto-seeding.rb
CHANGED
@@ -7,6 +7,6 @@ module AutoSeeding
|
|
7
7
|
INFO = 'Auto seeding component for ActiveRecord'.freeze
|
8
8
|
DESC = 'A component to auto generate seed data with ActiveRecord using a set of predefined or custom rules respecting models validations'.freeze
|
9
9
|
AUTHORS = [ [ 'Mattia Roccoberton', 'mat@blocknot.es', 'http://blocknot.es' ] ].freeze
|
10
|
-
DATE = '2017-09-
|
11
|
-
VERSION = [ 0, 1,
|
10
|
+
DATE = '2017-09-30'.freeze
|
11
|
+
VERSION = [ 0, 1, 5 ].freeze
|
12
12
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
sources:
|
2
|
+
types:
|
3
|
+
boolean:
|
4
|
+
source_model: AutoSeeding::Source
|
5
|
+
source_method: random_boolean
|
6
|
+
date:
|
7
|
+
source_model: Date
|
8
|
+
source_method: today
|
9
|
+
post_process: ->( val ) { val + rand( -30..30 ) }
|
10
|
+
datetime:
|
11
|
+
source_model: Time
|
12
|
+
source_method: now
|
13
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
14
|
+
decimal:
|
15
|
+
source_model: Random
|
16
|
+
source_method: rand
|
17
|
+
float:
|
18
|
+
source_model: Random
|
19
|
+
source_method: rand
|
20
|
+
integer:
|
21
|
+
source_model: Random
|
22
|
+
source_method: rand
|
23
|
+
# source_args: 20..25
|
24
|
+
string:
|
25
|
+
source_model: AutoSeeding::Source
|
26
|
+
source_method: random_string
|
27
|
+
text:
|
28
|
+
source_model: AutoSeeding::Source
|
29
|
+
source_method: random_string
|
30
|
+
source_args: 50
|
31
|
+
time:
|
32
|
+
source_model: Time
|
33
|
+
source_method: now
|
34
|
+
post_process: ->( val ) { val + rand( 0..DAY_SECONDS ) }
|
35
|
+
timestamp:
|
36
|
+
source_model: Time
|
37
|
+
source_method: now
|
38
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
39
|
+
fields:
|
40
|
+
-
|
41
|
+
in:
|
42
|
+
- name
|
43
|
+
- title
|
44
|
+
source_model: AutoSeeding::Source
|
45
|
+
source_method: random_string
|
46
|
+
source_args: 5
|
47
|
+
type: string
|
@@ -0,0 +1,75 @@
|
|
1
|
+
sources:
|
2
|
+
types:
|
3
|
+
boolean:
|
4
|
+
source_model: AutoSeeding::Source
|
5
|
+
source_method: random_boolean
|
6
|
+
date:
|
7
|
+
source_model: Date
|
8
|
+
source_method: today
|
9
|
+
post_process: ->( val ) { val + rand( -30..30 ) }
|
10
|
+
datetime:
|
11
|
+
source_model: Time
|
12
|
+
source_method: now
|
13
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
14
|
+
decimal:
|
15
|
+
source_model: Random
|
16
|
+
source_method: rand
|
17
|
+
float:
|
18
|
+
source_model: Random
|
19
|
+
source_method: rand
|
20
|
+
integer:
|
21
|
+
source_model: Random
|
22
|
+
source_method: rand
|
23
|
+
string:
|
24
|
+
source_model: Faker::Hipster
|
25
|
+
source_method: sentence
|
26
|
+
text:
|
27
|
+
source_model: Faker::Hipster
|
28
|
+
source_method: paragraph
|
29
|
+
time:
|
30
|
+
source_model: Time
|
31
|
+
source_method: now
|
32
|
+
post_process: ->( val ) { val + rand( 0..DAY_SECONDS ) }
|
33
|
+
timestamp:
|
34
|
+
source_model: Time
|
35
|
+
source_method: now
|
36
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
37
|
+
fields:
|
38
|
+
# -
|
39
|
+
# in:
|
40
|
+
# - name
|
41
|
+
# - title
|
42
|
+
# source_model: Faker::Hipster
|
43
|
+
# source_method: word
|
44
|
+
# type: string
|
45
|
+
-
|
46
|
+
regexp: ^(.+_|)email(|_.+)$
|
47
|
+
source_model: Faker::Internet
|
48
|
+
source_method: safe_email
|
49
|
+
type: string
|
50
|
+
-
|
51
|
+
regexp: ^(.+_|)name(|_.+)$
|
52
|
+
source_model: Faker::Hipster
|
53
|
+
source_method: word
|
54
|
+
type: string
|
55
|
+
-
|
56
|
+
regexp: ^(.+_|)title(|_.+)$
|
57
|
+
source_model: Faker::Book
|
58
|
+
source_method: genre
|
59
|
+
type: string
|
60
|
+
-
|
61
|
+
regexp: ^(.+_|)url(|_.+)$
|
62
|
+
source_model: Faker::Internet
|
63
|
+
source_method: url
|
64
|
+
source_args: example.com
|
65
|
+
type: string
|
66
|
+
# source_args:
|
67
|
+
# - example.com
|
68
|
+
# - /foobar.html
|
69
|
+
# Models
|
70
|
+
-
|
71
|
+
model: ^(.+_|)admin(|_.+)$|^(.+_|)author(|_.+)$|^(.+_|)user(|_.+)$
|
72
|
+
regexp: ^(.+_|)name(|_.+)$
|
73
|
+
source_model: Faker::Book
|
74
|
+
source_method: author
|
75
|
+
type: string
|
@@ -0,0 +1,69 @@
|
|
1
|
+
sources:
|
2
|
+
types:
|
3
|
+
boolean:
|
4
|
+
source_model: FFaker::Boolean
|
5
|
+
source_method: random
|
6
|
+
date:
|
7
|
+
source_model: Date
|
8
|
+
source_method: today
|
9
|
+
post_process: ->( val ) { val + rand( -30..30 ) }
|
10
|
+
datetime:
|
11
|
+
source_model: Time
|
12
|
+
source_method: now
|
13
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
14
|
+
decimal:
|
15
|
+
source_model: Random
|
16
|
+
source_method: rand
|
17
|
+
float:
|
18
|
+
source_model: Random
|
19
|
+
source_method: rand
|
20
|
+
integer:
|
21
|
+
source_model: Random
|
22
|
+
source_method: rand
|
23
|
+
string:
|
24
|
+
source_model: FFaker::Book
|
25
|
+
source_method: genre
|
26
|
+
text:
|
27
|
+
source_model: FFaker::Book
|
28
|
+
source_method: description
|
29
|
+
time:
|
30
|
+
source_model: Time
|
31
|
+
source_method: now
|
32
|
+
post_process: ->( val ) { val + rand( 0..DAY_SECONDS ) }
|
33
|
+
timestamp:
|
34
|
+
source_model: Time
|
35
|
+
source_method: now
|
36
|
+
post_process: ->( val ) { ( val + rand( -MONTH_SECONDS..MONTH_SECONDS ) ).to_datetime }
|
37
|
+
fields:
|
38
|
+
# -
|
39
|
+
# in:
|
40
|
+
# - title
|
41
|
+
# source_model: FFaker::Book
|
42
|
+
# source_method: title
|
43
|
+
# type: string
|
44
|
+
-
|
45
|
+
regexp: ^(.+_|)email(|_.+)$
|
46
|
+
source_model: FFaker::Internet
|
47
|
+
source_method: safe_email
|
48
|
+
type: string
|
49
|
+
-
|
50
|
+
regexp: ^(.+_|)name(|_.+)$
|
51
|
+
source_model: FFaker::Movie
|
52
|
+
source_method: title
|
53
|
+
type: string
|
54
|
+
-
|
55
|
+
regexp: ^(.+_|)title(|_.+)$
|
56
|
+
source_model: FFaker::Book
|
57
|
+
source_method: title
|
58
|
+
type: string
|
59
|
+
-
|
60
|
+
regexp: ^(.+_|)url(|_.+)$
|
61
|
+
source_model: FFaker::Internet
|
62
|
+
source_method: http_url
|
63
|
+
type: string
|
64
|
+
-
|
65
|
+
model: ^(.+_|)admin(|_.+)$|^(.+_|)author(|_.+)$|^(.+_|)user(|_.+)$
|
66
|
+
regexp: ^(.+_|)name(|_.+)$
|
67
|
+
source_model: FFaker::Name
|
68
|
+
source_method: name
|
69
|
+
type: string
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module AutoSeeding
|
3
|
+
class Seeder
|
4
|
+
TYPES = [:boolean, :date, :datetime, :float, :decimal, :integer, :string, :text, :time, :timestamp]
|
5
|
+
|
6
|
+
attr_reader :options, :sources
|
7
|
+
|
8
|
+
@@globals = { conf: {}, sources: {} }
|
9
|
+
|
10
|
+
def initialize( opts = {} )
|
11
|
+
options = { conf: @@globals[:conf], sources: @@globals[:sources] }
|
12
|
+
AutoSeeding::_deep_merge!( options, opts )
|
13
|
+
|
14
|
+
@columns = {}
|
15
|
+
@models = {}
|
16
|
+
@extra_validations = { confirmations: [] }
|
17
|
+
options[:ignore_attrs] ||= [:id, :created_at, :updated_at]
|
18
|
+
options[:ignore_attrs] += @@globals[:ignore_attrs] if @@globals[:ignore_attrs]
|
19
|
+
options[:skip_associations] ||= []
|
20
|
+
options[:skip_associations] += @@globals[:skip_associations] if @@globals[:skip_associations]
|
21
|
+
|
22
|
+
path = options[:conf][:file]
|
23
|
+
if path
|
24
|
+
options[:conf].delete :seeder
|
25
|
+
else
|
26
|
+
yml_file = if options[:conf][:seeder] == :ffaker
|
27
|
+
puts 'warning: seeder set to ffaker but FFaker is not available' unless defined? FFaker
|
28
|
+
'ffaker.yml'
|
29
|
+
elsif options[:conf][:seeder] == :faker
|
30
|
+
puts 'warning: seeder set to faker but Faker is not available' unless defined? Faker
|
31
|
+
'faker.yml'
|
32
|
+
else
|
33
|
+
'basic.yml'
|
34
|
+
end
|
35
|
+
path = Pathname.new( File.dirname __FILE__ ).join( 'data', yml_file ).to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# Random.srand( options[:conf][:seed_number] ? options[:conf][:seed_number].to_i : Random.new_seed ) # NOTE: problems here
|
39
|
+
|
40
|
+
yml = Seeder::symbolize_keys YAML.load_file( path )
|
41
|
+
# @sources = yml[:sources].merge( options[:sources] ? options[:sources] : {} )
|
42
|
+
@sources = yml[:sources].dup
|
43
|
+
AutoSeeding::_deep_merge!( @sources, options[:sources] ) if options[:sources]
|
44
|
+
@sources[:fields] ||= {}
|
45
|
+
@sources[:fields].map! { |s| Seeder::symbolize_keys s }
|
46
|
+
@sources[:fields].sort! { |a, b| ( a['model'] || a[:model] ) ? -1 : ( ( b['model'] || b[:model] ) ? 1 : 0 ) }
|
47
|
+
@options = options
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def update( object )
|
53
|
+
model = object.class
|
54
|
+
|
55
|
+
model.content_columns.each do |column|
|
56
|
+
col = column.name.to_sym
|
57
|
+
next if @options[:ignore_attrs].include? col
|
58
|
+
@columns[col] ||= {
|
59
|
+
validators: prepare_validators( model._validators[col] )
|
60
|
+
}
|
61
|
+
|
62
|
+
found = false
|
63
|
+
@sources[:fields].each do |f|
|
64
|
+
if f[:model]
|
65
|
+
next unless Regexp.new( f[:model], Regexp::IGNORECASE ).match( model.to_s )
|
66
|
+
end
|
67
|
+
if( f[:in] && f[:in].include?( col.to_s ) ) ||
|
68
|
+
( f[:regexp] && Regexp.new( f[:regexp], Regexp::IGNORECASE ).match( col.to_s ) )
|
69
|
+
col_ = ( f[:prepend] || f[:append] ) ? ( f[:prepend].to_s + col.to_s + f[:append].to_s ) : col.to_s
|
70
|
+
@columns[col][:src] ||= Source.new( col, f[:type] ? f[:type].to_sym : :string, @columns[col][:validators], f )
|
71
|
+
object.send( col_ + '=', @columns[col][:src].gen )
|
72
|
+
found = true
|
73
|
+
break
|
74
|
+
end
|
75
|
+
end
|
76
|
+
next if found
|
77
|
+
|
78
|
+
if TYPES.include? column.type
|
79
|
+
object.send( col.to_s + '=', ( @columns[col][:src] ||= Source.new( col, column.type, @columns[col][:validators], @sources[:types][column.type] ) ).gen )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Setup associations
|
84
|
+
model._reflections.each do |association, data|
|
85
|
+
next if @options[:skip_associations].include? association.to_sym
|
86
|
+
model2 = data.klass
|
87
|
+
if @options[:auto_create] && @options[:auto_create].include?( association.to_sym )
|
88
|
+
auto_seeding = AutoSeeding::Seeder.new( { conf: { seeder: @options[:seeder] || @@globals[:conf][:seeder] }, skip_associations: [model.to_s.underscore.to_sym] } )
|
89
|
+
object.send( association + '=', auto_seeding.update( model2.new ) )
|
90
|
+
else
|
91
|
+
@models[model2.table_name] ||= model2.all
|
92
|
+
if data.is_a?( ActiveRecord::Reflection::ThroughReflection ) # many-to-many
|
93
|
+
sam = @models[model2.table_name].sample( rand( 5 ) )
|
94
|
+
object.send( association ).push( sam ) if sam.any?
|
95
|
+
elsif data.parent_reflection && data.parent_reflection.is_a?( ActiveRecord::Reflection::HasAndBelongsToManyReflection )
|
96
|
+
next
|
97
|
+
else
|
98
|
+
sam = @models[model2.table_name].sample
|
99
|
+
object.send( association + '=', sam ) if sam
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Extra validations
|
105
|
+
@extra_validations[:confirmations].each do |field|
|
106
|
+
object.send field.to_s+'_confirmation=', object[field]
|
107
|
+
end
|
108
|
+
|
109
|
+
object
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.config( options = nil )
|
113
|
+
@@globals = options ? options : { conf: {}, sources: {} }
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
def prepare_validators( validators )
|
119
|
+
ret = {}
|
120
|
+
validators.each do |validator|
|
121
|
+
case validator.class.name.split( '::' ).last
|
122
|
+
when 'AcceptanceValidator'
|
123
|
+
ret[:accept] = validator.options[:accept].is_a?( Array ) ? validator.options[:accept] : [ validator.options[:accept] ]
|
124
|
+
when 'ConfirmationValidator'
|
125
|
+
# ret[:confirmation] = validator.options[:confirmation]
|
126
|
+
@extra_validations[:confirmations] += validator.attributes
|
127
|
+
# case_sensitive - TODO: not implemented
|
128
|
+
when 'ExclusionValidator'
|
129
|
+
ret[:not_in] = validator.options[:in] # TODO: not implemented
|
130
|
+
when 'InclusionValidator'
|
131
|
+
ret[:in] = validator.options[:in]
|
132
|
+
when 'LengthValidator'
|
133
|
+
if validator.options[:is]
|
134
|
+
ret[:length_minimum] = validator.options[:is]
|
135
|
+
ret[:length_maximum] = validator.options[:is]
|
136
|
+
else
|
137
|
+
ret[:length_minimum] = validator.options[:minimum]
|
138
|
+
ret[:length_maximum] = validator.options[:maximum]
|
139
|
+
end
|
140
|
+
when 'NumericalityValidator'
|
141
|
+
ret[:num_gt] = validator.options[:greater_than]
|
142
|
+
ret[:num_gte] = validator.options[:greater_than_or_equal_to]
|
143
|
+
ret[:equal_to] = validator.options[:equal_to]
|
144
|
+
ret[:num_lt] = validator.options[:less_than]
|
145
|
+
ret[:num_lte] = validator.options[:less_than_or_equal_to]
|
146
|
+
# ret[:other_than] = validator.options[:other_than] # TODO: not implemented
|
147
|
+
# ret[:odd] = validator.options[:odd] # TODO: not implemented
|
148
|
+
# ret[:even] = validator.options[:even] # TODO: not implemented
|
149
|
+
when 'PresenceValidator'
|
150
|
+
# ret[:presence] = true # TODO: not implemented
|
151
|
+
when 'UniquenessValidator'
|
152
|
+
ret[:uniqueness] = true
|
153
|
+
else
|
154
|
+
# p validator.class.name.split( '::' ).last # DEBUG
|
155
|
+
end
|
156
|
+
end
|
157
|
+
ret
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.symbolize_keys( obj )
|
161
|
+
if obj.is_a?( Hash )
|
162
|
+
obj2 ||= {}
|
163
|
+
obj.each do |k, v|
|
164
|
+
obj2[k.to_sym] = symbolize_keys( v )
|
165
|
+
end
|
166
|
+
return obj2
|
167
|
+
end
|
168
|
+
obj
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module AutoSeeding
|
3
|
+
class Source
|
4
|
+
DAY_SECONDS = ( 60 * 60 * 24 )
|
5
|
+
MIN_INT = 0
|
6
|
+
MAX_INT = 1_000_000
|
7
|
+
MIN_FLOAT = 0.0
|
8
|
+
MAX_FLOAT = 1000.0
|
9
|
+
MONTH_SECONDS = ( DAY_SECONDS * 30 ).freeze
|
10
|
+
VOWELS = %w(a e i o u).freeze
|
11
|
+
CONSONANTS = (('a'..'z').to_a - VOWELS).freeze
|
12
|
+
|
13
|
+
def initialize( column, type, rules, options = {} )
|
14
|
+
@column = column
|
15
|
+
@type = type
|
16
|
+
@rules = rules ? rules : {}
|
17
|
+
@options = options || {}
|
18
|
+
if @options[:source_model] && @options[:source_method]
|
19
|
+
@source_class = Object.const_get @options[:source_model]
|
20
|
+
@source_method = @options[:source_method].to_sym
|
21
|
+
@source_args = @options[:source_args]
|
22
|
+
else
|
23
|
+
@source_class = AutoSeeding::Source
|
24
|
+
@source_method = :random_string
|
25
|
+
@source_args = nil
|
26
|
+
end
|
27
|
+
@uniqueness = {}
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def gen
|
32
|
+
@retry = 100
|
33
|
+
process @type
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.random_boolean
|
37
|
+
[false, true].sample
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.random_string( words = 10 )
|
41
|
+
(1..rand(words)+1).map do
|
42
|
+
(0..rand(10)+1).map do |i|
|
43
|
+
i % 2 == 0 ? CONSONANTS.sample : VOWELS.sample
|
44
|
+
end.join
|
45
|
+
end.join( ' ' ).capitalize
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def process( type = nil )
|
51
|
+
value =
|
52
|
+
if @rules[:equal_to]
|
53
|
+
@rules[:equal_to]
|
54
|
+
elsif @rules[:in]
|
55
|
+
@rules[:in].sample
|
56
|
+
elsif @rules[:accept]
|
57
|
+
@rules[:accept].sample
|
58
|
+
else
|
59
|
+
case type
|
60
|
+
when :float, :decimal
|
61
|
+
min = @rules[:num_gte] ? @rules[:num_gte].to_f : ( @rules[:num_gt] ? ( @rules[:num_gt].to_f + 0.1 ) : MIN_FLOAT )
|
62
|
+
max = @rules[:num_lte] ? @rules[:num_lte].to_f : ( @rules[:num_lt] ? ( @rules[:num_lt].to_f - 0.1 ) : MAX_FLOAT )
|
63
|
+
@source_class.send( @source_method, @source_args ? eval( @source_args ) : (min .. max) )
|
64
|
+
when :integer
|
65
|
+
min = @rules[:num_gte] ? @rules[:num_gte].to_i : ( @rules[:num_gt] ? ( @rules[:num_gt].to_i + 1 ) : MIN_INT )
|
66
|
+
max = @rules[:num_lte] ? @rules[:num_lte].to_i : ( @rules[:num_lt] ? ( @rules[:num_lt].to_i - 1 ) : MAX_INT )
|
67
|
+
@source_class.send( @source_method, @source_args ? eval( @source_args ) : (min .. max) )
|
68
|
+
when :string, :text
|
69
|
+
@source_class.send( @source_method, *@source_args ).to_s
|
70
|
+
else
|
71
|
+
@source_class.send( @source_method, *@source_args )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if @options[:post_process]
|
76
|
+
post_process = eval @options[:post_process]
|
77
|
+
value = post_process.call( value )
|
78
|
+
end
|
79
|
+
|
80
|
+
# validations
|
81
|
+
case type
|
82
|
+
when :float, :decimal
|
83
|
+
value = ( @rules[:num_gt].to_f + 0.1 ) if @rules[:num_gt] && ( value <= @rules[:num_gt].to_f )
|
84
|
+
value = @rules[:num_gte].to_f if @rules[:num_gte] && ( value < @rules[:num_gte].to_f )
|
85
|
+
value = ( @rules[:num_lt].to_f - 0.1 ) if @rules[:num_lt] && ( value >= @rules[:num_lt].to_f )
|
86
|
+
value = @rules[:num_lte].to_f if @rules[:num_lte] && ( value > @rules[:num_lte].to_f )
|
87
|
+
when :integer
|
88
|
+
value = ( @rules[:num_gt].to_i + 1 ) if @rules[:num_gt] && ( value <= @rules[:num_gt].to_i )
|
89
|
+
value = @rules[:num_gte].to_i if @rules[:num_gte] && ( value < @rules[:num_gte].to_i )
|
90
|
+
value = ( @rules[:num_lt].to_i - 1 ) if @rules[:num_lt] && ( value >= @rules[:num_lt].to_i )
|
91
|
+
value = @rules[:num_lte].to_i if @rules[:num_lte] && ( value > @rules[:num_lte].to_i )
|
92
|
+
when :string, :text
|
93
|
+
value = value.ljust( @rules[:length_minimum], '-' ) if @rules[:length_minimum]
|
94
|
+
value = value.slice( 0..( @rules[:length_maximum] - 1 ) ) if @rules[:length_maximum]
|
95
|
+
end
|
96
|
+
|
97
|
+
if @rules[:not_in] && @rules[:not_in].include?( value )
|
98
|
+
@retry -= 1
|
99
|
+
return @retry > 0 ? process( type ) : raise( Exception.new( 'Reserved value' ) )
|
100
|
+
end
|
101
|
+
|
102
|
+
if @rules[:uniqueness]
|
103
|
+
@uniqueness[@column] ||= {}
|
104
|
+
if @uniqueness[@column].has_key?( value )
|
105
|
+
return ( @retry -= 1 ) > 0 ? process( type ) : raise( Exception.new( 'Value already taken' ) )
|
106
|
+
else
|
107
|
+
@uniqueness[@column][value] = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
value
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AutoSeeding
|
2
|
+
# From activesupport
|
3
|
+
def self._deep_merge( hash, other_hash, &block )
|
4
|
+
_deep_merge!( hash.dup, other_hash, &block )
|
5
|
+
end
|
6
|
+
|
7
|
+
# From activesupport
|
8
|
+
def self._deep_merge!( hash, other_hash, &block )
|
9
|
+
other_hash.each_pair do |current_key, other_value|
|
10
|
+
this_value = hash[current_key]
|
11
|
+
|
12
|
+
hash[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
|
13
|
+
_deep_merge( this_value, other_value, &block )
|
14
|
+
else
|
15
|
+
if block_given? && key?(current_key)
|
16
|
+
block.call(current_key, this_value, other_value)
|
17
|
+
else
|
18
|
+
other_value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
end
|
data/test/conf.yml
ADDED
data/test/test_basic.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'pathname'
|
5
|
+
require 'yaml'
|
6
|
+
require_relative '../lib/auto-seeding'
|
7
|
+
|
8
|
+
# class TestObject
|
9
|
+
# # def initialize
|
10
|
+
# # super
|
11
|
+
# # end
|
12
|
+
|
13
|
+
# def self.content_columns
|
14
|
+
# [OpenStruct.new({
|
15
|
+
# name: 'test',
|
16
|
+
# })]
|
17
|
+
# end
|
18
|
+
|
19
|
+
# def self._reflections
|
20
|
+
# []
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def self._validators
|
24
|
+
# {
|
25
|
+
# test: []
|
26
|
+
# }
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
|
30
|
+
describe 'AutoSeeding basic' do
|
31
|
+
before do
|
32
|
+
@auto_seeding = AutoSeeding::Seeder.new
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'When generating a value of any type' do
|
36
|
+
it 'must return a correct value' do
|
37
|
+
@auto_seeding.sources[:types].each do |type, data|
|
38
|
+
value = AutoSeeding::Source.new( :test1, type, nil, data ).gen
|
39
|
+
case type
|
40
|
+
when :boolean
|
41
|
+
assert( value.is_a?( FalseClass ) || value.is_a?( TrueClass ) )
|
42
|
+
when :date
|
43
|
+
assert_instance_of( Date, value )
|
44
|
+
when :datetime, :timestamp
|
45
|
+
assert_instance_of( DateTime, value )
|
46
|
+
when :float, :decimal
|
47
|
+
assert_instance_of( Float, value )
|
48
|
+
when :integer
|
49
|
+
assert_instance_of( Fixnum, value )
|
50
|
+
when :string, :text
|
51
|
+
assert_instance_of( String, value )
|
52
|
+
v = value.strip
|
53
|
+
assert( v.length > 0 )
|
54
|
+
assert( !v.empty? )
|
55
|
+
when :time
|
56
|
+
assert_instance_of( Time, value )
|
57
|
+
else
|
58
|
+
assert( false, "Invalid type: #{type}" )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/test/test_faker.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'faker'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'pathname'
|
6
|
+
require 'yaml'
|
7
|
+
require_relative '../lib/auto-seeding'
|
8
|
+
|
9
|
+
describe 'AutoSeeding using Faker' do
|
10
|
+
before do
|
11
|
+
AutoSeeding::Seeder.config # reset global options
|
12
|
+
@test_faker = AutoSeeding::Seeder.new({
|
13
|
+
seeder: :faker
|
14
|
+
})
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'When generating a value of any type' do
|
18
|
+
it 'must return a correct value' do
|
19
|
+
@test_faker.sources[:types].each do |type, data|
|
20
|
+
value = AutoSeeding::Source.new( :test1, type, nil, data ).gen
|
21
|
+
case type
|
22
|
+
when :boolean
|
23
|
+
assert( value.is_a?( FalseClass ) || value.is_a?( TrueClass ) )
|
24
|
+
when :date
|
25
|
+
assert_instance_of( Date, value )
|
26
|
+
when :datetime, :timestamp
|
27
|
+
assert_instance_of( DateTime, value )
|
28
|
+
when :float, :decimal
|
29
|
+
assert_instance_of( Float, value )
|
30
|
+
when :integer
|
31
|
+
assert_instance_of( Fixnum, value )
|
32
|
+
when :string, :text
|
33
|
+
assert_instance_of( String, value )
|
34
|
+
v = value.strip
|
35
|
+
assert( v.length > 0 )
|
36
|
+
assert( !v.empty? )
|
37
|
+
when :time
|
38
|
+
assert_instance_of( Time, value )
|
39
|
+
else
|
40
|
+
assert( false, "Invalid type: #{type}" )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/test/test_ffaker.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ffaker'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'pathname'
|
6
|
+
require 'yaml'
|
7
|
+
require_relative '../lib/auto-seeding'
|
8
|
+
|
9
|
+
describe 'AutoSeeding using FFaker' do
|
10
|
+
before do
|
11
|
+
AutoSeeding::Seeder.config # reset global options
|
12
|
+
@test_ffaker = AutoSeeding::Seeder.new({
|
13
|
+
seeder: :ffaker
|
14
|
+
})
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'When generating a value of any type' do
|
18
|
+
it 'must return a correct value' do
|
19
|
+
@test_ffaker.sources[:types].each do |type, data|
|
20
|
+
value = AutoSeeding::Source.new( :test1, type, nil, data ).gen
|
21
|
+
case type
|
22
|
+
when :boolean
|
23
|
+
assert( value.is_a?( FalseClass ) || value.is_a?( TrueClass ) )
|
24
|
+
when :date
|
25
|
+
assert_instance_of( Date, value )
|
26
|
+
when :datetime, :timestamp
|
27
|
+
assert_instance_of( DateTime, value )
|
28
|
+
when :float, :decimal
|
29
|
+
assert_instance_of( Float, value )
|
30
|
+
when :integer
|
31
|
+
assert_instance_of( Fixnum, value )
|
32
|
+
when :string, :text
|
33
|
+
assert_instance_of( String, value )
|
34
|
+
v = value.strip
|
35
|
+
assert( v.length > 0 )
|
36
|
+
assert( !v.empty? )
|
37
|
+
when :time
|
38
|
+
assert_instance_of( Time, value )
|
39
|
+
else
|
40
|
+
assert( false, "Invalid type: #{type}" )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'pathname'
|
5
|
+
require 'yaml'
|
6
|
+
require 'ffaker'
|
7
|
+
require_relative '../lib/auto-seeding'
|
8
|
+
|
9
|
+
describe 'AutoSeeding global config' do
|
10
|
+
before do
|
11
|
+
AutoSeeding::Seeder.config({
|
12
|
+
skip_associations: [:versions],
|
13
|
+
conf: {
|
14
|
+
seeder: :ffaker,
|
15
|
+
},
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'When using config method' do
|
20
|
+
it 'must merge the internal options' do
|
21
|
+
auto_seeding = AutoSeeding::Seeder.new
|
22
|
+
assert_equal( auto_seeding.options[:conf][:seeder], :ffaker )
|
23
|
+
auto_seeding2 = AutoSeeding::Seeder.new
|
24
|
+
assert_equal( auto_seeding2.options[:conf][:seeder], :ffaker )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'pathname'
|
5
|
+
require 'yaml'
|
6
|
+
require_relative '../lib/auto-seeding'
|
7
|
+
|
8
|
+
describe 'AutoSeeding Options' do
|
9
|
+
before do
|
10
|
+
AutoSeeding::Seeder.config # reset global options
|
11
|
+
@test_options = AutoSeeding::Seeder.new({
|
12
|
+
auto_create: [:profile], # array of symbols - nested associations to create while seeding
|
13
|
+
conf: {
|
14
|
+
file: 'test/conf.yml', # string - local seed configuration file
|
15
|
+
seeder: :faker, # symbol - :faker or :ffaker
|
16
|
+
},
|
17
|
+
ignore_attrs: [:id], # array of symbols - ignored attributes
|
18
|
+
skip_associations: [:author], # array of symbols - ignored nested associations
|
19
|
+
sources: { # hash - keys: types, fields
|
20
|
+
types: { # hash - override basic types rules
|
21
|
+
integer: {
|
22
|
+
source_model: Random,
|
23
|
+
source_method: 'rand',
|
24
|
+
source_args: 0..100,
|
25
|
+
}
|
26
|
+
},
|
27
|
+
# fields: [ # array of hashes - overried fields rules
|
28
|
+
# {
|
29
|
+
# in: ['name'],
|
30
|
+
# source_model: 'Faker::Hipster',
|
31
|
+
# source_method: 'word',
|
32
|
+
# type: 'string'
|
33
|
+
# }
|
34
|
+
# ]
|
35
|
+
}
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'When using constructor options hash' do
|
40
|
+
it 'must merge the internal options' do
|
41
|
+
assert_nil( @test_options.options[:conf][:seeder] ) # overriden by file option
|
42
|
+
assert_equal( @test_options.options[:auto_create], [:profile] )
|
43
|
+
assert_equal( @test_options.options[:ignore_attrs], [:id] )
|
44
|
+
assert_equal( @test_options.options[:skip_associations], [:author] )
|
45
|
+
assert_equal( @test_options.sources[:fields][0][:source_args], 1 )
|
46
|
+
assert_equal( @test_options.sources[:types].keys, [:integer] )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'active_record'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'pathname'
|
6
|
+
require 'yaml'
|
7
|
+
require_relative '../lib/auto-seeding'
|
8
|
+
|
9
|
+
AutoSeeding::Seeder.class_eval do
|
10
|
+
attr_reader :columns, :extra_validations
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestObject # < ActiveRecord::Base
|
14
|
+
include ActiveRecord::Validations
|
15
|
+
|
16
|
+
FIELDS = {
|
17
|
+
privacy: :boolean,
|
18
|
+
privacy2: :boolean,
|
19
|
+
email: :string,
|
20
|
+
email2: :string,
|
21
|
+
letter: :string,
|
22
|
+
letter2: :string,
|
23
|
+
title: :string,
|
24
|
+
title2: :text,
|
25
|
+
title3: :text,
|
26
|
+
title4: :text,
|
27
|
+
title5: :text,
|
28
|
+
integer: :float,
|
29
|
+
number: :integer,
|
30
|
+
number2: :float,
|
31
|
+
number3: :decimal,
|
32
|
+
number4: :integer,
|
33
|
+
unique: :string,
|
34
|
+
unique2: :integer
|
35
|
+
}.freeze
|
36
|
+
FIELDS_EXTRA = [:email_confirmation, :email_confirmation2].freeze
|
37
|
+
FIELDS_SET = ( FIELDS.keys + FIELDS_EXTRA ).map { |field| ( field.to_s + '=' ).to_sym }.freeze
|
38
|
+
|
39
|
+
validates :privacy, acceptance: true
|
40
|
+
validates :privacy2, acceptance: { accept: true }
|
41
|
+
validates :email, confirmation: true
|
42
|
+
validates :email_confirmation, presence: true
|
43
|
+
validates :email2, confirmation: { case_sensitive: false }
|
44
|
+
validates :letter, exclusion: { in: %w(a b c) }, length: { is: 1 }
|
45
|
+
validates :letter2, inclusion: { in: %w(a b c) }, length: { is: 1 }
|
46
|
+
validates :title, length: { in: 15..20 }
|
47
|
+
validates :title2, length: { within: 15..20 }
|
48
|
+
validates :title3, length: { minimum: 15 }
|
49
|
+
validates :title4, length: { maximum: 20 }
|
50
|
+
validates :title5, length: { is: 18 }
|
51
|
+
validates :integer, numericality: { only_integer: true }
|
52
|
+
validates :number, numericality: true
|
53
|
+
validates :number2, numericality: { greater_than: 15, less_than_or_equal_to: 20 }
|
54
|
+
validates :number3, numericality: { greater_than_or_equal_to: 15, less_than: 20 }
|
55
|
+
validates :number4, numericality: { equal_to: 18 }
|
56
|
+
validates :unique, uniqueness: true
|
57
|
+
validates :unique2, uniqueness: { scope: :email }
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@data = {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def []( field )
|
64
|
+
@data[field.to_sym]
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.content_columns
|
68
|
+
FIELDS.map do |field, type|
|
69
|
+
OpenStruct.new( { name: field.to_s, type: type } )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self._reflections
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def method_missing( method, *args, &block )
|
80
|
+
if FIELDS.keys.include?( method ) || FIELDS_EXTRA.include?( method )
|
81
|
+
self[method]
|
82
|
+
elsif FIELDS_SET.include?( method )
|
83
|
+
# p method
|
84
|
+
@data[method.to_s.chop.to_sym] = args[0]
|
85
|
+
else
|
86
|
+
# p method
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def respond_to?( method, include_private = false )
|
92
|
+
FIELDS.keys.include?( method ) || FIELDS_EXTRA.include?( method ) || FIELDS_SET.include?( method ) || super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'Check validators' do
|
97
|
+
before do
|
98
|
+
AutoSeeding::Seeder.config # reset global options
|
99
|
+
@test_validators = AutoSeeding::Seeder.new
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'When update method is called' do
|
103
|
+
it 'must set the validators options' do
|
104
|
+
@test_validators.update( TestObject.new )
|
105
|
+
cols = @test_validators.columns
|
106
|
+
|
107
|
+
## Validation Helpers
|
108
|
+
# --- acceptance --------------------------------------------------------
|
109
|
+
assert_equal cols[:privacy][:validators][:accept], ['1', true]
|
110
|
+
assert_equal cols[:privacy2][:validators][:accept], [true]
|
111
|
+
# --- confirmation ------------------------------------------------------
|
112
|
+
assert_equal @test_validators.extra_validations[:confirmations], [:email, :email2]
|
113
|
+
# --- exclusion ---------------------------------------------------------
|
114
|
+
assert_equal cols[:letter][:validators][:not_in], ['a', 'b', 'c']
|
115
|
+
# --- format ------------------------------------------------------------
|
116
|
+
# TODO: not implemented
|
117
|
+
# --- inclusion ---------------------------------------------------------
|
118
|
+
assert_equal cols[:letter2][:validators][:in], ['a', 'b', 'c']
|
119
|
+
# --- length ------------------------------------------------------------
|
120
|
+
assert_equal cols[:title][:validators][:length_minimum], 15
|
121
|
+
assert_equal cols[:title][:validators][:length_maximum], 20
|
122
|
+
assert_equal cols[:title2][:validators][:length_minimum], 15
|
123
|
+
assert_equal cols[:title2][:validators][:length_maximum], 20
|
124
|
+
assert_equal cols[:title3][:validators][:length_minimum], 15
|
125
|
+
assert_equal cols[:title4][:validators][:length_maximum], 20
|
126
|
+
assert_equal cols[:title5][:validators][:length_minimum], 18
|
127
|
+
assert_equal cols[:title5][:validators][:length_maximum], 18
|
128
|
+
# --- numericality ------------------------------------------------------
|
129
|
+
# cols[:number] # -> set type to integer ?
|
130
|
+
# cols[:integer] # -> set type to integer ?
|
131
|
+
assert_equal cols[:number2][:validators][:num_gt], 15
|
132
|
+
assert_equal cols[:number2][:validators][:num_lte], 20
|
133
|
+
assert_equal cols[:number3][:validators][:num_gte], 15
|
134
|
+
assert_equal cols[:number3][:validators][:num_lt], 20
|
135
|
+
assert_equal cols[:number4][:validators][:equal_to], 18
|
136
|
+
# --- presence ----------------------------------------------------------
|
137
|
+
# TODO: not implemented
|
138
|
+
# --- absence -----------------------------------------------------------
|
139
|
+
# TODO: not implemented
|
140
|
+
# --- uniqueness --------------------------------------------------------
|
141
|
+
assert cols[:unique][:validators][:uniqueness]
|
142
|
+
assert cols[:unique2][:validators][:uniqueness]
|
143
|
+
# --- validates_with ----------------------------------------------------
|
144
|
+
# TODO: not implemented
|
145
|
+
# --- validates_each ----------------------------------------------------
|
146
|
+
# TODO: not implemented
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'must generate values respecting validations rules' do
|
150
|
+
obj = @test_validators.update( TestObject.new )
|
151
|
+
assert obj.privacy == '1' || obj.privacy == true
|
152
|
+
assert obj.privacy2
|
153
|
+
assert obj.email == obj.email_confirmation
|
154
|
+
assert obj.email2 == obj.email2_confirmation
|
155
|
+
assert !( ['a', 'b', 'c'].include?( obj.letter ) )
|
156
|
+
assert ['a', 'b', 'c'].include?( obj.letter2 )
|
157
|
+
assert obj.title.length >= 15 && obj.title.length <= 20
|
158
|
+
assert obj.title2.length >= 15 && obj.title2.length <= 20
|
159
|
+
assert obj.title3.length >= 15
|
160
|
+
assert obj.title4.length <= 20
|
161
|
+
assert obj.title5.length == 18
|
162
|
+
assert obj.number2 > 15 && obj.number2 <= 20
|
163
|
+
assert obj.number3 >= 15 && obj.number3 < 20
|
164
|
+
assert obj.number4 == 18
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: auto-seeding
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Roccoberton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -87,7 +87,26 @@ executables: []
|
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- auto-seeding.gemspec
|
90
96
|
- lib/auto-seeding.rb
|
97
|
+
- lib/auto-seeding/data/basic.yml
|
98
|
+
- lib/auto-seeding/data/faker.yml
|
99
|
+
- lib/auto-seeding/data/ffaker.yml
|
100
|
+
- lib/auto-seeding/seeder.rb
|
101
|
+
- lib/auto-seeding/source.rb
|
102
|
+
- lib/auto-seeding/utils.rb
|
103
|
+
- test/conf.yml
|
104
|
+
- test/test_basic.rb
|
105
|
+
- test/test_faker.rb
|
106
|
+
- test/test_ffaker.rb
|
107
|
+
- test/test_globals.rb
|
108
|
+
- test/test_options.rb
|
109
|
+
- test/test_validators.rb
|
91
110
|
homepage: https://github.com/blocknotes/auto-seeding
|
92
111
|
licenses:
|
93
112
|
- MIT
|