auto-seeding 0.1.4 → 0.1.5
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/.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 [](https://badge.fury.io/rb/auto-seeding) [](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
|