active_record_importer 0.1.0 → 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: 43724c0cedd35917d14e3439f96482c8f2357943
4
- data.tar.gz: 7db97349cd0eafa87a2296906f56e6543c58395f
3
+ metadata.gz: 07cd0e7debf64daad8259915aa82f993c1c86f1c
4
+ data.tar.gz: 98df0543814ef29f1bbe2ec031c9f02394db723e
5
5
  SHA512:
6
- metadata.gz: 22f7c0e2eb463b49e0a717d1ee4da685c111424681f20cbc2f578ebe5bbbab57277384f05715aca3abf22ffb25fe957efe8820ee916d6add08cc8ba82fa82e84
7
- data.tar.gz: e90f8ab8ce49228e47441a3df8adcd214547fec041583912760e0792b1b6eff631f8378723d9cc73af23fbcc4605b8c58ee59578ca433a1c8a6070f413026502
6
+ metadata.gz: 521db635648cfe9da9d5ce938f58c519db6e6e16c7428df3efa25a251fce72f655c17ee98d61f9d98ac949b377107f07be5377b531e0ac1597edf4e9238aa5b2
7
+ data.tar.gz: 407647a49e620e64cc47c125f106017b132e24c44fb039d4e882f6ceea7f5b3ec9ed90ec65d8ed0e638ef79590c3bf660f50bf8f94a3557f765ed260facd09c6
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # ActiveRecordImporter
2
2
 
3
- Supports only Rails 4... (I'll release soon an update to support Rails 5)
3
+ Supports only Rails 4 and 5
4
4
 
5
5
  This gem helps you insert/update records easily. For now, it only accepts CSV file.
6
6
  This also helps you monitor how many rows are imported, and how many rows failed.
7
+ This gem also allows you to easily import to any model with few configurations.
8
+
7
9
  I'll release an update to enable this on background job.
8
- This gem allows you to easily import to any model with few configurations.
9
10
 
10
11
  ## Installation
11
12
 
@@ -46,7 +47,7 @@ class ActiveRecordImporterMigration < ActiveRecord::Migration
46
47
  end
47
48
  ```
48
49
 
49
- #### Import Model:
50
+ #### Add Import Model:
50
51
  ```ruby
51
52
  class Import < ActiveRecord::Base
52
53
  extend Enumerize
@@ -57,6 +58,7 @@ class Import < ActiveRecord::Base
57
58
  default: :upsert
58
59
 
59
60
  has_attached_file :file
61
+ has_attached_file :failed_file
60
62
 
61
63
  attr_accessor :execute_on_create
62
64
 
@@ -67,15 +69,22 @@ class Import < ActiveRecord::Base
67
69
  content_type: %w(text/plain text/csv)
68
70
  }
69
71
 
70
- after_create :execute, if: :execute_on_create
72
+ validates_attachment :failed_file,
73
+ content_type: {
74
+ content_type: %w(text/plain text/csv)
75
+ }
71
76
 
72
- # I'll add import options in the next release
77
+ # I'll add import options in the next major release
73
78
  # accepts_nested_attributes_for :import_options, allow_destroy: true
74
79
 
75
80
  def execute
76
81
  resource_class.import!(self, execute_on_create)
77
82
  end
78
83
 
84
+ def execute!
85
+ resource_class.import!(self, true)
86
+ end
87
+
79
88
  def resource_class
80
89
  resource.safe_constantize
81
90
  end
@@ -86,11 +95,20 @@ class Import < ActiveRecord::Base
86
95
 
87
96
  ##
88
97
  # Override this if you prefer have
89
- # a private permissions or you have
98
+ # private permissions or you have
90
99
  # private methods for reading files
91
100
  ##
92
101
  def import_file
93
- local_path? ? file.path : file.url
102
+ local_path?(file) ? file.path : file.url
103
+ end
104
+
105
+ ##
106
+ # Override this method if you have
107
+ # private permissions or you have private methods
108
+ # for reading/writing uploaded files
109
+ ##
110
+ def failed_file_path
111
+ local_path?(failed_file) ? failed_file.path : failed_file.url
94
112
  end
95
113
 
96
114
  private
@@ -100,13 +118,14 @@ class Import < ActiveRecord::Base
100
118
  errors.add(:find_options, "can't be blank") if find_options.blank?
101
119
  end
102
120
 
