activerecord-bixformer 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8cda410932eeba35b07cff8afac9eb446b02cbc
4
- data.tar.gz: 3a26402ab54b8acfc7044f504144f90331757578
3
+ metadata.gz: 525850c477621e80252c030d003cdfafedb124ad
4
+ data.tar.gz: c1b5d75f0fedc2d7cd5b0506f7d4d17e5ee3ec40
5
5
  SHA512:
6
- metadata.gz: 65823e13adc7ae8db2e61eb0f216b940ba55ab808430f2cfed3f2669a9fec76922ffd44b8e167ccc9562ad065cf19f61bd46fe2d8f994bf43efafafe6393ece1
7
- data.tar.gz: 68834b09f518dc2af1f79827e3ca5130c17c6efb45013a4f603ab9c08374b4f3fdab90284d1229c13c852c2ae3c1c2cd1ed6efb746e5ae378d3716332a2d3a82
6
+ metadata.gz: fdb0105819005d89f3c70ef030c2ce2316683f981687f7cf35a038074b7771646c4ed9f0350a00b7624a3f6711ef3d4a9ab20fc6636f6f934b98f4fe72e9bcfc
7
+ data.tar.gz: 4d8a6bab8708bfd6fb640ed0baaa7665db1dd66ac53207d0011dfceb4af18da9d821a751d14172e1f9e76628cc454881cd29ba924d595c25e7750390d0547c58
data/README-ja.md CHANGED
@@ -35,7 +35,7 @@ gem 'activerecord-bixformer'
35
35
 
36
36
  本フレームワークの構成要素と役割は以下のようになっています。
37
37
 
38
- ### Modeler
38
+ ### Plan
39
39
 
40
40
  インポート/エクスポートの一連の処理をどのように処理するかを定義する設定ファイルの役割です。
41
41
  処理実行時に、複数登録された本クラスのインスタンス群から採用するものを決定するフローになっていて、
@@ -51,27 +51,27 @@ gem 'activerecord-bixformer'
51
51
  ActiveRecord のモデルに対応して、そのモデルのインポート/エクスポート処理を担当するクラスです。
52
52
 
53
53
  ActiveRecord::Bixformer::Model::Base を継承した独自クラスを定義し、
54
- Modelerを適切に定義することで、それを使用して処理内容を切り替えることができます。
54
+ Planを適切に定義することで、それを使用して処理内容を切り替えることができます。
55
55
 
56
56
  ### Attribute
57
57
 
58
58
  ActiveRecord のモデルの持つ属性に対応して、その属性のインポート/エクスポート処理を担当するクラスです。
59
59
 
60
60
  ActiveRecord::Bixformer::Attribute::Base を継承した独自クラスを定義し、
61
- Modelerを適切に定義することで、それを使用して処理内容を切り替えることができます。
61
+ Planを適切に定義することで、それを使用して処理内容を切り替えることができます。
62
62
 
63
63
  # 使い方
64
64
 
65
- ## 1. Modelerの実装
65
+ ## 1. Planの実装
66
66
 
67
- ActiveRecord::Bixformer::Modeler::Base を継承して、以下のメソッドを適切に設定して下さい。
67
+ ActiveRecord::Bixformer::Plan::Base を継承して、以下のメソッドを適切に設定して下さい。
68
68
 
69
69
  ### model_name
70
70
 
71
71
  インポート/エクスポート対象のモデル名を返して下さい。
72
72
  例えばusersテーブルのデータが対象であれば、 `:user` です。
73
73
 
74
- ### entry_definition
74
+ ### entry
75
75
 
76
76
  インポート/エクスポート対象の属性と、それをどのように処理するかを定義した以下のようなハッシュを返して下さい。
77
77
 
@@ -110,7 +110,7 @@ ActiveRecord::Bixformer::Modeler::Base を継承して、以下のメソッド
110
110
 
