pokotarou 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,14 @@
1
+ module AdditionalMethods
2
+ class << self
3
+ @filepath = nil
4
+ attr_reader :filepath
5
+
6
+ def import filepath
7
+ @filepath = filepath
8
+ end
9
+
10
+ def remove
11
+ @filepath = nil
12
+ end
13
+ end
14
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Pokotarou
2
+ VERSION = '0.1.0'
3
+ 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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :pokotarou do
3
+ # # Task goes here
4
+ # 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: []