103
- def local_path?
104
- File.exist? import_file.file.path
121
+ def local_path?(f)
122
+ File.exist? f.path
105
123
  end
106
124
  end
107
125
  ```
108
126
 
109
- Add `acts_as_importable` to a model to make it importable
127
+ ### Add `acts_as_importable` to any ActiveRecord model to make it importable
128
+
110
129
  ```ruby
111
130
  class User < ActiveRecord::Base
112
131
  acts_as_importable
@@ -120,9 +139,35 @@ class User < ActiveRecord::Base
120
139
  last_name: 'dela Cruz' },
121
140
  find_options: %i(email),
122
141
  before_save: Proc.new { |user| user.password = 'temporarypassword123' }
142
+ after_save: Proc.new { |user| puts "THIS IS CALLED AFTER OBJECT IS SAVED" }
123
143
  end
124
144
  ```
125
145
 
146
+ If you're using ActiveRecord::Store, you may import values to your accessors by including them in the configuration:
147
+ ```ruby
148
+ class User < ActiveRecord::Base
149
+ store :properties, accessors: [:first_key, :second_key]
150
+
151
+ acts_as_importable store_accessors: [:first_key, :second_key]
152
+ end
153
+ ```
154
+
155
+ ### Add import form
156
+ This is a sample import HAML form:
157
+ ```ruby
158
+ # resource is your Model name
159
+ = f.input :resource
160
+ # batch_size is useful for large csv file
161
+ = f.input :batch_size
162
+ # insert_methods: [:upsert, :insert, :error_on_duplicate]
163
+ = f.input :insert_method, collection: insert_methods, class: 'form-control insert-method'
164
+ # `find_options` are the list of columns you want to use to update a certain instance or
165
+ # error when a duplicate is found. This is not required when your insert_method is `:insert`
166
+ = f.input :find_options
167
+ = f.input :file, as: :file,
168
+ input_html: { accept: '.csv' }
169
+ ```
170
+
126
171
  You may also add some options from the SmarterCSV gem:
127
172
 
128
173
  | Option | Default
@@ -135,24 +180,51 @@ You may also add some options from the SmarterCSV gem:
135
180
  | :chunk_size | 500
136
181
  | :col_sep | ","
137
182
 
138
- #### I'll add more options SOON!
183
+ https://github.com/tilo/smarter_csv
184
+
185
+
186
+ ```ruby
187
+ class User < ActiveRecord::Base
188
+ acts_as_importable csv_opts: {
189
+ chunk_size: 2000,
190
+ col_sep: '|',
191
+ convert_values_to_numeric: { only: [:age, :salary] }
192
+ }
193
+ end
194
+ ```
195
+
196
+ `I'll add more options SOON!`
139
197
 
140
- #### Imports Controller:
198
+
199
+ ### Create Imports Controller:
141
200
  ```ruby
142
201
  class ImportsController < ApplicationController
143
202
 
144
203
  def create
145
204
  @import = Import.create!(import_params)
146
- @import.execute_on_create = true
147
- @import.execute
205
+ @import.execute!
148
206
  end
149
207
 
208
+ private
209
+
210
+ def import_params
211
+ params.require(:import).permit(:file, :resource, :insert_method, :batch_size)
212
+ end
150
213
  end
151
214
  ```
152
215
 
153
- You may include execute_on_create as a checkbox field in your Import form so you don't have to
154
- include `@import.execute_on_create = true` in your controller.
155
-
216
+ #### Run it via Rails Console:
217
+ ```ruby
218
+ File.open(PATH_TO_CSV_FILE) do |file|
219
+ @import = Import.create!(
220
+ resource: 'User',
221
+ file: file,
222
+ insert_method: 'upsert',
223
+ find_options: 'first_name,last_name'
224
+ )
225
+ end
226
+ @import.execute!
227
+ ```
156
228
 
157
229
 
158
230
  ## Development
@@ -169,4 +241,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/michae
169
241
  ## License
170
242
 
171
243
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
172
-
@@ -17,11 +17,15 @@ module ActiveRecordImporter
17
17
  autoload :Importable, 'active_record_importer/importable'
18
18
  autoload :InstanceBuilder, 'active_record_importer/instance_builder'
19
19
  autoload :OptionsBuilder, 'active_record_importer/options_builder'
