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
@@ -1,40 +0,0 @@
|
|
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
|
@@ -1,160 +0,0 @@
|
|
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
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
module Bixformer
|
5
|
-
module Runner
|
6
|
-
class Base
|
7
|
-
attr_reader :errors
|
8
|
-
|
9
|
-
def initialize(format)
|
10
|
-
@format = format
|
11
|
-
@modelers = []
|
12
|
-
@errors = []
|
13
|
-
end
|
14
|
-
|
15
|
-
def add_modeler(*modelers)
|
16
|
-
modelers.each do |modeler|
|
17
|
-
unless modeler.format.to_s == @format.to_s
|
18
|
-
raise ArgumentError.new "modeler format unmatches to #{@format} as runner format : #{modeler.format}"
|
19
|
-
end
|
20
|
-
|
21
|
-
@modelers.push modeler
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def available_modelers
|
28
|
-
if @modelers.empty?
|
29
|
-
raise ArgumentError.new "Not exist any available modelers. You have to regist modeler by add_modeler."
|
30
|
-
end
|
31
|
-
|
32
|
-
@modelers
|
33
|
-
end
|
34
|
-
|
35
|
-
def active_modeler(force_detect = nil)
|
36
|
-
return @active_modeler if ! force_detect && @active_modeler
|
37
|
-
|
38
|
-
@active_modeler = detect_modeler
|
39
|
-
end
|
40
|
-
|
41
|
-
def detect_modeler
|
42
|
-
available_modelers.first
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
require 'csv'
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
module Bixformer
|
5
|
-
module Runner
|
6
|
-
class Csv < ::ActiveRecord::Bixformer::Runner::Base
|
7
|
-
def initialize
|
8
|
-
super(:csv)
|
9
|
-
end
|
10
|
-
|
11
|
-
def import(csv_data, options = {})
|
12
|
-
modeler = active_modeler(true)
|
13
|
-
model_constant = modeler.model_name.to_s.camelize.constantize
|
14
|
-
|
15
|
-
@errors = []
|
16
|
-
options[:headers] = true
|
17
|
-
|
18
|
-
model_attributes_list = parse_csv_rows(::CSV.parse(csv_data, options))
|
19
|
-
|
20
|
-
model_constant.transaction do
|
21
|
-
model_attributes_list.each.with_index(1) do |model_attributes, index|
|
22
|
-
next unless model_attributes&.present?
|
23
|
-
|
24
|
-
identified_value = model_attributes[model_constant.primary_key]
|
25
|
-
|
26
|
-
activerecord = if identified_value
|
27
|
-
find_activerecord(model_constant, identified_value)
|
28
|
-
else
|
29
|
-
build_activerecord(model_constant, model_attributes)
|
30
|
-
end
|
31
|
-
|
32
|
-
unless save_activerecord(activerecord, model_attributes)
|
33
|
-
@errors += make_error_messages(activerecord, index)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
raise ::ActiveRecord::Rollback unless @errors.empty?
|
38
|
-
end
|
39
|
-
|
40
|
-
@errors.empty? ? true : false
|
41
|
-
end
|
42
|
-
|
43
|
-
def export(active_records_or_relation, options = {})
|
44
|
-
modeler = active_modeler(true)
|
45
|
-
generator = modeler.new_module_instance(:generator, :csv_row, modeler, active_records_or_relation.first)
|
46
|
-
csv_titles = make_csv_titles(generator)
|
47
|
-
|
48
|
-
::CSV.generate(options) do |csv|
|
49
|
-
csv << csv_titles
|
50
|
-
|
51
|
-
active_records_or_relation.each do |activerecord|
|
52
|
-
generator = modeler.new_module_instance(:generator, :csv_row, modeler, activerecord)
|
53
|
-
|
54
|
-
csv << make_csv_row(generator, csv_titles)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def make_csv_titles(generator)
|
62
|
-
generator.compile.available_csv_titles
|
63
|
-
end
|
64
|
-
|
65
|
-
def make_csv_row(generator, csv_titles)
|
66
|
-
model_attributes = generator.generate
|
67
|
-
|
68
|
-
csv_titles.map { |title| model_attributes[title] }
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_csv_rows(csv_rows)
|
72
|
-
modeler = active_modeler
|
73
|
-
|
74
|
-
csv_rows.map do |csv_row|
|
75
|
-
generator = modeler.new_module_instance(:generator, :active_record, modeler, csv_row)
|
76
|
-
|
77
|
-
parse_csv_row(generator)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def parse_csv_row(generator)
|
82
|
-
generator.generate
|
83
|
-
end
|
84
|
-
|
85
|
-
def find_activerecord(model_class, id)
|
86
|
-
model_class.find(id)
|
87
|
-
end
|
88
|
-
|
89
|
-
def build_activerecord(model_class, attributes)
|
90
|
-
model_class.new(attributes)
|
91
|
-
end
|
92
|
-
|
93
|
-
def save_activerecord(activerecord, assigned_attributes)
|
94
|
-
# CSVで削除が可能だが、削除フラグは _destroy というattributesに入っており、こんなcolumnは存在しないので、このままだとエラーになる
|
95
|
-
# そのため、ここで要素から削除しておく
|
96
|
-
is_destroy = assigned_attributes.delete(:_destroy)
|
97
|
-
|
98
|
-
if activerecord.persisted?
|
99
|
-
if is_destroy
|
100
|
-
activerecord.destroy
|
101
|
-
else
|
102
|
-
activerecord.update(assigned_attributes)
|
103
|
-
end
|
104
|
-
else
|
105
|
-
activerecord.save
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def make_error_messages(activerecord, csv_row_index)
|
110
|
-
activerecord.errors.full_messages.map do |msg|
|
111
|
-
I18n.t(
|
112
|
-
:"bixformer.csv.errors.format", {
|
113
|
-
default: "Entry(%{csv_row_index}): %{message}",
|
114
|
-
csv_row_index: csv_row_index,
|
115
|
-
message: msg
|
116
|
-
}
|
117
|
-
)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|