pokotarou 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +342 -0
- data/Rakefile +33 -0
- data/lib/pokotarou/additional_methods.rb +14 -0
- data/lib/pokotarou/data_register.rb +176 -0
- data/lib/pokotarou/data_structure.rb +85 -0
- data/lib/pokotarou/expression_parser.rb +83 -0
- data/lib/pokotarou/loader.rb +16 -0
- data/lib/pokotarou/my_mthods.rb +10 -0
- data/lib/pokotarou/option.rb +42 -0
- data/lib/pokotarou/query_builder.rb +44 -0
- data/lib/pokotarou/seeder.rb +32 -0
- data/lib/pokotarou/version.rb +3 -0
- data/lib/pokotarou.rb +43 -0
- data/lib/tasks/pokotarou_tasks.rake +4 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 38bd381d74ed4731fdb2c3af3211af492cea80f5e08f7d0cd42944e2b722aa8c
|
4
|
+
data.tar.gz: 161e98919c174d0cd170c9401d70d65bc5a2311912e8201618c6fac3c841580b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a39ece97d618b6670b69e3db80064b3a499ac315f918293550f807c7a5b8d7360900631bbd32710c05445ff9b780865e7ea621c66f59a5712252d612f145872
|
7
|
+
data.tar.gz: a3d17bd0d59f04e373ac31039d1399592e43aec29fba8802b509b690f97c00780b228529e01c883276f046162c906059ac7d7e54e1289e50926e6910524a5a92
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2019
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,342 @@
|
|
1
|
+
![s_PokotarouLogo](https://user-images.githubusercontent.com/52961642/61586183-ef59b580-aba8-11e9-83b0-19eac7339982.png)
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/Tamatebako0205/Pokotarou.svg?branch=master)](https://travis-ci.org/Tamatebako0205/Pokotarou)
|
4
|
+
|
5
|
+
Pokotarou is convenient seeder of 'Ruby on Rails' that uses .yml
|
6
|
+
Currently only mysql supported
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
### Easy to use
|
11
|
+
Currently can be used only for simple configuration file settings
|
12
|
+
You don't have to write a program for seeder
|
13
|
+
|
14
|
+
### Fast speed
|
15
|
+
If it is the following table, 10,000 records can regist in 0.4s on average
|
16
|
+
Also, If you enable 'optimize option', 10,000 records can regist in 0.23s on average
|
17
|
+
|
18
|
+
|Field|Type|NULL|
|
19
|
+
|:---|:---|:---|
|
20
|
+
|id|bigint(20)| NO|
|
21
|
+
|name|varchar(255)|YES|
|
22
|
+
|created_at|datetime|NO|
|
23
|
+
|updated_at|datetime|NO|
|
24
|
+
|
25
|
+
## Getting started
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'pokotarou'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
```bash
|
34
|
+
$ bundle
|
35
|
+
```
|
36
|
+
|
37
|
+
Or install it yourself as:
|
38
|
+
```bash
|
39
|
+
$ gem install pokotarou
|
40
|
+
```
|
41
|
+
|
42
|
+
## License
|
43
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
Introduce how to execute and write configuration file.
|
47
|
+
If you want to know more information file settings, please refer to the test
|
48
|
+
|
49
|
+
### Extecute
|
50
|
+
After write configuration file, execute the following source code in seeds.rb of Ruby on Rails
|
51
|
+
|
52
|
+
```
|
53
|
+
Pokotarou.execute(ConfigrationFilePath)
|
54
|
+
```
|
55
|
+
### Configration file
|
56
|
+
|
57
|
+
Introduce how to write yml configuration file
|
58
|
+
|
59
|
+
#### Definition for explanation
|
60
|
+
Table name below is 'prefs' and model name is 'Pref'
|
61
|
+
|
62
|
+
|Field|Type|NULL|
|
63
|
+
|:---|:---|:---|
|
64
|
+
|id|bigint(20)| NO|
|
65
|
+
|name|varchar(255)|YES|
|
66
|
+
|created_at|datetime|NO|
|
67
|
+
|updated_at|datetime|NO|
|
68
|
+
|
69
|
+
|
70
|
+
Table name below is 'members' and model name is 'Member'
|
71
|
+
|
72
|
+
|Field|Type|NULL|
|
73
|
+
|:---|:---|:---|
|
74
|
+
|id|bigint(20)| NO|
|
75
|
+
|name|varchar(255)|YES|
|
76
|
+
|remarks|text|YES|
|
77
|
+
|birthday|date|YES|
|
78
|
+
|pref_id|bigint(20)|YES|
|
79
|
+
|created_at|datetime|NO|
|
80
|
+
|updated_at|datetime|NO|
|
81
|
+
|
82
|
+
|
83
|
+
#### Default data
|
84
|
+
If there is no column definition, prepared data is registerd three times
|
85
|
+
id column is basically registerd by autoincrement
|
86
|
+
|
87
|
+
```
|
88
|
+
Default:
|
89
|
+
Pref:
|
90
|
+
loop: 3
|
91
|
+
```
|
92
|
+
|
93
|
+
|
94
|
+
In the following source code, id, created_at, updated_at will be registerd with the prepared data
|
95
|
+
|
96
|
+
```
|
97
|
+
Default:
|
98
|
+
Pref:
|
99
|
+
loop: 3
|
100
|
+
col:
|
101
|
+
name: "Hokkaido"
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Array
|
105
|
+
Array data is registerd one by one
|
106
|
+
|
107
|
+
```
|
108
|
+
Default:
|
109
|
+
Pref:
|
110
|
+
loop: 3
|
111
|
+
col:
|
112
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
113
|
+
```
|
114
|
+
|
115
|
+
#### Maked data
|
116
|
+
Registration is possible using registerd data
|
117
|
+
|
118
|
+
```
|
119
|
+
Default:
|
120
|
+
Pref:
|
121
|
+
loop: 2
|
122
|
+
col:
|
123
|
+
name: ["Hokkaido", "Aomori"]
|
124
|
+
Member:
|
125
|
+
loop: 2
|
126
|
+
col:
|
127
|
+
name: <maked[:Default][:Pref][:name]>
|
128
|
+
pref_id: F|Pref
|
129
|
+
```
|
130
|
+
|
131
|
+
```
|
132
|
+
Default:
|
133
|
+
Pref:
|
134
|
+
loop: 3
|
135
|
+
col:
|
136
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
137
|
+
Member:
|
138
|
+
loop: 3
|
139
|
+
col:
|
140
|
+
name: ["Tarou", "Jirou", "Saburou"]
|
141
|
+
remarks: <maked[:Default][:Member][:name]>
|
142
|
+
pref_id: F|Pref
|
143
|
+
|
144
|
+
```
|
145
|
+
|
146
|
+
```
|
147
|
+
Default:
|
148
|
+
Pref:
|
149
|
+
loop: 2
|
150
|
+
col:
|
151
|
+
name: ["Hokkaido", "Aomori"]
|
152
|
+
Default2:
|
153
|
+
Member:
|
154
|
+
loop: 2
|
155
|
+
col:
|
156
|
+
name: <maked[:Default][:Pref][:name]>
|
157
|
+
pref_id: F|Pref
|
158
|
+
```
|
159
|
+
|
160
|
+
#### Foreign key
|
161
|
+
|
162
|
+
**※ If you set association(belongs_to, has_many...), Pokotarou automatically register foreign keys**
|
163
|
+
|
164
|
+
What a means is ' F| ' foreign key
|
165
|
+
In the following source code, Foreign key of prefectures is registerd
|
166
|
+
|
167
|
+
```
|
168
|
+
Default:
|
169
|
+
Pref:
|
170
|
+
loop: 3
|
171
|
+
col:
|
172
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
173
|
+
Member:
|
174
|
+
loop: 3
|
175
|
+
col:
|
176
|
+
pref_id: F|Pref
|
177
|
+
```
|
178
|
+
|
179
|
+
#### Expression expansion
|
180
|
+
What a means is '< >' expression expansion
|
181
|
+
You can run ruby code in '< >'
|
182
|
+
|
183
|
+
```
|
184
|
+
Default:
|
185
|
+
Pref:
|
186
|
+
loop: 3
|
187
|
+
col:
|
188
|
+
name: <["Hokkaido", "Aomori", "Iwate"]>
|
189
|
+
created_at: <Date.parse('1997/02/05')>
|
190
|
+
```
|
191
|
+
|
192
|
+
#### Add method
|
193
|
+
You can add method and use it in pokotarou
|
194
|
+
|
195
|
+
```
|
196
|
+
Default:
|
197
|
+
Pref:
|
198
|
+
loop: 3
|
199
|
+
col:
|
200
|
+
name: <pref_name>
|
201
|
+
```
|
202
|
+
|
203
|
+
Prepare the following ruby file
|
204
|
+
```
|
205
|
+
def pref_name
|
206
|
+
["Hokkaido", "Aomori", "Iwate"]
|
207
|
+
end
|
208
|
+
```
|
209
|
+
and execute the following source code in seeds.rb of Ruby on Rails
|
210
|
+
|
211
|
+
```
|
212
|
+
Pokotarou.import(MethodFilePath)
|
213
|
+
Pokotarou.execute(ConfigrationFilePath)
|
214
|
+
```
|
215
|
+
|
216
|
+
|
217
|
+
#### Use multiple blocks
|
218
|
+
|
219
|
+
Registration is possible using two blocks
|
220
|
+
|
221
|
+
```
|
222
|
+
Default:
|
223
|
+
Pref:
|
224
|
+
loop: 3
|
225
|
+
Default2:
|
226
|
+
Pref:
|
227
|
+
loop: 3
|
228
|
+
```
|
229
|
+
|
230
|
+
and, You can change the name of the block
|
231
|
+
|
232
|
+
```
|
233
|
+
Hoge:
|
234
|
+
Pref:
|
235
|
+
loop: 3
|
236
|
+
Fuga:
|
237
|
+
Pref:
|
238
|
+
loop: 3
|
239
|
+
```
|
240
|
+
|
241
|
+
|
242
|
+
#### Random
|
243
|
+
Shuffle seed data when regist
|
244
|
+
|
245
|
+
```
|
246
|
+
Default:
|
247
|
+
Pref:
|
248
|
+
loop: 3
|
249
|
+
col:
|
250
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
251
|
+
option:
|
252
|
+
name: ["random"]
|
253
|
+
```
|
254
|
+
|
255
|
+
The following results change from run to run
|
256
|
+
|
257
|
+
```
|
258
|
+
["Aomori", "Iwate", "Iwate"]
|
259
|
+
```
|
260
|
+
|
261
|
+
#### Add_id
|
262
|
+
Add individual number to seed data of String type
|
263
|
+
|
264
|
+
```
|
265
|
+
Default:
|
266
|
+
Pref:
|
267
|
+
loop: 3
|
268
|
+
col:
|
269
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
270
|
+
option:
|
271
|
+
name: ["add_id"]
|
272
|
+
```
|
273
|
+
|
274
|
+
```
|
275
|
+
["Hokkaido_0", "Aomori"_1, "Iwate_2"]
|
276
|
+
```
|
277
|
+
|
278
|
+
#### Combine serveral options
|
279
|
+
Combination of options is possible
|
280
|
+
|
281
|
+
```
|
282
|
+
Default:
|
283
|
+
Pref:
|
284
|
+
loop: 3
|
285
|
+
col:
|
286
|
+
name: ["Hokkaido", "Aomori", "Iwate"]
|
287
|
+
option:
|
288
|
+
name: ["add_id", "random"]
|
289
|
+
```
|
290
|
+
|
291
|
+
The following results change from run to run
|
292
|
+
|
293
|
+
```
|
294
|
+
["Hokkaido_0", "Iwate_1", "Hokkaido_2"]
|
295
|
+
```
|
296
|
+
|
297
|
+
#### Validation
|
298
|
+
|
299
|
+
Run validation when regist
|
300
|
+
|
301
|
+
```
|
302
|
+
Default:
|
303
|
+
Pref:
|
304
|
+
loop: 3
|
305
|
+
validate: true
|
306
|
+
```
|
307
|
+
|
308
|
+
#### Autoincrement
|
309
|
+
|
310
|
+
You can disable the autoincrement setting
|
311
|
+
|
312
|
+
If you disable the setting, you can register id data prepared by myself
|
313
|
+
|
314
|
+
```
|
315
|
+
Default:
|
316
|
+
Pref:
|
317
|
+
loop: 3
|
318
|
+
autoincrement: false
|
319
|
+
col:
|
320
|
+
id: [100, 101, 102]
|
321
|
+
```
|
322
|
+
|
323
|
+
#### Optimize
|
324
|
+
|
325
|
+
If you enable 'optimize', Pokotarou Run a query builder, but disable validation
|
326
|
+
|
327
|
+
```
|
328
|
+
Default:
|
329
|
+
Pref:
|
330
|
+
loop: 3
|
331
|
+
optimaize: true
|
332
|
+
```
|
333
|
+
|
334
|
+
#### Update parameter
|
335
|
+
|
336
|
+
You can update pokotarou's parameter
|
337
|
+
|
338
|
+
```
|
339
|
+
config_data = Pokotarou.get_config("ConfigrationFilePath")
|
340
|
+
config_data[:Default][:Pref][:loop] = 6
|
341
|
+
Pokotarou.do_seed(config_data)
|
342
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Pokotarou'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
task default: :test
|
@@ -0,0 +1,176 @@
|
|
1
|
+
class RegistError < StandardError; end
|
2
|
+
class SeedError < StandardError; end
|
3
|
+
|
4
|
+
class DataRegister
|
5
|
+
class << self
|
6
|
+
def regist data
|
7
|
+
# init maked to accumulate maded data
|
8
|
+
maked = Hash.new
|
9
|
+
# init model_data to cache data of model
|
10
|
+
model_cache = Hash.new
|
11
|
+
ActiveRecord::Base.transaction do
|
12
|
+
begin
|
13
|
+
data.each do |sym_block, model_data|
|
14
|
+
regist_models(sym_block, model_data, maked, model_cache)
|
15
|
+
end
|
16
|
+
rescue => e
|
17
|
+
raise StandardError.new("#{e.message}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def execute model, config_data, table_name, col_arr, seed_arr
|
25
|
+
# optimize is more faster than activerecord-import
|
26
|
+
# however, sql.conf setting is necessary to use
|
27
|
+
if config_data[:optimize]
|
28
|
+
# seed_arr.transpose: [[col1_element, col2_element], [col1_element, col2_element]...]
|
29
|
+
insert_query = QueryBuilder.insert(table_name, col_arr, seed_arr.transpose)
|
30
|
+
ActiveRecord::Base.connection.execute(insert_query)
|
31
|
+
else
|
32
|
+
model.import(col_arr, seed_arr.transpose, validate: config_data[:validate], timestamps: false)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def regist_models sym_block, model_data, maked, model_cache
|
37
|
+
model_data.each do |e|
|
38
|
+
str_model = e.first.to_s
|
39
|
+
save_model_cache(model_cache, str_model)
|
40
|
+
# model_data.values is config_data
|
41
|
+
config_data = e.second
|
42
|
+
# col_arr: [:col1, :col2, :col3]
|
43
|
+
col_arr = config_data[:col].keys
|
44
|
+
|
45
|
+
# set expand expression for loop '<>' and ':' and so on...
|
46
|
+
set_loop_expand_expression(config_data, maked)
|
47
|
+
# if there is no setting data, set default seed data
|
48
|
+
set_default_seed(config_data)
|
49
|
+
# seed_arr: [[col1_element, col1_element], [col2_element, col2_element]...]
|
50
|
+
seed_arr =
|
51
|
+
get_seed_arr(model_cache[str_model][:model], sym_block,
|
52
|
+
model_cache[str_model][:sym_model], config_data, maked)
|
53
|
+
|
54
|
+
output_log(config_data[:log])
|
55
|
+
begin
|
56
|
+
# execute insert
|
57
|
+
execute(model_cache[str_model][:model],
|
58
|
+
config_data, model_cache[str_model][:table_name], col_arr, seed_arr)
|
59
|
+
rescue => e
|
60
|
+
raise RegistError.new("
|
61
|
+
block: #{sym_block}
|
62
|
+
model: #{str_model}
|
63
|
+
message: #{e.message}
|
64
|
+
")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def save_model_cache model_cache, str_model
|
70
|
+
return if model_cache[str_model].present?
|
71
|
+
model = eval(str_model)
|
72
|
+
model_cache[str_model] = {
|
73
|
+
model: model,
|
74
|
+
sym_model: str_model.to_sym,
|
75
|
+
table_name: model.table_name
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_default_seed config_data
|
80
|
+
block = ->(symbol){ :id == symbol }
|
81
|
+
# each column type, key is symbolize column name
|
82
|
+
config_data[:type].each do |key, _|
|
83
|
+
# if it is id, skip
|
84
|
+
next if block.call(key)
|
85
|
+
# if there is data already, skip
|
86
|
+
next if config_data[:col][key].present?
|
87
|
+
|
88
|
+
config_data[:col][key] = Seeder.gen(config_data, key)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_seed_arr model, sym_block, sym_model, config_data, maked
|
93
|
+
options = config_data[:option]
|
94
|
+
loop_size = config_data[:loop]
|
95
|
+
|
96
|
+
|
97
|
+
if apply_autoincrement?(config_data[:autoincrement])
|
98
|
+
set_autoincrement(config_data, model, loop_size)
|
99
|
+
end
|
100
|
+
|
101
|
+
config_data[:col].map do |key, val|
|
102
|
+
begin
|
103
|
+
# set expand expression '<>' and ':' and so on...
|
104
|
+
set_expand_expression(config_data, key, val, maked)
|
105
|
+
expanded_val = config_data[:col][key]
|
106
|
+
option_conf = options.nil? ? nil : Option.gen(options[key])
|
107
|
+
# Take count yourself, because .with_index is slow
|
108
|
+
cnt = 0
|
109
|
+
seeds =
|
110
|
+
loop_size.times.map do
|
111
|
+
seed = option_conf.nil? ? get_seed(expanded_val, cnt) : get_seed_with_option(expanded_val, option_conf, cnt)
|
112
|
+
cnt += 1
|
113
|
+
|
114
|
+
seed
|
115
|
+
end
|
116
|
+
update_maked_data(maked, sym_block, sym_model, key, seeds)
|
117
|
+
|
118
|
+
seeds
|
119
|
+
rescue => e
|
120
|
+
raise SeedError.new("
|
121
|
+
block: #{sym_block}
|
122
|
+
model: #{sym_model}
|
123
|
+
column: #{key}
|
124
|
+
message: #{e.message}
|
125
|
+
")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def apply_autoincrement? autoincrement_flg
|
131
|
+
# default true
|
132
|
+
return true if autoincrement_flg.nil?
|
133
|
+
autoincrement_flg
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_autoincrement config_data, model, loop_size
|
137
|
+
last_record = model.last
|
138
|
+
# use pluck to optimize(suppress make object)
|
139
|
+
additions = model.all.pluck(:id).size + loop_size
|
140
|
+
latest_id = last_record.nil? ? 1 : last_record.id + 1
|
141
|
+
config_data[:col][:id] = [*latest_id..additions]
|
142
|
+
end
|
143
|
+
|
144
|
+
def set_expand_expression config_data, key, val, maked
|
145
|
+
# if it exists type, there is no need for doing 'expand expression'
|
146
|
+
return if config_data[:type][key].present?
|
147
|
+
config_data[:col][key] = ExpressionParser.parse(val, maked)
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_loop_expand_expression config_data, maked
|
151
|
+
config_data[:loop] =
|
152
|
+
LoopExpressionParser.parse(config_data[:loop], maked)
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_seed arr, cnt
|
156
|
+
get_rotated_val(arr, cnt)
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_seed_with_option arr, option, cnt
|
160
|
+
Option.apply(arr, option, cnt)
|
161
|
+
end
|
162
|
+
|
163
|
+
def update_maked_data maked, sym_block, sym_model, col, seed
|
164
|
+
# maked: { key: Model, value: {key: col1, val: [col1_element, col1_element]} }
|
165
|
+
maked[sym_block] ||= Hash.new
|
166
|
+
maked[sym_block][sym_model] ||= Hash.new
|
167
|
+
maked[sym_block][sym_model][col] = seed
|
168
|
+
end
|
169
|
+
|
170
|
+
def output_log log
|
171
|
+
return if log.nil?
|
172
|
+
puts log
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class DataStructure
|
2
|
+
class << self
|
3
|
+
def gen data
|
4
|
+
# return data structure bellow
|
5
|
+
# [{ block_name => { model_name => { column_configration }}}, ...]
|
6
|
+
data.reduce(Hash.new) do |acc, r|
|
7
|
+
# r.first is block_name
|
8
|
+
# r.second is model_data, like { Pref: {loop: 3}, Member: {loop: 3}... }
|
9
|
+
acc[r.first] = gen_structure(r.second)
|
10
|
+
|
11
|
+
acc
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def gen_structure model_data
|
18
|
+
model_data.reduce(Hash.new) do |acc, r|
|
19
|
+
# r.second is config_data, like {loop: 3, ...}
|
20
|
+
set_col_type(r.second, r[0].to_s)
|
21
|
+
acc[r[0]] = r.second
|
22
|
+
|
23
|
+
acc
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_col_type config_data, str_model
|
28
|
+
model = eval(str_model)
|
29
|
+
foreign_key_data = get_foreign_key_data(model)
|
30
|
+
|
31
|
+
config_data[:col] ||= Hash.new
|
32
|
+
model.columns.each do |e|
|
33
|
+
symbol_col_name = e.name.to_sym
|
34
|
+
|
35
|
+
unless exists_seed_data?(config_data, symbol_col_name)
|
36
|
+
# prepare setting to run default seed
|
37
|
+
# set nil to seed data
|
38
|
+
config_data[:col][symbol_col_name] = nil
|
39
|
+
|
40
|
+
# set type info
|
41
|
+
config_data[:type] ||= Hash.new
|
42
|
+
config_data[:type][symbol_col_name] = e.type.to_s
|
43
|
+
|
44
|
+
# set enum info
|
45
|
+
config_data[:enum] ||= Hash.new
|
46
|
+
if is_enum?(e.sql_type.to_s)
|
47
|
+
config_data[:enum][symbol_col_name] =
|
48
|
+
e.sql_type.to_s[5..-2].tr("'", "").split(",")
|
49
|
+
end
|
50
|
+
|
51
|
+
# set foreign_key info
|
52
|
+
config_data[:foreign_key] ||= Hash.new
|
53
|
+
if is_foreign_key?(symbol_col_name, foreign_key_data)
|
54
|
+
config_data[:foreign_key][symbol_col_name] = foreign_key_data[symbol_col_name]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_foreign_key_data model
|
62
|
+
associations = model.reflect_on_all_associations(:belongs_to)
|
63
|
+
return { } if associations.empty?
|
64
|
+
associations.reduce(Hash.new)do |acc, r|
|
65
|
+
acc[r.foreign_key.to_sym] = eval(r.name.to_s.classify)
|
66
|
+
|
67
|
+
acc
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def is_foreign_key? symbol_col_name, foreign_key_data
|
72
|
+
foreign_key_data[symbol_col_name].present?
|
73
|
+
end
|
74
|
+
|
75
|
+
def exists_seed_data? config_data ,symbol_col_name
|
76
|
+
config_data[:col].has_key?(symbol_col_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
ENUM = /^enum(\s*.)*$/
|
80
|
+
def is_enum? val
|
81
|
+
return false unless val.kind_of?(String)
|
82
|
+
ENUM =~ val
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "pokotarou/additional_methods.rb"
|
2
|
+
class ParseError < StandardError; end
|
3
|
+
|
4
|
+
# for seed data
|
5
|
+
class ExpressionParser
|
6
|
+
class << self
|
7
|
+
FOREIGN_KEY_SYMBOL = "F|"
|
8
|
+
def parse config_val, maked
|
9
|
+
begin
|
10
|
+
require AdditionalMethods.filepath if AdditionalMethods.filepath.present?
|
11
|
+
case
|
12
|
+
when config_val.instance_of?(Array)
|
13
|
+
return config_val
|
14
|
+
when config_val.nil?
|
15
|
+
return nil
|
16
|
+
when is_foreign_key?(config_val)
|
17
|
+
# remove 'F|'
|
18
|
+
str_model = config_val.sub(FOREIGN_KEY_SYMBOL, "")
|
19
|
+
model = eval(str_model)
|
20
|
+
return model.pluck(:id)
|
21
|
+
when is_expression?(config_val)
|
22
|
+
# remove '<>'
|
23
|
+
expression = config_val.strip[1..-2]
|
24
|
+
return self.parse(eval(expression), maked)
|
25
|
+
else
|
26
|
+
if config_val.instance_of?(String)
|
27
|
+
# escape \\
|
28
|
+
[config_val.tr("\\","")]
|
29
|
+
else
|
30
|
+
[config_val]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue => e
|
34
|
+
ParseError.new("Failed Expression parse:#{e.message}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# for loop data
|
41
|
+
class LoopExpressionParser
|
42
|
+
class << self
|
43
|
+
FOREIGN_KEY_SYMBOL = "F|"
|
44
|
+
def parse config_val, maked
|
45
|
+
begin
|
46
|
+
require AdditionalMethods.filepath if AdditionalMethods.filepath.present?
|
47
|
+
case
|
48
|
+
when config_val.instance_of?(Array)
|
49
|
+
return config_val.size
|
50
|
+
when config_val.instance_of?(Integer)
|
51
|
+
return config_val
|
52
|
+
when config_val.nil?
|
53
|
+
return 1
|
54
|
+
when is_foreign_key?(config_val)
|
55
|
+
# remove 'F|'
|
56
|
+
str_model = config_val.sub(FOREIGN_KEY_SYMBOL, "")
|
57
|
+
model = eval(str_model)
|
58
|
+
return model.pluck(:id).size
|
59
|
+
when is_expression?(config_val)
|
60
|
+
# remove '<>'
|
61
|
+
expression = config_val.strip[1..-2]
|
62
|
+
return self.parse(eval(expression), maked)
|
63
|
+
else
|
64
|
+
return 1
|
65
|
+
end
|
66
|
+
rescue => e
|
67
|
+
ParseError.new("Failed Loop Expression parse: #{e.message}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
FOREIGN_KEY = /^F\|[A-Z][A-Za-z0-9]*$/
|
74
|
+
def is_foreign_key? val;
|
75
|
+
return false unless val.kind_of?(String)
|
76
|
+
FOREIGN_KEY =~ val
|
77
|
+
end
|
78
|
+
|
79
|
+
EXPRESSION = /^\s*<.*>\s*$/
|
80
|
+
def is_expression? val
|
81
|
+
return false unless val.kind_of?(String)
|
82
|
+
EXPRESSION =~ val
|
83
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class FileLoader
|
2
|
+
class << self
|
3
|
+
# load data with the use of indicated filepath
|
4
|
+
def load filepath
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class YmlLoader < FileLoader
|
10
|
+
class << self
|
11
|
+
def load filepath
|
12
|
+
# convert 'str_key' to 'symbol_key'
|
13
|
+
YAML.load_file(filepath).deep_symbolize_keys!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class NothingDataError < StandardError; end
|
2
|
+
|
3
|
+
# return rotated val in passed array
|
4
|
+
def get_rotated_val array, cnt
|
5
|
+
raise NothingDataError.new("Nothing seed data") if array.nil?
|
6
|
+
size = array.size
|
7
|
+
raise NothingDataError.new("Seed data is empty") if size.zero?
|
8
|
+
x = (size + cnt) / size
|
9
|
+
array[ size + cnt - size * x ]
|
10
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Option
|
2
|
+
class << self
|
3
|
+
def gen option
|
4
|
+
# shape option data
|
5
|
+
# [option1, option2] => { select: [option2], add: [option1] }
|
6
|
+
# [] => { select: [], add: [] }
|
7
|
+
option.nil? ? { select: [], add: [] } : separate(option)
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply arr, option_conf, cnt = 0
|
11
|
+
selected_val = select(option_conf[:select], arr, cnt)
|
12
|
+
add(option_conf[:add], selected_val, cnt)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def separate option
|
18
|
+
# separate option to 'select' and 'add'
|
19
|
+
# { select = >[], add => [] }
|
20
|
+
select_filter = ->(name){ ["rotate", "random"].include?(name) }
|
21
|
+
add_filter = ->(name){ ["add_id"].include?(name) }
|
22
|
+
|
23
|
+
{
|
24
|
+
select: option.find{|s| select_filter.call(s)},
|
25
|
+
add: option.find{|s| add_filter.call(s)}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def select option, arr, cnt
|
30
|
+
return arr.sample if option == "random"
|
31
|
+
# default return rotate
|
32
|
+
get_rotated_val(arr, cnt)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add option, val, cnt
|
36
|
+
# use '+', not use '<<' to avoid destructive effect
|
37
|
+
return val + "_#{cnt}" if option == "add_id"
|
38
|
+
|
39
|
+
val
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class QueryBuilder
|
2
|
+
class << self
|
3
|
+
|
4
|
+
# build insert query
|
5
|
+
def insert table_name, col_arr, seed_arr
|
6
|
+
col_str = convert_col_to_sql_str(col_arr)
|
7
|
+
seed_str = convert_seed_to_sql_str(seed_arr)
|
8
|
+
|
9
|
+
"INSERT INTO #{table_name} #{col_str} VALUES#{seed_str}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def convert_to_sql_str arr
|
13
|
+
arr_str =
|
14
|
+
arr.reduce("(") do |acc, r|
|
15
|
+
acc << add_double_quote(r.to_s) << ","
|
16
|
+
end
|
17
|
+
# remove ' , ' and add ' ) '
|
18
|
+
arr_str.chop << ")"
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_seed_to_sql_str seed_arr
|
22
|
+
seed_str =
|
23
|
+
seed_arr.reduce("") do |acc, r|
|
24
|
+
acc << convert_to_sql_str(r) << ","
|
25
|
+
end
|
26
|
+
# remove ' , '
|
27
|
+
seed_str.chop
|
28
|
+
end
|
29
|
+
|
30
|
+
def convert_col_to_sql_str col_arr
|
31
|
+
col_str =
|
32
|
+
col_arr.reduce("(") do |acc, r|
|
33
|
+
acc << r.to_s << ","
|
34
|
+
end
|
35
|
+
# remove ' , ' and add ' ) '
|
36
|
+
col_str.chop << ")"
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def add_double_quote str
|
41
|
+
"\"" << str << "\""
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Seeder
|
2
|
+
class << self
|
3
|
+
MAX_LOOP = 100
|
4
|
+
def gen config_data, key
|
5
|
+
n = config_data[:loop] > MAX_LOOP ? MAX_LOOP : config_data[:loop]
|
6
|
+
type = config_data[:type][key]
|
7
|
+
enum = config_data[:enum][key]
|
8
|
+
foreign_key = config_data[:foreign_key][key]
|
9
|
+
|
10
|
+
return foreign_key.pluck(:id) if foreign_key.present?
|
11
|
+
return enum if enum.present?
|
12
|
+
return make_array(n, ->(){ rand(100) }) if type == "integer"
|
13
|
+
return make_array(n, ->(){ rand(0.0..100.0) }) if type == "float"
|
14
|
+
return make_array(n, ->(){ rand(0.0..1_000_000_000.0) }) if type == "decimal"
|
15
|
+
return make_array(n, ->(){ SecureRandom.hex(20) }) if type == "string"
|
16
|
+
return make_array(n, ->(){ SecureRandom.hex(300) }) if ["text", "binary"].include?(type)
|
17
|
+
return make_array(n, ->(){ [1, 0].sample }) if type == "boolean"
|
18
|
+
return make_string_array(n, enum) if type == "string"
|
19
|
+
return make_datetime_array() if ["string", "datetime", "date", "time"].include?(type)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def make_array n, proc
|
25
|
+
Array.new(n).map{ proc.call() }
|
26
|
+
end
|
27
|
+
|
28
|
+
def make_datetime_array
|
29
|
+
[Time.now.to_s(:db)]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/pokotarou.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Dir[File.expand_path('../pokotarou', __FILE__) << '/*.rb'].each do |file|
|
2
|
+
require file
|
3
|
+
end
|
4
|
+
require "activerecord-import"
|
5
|
+
|
6
|
+
module Pokotarou
|
7
|
+
class NotFoundLoader < StandardError; end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def execute filepath
|
11
|
+
do_seed(get_config(filepath))
|
12
|
+
end
|
13
|
+
|
14
|
+
def import filepath
|
15
|
+
AdditionalMethods.import(filepath)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
AdditionalMethods.remove()
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_config filepath
|
23
|
+
contents = load_file(filepath)
|
24
|
+
DataStructure.gen(contents)
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_seed data
|
28
|
+
DataRegister.regist(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def load_file filepath
|
34
|
+
case File.extname(filepath)
|
35
|
+
when ".yml"
|
36
|
+
return YmlLoader.load(filepath)
|
37
|
+
else
|
38
|
+
raise NotFoundLoader.new("not found loader")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pokotarou
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kashiwara
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord-import
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.00'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.00'
|
41
|
+
description: 'Pokotarou is fast seeder of ''Ruby on Rails'' URL: https://github.com/Tamatebako0205/Pokotarou'
|
42
|
+
email:
|
43
|
+
- tamatebako0205@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- MIT-LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- lib/pokotarou.rb
|
52
|
+
- lib/pokotarou/additional_methods.rb
|
53
|
+
- lib/pokotarou/data_register.rb
|
54
|
+
- lib/pokotarou/data_structure.rb
|
55
|
+
- lib/pokotarou/expression_parser.rb
|
56
|
+
- lib/pokotarou/loader.rb
|
57
|
+
- lib/pokotarou/my_mthods.rb
|
58
|
+
- lib/pokotarou/option.rb
|
59
|
+
- lib/pokotarou/query_builder.rb
|
60
|
+
- lib/pokotarou/seeder.rb
|
61
|
+
- lib/pokotarou/version.rb
|
62
|
+
- lib/tasks/pokotarou_tasks.rake
|
63
|
+
homepage:
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata: {}
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.7.3
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: This gem is seeder which is very usefull
|
87
|
+
test_files: []
|