20
- autoload :AttributesBuilder, 'active_record_importer/attributes_builder'
21
- autoload :FindOptionsBuilder, 'active_record_importer/find_options_builder'
22
20
  autoload :TransitionProcessor, 'active_record_importer/transition_processor'
23
21
  autoload :ImportCallbacker, 'active_record_importer/import_callbacker'
24
- autoload :Helpers, 'active_record_importer/helpers'
22
+ autoload :FailedFileBuilder, 'active_record_importer/failed_file_builder'
23
+
24
+ module Attribute
25
+ autoload :AttributesBuilder, 'active_record_importer/attribute/attributes_builder'
26
+ autoload :FindOptionsBuilder, 'active_record_importer/attribute/find_options_builder'
27
+ autoload :Helpers, 'active_record_importer/attribute/helpers'
28
+ end
25
29
 
26
30
  require 'active_record_importer/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
27
31
  end
@@ -0,0 +1,69 @@
1
+ module ActiveRecordImporter
2
+ module Attribute
3
+ class AttributesBuilder
4
+ include Attribute::Helpers
5
+
6
+ attr_reader :importable, :row_attrs, :processed_attrs
7
+
8
+ delegate :importer_options, to: :importable
9
+ delegate :importable_columns,
10
+ :default_attributes,
11
+ :find_assoc_opts,
12
+ to: :importer_options
13
+
14
+ def initialize(importable, row_attrs)
15
+ @importable = importable
16
+ @row_attrs = row_attrs
17
+ @processed_attrs = {}
18
+ end
19
+
20
+ def build
21
+ force_encode_attributes
22
+ fetch_time_attributes
23
+ processed_attrs
24
+ end
25
+
26
+ private
27
+
28
+ def default_attrs
29
+ def_attrs = { importing: true }
30
+ default_attributes.each do |key, value|
31
+ def_attrs[key] = fetch_value(value)
32
+ end
33
+ end
34
+
35
+ def force_encode_attributes
36
+ @processed_attrs = force_utf8_encode(merged_attributes)
37
+ end
38
+
39
+ def fetch_time_attributes
40
+ @processed_attrs.merge!(time_attributes(processed_attrs))
41
+ end
42
+
43
+ def fetch_value(value)
44
+ case value
45
+ when Proc
46
+ value.call(row_attrs)
47
+ when Symbol
48
+ importable.send(value, row_attrs)
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ def merged_attributes
55
+ attributes = row_attrs.slice(*importable_columns)
56
+ row_attrs = attributes.inject({}) do |row_attrs, key_value|
57
+ row_attrs[key_value.first] = key_value.last if key_value.last.present?
58
+ row_attrs
59
+ end
60
+ default_attrs.merge(row_attrs)
61
+ end
62
+
63
+ def has_column?(column)
64
+ return if column.blank?
65
+ importable_columns.include?(column.to_sym)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,54 @@
1
+ module ActiveRecordImporter
2
+ module Attribute
3
+ class FindOptionsBuilder
4
+ include Virtus.model
5
+ include Helpers
6
+
7
+ attribute :resource, String
8
+ attribute :attrs, Hash, default: {}
9
+ attribute :find_options, String
10
+ attribute :prefix, String
11
+
12
+ def build
13
+ get_find_opts
14
+ slice_attributes
15
+ end
16
+
17
+ private
18
+
19
+ def klass
20
+ resource.safe_constantize
21
+ end
22
+
23
+ delegate :importer_options, to: :klass
24
+
25
+ delegate :required_attributes, to: :importer_options
26
+
27
+ def get_find_opts
28
+ @options = strip_and_symbolize
29
+ @options ||= importer_options.find_options || required_attributes
30
+ @options
31
+ end
32
+
33
+ def slice_attributes
34
+ return attrs.slice(*@options).compact if prefix.blank?
35
+
36
+ @options.inject({}) do |attr, key|
37
+ attr[key] = attrs[prefixed_key(key)].presence
38
+ attr
39
+ end.compact
40
+ end
41
+
42
+ def prefixed_key(key)
43
+ "#{prefix}#{key}".to_sym
44
+ end
45
+
46
+ def strip_and_symbolize
47
+ return if find_options.blank?
48
+ find_options.split(',').map do |key|
49
+ key.strip.to_sym
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveRecordImporter
2
+ module Attribute
3
+ module Helpers
4
+
5
+ def parse_datetime(datetime = nil)
6
+ return if datetime.blank?
7
+ Time.parse(datetime)
8
+ end
9
+
10
+ def force_utf8_encode(data = {})
11
+ return data if data.blank?
12
+
13
+ data.keys.each do |key|
14
+ data[key] = data[key].force_encoding('UTF-8') if data[key].is_a?(String)
15
+ end
16
+
17
+ data
18
+ end
19
+
20
+ def time_attributes(data = {})
21
+ attrs = {}
22
+ attrs[:created_at] = parse_datetime(data[:created_at]) || Time.now
23
+ attrs[:updated_at] = parse_datetime(data[:updated_at]) || attrs[:created_at]
24
+ attrs
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,32 +1,44 @@
1
1
  module ActiveRecordImporter
