activerecord-bixformer 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,11 +0,0 @@
1
- module ActiveRecord
2
- module Bixformer
3
- module Modeler
4
- class Csv < ::ActiveRecord::Bixformer::Modeler::Base
5
- def initialize
6
- super(:csv)
7
- end
8
- end
9
- end
10
- end
11
- 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