activerecord-bixformer 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +4 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README-ja.md +296 -0
- data/README.md +26 -0
- data/Rakefile +6 -0
- data/activerecord-bixformer.gemspec +37 -0
- data/lib/activerecord-bixformer/attribute/base.rb +23 -0
- data/lib/activerecord-bixformer/attribute/boolean.rb +26 -0
- data/lib/activerecord-bixformer/attribute/booletania.rb +21 -0
- data/lib/activerecord-bixformer/attribute/date.rb +35 -0
- data/lib/activerecord-bixformer/attribute/enumerize.rb +20 -0
- data/lib/activerecord-bixformer/attribute/override.rb +15 -0
- data/lib/activerecord-bixformer/attribute/time.rb +35 -0
- data/lib/activerecord-bixformer/generator/active_record.rb +170 -0
- data/lib/activerecord-bixformer/generator/base.rb +66 -0
- data/lib/activerecord-bixformer/generator/csv_row.rb +40 -0
- data/lib/activerecord-bixformer/model/base.rb +150 -0
- data/lib/activerecord-bixformer/model/csv/base.rb +47 -0
- data/lib/activerecord-bixformer/model/csv/indexed.rb +56 -0
- data/lib/activerecord-bixformer/modeler/base.rb +160 -0
- data/lib/activerecord-bixformer/modeler/csv.rb +11 -0
- data/lib/activerecord-bixformer/runner/base.rb +47 -0
- data/lib/activerecord-bixformer/runner/csv.rb +123 -0
- data/lib/activerecord-bixformer/translator/i18n.rb +50 -0
- data/lib/activerecord-bixformer/version.rb +5 -0
- data/lib/activerecord-bixformer.rb +8 -0
- metadata +267 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Generator
|
4
|
+
class ActiveRecord < ::ActiveRecord::Bixformer::Generator::Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def association_generator
|
8
|
+
:new_as_association_for_import
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_model_value(model)
|
12
|
+
generate_attributes_value(model).merge(generate_association_value(model))
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_attributes_value(model)
|
16
|
+
attribute_value_map = model.generate_import_value_map
|
17
|
+
required_attributes = @modeler.config_value_for(model, :required_attributes, [])
|
18
|
+
identified_column_name = identified_column_name_of(model)
|
19
|
+
|
20
|
+
# 必須な属性が渡されていない場合には、取り込みしない
|
21
|
+
return {} if required_attributes.any? { |attribute_name| ! presence_value?(attribute_value_map[attribute_name]) }
|
22
|
+
|
23
|
+
set_required_condition(model, attribute_value_map)
|
24
|
+
set_parent_key(model, attribute_value_map, identified_column_name)
|
25
|
+
set_activerecord_id(model, attribute_value_map, identified_column_name)
|
26
|
+
|
27
|
+
# 空でない要素が無いなら、空ハッシュで返す
|
28
|
+
presence_value?(attribute_value_map) ? attribute_value_map : {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_association_value(parent_model)
|
32
|
+
association_value_map = {}.with_indifferent_access
|
33
|
+
|
34
|
+
parent_model.association_map.each do |association_name, model_or_models|
|
35
|
+
association_value = if model_or_models.is_a?(::Array)
|
36
|
+
model_or_models.map { |m| generate_model_value(m) }.reject { |v| ! presence_value?(v) }
|
37
|
+
else
|
38
|
+
generate_model_value(model_or_models)
|
39
|
+
end
|
40
|
+
|
41
|
+
# 取り込み時は、オプショナルな関連では、空と思われる値は取り込まない
|
42
|
+
next if ! presence_value?(association_value) &&
|
43
|
+
parent_model.optional_attributes.include?(association_name.to_s)
|
44
|
+
|
45
|
+
association_value_map["#{association_name}_attributes".to_sym] = association_value
|
46
|
+
end
|
47
|
+
|
48
|
+
association_value_map
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_required_condition(model, attribute_value_map)
|
52
|
+
# 結果ハッシュが空なら、取り込みしないように追加はしない
|
53
|
+
return unless presence_value?(attribute_value_map)
|
54
|
+
|
55
|
+
# 設定するのはルートの場合のみ
|
56
|
+
return if model.parent
|
57
|
+
|
58
|
+
attribute_value_map.merge!(@modeler.required_condition)
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_parent_key(model, attribute_value_map, identified_column_name)
|
62
|
+
# 結果ハッシュが空なら、取り込みしないように追加はしない
|
63
|
+
return unless presence_value?(attribute_value_map)
|
64
|
+
|
65
|
+
# 設定するのは親がいる場合のみ
|
66
|
+
return unless model.parent
|
67
|
+
|
68
|
+
parent_id = model.parent&.activerecord_id
|
69
|
+
|
70
|
+
if parent_id
|
71
|
+
# 親のレコードが見つかっているなら、それも結果ハッシュに追加する
|
72
|
+
attribute_value_map[model.parent_foreign_key] = parent_id
|
73
|
+
else
|
74
|
+
# 見つかっていないなら、間違った値が指定されている可能性があるので、キー自体を削除
|
75
|
+
attribute_value_map.delete(model.parent_foreign_key)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_activerecord_id(model, attribute_value_map, identified_column_name)
|
80
|
+
# 更新の場合は、インポートデータを元にデータベースから対象のレコードを検索してIDを取得
|
81
|
+
model.activerecord_id = verified_activerecord_id(model, attribute_value_map, identified_column_name)
|
82
|
+
|
83
|
+
if model.activerecord_id
|
84
|
+
# 更新なら、ID属性を改めて設定
|
85
|
+
attribute_value_map[identified_column_name] = model.activerecord_id
|
86
|
+
else
|
87
|
+
# 見つかっていないなら、間違った値が指定されている可能性があるので、キー自体を削除
|
88
|
+
attribute_value_map.delete(identified_column_name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def verified_activerecord_id(model, attribute_value_map, identified_column_name)
|
93
|
+
# 更新対象のレコードを特定できるかチェック
|
94
|
+
identified_value = attribute_value_map[identified_column_name]
|
95
|
+
|
96
|
+
uniqueness_condition = if identified_value
|
97
|
+
{ identified_column_name => identified_value }
|
98
|
+
else
|
99
|
+
find_unique_condition(model, attribute_value_map, identified_column_name)
|
100
|
+
end
|
101
|
+
|
102
|
+
# レコードが特定できないなら、更新処理ではないので終了
|
103
|
+
return nil unless uniqueness_condition
|
104
|
+
|
105
|
+
# 更新対象のレコードを正しく特定できているか確認するための検証条件を取得
|
106
|
+
required_condition = if model.parent
|
107
|
+
key = model.parent_foreign_key
|
108
|
+
|
109
|
+
{ key => attribute_value_map[key] }
|
110
|
+
else
|
111
|
+
@modeler.required_condition
|
112
|
+
end
|
113
|
+
|
114
|
+
# 検証条件は、必ず値がなければならない
|
115
|
+
return nil if required_condition.any? { |_k, v| ! presence_value?(v) }
|
116
|
+
|
117
|
+
# インポートされてきた、レコードを特定する条件が、誤った値でないかどうかを、
|
118
|
+
# 特定されるレコードが、更新すべき正しいレコードであるかチェックするための
|
119
|
+
# 検証条件とマージして、データベースに登録されているか確認する
|
120
|
+
verified_condition = uniqueness_condition.merge(required_condition)
|
121
|
+
|
122
|
+
find_verified_activerecord_by!(model.activerecord_constant, verified_condition).__send__(identified_column_name)
|
123
|
+
rescue ::ActiveRecord::RecordNotFound => e
|
124
|
+
# ID属性が指定されているのに、データベースに見つからない場合はエラーにする
|
125
|
+
raise e if identified_value
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_unique_condition(model, attribute_value_map, identified_column_name)
|
129
|
+
unique_indexes = @modeler.config_value_for(model, :unique_indexes, [])
|
130
|
+
|
131
|
+
# ユニーク条件が指定されていないなら終了
|
132
|
+
return nil if unique_indexes.empty?
|
133
|
+
|
134
|
+
unique_condition = unique_indexes.map do |key|
|
135
|
+
[key, attribute_value_map[key]]
|
136
|
+
end.to_h
|
137
|
+
|
138
|
+
# ユニーク条件は、必ず値がなければならない
|
139
|
+
return nil if unique_condition.any? { |_k, v| ! presence_value?(v) }
|
140
|
+
|
141
|
+
unique_condition
|
142
|
+
end
|
143
|
+
|
144
|
+
def find_verified_activerecord_by!(klass, verified_uniqueness_condition)
|
145
|
+
klass.find_by!(verified_uniqueness_condition)
|
146
|
+
end
|
147
|
+
|
148
|
+
def identified_column_name_of(model)
|
149
|
+
model.activerecord_constant.primary_key
|
150
|
+
end
|
151
|
+
|
152
|
+
def presence_value?(value)
|
153
|
+
# 空でない要素であるか or 空でない要素を含んでいるかどうか
|
154
|
+
case value
|
155
|
+
when ::Hash
|
156
|
+
value.values.any? { |v| presence_value?(v) }
|
157
|
+
when ::Array
|
158
|
+
value.any? { |v| presence_value?(v) }
|
159
|
+
when ::String
|
160
|
+
! value.blank?
|
161
|
+
when ::TrueClass, ::FalseClass
|
162
|
+
true
|
163
|
+
else
|
164
|
+
value ? true : false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Generator
|
4
|
+
class Base
|
5
|
+
attr_reader :data_source
|
6
|
+
|
7
|
+
def initialize(modeler, data_source)
|
8
|
+
@modeler = modeler
|
9
|
+
@data_source = data_source
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile
|
13
|
+
return @model if @model
|
14
|
+
|
15
|
+
model_name = @modeler.model_name
|
16
|
+
model_type, model_options = @modeler.parse_to_type_and_options(@modeler.entry_definition[:type])
|
17
|
+
|
18
|
+
@model = @modeler.new_module_instance(:model, model_type, model_name, model_options)
|
19
|
+
|
20
|
+
@model.data_source = @data_source
|
21
|
+
|
22
|
+
compile_model(@model)
|
23
|
+
|
24
|
+
@model
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate
|
28
|
+
generate_model_value(compile)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def compile_model(model)
|
34
|
+
model.setup_with_modeler(@modeler)
|
35
|
+
|
36
|
+
compile_associations(model)
|
37
|
+
end
|
38
|
+
|
39
|
+
def compile_associations(parent_model)
|
40
|
+
association_definitions = @modeler.config_value_for(parent_model, :entry_definition, {})[:associations] || {}
|
41
|
+
|
42
|
+
association_definitions.each do |association_name, association_definition|
|
43
|
+
association_type, association_options = @modeler.parse_to_type_and_options(association_definition[:type])
|
44
|
+
association_constant = @modeler.find_module_constant(:model, association_type)
|
45
|
+
|
46
|
+
model_or_models = association_constant.__send__(
|
47
|
+
association_generator, parent_model, association_name, association_options
|
48
|
+
)
|
49
|
+
|
50
|
+
parent_model.add_association(model_or_models)
|
51
|
+
|
52
|
+
if model_or_models.is_a?(::Array)
|
53
|
+
model_or_models.each { |model| compile_model(model) }
|
54
|
+
else
|
55
|
+
compile_model(model_or_models)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def association_generator
|
61
|
+
raise ::NotImplementedError.new "You must implement #{self.class}##{__method__}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Generator
|
4
|
+
class CsvRow < ::ActiveRecord::Bixformer::Generator::Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def association_generator
|
8
|
+
:new_as_association_for_export
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_model_value(model)
|
12
|
+
generate_attributes_value(model).merge(generate_association_value(model))
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_attributes_value(model)
|
16
|
+
model.generate_export_value_map.map do |attribute_name, attribute_value|
|
17
|
+
[model.csv_title(attribute_name), attribute_value]
|
18
|
+
end.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate_association_value(parent_model)
|
22
|
+
parent_model.association_map.values.inject({}) do |association_value, model_or_models|
|
23
|
+
models = model_or_models.is_a?(::Array) ? model_or_models : [model_or_models]
|
24
|
+
|
25
|
+
# 全関連レコードの生成結果を単一ハッシュにマージ
|
26
|
+
association_value.merge(
|
27
|
+
models.inject({}) do |current_association_value, model|
|
28
|
+
# 関連レコードの全生成結果を単一ハッシュにマージ
|
29
|
+
current_association_value.merge(
|
30
|
+
# キーをCSVカラム名に置き換えたハッシュを作成
|
31
|
+
generate_model_value(model)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Model
|
4
|
+
class Base
|
5
|
+
attr_accessor :data_source,
|
6
|
+
:activerecord_id
|
7
|
+
|
8
|
+
attr_reader :name,
|
9
|
+
:parent,
|
10
|
+
:attribute_map,
|
11
|
+
:optional_attributes,
|
12
|
+
:association_map,
|
13
|
+
:translator,
|
14
|
+
:modeler
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def new_as_association_for_import(parent, association_name, options)
|
18
|
+
raise ::NotImplementedError.new "You must implement #{self.class}##{__method__}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_as_association_for_export(parent, association_name, options)
|
22
|
+
model = self.new(association_name, options)
|
23
|
+
|
24
|
+
model.data_source = parent.data_source && parent.data_source.__send__(association_name) # parent.data_source is ActiveRecord::Base
|
25
|
+
|
26
|
+
unless model.data_source.is_a?(::ActiveRecord::Base)
|
27
|
+
parent_name = model.parents.map(&:name).join('.')
|
28
|
+
|
29
|
+
raise ::ArgumentError.new "#{parent_name}.#{association_name} is not a ActiveRecord instance"
|
30
|
+
end
|
31
|
+
|
32
|
+
model
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(model_or_association_name, options)
|
37
|
+
@name = model_or_association_name.to_s
|
38
|
+
@options = options
|
39
|
+
@association_map = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup_with_modeler(modeler)
|
43
|
+
@modeler = modeler
|
44
|
+
|
45
|
+
entry_definition = @modeler.config_value_for(self, :entry_definition, {})
|
46
|
+
|
47
|
+
@attribute_map = (entry_definition[:attributes] || {}).map do |attribute_name, attribute_value|
|
48
|
+
attribute_type, attribute_options = @modeler.parse_to_type_and_options(attribute_value)
|
49
|
+
|
50
|
+
attribute = @modeler.new_module_instance(:attribute, attribute_type, self, attribute_name, attribute_options)
|
51
|
+
|
52
|
+
[attribute_name, attribute]
|
53
|
+
end.to_h
|
54
|
+
|
55
|
+
@optional_attributes = @modeler.config_value_for(self, :optional_attributes, [])
|
56
|
+
@default_values = @modeler.config_value_for(self, :default_values, {})
|
57
|
+
|
58
|
+
# At present, translation function is only i18n
|
59
|
+
@translator = ::ActiveRecord::Bixformer::Translator::I18n.new
|
60
|
+
|
61
|
+
@translator.config = @modeler.translation_config.dup
|
62
|
+
@translator.model = self
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_parent(model)
|
66
|
+
@parent = model
|
67
|
+
end
|
68
|
+
|
69
|
+
def parents
|
70
|
+
@parent ? [*parent.parents, @parent] : []
|
71
|
+
end
|
72
|
+
|
73
|
+
def parent_foreign_key
|
74
|
+
return nil unless @parent
|
75
|
+
|
76
|
+
@parent_foreign_key ||= @parent.activerecord_constant.reflections[@name].foreign_key
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_association(model_or_models)
|
80
|
+
models = model_or_models.is_a?(::Array) ? model_or_models : [model_or_models]
|
81
|
+
|
82
|
+
association_name = models.first.name
|
83
|
+
|
84
|
+
@association_map[association_name] = model_or_models
|
85
|
+
|
86
|
+
models.each { |model| model.set_parent(self) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def activerecord_constant
|
90
|
+
@activerecord_constant ||=
|
91
|
+
if @parent
|
92
|
+
@parent.activerecord_constant.reflections[@name].table_name.classify.constantize
|
93
|
+
else
|
94
|
+
@name.camelize.constantize
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def generate_export_value_map
|
99
|
+
@attribute_map.keys.map do |attribute_name|
|
100
|
+
[attribute_name, make_export_value(attribute_name)]
|
101
|
+
end.to_h.with_indifferent_access
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_import_value_map
|
105
|
+
value_map = {}.with_indifferent_access
|
106
|
+
|
107
|
+
@attribute_map.keys.each do |attribute_name|
|
108
|
+
attribute_value = make_import_value(attribute_name)
|
109
|
+
|
110
|
+
attribute_value = @default_values[attribute_name] unless presence_value?(attribute_value)
|
111
|
+
|
112
|
+
# 取り込み時は、オプショナルな属性では、空と思われる値は取り込まない
|
113
|
+
next if ! presence_value?(attribute_value) &&
|
114
|
+
@optional_attributes.include?(attribute_name.to_s)
|
115
|
+
|
116
|
+
value_map[attribute_name] = attribute_value
|
117
|
+
end
|
118
|
+
|
119
|
+
value_map
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def make_export_value(attribute_name)
|
125
|
+
return nil unless @data_source
|
126
|
+
|
127
|
+
attribute = @attribute_map[attribute_name]
|
128
|
+
data_source_attribute_value = @data_source.__send__(attribute_name)
|
129
|
+
|
130
|
+
attribute.make_export_value(data_source_attribute_value)
|
131
|
+
end
|
132
|
+
|
133
|
+
def make_import_value(attribute_name)
|
134
|
+
raise ::NotImplementedError.new "You must implement #{self.class}##{__method__}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def presence_value?(value)
|
138
|
+
case value
|
139
|
+
when ::String
|
140
|
+
! value.blank?
|
141
|
+
when ::TrueClass, ::FalseClass
|
142
|
+
true
|
143
|
+
else
|
144
|
+
value ? true : false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Model
|
4
|
+
module Csv
|
5
|
+
class Base < ::ActiveRecord::Bixformer::Model::Base
|
6
|
+
class << self
|
7
|
+
def new_as_association_for_import(parent, association_name, options)
|
8
|
+
model = self.new(association_name, options)
|
9
|
+
|
10
|
+
model.data_source = parent.data_source # parent.data_source is CSV::Row
|
11
|
+
|
12
|
+
model
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def csv_title(attribute_name)
|
17
|
+
@translator.translate_attribute(attribute_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def available_csv_titles
|
21
|
+
[
|
22
|
+
*@attribute_map.keys.map do |attribute_name|
|
23
|
+
csv_title(attribute_name)
|
24
|
+
end,
|
25
|
+
*@association_map.values.flat_map do |model_or_models|
|
26
|
+
models = model_or_models.is_a?(::Array) ? model_or_models : [model_or_models]
|
27
|
+
|
28
|
+
models.flat_map { |m| m.available_csv_titles }
|
29
|
+
end
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def make_import_value(attribute_name)
|
36
|
+
return nil unless @data_source
|
37
|
+
|
38
|
+
attribute = @attribute_map[attribute_name]
|
39
|
+
data_source_attribute_value = @data_source[csv_title(attribute_name)]
|
40
|
+
|
41
|
+
attribute.make_import_value(data_source_attribute_value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Model
|
4
|
+
module Csv
|
5
|
+
class Indexed < ::ActiveRecord::Bixformer::Model::Csv::Base
|
6
|
+
class << self
|
7
|
+
def new_as_association_for_import(parent, association_name, options)
|
8
|
+
options = options.is_a?(Hash) ? options : {}
|
9
|
+
limit_size = options[:size] || 1
|
10
|
+
|
11
|
+
(1..limit_size).map do |index|
|
12
|
+
model = self.new(association_name, options.merge(index: index))
|
13
|
+
|
14
|
+
model.data_source = parent.data_source # parent.data_source is CSV::Row
|
15
|
+
|
16
|
+
model
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_as_association_for_export(parent, association_name, options)
|
21
|
+
options = options.is_a?(Hash) ? options : {}
|
22
|
+
limit_size = options[:size] || 1
|
23
|
+
associations = parent.data_source ? parent.data_source.__send__(association_name).to_a : []
|
24
|
+
|
25
|
+
(1..limit_size).map do |index|
|
26
|
+
model = self.new(association_name, options.merge(index: index))
|
27
|
+
|
28
|
+
model.data_source = associations[index - 1]
|
29
|
+
|
30
|
+
model
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup_with_modeler(modeler)
|
36
|
+
super
|
37
|
+
|
38
|
+
@translator.model_arguments = { index: @options[:index] }
|
39
|
+
|
40
|
+
@translator.attribute_arguments_map = @attribute_map.keys.map do |attribute_name|
|
41
|
+
[attribute_name, { index: @options[:index] }]
|
42
|
+
end.to_h
|
43
|
+
end
|
44
|
+
|
45
|
+
def csv_title(attribute_name)
|
46
|
+
if parents.find { |parent| parent.is_a?(ActiveRecord::Bixformer::Model::Csv::Indexed) }
|
47
|
+
parents.map { |parent| parent.translator.translate_model }.join + super
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Bixformer
|
3
|
+
module Modeler
|
4
|
+
class Base
|
5
|
+
attr_reader :format
|
6
|
+
|
7
|
+
def initialize(format)
|
8
|
+
@format = format
|
9
|
+
@module_load_namespaces_of = {}
|
10
|
+
@module_constant_of = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def model_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def entry_definition
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
def optional_attributes
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_attributes
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
def unique_indexes
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def required_condition
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_values
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
def translation_config
|
41
|
+
{
|
42
|
+
scope: :bixformer,
|
43
|
+
extend_scopes: []
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def module_load_namespaces(module_type)
|
48
|
+
[
|
49
|
+
"::ActiveRecord::Bixformer::#{module_type.to_s.camelize}::#{format.to_s.camelize}",
|
50
|
+
"::ActiveRecord::Bixformer::#{module_type.to_s.camelize}",
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def config_value_for(model, config_name, default_value = nil)
|
55
|
+
model_names_without_root = (model.parents.map(&:name) + [model.name]).drop(1)
|
56
|
+
|
57
|
+
# 指定された設定の全設定値を取得
|
58
|
+
entire_config_value = __send__(config_name)
|
59
|
+
|
60
|
+
if entire_config_value.is_a?(::Hash)
|
61
|
+
# Hashなら、with_indifferent_accessしておく
|
62
|
+
entire_config_value = entire_config_value.with_indifferent_access
|
63
|
+
elsif entire_config_value.is_a?(::Array) && entire_config_value.last.is_a?(::Hash)
|
64
|
+
# Arrayで最後の要素がHashなら、with_indifferent_accessしておく
|
65
|
+
config_value = entire_config_value.pop
|
66
|
+
|
67
|
+
entire_config_value.push config_value.with_indifferent_access
|
68
|
+
end
|
69
|
+
|
70
|
+
# その中から、指定のmodelに対応する設定部分を抽出
|
71
|
+
config_value = if config_name == :entry_definition
|
72
|
+
find_entry_definition(entire_config_value, model_names_without_root)
|
73
|
+
else
|
74
|
+
find_nested_config_value(entire_config_value, model_names_without_root)
|
75
|
+
end
|
76
|
+
|
77
|
+
if config_value.is_a?(::Array)
|
78
|
+
# Arrayで最後の要素がHashの場合、それは子要素の設定値なので、結果に含めない
|
79
|
+
config_value.pop if config_value.last.is_a?(::Hash)
|
80
|
+
|
81
|
+
# Arrayなら、要素は文字列化しておく
|
82
|
+
config_value = config_value.map { |v| v.to_s }
|
83
|
+
end
|
84
|
+
|
85
|
+
config_value || default_value
|
86
|
+
end
|
87
|
+
|
88
|
+
def new_module_instance(module_type, name_or_instance, *initializers)
|
89
|
+
name_or_instance = :base unless name_or_instance
|
90
|
+
|
91
|
+
name_or_instance = name_or_instance.to_s if name_or_instance.is_a?(::Symbol)
|
92
|
+
|
93
|
+
return name_or_instance unless name_or_instance.is_a?(::String)
|
94
|
+
|
95
|
+
if initializers.size > 0
|
96
|
+
find_module_constant(module_type, name_or_instance).new(*initializers)
|
97
|
+
else
|
98
|
+
find_module_constant(module_type, name_or_instance).new
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_module_constant(module_type, name)
|
103
|
+
name = :base unless name
|
104
|
+
|
105
|
+
module_constant = @module_constant_of["#{module_type}/#{name}"]
|
106
|
+
|
107
|
+
return module_constant if module_constant
|
108
|
+
|
109
|
+
namespaces = @module_load_namespaces_of[module_type] ||= module_load_namespaces(module_type)
|
110
|
+
|
111
|
+
namespaces.each do |namespace|
|
112
|
+
constant = "#{namespace}::#{name.to_s.camelize}".safe_constantize
|
113
|
+
|
114
|
+
return @module_constant_of["#{module_type}/#{name}"] = constant if constant
|
115
|
+
end
|
116
|
+
|
117
|
+
raise ::ArgumentError.new "Not found module named #{name.to_s.camelize} in module_load_namespaces('#{module_type}')"
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse_to_type_and_options(value)
|
121
|
+
value = value.dup if value.is_a?(::Array) || value.is_a?(::Hash)
|
122
|
+
type = value.is_a?(::Array) ? value.shift : value
|
123
|
+
|
124
|
+
arguments = if value.is_a?(::Array) && value.size == 1 && value.first.is_a?(::Hash)
|
125
|
+
value.first
|
126
|
+
elsif value.is_a?(::Array)
|
127
|
+
value
|
128
|
+
else
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
132
|
+
[type, arguments]
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def find_nested_config_value(config, keys)
|
138
|
+
return config ? config.dup : nil if keys.empty?
|
139
|
+
|
140
|
+
key = keys.shift
|
141
|
+
|
142
|
+
# config が Array なら、子要素は最後の要素にハッシュで定義してあるはず
|
143
|
+
config_map = config.is_a?(::Array) ? config.last : config
|
144
|
+
|
145
|
+
return nil unless config_map.is_a?(::Hash)
|
146
|
+
|
147
|
+
find_nested_config_value(config_map[key], keys)
|
148
|
+
end
|
149
|
+
|
150
|
+
def find_entry_definition(config, keys)
|
151
|
+
return config ? config.dup : nil if keys.empty?
|
152
|
+
|
153
|
+
key = keys.shift
|
154
|
+
|
155
|
+
find_entry_definition(config[:associations][key], keys)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|