2
2
  class BatchImporter
3
3
 
4
- attr_reader :data, :import
4
+ attr_reader :data, :import, :failed_file, :processor
5
5
 
6
6
  def initialize(import, data)
7
7
  @import = import
8
8
  @data = data
9
+ @failed_file = FailedFileBuilder.new(import)
9
10
  end
10
11
 
11
12
  def process!
12
13
  @imported_count, @failed_count = 0, 0
13
-
14
14
  data.each do |row|
15
15
  next if row.blank?
16
- processor = DataProcessor.new(import, row.symbolize_keys!)
17
- processor.process ? @imported_count += 1 : @failed_count += 1
16
+ process_row(row.symbolize_keys!)
18
17
  end
19
18
 
20
19
  set_import_count
20
+ finalize_batch_import
21
21
  end
22
22
 
23
23
  private
24
24
 
25
+ def process_row(row)
26
+ processor = DataProcessor.new(import, row)
27
+ return @imported_count += 1 if processor.process
28
+
29
+ @failed_file.failed_rows << row.merge(import_errors: processor.row_errors)
30
+ @failed_count += 1
31
+ end
32
+
25
33
  def set_import_count
26
34
  Import.update_counters(import.id, imported_rows: @imported_count)
27
35
  Import.update_counters(import.id, failed_rows: @failed_count)
28
36
  end
29
37
 
38
+ def finalize_batch_import
39
+ @failed_file.build
40
+ end
41
+
30
42
  def importable
31
43
  import.resource.safe_constantize
32
44
  end
@@ -38,7 +38,7 @@ module ActiveRecordImporter
38
38
  end
39
39
 
40
40
  def fetch_instance_attributes
