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 +4 -4
- data/README-ja.md +10 -10
- data/lib/activerecord-bixformer/assignable_attributes_normalizer.rb +125 -0
- data/lib/activerecord-bixformer/attribute/booletania.rb +4 -6
- data/lib/activerecord-bixformer/attribute/enumerize.rb +4 -2
- data/lib/activerecord-bixformer/autoload.rb +27 -0
- data/lib/activerecord-bixformer/compiler.rb +45 -0
- data/lib/activerecord-bixformer/format/csv.rb +20 -0
- data/lib/activerecord-bixformer/from/csv.rb +15 -0
- data/lib/activerecord-bixformer/import_value_validatable.rb +21 -0
- data/lib/activerecord-bixformer/model/base.rb +67 -81
- data/lib/activerecord-bixformer/model/csv/base.rb +31 -23
- data/lib/activerecord-bixformer/model/csv/indexed.rb +31 -24
- data/lib/activerecord-bixformer/plan.rb +56 -0
- data/lib/activerecord-bixformer/plan_accessor.rb +131 -0
- data/lib/activerecord-bixformer/to/csv.rb +56 -0
- data/lib/activerecord-bixformer/version.rb +1 -1
- data/lib/activerecord-bixformer.rb +2 -43
- metadata +11 -9
- data/lib/activerecord-bixformer/generator/active_record.rb +0 -170
- data/lib/activerecord-bixformer/generator/base.rb +0 -66
- data/lib/activerecord-bixformer/generator/csv_row.rb +0 -40
- data/lib/activerecord-bixformer/modeler/base.rb +0 -160
- data/lib/activerecord-bixformer/modeler/csv.rb +0 -11
- data/lib/activerecord-bixformer/runner/base.rb +0 -47
- data/lib/activerecord-bixformer/runner/csv.rb +0 -123
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 525850c477621e80252c030d003cdfafedb124ad
|
4
|
+
data.tar.gz: c1b5d75f0fedc2d7cd5b0506f7d4d17e5ee3ec40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
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
|
-
|
54
|
+
Planを適切に定義することで、それを使用して処理内容を切り替えることができます。
|
55
55
|
|
56
56
|
### Attribute
|
57
57
|
|
58
58
|
ActiveRecord のモデルの持つ属性に対応して、その属性のインポート/エクスポート処理を担当するクラスです。
|
59
59
|
|
60
60
|
ActiveRecord::Bixformer::Attribute::Base を継承した独自クラスを定義し、
|
61
|
-
|
61
|
+
Planを適切に定義することで、それを使用して処理内容を切り替えることができます。
|
62
62
|
|
63
63
|
# 使い方
|
64
64
|
|
65
|
-
## 1.
|
65
|
+
## 1. Planの実装
|
66
66
|
|
67
|
-
ActiveRecord::Bixformer::
|
67
|
+
ActiveRecord::Bixformer::Plan::Base を継承して、以下のメソッドを適切に設定して下さい。
|
68
68
|
|
69
69
|
### model_name
|
70
70
|
|
71
71
|
インポート/エクスポート対象のモデル名を返して下さい。
|
72
72
|
例えばusersテーブルのデータが対象であれば、 `:user` です。
|
73
73
|
|
74
|
-
###
|
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 )の属性名。
|
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
|
-
`
|
229
|
-
ActiveRecord::Bixformer::
|
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.
|
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
|
-
|
7
|
-
|
8
|
-
|
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").
|
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
|
-
|
6
|
+
active_record_value = active_record_value.to_s
|
7
7
|
|
8
|
-
@model.
|
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
|
-
|
6
|
-
:activerecord_id
|
18
|
+
include ::ActiveRecord::Bixformer::ImportValueValidatable
|
7
19
|
|
8
|
-
attr_reader :name,
|
9
|
-
:
|
10
|
-
:attribute_map,
|
20
|
+
attr_reader :name, :parent,
|
21
|
+
:attributes, :associations,
|
11
22
|
:optional_attributes,
|
12
|
-
:association_map,
|
13
23
|
:translator,
|
14
|
-
:
|
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
|
38
|
-
@options
|
39
|
-
@
|
27
|
+
@name = model_or_association_name.to_s
|
28
|
+
@options = options
|
29
|
+
@associations = []
|
40
30
|
end
|
41
31
|
|
42
|
-
def
|
43
|
-
@
|
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
|
-
|
48
|
-
attribute_type, attribute_options = @modeler.parse_to_type_and_options(attribute_value)
|
35
|
+
entry = @plan.pickup_value_for(self, :entry, {})
|
49
36
|
|
50
|
-
|
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
|
-
|
53
|
-
end
|
40
|
+
@plan.new_module_instance(:attribute, attribute_type, self, attribute_name, attribute_options)
|
41
|
+
end
|
54
42
|
|
55
|
-
@optional_attributes = @
|
56
|
-
@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 = @
|
62
|
-
@translator.model
|
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(
|
80
|
-
|
81
|
-
|
82
|
-
association_name = models.first.name
|
72
|
+
def add_association(model)
|
73
|
+
@associations.push(model)
|
83
74
|
|
84
|
-
|
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
|
99
|
-
|
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
|
-
|
105
|
-
|
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
|
-
@
|
108
|
-
attribute_value =
|
98
|
+
@attributes.each do |attr|
|
99
|
+
attribute_value = block.call(attr)
|
109
100
|
|
110
|
-
attribute_value = @default_values[
|
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?(
|
105
|
+
@optional_attributes.include?(attr.name.to_s)
|
115
106
|
|
116
|
-
|
107
|
+
values[attr.name] = attribute_value
|
117
108
|
end
|
118
109
|
|
119
|
-
|
110
|
+
normalizer.normalize(values)
|
120
111
|
end
|
121
112
|
|
122
|
-
|
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
|
-
|
128
|
-
|
116
|
+
@associations.each do |association|
|
117
|
+
association_value = block.call(association, self_activerecord_id)
|
129
118
|
|
130
|
-
|
131
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
124
|
+
# 取り込み時は、オプショナルな関連では、空と思われる値は取り込まない
|
125
|
+
next if ! presence_value?(association_value) &&
|
126
|
+
@optional_attributes.include?(association.name.to_s)
|
136
127
|
|
137
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
+
[csv_title(attr.name), attr.make_export_value(attribute_value)]
|
12
|
+
end.to_h.with_indifferent_access
|
11
13
|
|
12
|
-
|
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
|
17
|
-
|
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
|
35
|
+
def csv_titles
|
21
36
|
[
|
22
|
-
*@
|
23
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
return nil unless @data_source
|
42
|
+
def parse_self_data_source(csv_row)
|
43
|
+
csv_row
|
44
|
+
end
|
37
45
|
|
38
|
-
|
39
|
-
data_source_attribute_value = @data_source[csv_title(attribute_name)]
|
46
|
+
private
|
40
47
|
|
41
|
-
|
48
|
+
def csv_title(attribute_name)
|
49
|
+
@translator.translate_attribute(attribute_name)
|
42
50
|
end
|
43
51
|
end
|
44
52
|
end
|