flextures 2.1.0 → 3.0.0
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.
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/README.ja.rdoc +33 -22
- data/README.rdoc +29 -22
- data/VERSION +1 -1
- data/flextures.gemspec +7 -6
- data/lib/flextures/flextures.rake +28 -4
- data/lib/flextures/flextures.rb +33 -28
- data/lib/flextures/flextures_base_config.rb +17 -6
- data/lib/flextures/flextures_command.rb +25 -27
- data/lib/flextures/flextures_dumper.rb +82 -55
- data/lib/flextures/flextures_extension_modules.rb +12 -8
- data/lib/flextures/flextures_factory.rb +13 -11
- data/lib/flextures/flextures_loader.rb +208 -132
- data/lib/flextures/rspec_flextures_support.rb +13 -7
- data/lib/flextures/testunit_flextures_support.rb +30 -4
- data/lib/flextures/version.rb +1 -1
- data/test/unit/test_flextures.rb +1 -1
- data/test/unit/test_flextures_args.rb +70 -17
- data/test/unit/test_flextures_dumper.rb +48 -37
- data/test/unit/test_flextures_extention_modules.rb +37 -0
- data/test/unit/test_flextures_hooks.rb +66 -17
- data/test/unit/test_flextures_loader.rb +89 -14
- data/test/unit/test_simple.rb +1 -1
- metadata +12 -21
@@ -1,16 +1,27 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
#
|
3
|
+
# base configurations
|
4
|
+
# if you want to change setting, create 'config/flextures.config.rb', and overwrite setting
|
5
|
+
#
|
6
|
+
# example:
|
7
|
+
# module Flextures
|
8
|
+
# # load and dump directroy setting change "spec/fixtures/" to "test/fixtures/"
|
9
|
+
# Config.fixture_load_directory = "test/fixtures/"
|
10
|
+
# Config.fixture_dump_directory = "test/fixtures/"
|
11
|
+
# end
|
12
|
+
#
|
4
13
|
module Flextures
|
5
14
|
module Config
|
6
15
|
@@read_onlys=[]
|
7
16
|
@@configs={
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
ignore_tables: ["schema_migrations"], # 'ignore_tables' table data is not deleted by flextures delete_all method
|
18
|
+
fixture_load_directory: "spec/fixtures/", # base load directory
|
19
|
+
fixture_dump_directory: "spec/fixtures/", # dump load directory
|
20
|
+
init_all_tables: false, # if this option is 'true', when start unit test, all table data is delete
|
21
|
+
options: {}, # options(example { unfilter: true })
|
22
|
+
table_load_order: [], # set load options
|
12
23
|
}
|
13
|
-
#
|
24
|
+
# hash key change to getter and setter
|
14
25
|
class<< self
|
15
26
|
@@configs.each do |setting_key, setting_value|
|
16
27
|
define_method(setting_key){ @@configs[setting_key] }
|
@@ -15,49 +15,47 @@ module Flextures
|
|
15
15
|
Flextures::init_load
|
16
16
|
table_names = Flextures::ARGS.parse
|
17
17
|
puts "dumping..."
|
18
|
-
|
18
|
+
case ENV["FORMAT"].to_s.to_sym
|
19
|
+
when :yml,:yaml
|
19
20
|
table_names.map { |fmt| Flextures::Dumper::yml(fmt) }
|
21
|
+
when :csv
|
22
|
+
table_names.map { |fmt| Flextures::Dumper::csv(fmt) }
|
20
23
|
else
|
21
24
|
table_names.map { |fmt| Flextures::Dumper::csv(fmt) }
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
25
|
-
def self.csvdump
|
26
|
-
Flextures::init_load
|
27
|
-
puts "dumping..."
|
28
|
-
table_names = Flextures::ARGS.parse
|
29
|
-
table_names.map { |fmt| Flextures::Dumper::csv(fmt) }
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.ymldump
|
33
|
-
Flextures::init_load
|
34
|
-
puts "dumping..."
|
35
|
-
table_names = Flextures::ARGS.parse
|
36
|
-
table_names.map { |fmt| Flextures::Dumper::yml(fmt) }
|
37
|
-
end
|
38
|
-
|
39
28
|
def self.load
|
40
29
|
Flextures::init_load
|
41
30
|
table_names = Flextures::ARGS.parse
|
42
31
|
Flextures::init_tables unless ENV["T"] or ENV["TABLE"] or ENV["M"] or ENV["MODEL"] or ENV["F"] or ENV["FIXTUES"]
|
32
|
+
file_format = ENV["FORMAT"]
|
43
33
|
puts "loading..."
|
44
|
-
|
34
|
+
case file_format.to_s.to_sym
|
35
|
+
when :csv
|
36
|
+
table_names.map { |fmt| Flextures::Loader::csv(fmt) }
|
37
|
+
when :yml
|
38
|
+
table_names.map { |fmt| Flextures::Loader::yml(fmt) }
|
39
|
+
else
|
40
|
+
table_names.map { |fmt| Flextures::Loader::load(fmt) }
|
41
|
+
end
|
45
42
|
end
|
46
43
|
|
47
|
-
|
44
|
+
# load and dump data
|
45
|
+
def self.generate
|
48
46
|
Flextures::init_load
|
49
47
|
table_names = Flextures::ARGS.parse
|
50
48
|
Flextures::init_tables unless ENV["T"] or ENV["TABLE"] or ENV["M"] or ENV["MODEL"] or ENV["F"] or ENV["FIXTUES"]
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
file_format = ENV["FORMAT"]
|
50
|
+
puts "generating..."
|
51
|
+
case file_format.to_s.to_sym
|
52
|
+
when :yml
|
53
|
+
table_names.map { |fmt| Flextures::Loader::yml(fmt); Flextures::Dumper::yml(fmt) }
|
54
|
+
when :csv
|
55
|
+
table_names.map { |fmt| Flextures::Loader::csv(fmt); Flextures::Dumper::csv(fmt) }
|
56
|
+
else
|
57
|
+
table_names.map { |fmt| Flextures::Loader::csv(fmt); Flextures::Dumper::csv(fmt) }
|
58
|
+
end
|
61
59
|
end
|
62
60
|
end
|
63
61
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "fileutils"
|
4
|
+
|
3
5
|
module Flextures
|
4
|
-
#
|
6
|
+
# defined data dump methods
|
5
7
|
module Dumper
|
6
8
|
PARENT = Flextures
|
7
9
|
# procに機能追加、関数合成のためのメソッドを追加する
|
8
10
|
class Proc < ::Proc
|
9
11
|
def *(other)
|
10
12
|
if self.lambda? and other.lambda?
|
11
|
-
lambda {|*x| other.call(self.call(*x)) }
|
13
|
+
lambda { |*x| other.call(self.call(*x)) }
|
12
14
|
elsif not self.lambda? and not other.lambda?
|
13
15
|
Proc.new {|*x| other.call(self.call(*x)) }
|
14
16
|
else
|
@@ -21,8 +23,9 @@ module Flextures
|
|
21
23
|
Proc.new(&b)
|
22
24
|
end
|
23
25
|
|
26
|
+
# create data translater
|
24
27
|
def self.translate_creater( val, rules )
|
25
|
-
rule_map ={
|
28
|
+
rule_map = {
|
26
29
|
nullstr: proc { |d|
|
27
30
|
return "null" if d.nil?
|
28
31
|
d
|
@@ -77,6 +80,7 @@ module Flextures
|
|
77
80
|
procs.call(val)
|
78
81
|
end
|
79
82
|
|
83
|
+
# data translaters
|
80
84
|
TRANSLATER = {
|
81
85
|
binary:->( d, format ){
|
82
86
|
procs = (format == :yml)?
|
@@ -144,84 +148,107 @@ module Flextures
|
|
144
148
|
[proc { |d| d.to_s }]
|
145
149
|
self.translate_creater d, procs
|
146
150
|
},
|
151
|
+
# use null only value
|
152
|
+
null:->( d, format ){
|
153
|
+
format==:yml ? "null" : ""
|
154
|
+
},
|
147
155
|
}
|
148
156
|
|
149
|
-
#
|
157
|
+
# translate data
|
158
|
+
# @params [Object] value
|
159
|
+
# @params [Symbol] type datatype
|
160
|
+
# @params [Symbol] format data type (:yml or :csv)
|
161
|
+
# @return translated value
|
150
162
|
def self.trans( v, type, format )
|
151
163
|
trans = TRANSLATER[type]
|
152
164
|
return trans.call( v, format ) if trans
|
153
165
|
v
|
154
166
|
end
|
155
167
|
|
156
|
-
#
|
168
|
+
# dump attributes data
|
169
|
+
# @params klass dump table Model
|
170
|
+
# @params [Hash] options dump options
|
171
|
+
# @return [Array] columns format information
|
172
|
+
def self.dump_attributes klass, options
|
173
|
+
columns = klass.columns.map { |column| { name: column.name, type: column.type } }
|
174
|
+
# option[:minus] colum is delete columns
|
175
|
+
columns.reject! { |column| options[:minus].include?(column[:name]) } if options[:minus]
|
176
|
+
# option[:plus] colum is new columns
|
177
|
+
# values is all nil
|
178
|
+
plus = options[:plus].to_a.map { |colname| { name: colname, type: :null } }
|
179
|
+
columns + plus
|
180
|
+
end
|
181
|
+
|
182
|
+
# filter is translate value safe YAML or CSV string
|
183
|
+
# @params [Class] klass ActiveRecord class
|
184
|
+
# @params [String] table_name table name
|
185
|
+
# @params [Hash] options options
|
186
|
+
# @params [Symbol] type format type (:yml or :csv)
|
187
|
+
# @return [Proc] filter function
|
188
|
+
def self.create_filter attr_type, format, type
|
189
|
+
filter = DumpFilter[format[:table].to_s.to_sym] || {}
|
190
|
+
->(row) {
|
191
|
+
attr_type.map do |h|
|
192
|
+
v = filter[h[:name].to_sym] ? filter[h[:name].to_sym].call(row[h[:name]]) : trans(row[h[:name]], h[:type], type)
|
193
|
+
[h[:name],v]
|
194
|
+
end
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
# data dump to csv format
|
199
|
+
# @params [Hash] format file format data
|
200
|
+
# @params [Hash] options dump format options
|
157
201
|
def self.csv format
|
202
|
+
klass = PARENT.create_model(format[:table])
|
203
|
+
attr_type = self.dump_attributes klass, format
|
204
|
+
filter = self.create_filter attr_type, format, :csv
|
205
|
+
self.dump_csv klass, attr_type, filter, format
|
206
|
+
end
|
207
|
+
|
208
|
+
# dump csv format data
|
209
|
+
def self.dump_csv klass, attr_type, values_filter, format
|
210
|
+
# TODO: 拡張子は指定してもしなくても良いようにする
|
158
211
|
file_name = format[:file] || format[:table]
|
159
|
-
dir_name = format[:dir]
|
160
|
-
|
161
|
-
recursive_mkdir(dir_name)
|
212
|
+
dir_name = File.join( Flextures::Config.fixture_dump_directory, format[:dir].to_s )
|
213
|
+
FileUtils.mkdir_p(dir_name)
|
162
214
|
outfile = File.join(dir_name, "#{file_name}.csv")
|
163
|
-
table_name = format[:table]
|
164
|
-
klass = PARENT.create_model(table_name)
|
165
|
-
attributes = klass.columns.map { |column| column.name }
|
166
|
-
filter = DumpFilter[table_name]
|
167
215
|
CSV.open(outfile,'w') do |csv|
|
168
|
-
|
169
|
-
csv<<
|
216
|
+
# dump column names
|
217
|
+
csv<< attr_type.map { |h| h[:name].to_s }
|
218
|
+
# dump column datas
|
170
219
|
klass.all.each do |row|
|
171
|
-
csv<<
|
172
|
-
if (filter && filter[h[:name].to_sym])
|
173
|
-
filter[h[:name].to_sym].call(row[h[:name]])
|
174
|
-
else
|
175
|
-
trans(row[h[:name]], h[:type], :csv)
|
176
|
-
end
|
177
|
-
end
|
220
|
+
csv<< values_filter.call(row).map(&:last)
|
178
221
|
end
|
179
222
|
end
|
180
223
|
outfile
|
181
224
|
end
|
182
225
|
|
183
|
-
#
|
226
|
+
# data dump to yaml format
|
227
|
+
# @params [Hash] format file format data
|
228
|
+
# @params [Hash] options dump format options
|
184
229
|
def self.yml format
|
230
|
+
klass = PARENT::create_model(format[:table])
|
231
|
+
attr_type = self.dump_attributes klass, format
|
232
|
+
filter = self.create_filter attr_type, format, :yml
|
233
|
+
self.dump_yml klass, filter, format
|
234
|
+
end
|
235
|
+
|
236
|
+
# dump yml format data
|
237
|
+
def self.dump_yml klass, values_filter, format
|
238
|
+
# TODO: 拡張子は指定してもしなくても良いようにする
|
239
|
+
table_name = format[:table]
|
185
240
|
file_name = format[:file] || format[:table]
|
186
|
-
dir_name = format[:dir]
|
187
|
-
|
188
|
-
recursive_mkdir(dir_name)
|
241
|
+
dir_name = File.join( Flextures::Config.fixture_dump_directory, format[:dir].to_s )
|
242
|
+
FileUtils.mkdir_p(dir_name)
|
189
243
|
outfile = File.join(dir_name, "#{file_name}.yml")
|
190
|
-
table_name = format[:table]
|
191
|
-
klass = PARENT::create_model(table_name)
|
192
|
-
attributes = klass.columns.map { |colum| colum.name }
|
193
|
-
columns = klass.columns
|
194
|
-
# テーブルからカラム情報を取り出し
|
195
|
-
column_hash = {}
|
196
|
-
columns.each { |col| column_hash[col.name] = col }
|
197
|
-
# 自動補完が必要なはずのカラム
|
198
|
-
lack_columns = columns.select { |c| !c.null and !c.default }.map{ |o| o.name.to_sym }
|
199
|
-
not_nullable_columns = columns.select { |c| !c.null }.map &:name
|
200
|
-
|
201
|
-
filter = DumpFilter[table_name]
|
202
244
|
File.open(outfile,"w") do |f|
|
203
|
-
klass.all.
|
204
|
-
|
205
|
-
|
206
|
-
colname, coltype = column.name, column.type
|
207
|
-
if (filter && filter[colname.to_sym])
|
208
|
-
v = filter[colname.to_sym].call(row[colname.to_sym])
|
209
|
-
else
|
210
|
-
v = trans(row[colname], coltype, :yml)
|
211
|
-
end
|
212
|
-
" #{colname}: #{v}\n"
|
213
|
-
}.join
|
245
|
+
klass.all.each.with_index do |row,idx|
|
246
|
+
values = values_filter.call(row).map { |k,v| " #{k}: #{v}\n" }.join
|
247
|
+
f<< "#{table_name}_#{idx}:\n" + values
|
214
248
|
end
|
215
249
|
end
|
216
250
|
outfile
|
217
251
|
end
|
218
|
-
|
219
|
-
def self.recursive_mkdir(path)
|
220
|
-
return if FileTest.exist?(path)
|
221
|
-
dir = File.dirname(path)
|
222
|
-
recursive_mkdir(dir)
|
223
|
-
Dir.mkdir(path)
|
224
|
-
end
|
225
252
|
end
|
226
253
|
end
|
227
254
|
|
@@ -3,25 +3,29 @@
|
|
3
3
|
require 'ostruct'
|
4
4
|
|
5
5
|
module Flextures
|
6
|
-
# Plug-in
|
6
|
+
# OpenStruct hack in flextures Plug-in
|
7
7
|
class OpenStruct < ::OpenStruct
|
8
|
-
#
|
8
|
+
# Struct Data translate to Hash
|
9
9
|
def to_hash
|
10
|
-
h={}
|
11
10
|
(self.methods - ::OpenStruct.new.methods)
|
12
11
|
.select{ |name| name.to_s.match(/\w+=/) }
|
13
12
|
.map{ |name| name.to_s.gsub(/=/,'').to_sym }
|
14
|
-
.
|
15
|
-
h
|
13
|
+
.inject({}){ |k,h| h[k]=self.send(k); h }
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
19
17
|
module Extensions
|
20
18
|
module Array
|
19
|
+
# use Object#extend
|
20
|
+
# @params [Array] keys hash keys
|
21
|
+
# @return [Hash] tanslated Hash data
|
22
|
+
# example:
|
23
|
+
# hash = array.extend(Extensions::Array).to_hash(keys)
|
21
24
|
def to_hash keys
|
22
|
-
|
23
|
-
[keys
|
24
|
-
|
25
|
+
values = self
|
26
|
+
values = values[0..keys.size-1] if keys.size < values.size
|
27
|
+
values = values+[nil]*(keys.size-values.size) if keys.size > values.size
|
28
|
+
[keys,values].transpose.inject({}){ |h,pair| k,v=pair; h[k]=v; h }
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Flextures
|
4
|
-
#
|
4
|
+
# Flextures FactoryFilter is program to translate ActiveRecord data
|
5
5
|
class LoadFilter
|
6
|
-
#
|
6
|
+
# FactoryFilter data
|
7
7
|
FACTORIES={}
|
8
8
|
|
9
|
-
#
|
10
|
-
# @params table_name
|
11
|
-
# @params options
|
12
|
-
# @params block
|
13
|
-
# @return Flextures::Factory
|
9
|
+
# set FactoryFilter
|
10
|
+
# @params [String] table_name
|
11
|
+
# @params [Array] options arguments ActiveRecord Model
|
12
|
+
# @params [Proc] block FactoryFilter
|
14
13
|
def self.define table_name, *options, &block
|
15
14
|
h={ block: block }
|
16
15
|
options.each do |o|
|
@@ -22,18 +21,21 @@ module Flextures
|
|
22
21
|
FACTORIES[table_name.to_sym]=h
|
23
22
|
end
|
24
23
|
|
25
|
-
#
|
24
|
+
# get FactoryFilter
|
25
|
+
# @params [String|Symbol] table_name
|
26
|
+
# @return [Proc] filter block
|
26
27
|
def self.get table_name
|
27
28
|
f = FACTORIES[table_name.to_sym]
|
28
29
|
f && f[:block]
|
29
30
|
end
|
30
31
|
def self.[](table_name); self.get(table_name); end
|
31
32
|
end
|
33
|
+
|
32
34
|
class DumpFilter
|
33
|
-
#
|
35
|
+
# FactoryDumpFilter data
|
34
36
|
FACTORIES={}
|
35
37
|
|
36
|
-
#
|
38
|
+
# set FactoryFilter
|
37
39
|
# @params table_name
|
38
40
|
# @params options
|
39
41
|
# @params block
|
@@ -42,7 +44,7 @@ module Flextures
|
|
42
44
|
FACTORIES[table_name.to_sym]=hash
|
43
45
|
end
|
44
46
|
|
45
|
-
#
|
47
|
+
# get FactoryFilter
|
46
48
|
def self.get table_name
|
47
49
|
FACTORIES[table_name.to_sym]
|
48
50
|
end
|
@@ -9,11 +9,11 @@ require 'flextures/flextures'
|
|
9
9
|
require 'flextures/flextures_factory'
|
10
10
|
|
11
11
|
module Flextures
|
12
|
-
#
|
12
|
+
# data loader
|
13
13
|
module Loader
|
14
14
|
PARENT = Flextures
|
15
15
|
|
16
|
-
@@
|
16
|
+
@@option_cache = {}
|
17
17
|
|
18
18
|
# column set default value
|
19
19
|
COMPLETER = {
|
@@ -38,7 +38,7 @@ module Flextures
|
|
38
38
|
},
|
39
39
|
boolean:->(d){
|
40
40
|
return d if d.nil?
|
41
|
-
(0==d || ""==d || !d)
|
41
|
+
!(0==d || ""==d || !d)
|
42
42
|
},
|
43
43
|
date:->(d){
|
44
44
|
return d if d.nil?
|
@@ -51,27 +51,23 @@ module Flextures
|
|
51
51
|
DateTime.parse(d.to_s)
|
52
52
|
},
|
53
53
|
decimal:->(d){
|
54
|
-
return d
|
54
|
+
return d if d.nil?
|
55
55
|
d.to_i
|
56
56
|
},
|
57
57
|
float:->(d){
|
58
|
-
return d
|
58
|
+
return d if d.nil?
|
59
59
|
d.to_f
|
60
60
|
},
|
61
61
|
integer:->(d){
|
62
|
-
return d
|
62
|
+
return d if d.nil?
|
63
63
|
d.to_i
|
64
64
|
},
|
65
65
|
string:->(d){
|
66
|
-
return d if d.nil?
|
67
|
-
return d if d.is_a?(Hash)
|
68
|
-
return d if d.is_a?(Array)
|
66
|
+
return d if d.nil? or d.is_a?(Hash) or d.is_a?(Array)
|
69
67
|
d.to_s
|
70
68
|
},
|
71
69
|
text:->(d){
|
72
|
-
return d if d.nil?
|
73
|
-
return d if d.is_a?(Hash)
|
74
|
-
return d if d.is_a?(Array)
|
70
|
+
return d if d.nil? or d.is_a?(Hash) or d.is_a?(Array)
|
75
71
|
d.to_s
|
76
72
|
},
|
77
73
|
time:->(d){
|
@@ -86,163 +82,243 @@ module Flextures
|
|
86
82
|
},
|
87
83
|
}
|
88
84
|
|
89
|
-
#
|
90
|
-
# @param [Hash] format ロードしたいファイルの情報
|
91
|
-
# @return 存在するファイルの種類(csv,yml)、どちも存在しないならnil
|
92
|
-
def self.file_exist format, type = [:csv,:yml]
|
93
|
-
table_name = format[:table].to_s
|
94
|
-
file_name = format[:file] || format[:table]
|
95
|
-
dir_name = format[:dir] || LOAD_DIR
|
96
|
-
|
97
|
-
ext=->{
|
98
|
-
if type.member?(:csv) and File.exist? "#{dir_name}#{file_name}.csv"
|
99
|
-
:csv
|
100
|
-
elsif type.member?(:yml) and File.exist? "#{dir_name}#{file_name}.yml"
|
101
|
-
:yml
|
102
|
-
else
|
103
|
-
nil
|
104
|
-
end
|
105
|
-
}.call
|
106
|
-
|
107
|
-
[table_name, "#{dir_name}#{file_name}",ext]
|
108
|
-
end
|
109
|
-
|
110
|
-
# flextures関数の引数をパースして
|
111
|
-
# 単純な読み込み向け形式に変換します
|
85
|
+
# load fixture datas
|
112
86
|
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
|
116
|
-
|
117
|
-
|
87
|
+
# example:
|
88
|
+
# flextures :all # load all table data
|
89
|
+
# flextures :users, :items # load table data, received arguments
|
90
|
+
# flextures :users => :users2 # :table_name => :file_name
|
91
|
+
#
|
92
|
+
# @params [Hash] fixtures load table data
|
93
|
+
def self.flextures *fixtures
|
94
|
+
load_list = parse_flextures_options(*fixtures)
|
95
|
+
load_list.sort(&self.loading_order).each{ |params| Loader::load params }
|
96
|
+
end
|
118
97
|
|
119
|
-
|
120
|
-
|
98
|
+
# @return [Proc] order rule block (user Array#sort methd)
|
99
|
+
def self.loading_order
|
100
|
+
->(a,b){
|
101
|
+
a = Flextures::Config.table_load_order.index(a) || -1
|
102
|
+
b = Flextures::Config.table_load_order.index(b) || -1
|
103
|
+
b <=> a
|
104
|
+
}
|
105
|
+
end
|
121
106
|
|
122
|
-
|
123
|
-
|
107
|
+
# called by Rspec or Should
|
108
|
+
# set options
|
109
|
+
# @params [Hash] options exmple : { cashe: true, dir: "models/users" }
|
110
|
+
def self.set_options options
|
111
|
+
@@option_cache ||= {}
|
112
|
+
@@option_cache.merge!(options)
|
113
|
+
end
|
124
114
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
115
|
+
# called by Rspec or Should after filter
|
116
|
+
# reflesh options
|
117
|
+
def self.delete_options
|
118
|
+
@@option_cache = {}
|
129
119
|
end
|
130
120
|
|
131
|
-
#
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
# テーブル名で一覧する
|
136
|
-
# flextures :users, :items
|
137
|
-
# ハッシュで指定
|
138
|
-
# flextures :users => :users2
|
139
|
-
#
|
140
|
-
# @params [Hash] 読み込むテーブルとファイル名のペア
|
141
|
-
def self.flextures *fixtures
|
142
|
-
load_hash, options = parse_flextures_options(*fixtures)
|
143
|
-
load_hash.each{ |params| Loader::load params }
|
121
|
+
# return current option status
|
122
|
+
# @return [Hash] current option status
|
123
|
+
def self.flextures_options
|
124
|
+
@@option_cache
|
144
125
|
end
|
145
126
|
|
146
|
-
#
|
147
|
-
|
148
|
-
|
127
|
+
# load fixture data
|
128
|
+
# fixture file prefer YAML to CSV
|
129
|
+
# @params [Hash] format file load format(table name, file name, options...)
|
130
|
+
def self.load format
|
131
|
+
file_name, method = file_exist format
|
149
132
|
if method
|
150
|
-
send(method, format
|
133
|
+
send(method, format)
|
151
134
|
else
|
152
|
-
#
|
153
|
-
print "Warning: #{file_name} is not exist!\n"
|
135
|
+
puts "Warning: #{file_name} is not exist!" unless format[:silent]
|
154
136
|
end
|
155
137
|
end
|
156
138
|
|
157
|
-
# CSV
|
158
|
-
|
159
|
-
|
139
|
+
# load CSV data
|
140
|
+
# @params [Hash] format file load format(table name, file name, options...)
|
141
|
+
def self.csv format
|
142
|
+
type = :csv
|
143
|
+
file_name, ext = file_exist format, [type]
|
144
|
+
return unless self.file_loadable? format, file_name
|
145
|
+
klass, filter = self.create_model_filter format, file_name, type
|
146
|
+
self.load_csv format, klass, filter, file_name
|
147
|
+
end
|
160
148
|
|
161
|
-
|
162
|
-
|
163
|
-
|
149
|
+
# load YAML data
|
150
|
+
# @params [Hash] format file load format( table: name, file: name, options...)
|
151
|
+
def self.yml format
|
152
|
+
type = :yml
|
153
|
+
file_name, ext = file_exist format, [type]
|
164
154
|
|
165
|
-
|
166
|
-
return nil unless File.exist? "#{file_name}.csv"
|
155
|
+
return unless self.file_loadable? format, file_name
|
167
156
|
|
168
|
-
klass =
|
169
|
-
|
170
|
-
|
171
|
-
# rails3_acts_as_paranoid がdelete_allで物理削除しないことの対策
|
172
|
-
klass.send( klass.respond_to?(:delete_all!) ? :delete_all! : :delete_all )
|
157
|
+
klass, filter = self.create_model_filter format, file_name, type
|
158
|
+
self.load_yml format, klass, filter, file_name
|
159
|
+
end
|
173
160
|
|
174
|
-
|
175
|
-
|
176
|
-
|
161
|
+
def self.load_csv format, klass, filter, file_name
|
162
|
+
attributes = klass.columns.map &:name
|
163
|
+
CSV.open( file_name ) do |csv|
|
164
|
+
keys = csv.shift # active record column names
|
165
|
+
warning "CSV", attributes, keys unless format[:silent]
|
177
166
|
csv.each do |values|
|
178
167
|
h = values.extend(Extensions::Array).to_hash(keys)
|
179
|
-
|
180
|
-
o.save( validate: false )
|
168
|
+
filter.call h
|
181
169
|
end
|
182
170
|
end
|
183
|
-
|
171
|
+
file_name
|
184
172
|
end
|
185
173
|
|
186
|
-
|
187
|
-
|
188
|
-
|
174
|
+
def self.load_yml format, klass, filter, file_name
|
175
|
+
yaml = YAML.load File.open(file_name)
|
176
|
+
return false unless yaml # if file is empty
|
177
|
+
attributes = klass.columns.map &:name
|
178
|
+
yaml.each do |k,h|
|
179
|
+
warning "YAML", attributes, h.keys unless format[:silent]
|
180
|
+
filter.call h
|
181
|
+
end
|
182
|
+
file_name
|
183
|
+
end
|
184
|
+
|
185
|
+
# if parameter include controller, action value
|
186
|
+
# load directroy is change
|
187
|
+
# spec/fixtures/:controller_name/:action_name/
|
188
|
+
# @return [String] directory path
|
189
|
+
def self.parse_controller_option options
|
190
|
+
controller_dir = ["controllers"]
|
191
|
+
controller_dir<< options[:controller] if options[:controller]
|
192
|
+
controller_dir<< options[:action] if options[:controller] and options[:action]
|
193
|
+
File.join(*controller_dir)
|
194
|
+
end
|
195
|
+
|
196
|
+
# if parameter include controller, action value
|
197
|
+
# load directroy is change
|
198
|
+
# spec/fixtures/:model_name/:method_name/
|
199
|
+
# @return [String] directory path
|
200
|
+
def self.parse_model_options options
|
201
|
+
model_dir = ["models"]
|
202
|
+
model_dir<< options[:model] if options[:model]
|
203
|
+
model_dir<< options[:method] if options[:model] and options[:method]
|
204
|
+
File.join(*model_dir)
|
205
|
+
end
|
206
|
+
|
207
|
+
# parse flextures function arguments
|
208
|
+
# @params [Hash] fixtures function arguments
|
209
|
+
# @return [Array] formatted load options
|
210
|
+
def self.parse_flextures_options *fixtures
|
211
|
+
options = {}
|
212
|
+
options = fixtures.shift if fixtures.size > 1 and fixtures.first.is_a?(Hash)
|
189
213
|
|
190
|
-
|
191
|
-
|
192
|
-
@@table_cache[table_name.to_sym] = file_name
|
214
|
+
options[:dir] = self.parse_controller_option( options ) if options[:controller]
|
215
|
+
options[:dir] = self.parse_model_options( options ) if options[:model]
|
193
216
|
|
194
|
-
|
195
|
-
|
217
|
+
# :all value load all loadable fixtures
|
218
|
+
fixtures = Flextures::deletable_tables if fixtures.size==1 and :all == fixtures.first
|
219
|
+
last_hash = fixtures.last.is_a?(Hash) ? fixtures.pop : {}
|
220
|
+
load_hash = fixtures.inject({}){ |h,name| h[name.to_sym] = name.to_s; h } # if name is string is buged
|
221
|
+
load_hash.merge!(last_hash)
|
222
|
+
load_hash.map { |k,v| { table: k, file: v, loader: :fun }.merge(@@option_cache).merge(options) }
|
223
|
+
end
|
196
224
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
225
|
+
# example:
|
226
|
+
# self.create_stair_list("foo/bar/baz")
|
227
|
+
# return ["foo/bar/baz","foo/bar","foo",""]
|
228
|
+
def self.stair_list dir, stair=true
|
229
|
+
return [dir.to_s] unless stair
|
230
|
+
l = []
|
231
|
+
dir.to_s.split("/").inject([]){ |a,d| a<< d; l.unshift(a.join("/")); a }
|
232
|
+
l<< ""
|
233
|
+
l
|
234
|
+
end
|
205
235
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
236
|
+
# parse format option and return load file info
|
237
|
+
# @param [Hash] format load file format informations
|
238
|
+
# @return [Array] [file_name, filt_type(:csv or :yml)]
|
239
|
+
def self.file_exist format, type = [:csv,:yml]
|
240
|
+
table_name = format[:table].to_s
|
241
|
+
file_name = (format[:file] || format[:table]).to_s
|
242
|
+
base_dir_name = Flextures::Config.fixture_load_directory
|
243
|
+
self.stair_list(format[:dir], format[:stair]).each do |dir|
|
244
|
+
file_path = File.join( base_dir_name, dir, file_name )
|
245
|
+
return ["#{file_path}.csv", :csv] if type.member?(:csv) and File.exist? "#{file_path}.csv"
|
246
|
+
return ["#{file_path}.yml", :yml] if type.member?(:yml) and File.exist? "#{file_path}.yml"
|
212
247
|
end
|
213
|
-
|
248
|
+
|
249
|
+
[ File.join(base_dir_name, "#{file_name}.csv"), nil ]
|
250
|
+
end
|
251
|
+
|
252
|
+
# file load check
|
253
|
+
# @return [Bool] lodable is 'true'
|
254
|
+
def self.file_loadable? format, file_name
|
255
|
+
return unless File.exist? file_name
|
256
|
+
puts "try loading #{file_name}" if !format[:silent] and ![:fun].include?(format[:loader])
|
257
|
+
true
|
214
258
|
end
|
215
259
|
|
216
|
-
#
|
260
|
+
# print warinig message that lack or not exist colum names
|
217
261
|
def self.warning format, attributes, keys
|
218
|
-
(attributes-keys).each { |name|
|
219
|
-
(keys-attributes).each { |name|
|
262
|
+
(attributes-keys).each { |name| puts "Warning: #{format} colum is missing! [#{name}]" }
|
263
|
+
(keys-attributes).each { |name| puts "Warning: #{format} colum is left over! [#{name}]" }
|
220
264
|
end
|
221
265
|
|
222
|
-
#
|
223
|
-
def self.
|
266
|
+
# create filter and table info
|
267
|
+
def self.create_model_filter format, file_name, type
|
268
|
+
table_name = format[:table].to_s
|
269
|
+
klass = PARENT::create_model table_name
|
270
|
+
# if you use 'rails3_acts_as_paranoid' gem, that is not delete data 'delete_all' method
|
271
|
+
klass.send (klass.respond_to?(:delete_all!) ? :delete_all! : :delete_all)
|
272
|
+
|
273
|
+
filter = ->(h){
|
274
|
+
filter = create_filter klass, LoadFilter[table_name.to_sym], file_name, type, format
|
275
|
+
o = klass.new
|
276
|
+
o = filter.call o, h
|
277
|
+
o.save( validate: false )
|
278
|
+
o
|
279
|
+
}
|
280
|
+
[klass, filter]
|
281
|
+
end
|
282
|
+
|
283
|
+
# return flextures data translate filter
|
284
|
+
# translate filter is some functions
|
285
|
+
# 1. column value is fill, if colum is not nullable
|
286
|
+
# 2. factory filter
|
287
|
+
# @params [ActiveRecord::Base] klass ActiveRecord model data
|
288
|
+
# @params [Proc] factory FactoryFilter
|
289
|
+
# @params [String] filename
|
290
|
+
# @params [Symbol] ext file type (:csv or :yml)
|
291
|
+
# @params [Hash] options other options
|
292
|
+
# @return [Proc] translate filter
|
293
|
+
def self.create_filter klass, factory, filename, ext, options
|
224
294
|
columns = klass.columns
|
225
|
-
#
|
226
|
-
column_hash = {}
|
227
|
-
columns.
|
228
|
-
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
#
|
295
|
+
# data translat array to hash
|
296
|
+
column_hash = columns.inject({}) { |h,col| h[col.name] = col; h }
|
297
|
+
lack_columns = columns.reject { |c| c.null and c.default }.map{ |o| o.name.to_sym }
|
298
|
+
# default value shound not be null columns
|
299
|
+
not_nullable_columns = columns.reject(&:null).map &:name
|
300
|
+
strict_filter=->(o,h){
|
301
|
+
# if value is not 'nil', value translate suitable form
|
302
|
+
h.each{ |k,v| v.nil? || o[k] = (TRANSLATER[column_hash[k].type] && TRANSLATER[column_hash[k].type].call(v)) }
|
303
|
+
# call FactoryFilter
|
304
|
+
factory.call(*[o, filename, ext][0,factory.arity]) if factory and !options[:unfilter]
|
305
|
+
o
|
306
|
+
}
|
307
|
+
# receives hased data and translate ActiveRecord Model data
|
308
|
+
# loose filter correct error values
|
309
|
+
# strict filter don't correct errora values and raise error
|
310
|
+
loose_filter=->(o,h){
|
311
|
+
h.reject! { |k,v| options[:minus].include?(k) } if options[:minus]
|
312
|
+
# if column name is not include database table columns, those names delete
|
234
313
|
h.select! { |k,v| column_hash[k] }
|
235
|
-
o
|
236
|
-
#
|
237
|
-
|
238
|
-
#
|
239
|
-
|
240
|
-
# 値がnilの列にデフォルト値を補間
|
241
|
-
not_nullable_columns.each{ |k| o[k]==nil && o[k] = (COMPLETER[column_hash[k].type] ? COMPLETER[column_hash[k].type].call : nil) }
|
242
|
-
# 列ごと抜けているデータを保管
|
243
|
-
lack_columns.each { |k| nil==o[k] && o[k] = COMPLETER[column_hash[k].type].call }
|
314
|
+
strict_filter.call(o,h)
|
315
|
+
# set default value if value is 'nil'
|
316
|
+
not_nullable_columns.each{ |k| o[k].nil? && o[k] = (column_hash[k] && COMPLETER[column_hash[k].type] && COMPLETER[column_hash[k].type].call) }
|
317
|
+
# fill span values if column is not exist
|
318
|
+
lack_columns.each { |k| o[k].nil? && o[k] = (column_hash[k] && COMPLETER[column_hash[k].type] && COMPLETER[column_hash[k].type].call) }
|
244
319
|
o
|
245
320
|
}
|
321
|
+
(options[:strict]==true) ? strict_filter : loose_filter
|
246
322
|
end
|
247
323
|
end
|
248
324
|
end
|