41
- @attributes = AttributesBuilder.new(
41
+ @attributes = Attribute::AttributesBuilder.new(
42
42
  importable, row_attrs
43
43
  ).build
44
44
  rescue => exception
@@ -46,7 +46,7 @@ module ActiveRecordImporter
46
46
  end
47
47
 
48
48
  def fetch_find_attributes
49
- @find_attributes = FindOptionsBuilder.new(
49
+ @find_attributes = Attribute::FindOptionsBuilder.new(
50
50
  resource: import.resource,
51
51
  find_options: import.find_options,
52
52
  attrs: attributes
@@ -12,6 +12,10 @@ module ActiveRecordImporter
12
12
  end
13
13
  end
14
14
 
15
- class DuplicateRecord < StandardError; end
15
+ class DuplicateRecord < StandardError
16
+ def initialize
17
+ super 'Duplicate record found!'
18
+ end
19
+ end
16
20
  end
17
21
  end
@@ -0,0 +1,81 @@
1
+ module ActiveRecordImporter
2
+ class FailedFileBuilder
3
+ attr_reader :import
4
+ attr_accessor :failed_rows
5
+
6
+ def initialize(import)
7
+ @import = import
8
+ @failed_rows = []
9
+ end
10
+
11
+ def build
12
+ return if failed_rows.blank?
13
+
14
+ create_or_append_to_csv
15
+ create_import_failed_file
16
+ destroy_temp_file
17
+ end
18
+
19
+ private
20
+
21
+ def create_or_append_to_csv
22
+ puts 'TEST!!!'
23
+ if import.failed_file.present?
24
+ puts 'APPEND!!!!'
25
+ append_rows_to_file
26
+ else
27
+ puts 'WRITE!!!!'
28
+ write_csv_file
29
+ end
30
+ end
31
+
32
+ def write_csv_file
33
+ CSV.open(temp_file_path, 'wb') do |csv|
34
+ csv << failed_rows.first.keys
35
+ insert_failed_rows(csv)
36
+ end
37
+ end
38
+
39
+ def append_rows_to_file
40
+ return if import.failed_file.blank?
41
+ CSV.open(import.failed_file_path, 'a+') do |csv|
42
+ insert_failed_rows(csv)
43
+ end
44
+ end
45
+
46
+
47
+ def insert_failed_rows(csv)
48
+ failed_rows.each do |hash|
49
+ csv << hash.values
50
+ end
51
+ end
52
+
53
+ def destroy_temp_file
54
+ return unless File.exists?(temp_file_path)
55
+ FileUtils.rm(temp_file_path)
56
+ end
57
+
58
+ def temp_file_path
59
+ "/tmp/#{target_file_name}"
60
+ end
61
+
62
+ def target_file_name
63
+ "failed_file_#{import.id}.csv"
64
+ end
65
+
66
+ def create_import_failed_file
67
+ return if import.failed_file.present?
68
+ File.open(temp_file_path) do |file|
69
+ import.failed_file = file
70
+
71
+ # I forced to save it as 'text/csv' because
72
+ # the file is being saved as 'text/x-pascal'
73
+ # and I still have no idea why?!?
74
+
75
+ import.failed_file_content_type = 'text/csv'
76
+ import.save!
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordImporter
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -8,6 +8,7 @@ module ActiveRecordImporter
8
8
  default: :upsert
9
9
 
10
10
  has_attached_file :file
11
+ has_attached_file :failed_file
11
12
 
12
13
  attr_accessor :execute_on_create
13
14
 
@@ -18,15 +19,22 @@ module ActiveRecordImporter
18
19
  content_type: %w(text/plain text/csv)
19
20
  }
20
21
 
21
- after_create :execute, if: :execute_on_create
22
+ validates_attachment :failed_file,
23
+ content_type: {
24
+ content_type: %w(text/plain text/csv)
25
+ }
22
26
 
23
- # I'll add import options in the next release
27
+ # I'll add import options in the next major release
24
28
  # accepts_nested_attributes_for :import_options, allow_destroy: true
25
29
 
26
30
  def execute
27
31
  resource_class.import!(self, execute_on_create)
28
32
  end
29
33
 
34
+ def execute!
35
+ resource_class.import!(self, true)
36
+ end
37
+
30
38
  def resource_class
31
39
  resource.safe_constantize
32
40
  end
@@ -37,11 +45,20 @@ module ActiveRecordImporter
37
45
 
38
46
  ##
39
47
  # Override this if you prefer have
40
- # a private permissions or you have
48
+ # private permissions or you have
41
49
  # private methods for reading files
42
50
  ##
43
51
  def import_file
44
- local_path? ? file.path : file.url
52
+ local_path?(file) ? file.path : file.url
53
+ end
54
+
55
+ ##
56
+ # Override this method if you have
57
+ # private permissions or you have private methods
58
+ # for reading/writing uploaded files
59
+ ##
60
+ def failed_file_path
61
+ local_path?(failed_file) ? failed_file.path : failed_file.url
45
62
  end
46
63
 
47
64
  private
@@ -51,8 +68,8 @@ module ActiveRecordImporter
51
68
  errors.add(:find_options, "can't be blank") if find_options.blank?
52
69
  end
53
70
 
54
- def local_path?
55
- File.exist? import_file.file.path
71
+ def local_path?(f)
72
+ File.exist? f.path
56
73
  end
57
74
  end
58
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_importer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Nera
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -152,14 +152,15 @@ files:
152
152
  - bin/setup
153
153
  - db/migrate/1_active_record_importer_migration.rb
154
154
  - lib/active_record_importer.rb
155
- - lib/active_record_importer/attributes_builder.rb
155
+ - lib/active_record_importer/attribute/attributes_builder.rb
156
+ - lib/active_record_importer/attribute/find_options_builder.rb
157
+ - lib/active_record_importer/attribute/helpers.rb
156
158
  - lib/active_record_importer/batch_importer.rb
157
159
  - lib/active_record_importer/data_processor.rb
158
160
  - lib/active_record_importer/dispatcher.rb
159
161
  - lib/active_record_importer/engine.rb
160
162
  - lib/active_record_importer/errors.rb
161
- - lib/active_record_importer/find_options_builder.rb
162
- - lib/active_record_importer/helpers.rb
163
+ - lib/active_record_importer/failed_file_builder.rb
163
164
  - lib/active_record_importer/import_callbacker.rb
164
165
  - lib/active_record_importer/importable.rb
165
166
  - lib/active_record_importer/instance_builder.rb
@@ -1,67 +0,0 @@
1
- module ActiveRecordImporter
2
- class AttributesBuilder
3
- include Helpers
4
-
5
- attr_reader :importable, :row_attrs, :processed_attrs
6
-
7
- delegate :importer_options, to: :importable
8
- delegate :importable_columns,
9
- :default_attributes,
10
- :find_assoc_opts,
11
- to: :importer_options
12
-
13
- def initialize(importable, row_attrs)
14
- @importable = importable
15
- @row_attrs = row_attrs
16
- @processed_attrs = {}
17
- end
18
-
19
- def build
20
- force_encode_attributes
21
- fetch_time_attributes
22
- processed_attrs
23
- end
24
-
25
- private
26
-
27
- def default_attrs
28
- def_attrs = { importing: true }
29
- default_attributes.each do |key, value|
30
- def_attrs[key] = fetch_value(value)
31
- end
32
- end
33
-
34
- def force_encode_attributes
35
- @processed_attrs = force_utf8_encode(merged_attributes)
36
- end
37
-
38
- def fetch_time_attributes
39
- @processed_attrs.merge!(time_attributes(processed_attrs))
40
- end
41
-
42
- def fetch_value(value)
43
- case value
44
- when Proc
45
- value.call(row_attrs)
46
- when Symbol
47
- importable.send(value, row_attrs)
48
- else
49
- value
50
- end
51
- end
52
-
53
- def merged_attributes
54
- attributes = row_attrs.slice(*importable_columns)
55
- row_attrs = attributes.inject({}) do |row_attrs, key_value|
56
- row_attrs[key_value.first] = key_value.last if key_value.last.present?
57
- row_attrs
58
- end
59
- default_attrs.merge(row_attrs)
60
- end
61
-
62
- def has_column?(column)
63
- return if column.blank?
64
- importable_columns.include?(column.to_sym)
65
- end
66
- end
67
- end
@@ -1,52 +0,0 @@
1
- module ActiveRecordImporter
2
- class FindOptionsBuilder
3
- include Virtus.model
4
- include Helpers
5
-
6
- attribute :resource, String
7
- attribute :attrs, Hash, default: {}
8
- attribute :find_options, String
9
- attribute :prefix, String
10
-
11
- def build
12
- get_find_opts
13
- slice_attributes
14
- end
15
-
16
- private
17
-
18
- def klass
19
- resource.safe_constantize
20
- end
21
-
22
- delegate :importer_options, to: :klass
23
-
24
- delegate :required_attributes, to: :importer_options
25
-
26
- def get_find_opts
27
- @options = strip_and_symbolize
28
- @options ||= importer_options.find_options || required_attributes
29
- @options
30
- end
31
-
32
- def slice_attributes
33
- return attrs.slice(*@options).compact if prefix.blank?
34
-
35
- @options.inject({}) do |attr, key|
36
- attr[key] = attrs[prefixed_key(key)].presence
37
- attr
38
- end.compact
39
- end
40
-
41
- def prefixed_key(key)
42
- "#{prefix}#{key}".to_sym
43
- end
44
-
45
- def strip_and_symbolize
46
- return if find_options.blank?
47
- find_options.split(',').map do |key|
48
- key.strip.to_sym
49
- end
50
- end
51
- end
52
- end
@@ -1,26 +0,0 @@
1
- module ActiveRecordImporter
2
- module Helpers
3
-
4
- def parse_datetime(datetime = nil)
5
- return if datetime.blank?
6
- Time.parse(datetime)
7
- end
8
-
9
- def force_utf8_encode(data = {})
10
- return data if data.blank?
11
-
12
- data.keys.each do |key|
13
- data[key] = data[key].force_encoding('UTF-8') if data[key].is_a?(String)
14
- end
15
-
16
- data
17
- end
18
-
19
- def time_attributes(data = {})
20
- attrs = {}
21
- attrs[:created_at] = parse_datetime(data[:created_at]) || Time.now
22
- attrs[:updated_at] = parse_datetime(data[:updated_at]) || attrs[:created_at]
23
- attrs
24
- end
25
- end
26
- end