active_importer 0.0.3 → 0.1.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.
- data/README.md +60 -1
- data/lib/active_importer/base.rb +37 -16
- data/lib/active_importer/version.rb +1 -1
- data/spec/active_importer/base_spec.rb +8 -1
- data/spec/stubs/employee.rb +5 -0
- metadata +3 -3
data/README.md
CHANGED
@@ -71,7 +71,66 @@ of the spreadsheet.
|
|
71
71
|
|
72
72
|
### Callbacks
|
73
73
|
|
74
|
-
|
74
|
+
An importer class can define blocks of code acting as callbacks, to be notified
|
75
|
+
of certain events that occur while importing the data.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class EmployeeImporter < ActiveImporter::Base
|
79
|
+
imports Employee
|
80
|
+
|
81
|
+
attr_reader :row_count
|
82
|
+
|
83
|
+
column 'First name', :first_name
|
84
|
+
column 'Last name', :last_name
|
85
|
+
column 'Department', :department do |department_name|
|
86
|
+
Department.find_by(name: department_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
on :import_started do
|
90
|
+
@row_count = 0
|
91
|
+
end
|
92
|
+
|
93
|
+
on :row_processed do
|
94
|
+
@row_count += 1
|
95
|
+
end
|
96
|
+
|
97
|
+
on :import_finished do
|
98
|
+
send_notification("Data imported successfully!")
|
99
|
+
end
|
100
|
+
|
101
|
+
on :import_failed do |exception|
|
102
|
+
send_notification("Fatal error while importing data: #{exception.message}")
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def send_notification(message)
|
108
|
+
# ...
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
The supported events are:
|
114
|
+
|
115
|
+
- **import_failed:** Fired once **before** the beginning of the data
|
116
|
+
processing, if the input data cannot be processed for some reason. If this
|
117
|
+
event is fired by an importer, none of its other events are ever fired.
|
118
|
+
- **import_started:** Fired once at the beginning of the data processing,
|
119
|
+
before the first row is processed.
|
120
|
+
- **row_processed:** Fired once for each row that has been processed,
|
121
|
+
regardless of whether it resulted in success or error.
|
122
|
+
- **row_success:** Fired once for each row that was imported successfully into
|
123
|
+
the data model.
|
124
|
+
- **row_error:** Fired once for each row that was **not** imported successfully
|
125
|
+
into the data model.
|
126
|
+
- **import_finished:** Fired once **after** all rows have been processed.
|
127
|
+
|
128
|
+
More than one block of code can be provided for each of these events, and they
|
129
|
+
will all be invoked in the same order in which they were declared. All blocks
|
130
|
+
are executed in the context of the importer instance, so they have access to
|
131
|
+
all the importer attributes and instance variables. Error-related events
|
132
|
+
(`:import_failed` and `:row_error`) pass to the blocks the instance of the
|
133
|
+
exception that provoked the error condition.
|
75
134
|
|
76
135
|
## Contributing
|
77
136
|
|
data/lib/active_importer/base.rb
CHANGED
@@ -38,6 +38,36 @@ module ActiveImporter
|
|
38
38
|
new(file, options).import
|
39
39
|
end
|
40
40
|
|
41
|
+
#
|
42
|
+
# Callbacks
|
43
|
+
#
|
44
|
+
|
45
|
+
EVENTS = [
|
46
|
+
:row_success,
|
47
|
+
:row_error,
|
48
|
+
:row_processed,
|
49
|
+
:import_started,
|
50
|
+
:import_finished,
|
51
|
+
:import_failed,
|
52
|
+
]
|
53
|
+
|
54
|
+
def self.event_handlers
|
55
|
+
@event_handlers ||= EVENTS.inject({}) { |hash, event| hash.merge({event => []}) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.on(event, &block)
|
59
|
+
raise "Unknown ActiveImporter event '#{event}'" unless EVENTS.include?(event)
|
60
|
+
event_handlers[event] << block
|
61
|
+
end
|
62
|
+
|
63
|
+
def fire_event(event, param = nil)
|
64
|
+
self.class.event_handlers[event].each do |block|
|
65
|
+
self.instance_exec(param, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private :fire_event
|
70
|
+
|
41
71
|
#
|
42
72
|
# Implementation
|
43
73
|
#
|
@@ -60,7 +90,7 @@ module ActiveImporter
|
|
60
90
|
@book = @header = nil
|
61
91
|
@row_count = 0
|
62
92
|
@row_index = 1
|
63
|
-
import_failed
|
93
|
+
fire_event :import_failed, e
|
64
94
|
end
|
65
95
|
|
66
96
|
def fetch_model
|
@@ -69,12 +99,13 @@ module ActiveImporter
|
|
69
99
|
|
70
100
|
def import
|
71
101
|
return if @book.nil?
|
102
|
+
fire_event :import_started
|
72
103
|
@data_row_indices.each do |index|
|
73
104
|
@row_index = index
|
74
105
|
@row = row_to_hash @book.row(index)
|
75
106
|
import_row
|
76
107
|
end
|
77
|
-
import_finished
|
108
|
+
fire_event :import_finished
|
78
109
|
end
|
79
110
|
|
80
111
|
def row_processed_count
|
@@ -92,18 +123,6 @@ module ActiveImporter
|
|
92
123
|
def hook
|
93
124
|
end
|
94
125
|
|
95
|
-
def row_success
|
96
|
-
end
|
97
|
-
|
98
|
-
def row_error(error_message)
|
99
|
-
end
|
100
|
-
|
101
|
-
def import_failed(error_message)
|
102
|
-
end
|
103
|
-
|
104
|
-
def import_finished
|
105
|
-
end
|
106
|
-
|
107
126
|
private
|
108
127
|
|
109
128
|
def columns
|
@@ -134,11 +153,13 @@ module ActiveImporter
|
|
134
153
|
model.save!
|
135
154
|
rescue => e
|
136
155
|
@row_errors << { row_index: row_index, error_message: e.message }
|
137
|
-
row_error
|
156
|
+
fire_event :row_error, e
|
138
157
|
return false
|
139
158
|
end
|
140
|
-
row_success
|
159
|
+
fire_event :row_success
|
141
160
|
true
|
161
|
+
ensure
|
162
|
+
fire_event :row_processed
|
142
163
|
end
|
143
164
|
|
144
165
|
def build_model
|
@@ -27,8 +27,9 @@ describe ActiveImporter::Base do
|
|
27
27
|
EmployeeImporter.import('/dummy/file')
|
28
28
|
end
|
29
29
|
|
30
|
-
it 'notifies when the import process
|
30
|
+
it 'notifies when the import process starts and finishes' do
|
31
31
|
expect(EmployeeImporter).to receive(:new).once.and_return(importer)
|
32
|
+
expect(importer).to receive(:import_started).once
|
32
33
|
expect(importer).to receive(:import_finished).once
|
33
34
|
EmployeeImporter.import('/dummy/file')
|
34
35
|
end
|
@@ -59,6 +60,12 @@ describe ActiveImporter::Base do
|
|
59
60
|
expect(EmployeeImporter).to receive(:new).once.and_return(importer)
|
60
61
|
expect { EmployeeImporter.import('/dummy/file') }.to change(importer.row_errors, :count).by(2)
|
61
62
|
end
|
63
|
+
|
64
|
+
it 'still notifies all rows as processed' do
|
65
|
+
expect(EmployeeImporter).to receive(:new).once.and_return(importer)
|
66
|
+
expect(importer).to receive(:row_processed).exactly(4).times
|
67
|
+
EmployeeImporter.import('/dummy/file')
|
68
|
+
end
|
62
69
|
end
|
63
70
|
|
64
71
|
context 'when the import fails' do
|
data/spec/stubs/employee.rb
CHANGED
@@ -22,4 +22,9 @@ class EmployeeImporter < ActiveImporter::Base
|
|
22
22
|
def find_department(name)
|
23
23
|
name.length # Quick dummy way to get an integer out of a string
|
24
24
|
end
|
25
|
+
|
26
|
+
ActiveImporter::Base::EVENTS.each do |event_name|
|
27
|
+
define_method(event_name) {}
|
28
|
+
on(event_name) { send event_name }
|
29
|
+
end
|
25
30
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_importer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -112,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
112
|
version: '0'
|
113
113
|
segments:
|
114
114
|
- 0
|
115
|
-
hash:
|
115
|
+
hash: -645039623466056614
|
116
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
117
|
none: false
|
118
118
|
requirements:
|
@@ -121,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
121
|
version: '0'
|
122
122
|
segments:
|
123
123
|
- 0
|
124
|
-
hash:
|
124
|
+
hash: -645039623466056614
|
125
125
|
requirements: []
|
126
126
|
rubyforge_project:
|
127
127
|
rubygems_version: 1.8.23
|