111
111
  ```ruby
112
112
  [
113
- # 対象モデル(上の例なら user )の属性名。 entry_definition で定義されていること
113
+ # 対象モデル(上の例なら user )の属性名。 entry で定義されていること
114
114
  :name,
115
115
 
116
116
  # 関連名も指定可能。この場合は、その関連モデルの処理対象の属性全てが有効な値でない場合
@@ -225,8 +225,8 @@ end
225
225
 
226
226
  ### module_load_namespaces
227
227
 
228
- `entry_definition` で指定されたクラス名のクラスを探索する namespace を定義した配列を返して下さい。
229
- ActiveRecord::Bixformer::Modeler::Base には、以下のように定義されています。
228
+ `entry` で指定されたクラス名のクラスを探索する namespace を定義した配列を返して下さい。
229
+ ActiveRecord::Bixformer::Plan::Base には、以下のように定義されています。
230
230
 
231
231
  ```ruby
232
232
  def module_load_namespaces(module_type)
@@ -248,7 +248,7 @@ CSVを扱う簡単なサンプルコードは以下のような感じになり
248
248
  ```ruby
249
249
  runner = ActiveRecord::Bixformer::Runner::Csv.new
250
250
 
251
- runner.add_modeler(Your::Modeler.new)
251
+ runner.add_plan(Your::Plan.new)
252
252
 
253
253
  csv_data = runner.export(User.all, force_quotes: true)
254
254
 
@@ -0,0 +1,125 @@
1
+ module ActiveRecord
2
+ module Bixformer
3
+ class AssignableAttributesNormalizer
4
+ include ::ActiveRecord::Bixformer::ImportValueValidatable
5
+
6
+ def initialize(plan, model, parent_activerecord_id)
7
+ @plan = ActiveRecord::Bixformer::PlanAccessor.new(plan)
8
+ @model = model
9
+ @parent_activerecord_id = parent_activerecord_id
10
+ @identified_column_name = @model.activerecord_constant.primary_key
11
+ end
12
+
13
+ def normalize(model_attributes)
14
+ @model_attributes = model_attributes
15
+
16
+ return {} unless @model_attributes
17
+
18
+ # 必須な属性が渡されていない場合には、取り込みしない
19
+ return {} unless validate_required_attributes
20
+
21
+ set_required_condition if presence_value?(@model_attributes)
22
+ set_parent_foreign_key if presence_value?(@model_attributes)
23
+ set_identified_attribute if presence_value?(@model_attributes)
24
+
25
+ # 空でない要素が無いなら、空ハッシュで返す
26
+ return {} unless presence_value?(@model_attributes)
27
+
28
+ @model_attributes
29
+ end
30
+
31
+ private
32
+
33
+ def validate_required_attributes
34
+ required_attributes = @plan.pickup_value_for(@model, :required_attributes, [])
35
+
36
+ required_attributes.all? { |attribute_name| presence_value?(@model_attributes[attribute_name]) }
37
+ end
38
+
39
+ def set_required_condition
40
+ # 設定するのはルートの場合のみ
41
+ return if @model.parent
42
+
43
+ @model_attributes.merge!(@plan.value_of(:required_condition))
44
+ end
45
+
46
+ def set_parent_foreign_key
47
+ # 設定するのは親がいる場合のみ
48
+ return unless @model.parent
49
+
50
+ if @parent_activerecord_id
51
+ # 親のレコードが見つかっているなら、それも結果ハッシュに追加する
52
+ @model_attributes[@model.parent_foreign_key] = @parent_activerecord_id
53
+ else
54
+ # 見つかっていないなら、間違った値が指定されている可能性があるので、キー自体を削除
55
+ @model_attributes.delete(@model.parent_foreign_key)
56
+ end
57
+ end
58
+
59
+ def set_identified_attribute
60
+ # 更新の場合は、インポートデータを元にデータベースから対象のレコードを検索してIDを取得
61
+ verified_id = verified_activerecord_id
62
+
63
+ if verified_id
64
+ # 更新なら、ID属性を改めて設定
65
+ @model_attributes[@identified_column_name] = verified_id
66
+ else
67
+ # 見つかっていないなら、間違った値が指定されている可能性があるので、キー自体を削除
68
+ @model_attributes.delete(@identified_column_name)
69
+ end
70
+ end
71
+
72
+ def verified_activerecord_id
73
+ # 更新対象のレコードを特定できるかチェック
74
+ identified_value = @model_attributes[@identified_column_name]
75
+
76
+ uniqueness_condition = if identified_value
77
+ { @identified_column_name => identified_value }
78
+ else
79
+ find_unique_condition
80
+ end
81
+
82
+ # レコードが特定できないなら、更新処理ではないので終了
83
+ return nil unless uniqueness_condition
84
+
85
+ # 更新対象のレコードを正しく特定できているか確認するための検証条件を取得
86
+ required_condition = if @model.parent
87
+ key = @model.parent_foreign_key
88
+
89
+ { key => @model_attributes[key] }
90
+ else
91
+ @plan.value_of(:required_condition)
92
+ end
93
+
94
+ # 検証条件は、必ず値がなければならない
95
+ return nil if required_condition.any? { |_k, v| ! presence_value?(v) }
96
+
97
+ # インポートされてきた、レコードを特定する条件が、誤った値でないかどうかを、
98
+ # 特定されるレコードが、更新すべき正しいレコードであるかチェックするための
99
+ # 検証条件とマージして、データベースに登録されているか確認する
100
+ verified_condition = uniqueness_condition.merge(required_condition)
101
+
102
+ @model.find_activerecord_by!(verified_condition).__send__(@identified_column_name)
103
+ rescue ::ActiveRecord::RecordNotFound => e
104
+ # ID属性が指定されているのに、データベースに見つからない場合はエラーにする
105
+ raise e if identified_value
106
+ end
107
+
108
+ def find_unique_condition
109
+ unique_indexes = @plan.pickup_value_for(@model, :unique_indexes, [])
110
+
111
+ # ユニーク条件が指定されていないなら終了
112
+ return nil if unique_indexes.empty?
113
+
114
+ unique_condition = unique_indexes.map do |key|
115
+ [key, @model_attributes[key]]
116
+ end.to_h
117
+
118
+ # ユニーク条件は、必ず値がなければならない
119
+ return nil if unique_condition.any? { |_k, v| ! presence_value?(v) }
120
+
121
+ unique_condition
122
+ end
123
+ end
124
+ end
125
+ end
@@ -3,17 +3,15 @@ module ActiveRecord
3
3
  module Attribute
4
4
  class Booletania < ::ActiveRecord::Bixformer::Attribute::Base
5
5
  def make_export_value(active_record_value)
6
- return nil unless @model.data_source
7
-
8
- @model.data_source.__send__("#{@name}_text")
6
+ @model.activerecord_constant.__send__("#{@name}_options").find do |text, bool|
7
+ bool == active_record_value
8
+ end&.first
9
9
  end
10
10
 
11
11
  def make_import_value(value)
12
12
  return nil unless value
13
13
 
14
- @model.activerecord_constant.__send__("#{@name}_options").find do |options|
15
- options.first == value.strip
16
- end&.last
14
+ @model.activerecord_constant.__send__("#{@name}_options").to_h[value.strip]
17
15
  end
18
16
  end
19
17
  end
@@ -3,9 +3,11 @@ module ActiveRecord
3
3
  module Attribute
4
4
  class Enumerize < ::ActiveRecord::Bixformer::Attribute::Base
5
5
  def make_export_value(active_record_value)
6
- return nil unless @model.data_source
6
+ active_record_value = active_record_value.to_s
7
7
 
8
- @model.data_source.__send__("#{@name}_text")
8
+ @model.activerecord_constant.__send__(@name).options.find do |text, key|
9
+ key == active_record_value
10
+ end&.first
9
11
  end
10
12
 
11
13
  def make_import_value(value)
@@ -0,0 +1,27 @@
1
+ module ActiveRecord
2
+ module Bixformer
3
+ autoload :AssignableAttributesNormalizer, 'activerecord-bixformer/assignable_attributes_normalizer'
4
+ autoload :Compiler, 'activerecord-bixformer/compiler'
5
+ autoload :ImportValueValidatable, 'activerecord-bixformer/import_value_validatable'
6
+ autoload :Plan, 'activerecord-bixformer/plan'
7
+ autoload :PlanAccessor, 'activerecord-bixformer/plan_accessor'
8
+
9
+ module Attribute
10
+ autoload :Base, 'activerecord-bixformer/attribute/base'
11
+ autoload :Boolean, 'activerecord-bixformer/attribute/boolean'
12
+ autoload :Booletania, 'activerecord-bixformer/attribute/booletania'
13
+ autoload :Date, 'activerecord-bixformer/attribute/date'
14
+ autoload :Enumerize, 'activerecord-bixformer/attribute/enumerize'
15
+ autoload :Override, 'activerecord-bixformer/attribute/override'
16
+ autoload :Time, 'activerecord-bixformer/attribute/time'
17
+ end
18
+
19
+ module Model
20
+ autoload :Base, 'activerecord-bixformer/model/base'
21
+ end
22
+
23
+ module Translator
24
+ autoload :I18n, 'activerecord-bixformer/translator/i18n'
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ module ActiveRecord
2
+ module Bixformer
3
+ class Compiler
4
+ def initialize(format, plan)
5
+ plan.__bixformer_format = format.to_s
6
+
7
+ @model_name = plan.class.__bixformer_model
8
+ @plan = ActiveRecord::Bixformer::PlanAccessor.new(plan)
9
+ end
10
+
11
+ def compile
12
+ model_type, model_options = @plan.parse_to_type_and_options(@plan.value_of(:entry)[:type])
13
+
14
+ model = @plan.new_module_instance(:model, model_type, @model_name, model_options)
15
+
16
+ compile_model(model)
17
+
18
+ model
19
+ end
20
+
21
+ private
22
+
23
+ def compile_model(model)
24
+ model.setup(@plan.raw_value)
25
+
26
+ compile_associations(model)
27
+ end
28
+
29
+ def compile_associations(parent_model)
30
+ association_entries = @plan.pickup_value_for(parent_model, :entry, {})[:associations] || {}
31
+
32
+ association_entries.each do |association_name, association_entry|
33
+ association_type, association_options = @plan.parse_to_type_and_options(association_entry[:type])
34
+ association_constant = @plan.find_module_constant(:model, association_type)
35
+
36
+ association_model = association_constant.new(association_name, association_options)
37
+
38
+ parent_model.add_association(association_model)
39
+
40
+ compile_model(association_model)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ require 'activerecord-bixformer/autoload'
2
+
3
+ module ActiveRecord
4
+ module Bixformer
5
+ module Model
6
+ module Csv
7
+ autoload :Base, 'activerecord-bixformer/model/csv/base'
8
+ autoload :Indexed, 'activerecord-bixformer/model/csv/indexed'
9
+ end
10
+ end
11
+
12
+ module From
13
+ autoload :Csv, 'activerecord-bixformer/from/csv'
14
+ end
15
+
16
+ module To
17
+ autoload :Csv, 'activerecord-bixformer/to/csv'
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module Bixformer
3
+ module From
4
+ class Csv < ::ActiveRecord::Bixformer::Compiler
5
+ def initialize(plan)
6
+ super(:csv, plan)
7
+ end
8
+
9
+ def assignable_attributes(csv_row)
10
+ compile.make_import_value(csv_row)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module Bixformer
3
+ module ImportValueValidatable
4
+ def presence_value?(value)
5
+ # 空でない要素であるか or 空でない要素を含んでいるかどうか
6
+ case value
7
+ when ::Hash
8
+ value.values.any? { |v| presence_value?(v) }
9
+ when ::Array
10
+ value.any? { |v| presence_value?(v) }
11
+ when ::String
12
+ ! value.blank?
13
+ when ::TrueClass, ::FalseClass
14
+ true
15
+ else
16
+ value ? true : false
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,65 +1,57 @@
1
1
  module ActiveRecord
2
2
  module Bixformer
3
3
  module Model
4
+ # @attr_reader [String] name
5
+ # the name or association name of handled ActiveRecord
6
+ # @attr_reader [ActiveRecord::Bixformer::Model::Base] parent
7
+ # the instance has parent association.
8
+ # @attr_reader [Hash<String, ActiveRecord::Bixformer::Attribute::Base>] attributes
9
+ # the import/export target attribute names and its instance.
10
+ # @attr_reader [Array<String>] optional_attributes
11
+ # the list of attribute name to not make key if its value is blank.
12
+ # @attr_reader [Hash<String, ActiveRecord::Bixformer::Model::Base>] associations
13
+ # the import/export target association names and its instance.
14
+ # @attr_reader [ActiveRecord::Bixformer::Translator::I18n] translator
15
+ # @attr_reader [ActiveRecord::Bixformer::Plan::Base] plan
16
+ # active plan in the import/export process.
4
17
  class Base
5
- attr_accessor :data_source,
6
- :activerecord_id
18
+ include ::ActiveRecord::Bixformer::ImportValueValidatable
7
19
 
8
- attr_reader :name,
9
- :parent,
10
- :attribute_map,
20
+ attr_reader :name, :parent,
21
+ :attributes, :associations,
11
22
  :optional_attributes,
12
- :association_map,
13
23
  :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
24
+ :options
35
25
 
36
26
  def initialize(model_or_association_name, options)
37
- @name = model_or_association_name.to_s
38
- @options = options
39
- @association_map = {}
27
+ @name = model_or_association_name.to_s
28
+ @options = options
29
+ @associations = []
40
30
  end
41
31
 
42
- def setup_with_modeler(modeler)
43
- @modeler = modeler
44
-
45
- entry_definition = @modeler.config_value_for(self, :entry_definition, {})
32
+ def setup(plan)
33
+ @plan = ActiveRecord::Bixformer::PlanAccessor.new(plan)
46
34
 
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)
35
+ entry = @plan.pickup_value_for(self, :entry, {})
49
36
 
50
- attribute = @modeler.new_module_instance(:attribute, attribute_type, self, attribute_name, attribute_options)
37
+ @attributes = (entry[:attributes] || {}).map do |attribute_name, attribute_value|
38
+ attribute_type, attribute_options = @plan.parse_to_type_and_options(attribute_value)
51
39
 
52
- [attribute_name, attribute]
53
- end.to_h
40
+ @plan.new_module_instance(:attribute, attribute_type, self, attribute_name, attribute_options)
41
+ end
54
42
 
55
- @optional_attributes = @modeler.config_value_for(self, :optional_attributes, [])
56
- @default_values = @modeler.config_value_for(self, :default_values, {})
43
+ @optional_attributes = @plan.pickup_value_for(self, :optional_attributes, [])
44
+ @default_values = @plan.pickup_value_for(self, :default_values, {})
57
45
 
58
46
  # At present, translation function is only i18n
59
47
  @translator = ::ActiveRecord::Bixformer::Translator::I18n.new
60
48
 
61
- @translator.config = @modeler.translation_config.dup
62
- @translator.model = self
49
+ @translator.config = @plan.value_of(:translation_config).dup
50
+ @translator.model = self
51
+ end
52
+
53
+ def plan
54
+ @plan.raw_value
63
55
  end
64
56
 
65
57
  def set_parent(model)
@@ -70,22 +62,20 @@ module ActiveRecord
70
62
  @parent ? [*parent.parents, @parent] : []
71
63
  end
72
64
 
65
+ # @return [String] the foreign key name to associate to parent ActiveRecord.
73
66
  def parent_foreign_key
74
67
  return nil unless @parent
75
68
 
76
69
  @parent_foreign_key ||= @parent.activerecord_constant.reflections[@name].foreign_key
77
70
  end
78
71
 
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
72
+ def add_association(model)
73
+ @associations.push(model)
83
74
 
84
- @association_map[association_name] = model_or_models
85
-
86
- models.each { |model| model.set_parent(self) }
75
+ model.set_parent(self)
87
76
  end
88
77
 
78
+ # @return [Constant] the constant value of handling ActiveRecord.
89
79
  def activerecord_constant
90
80
  @activerecord_constant ||=
91
81
  if @parent
@@ -95,54 +85,50 @@ module ActiveRecord
95
85
  end
96
86
  end
97
87
 
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
88
+ def find_activerecord_by!(condition)
89
+ activerecord_constant.find_by!(condition)
102
90
  end
103
91
 
104
- def generate_import_value_map
105
- value_map = {}.with_indifferent_access
92
+ private
93
+
94
+ def make_each_attribute_import_value(parent_activerecord_id = nil, &block)
95
+ values = {}.with_indifferent_access
96
+ normalizer = ::ActiveRecord::Bixformer::AssignableAttributesNormalizer.new(plan, self, parent_activerecord_id)
106
97
 
107
- @attribute_map.keys.each do |attribute_name|
108
- attribute_value = make_import_value(attribute_name)
98
+ @attributes.each do |attr|
99
+ attribute_value = block.call(attr)
109
100
 
110
- attribute_value = @default_values[attribute_name] unless presence_value?(attribute_value)
101
+ attribute_value = @default_values[attr.name] unless presence_value?(attribute_value)
111
102
 
112
103
  # 取り込み時は、オプショナルな属性では、空と思われる値は取り込まない
113
104
  next if ! presence_value?(attribute_value) &&
114
- @optional_attributes.include?(attribute_name.to_s)
105
+ @optional_attributes.include?(attr.name.to_s)
115
106
 
116
- value_map[attribute_name] = attribute_value
107
+ values[attr.name] = attribute_value
117
108
  end
118
109
 
119
- value_map
110
+ normalizer.normalize(values)
120
111
  end
121
112
 
122
- private
123
-
124
- def make_export_value(attribute_name)
125
- return nil unless @data_source
113
+ def make_each_association_import_value(values, &block)
114
+ self_activerecord_id = values[activerecord_constant.primary_key]
126
115
 
127
- attribute = @attribute_map[attribute_name]
128
- data_source_attribute_value = @data_source.__send__(attribute_name)
116
+ @associations.each do |association|
117
+ association_value = block.call(association, self_activerecord_id)
129
118
 
130
- attribute.make_export_value(data_source_attribute_value)
131
- end
119
+ if association_value.is_a?(::Array)
120
+ # has_many な場合は、配列が返ってくるが、空と思われる要素は結果に含めない
121
+ association_value = association_value.reject { |v| ! presence_value?(v) }
122
+ end
132
123
 
133
- def make_import_value(attribute_name)
134
- raise ::NotImplementedError.new "You must implement #{self.class}##{__method__}"
135
- end
124
+ # 取り込み時は、オプショナルな関連では、空と思われる値は取り込まない
125
+ next if ! presence_value?(association_value) &&
126
+ @optional_attributes.include?(association.name.to_s)
136
127
 
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
128
+ values["#{association.name}_attributes".to_sym] = association_value
145
129
  end
130
+
131
+ values
146
132
  end
147
133
  end
148
134
  end
@@ -3,42 +3,50 @@ module ActiveRecord
3
3
  module Model
4
4
  module Csv
5
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)
6
+ def make_export_value(activerecord_or_activerecords)
7
+ # has_one でしか使わない想定なので activerecord_or_activerecords は ActiveRecord::Base のはず
8
+ values = @attributes.map do |attr|
9
+ attribute_value = activerecord_or_activerecords && activerecord_or_activerecords.__send__(attr.name)
9
10
 
10
- model.data_source = parent.data_source # parent.data_source is CSV::Row
11
+ [csv_title(attr.name), attr.make_export_value(attribute_value)]
12
+ end.to_h.with_indifferent_access
11
13
 
12
- model
14
+ @associations.inject(values) do |each_values, association|
15
+ association_value = activerecord_or_activerecords && activerecord_or_activerecords.__send__(association.name)
16
+
17
+ association_value = association_value.to_a if association_value.is_a?(::ActiveRecord::Relation)
18
+
19
+ each_values.merge(association.make_export_value(association_value))
13
20
  end
14
21
  end
15
22
 
16
- def csv_title(attribute_name)
17
- @translator.translate_attribute(attribute_name)
23
+ def make_import_value(csv_row, parent_activerecord_id = nil)
24
+ values = make_each_attribute_import_value(parent_activerecord_id) do |attr|
25
+ csv_value = csv_row[csv_title(attr.name)]
26
+
27
+ attr.make_import_value(csv_value)
28
+ end
29
+
30
+ make_each_association_import_value(values) do |association, self_activerecord_id|
31
+ association.make_import_value(csv_row, self_activerecord_id)
32
+ end
18
33
  end
19
34
 
20
- def available_csv_titles
35
+ def csv_titles
21
36
  [
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
37
+ *@attributes.map { |attr| csv_title(attr.name) },
38
+ *@associations.flat_map(&:csv_titles)
30
39
  ]
31
40
  end
32
41
 
33
- private
34
-
35
- def make_import_value(attribute_name)
36
- return nil unless @data_source
42
+ def parse_self_data_source(csv_row)
43
+ csv_row
44
+ end
37
45
 
38
- attribute = @attribute_map[attribute_name]
39
- data_source_attribute_value = @data_source[csv_title(attribute_name)]
46
+ private
40
47
 
41
- attribute.make_import_value(data_source_attribute_value)
48
+ def csv_title(attribute_name)
49
+ @translator.translate_attribute(attribute_name)
42
50
  end
43
51
  end
44
52
  end