csv-importer 0.1.2 → 0.1.3
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/.gitignore +1 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile +1 -1
- data/Guardfile +2 -1
- data/README.md +36 -21
- data/lib/csv_importer.rb +10 -6
- data/lib/csv_importer/config.rb +5 -0
- data/lib/csv_importer/dsl.rb +8 -4
- data/lib/csv_importer/row.rb +20 -10
- data/lib/csv_importer/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ef35d5c77f6bc1eba14f38b6d68737e393ebb69
|
4
|
+
data.tar.gz: c670a2d4fa3b6dda957170eaeb1e46931ba57521
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6efcbaf329d95b552f8a43201d816c2f0649ef20d511084e00f6a211f94eaa8f6042f9e04de7892ca3f2892d95f9a1dfa1e819558d56a9d7f0e3f188344eb774
|
7
|
+
data.tar.gz: d0d251d39386bef710e3773fed1a0d32a3ee3d3a11db5485682748a6ebf128ab045f2d6522d4143982a65473347f36ed67c3a79f0ca8934b500b21b5cf78cbc6
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
### v0.1.3
|
4
|
+
|
5
|
+
* You can now change the configuration at runtime. Example:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
UserImport.new(file: csv_file) do
|
9
|
+
after_build do
|
10
|
+
user.import_by_user = current_user
|
11
|
+
end
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
* Add `after_build` hooks to perform arbitrary operations on a model
|
16
|
+
before saving it.
|
17
|
+
|
18
|
+
* `identifier` does not have to be a required attribute anymore. That
|
19
|
+
enables you to use `id` as an identifier and import new entries
|
20
|
+
without having to provide an `id`
|
21
|
+
|
22
|
+
### v0.1.2
|
23
|
+
|
24
|
+
* `run!` was not *returning* a report object when the header was invalid.
|
25
|
+
|
26
|
+
### v0.1.1
|
27
|
+
|
28
|
+
* When calling `run!` on an import with invalid header we update the
|
29
|
+
report object instead of raising an exception.
|
30
|
+
|
31
|
+
### v0.1.0
|
32
|
+
|
33
|
+
* Initial Release
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -48,6 +48,7 @@ guard :rspec, cmd: "bundle exec rspec" do
|
|
48
48
|
ruby = dsl.ruby
|
49
49
|
dsl.watch_spec_files_for(ruby.lib_files)
|
50
50
|
|
51
|
-
|
51
|
+
# Specs are fast, let's run them all!
|
52
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec" }
|
52
53
|
|
53
54
|
end
|
data/README.md
CHANGED
@@ -123,7 +123,8 @@ class ImportUserCSV
|
|
123
123
|
end
|
124
124
|
```
|
125
125
|
|
126
|
-
You can change the configuration at runtime to
|
126
|
+
You can change the configuration at runtime to scope down to associated
|
127
|
+
records.
|
127
128
|
|
128
129
|
```ruby
|
129
130
|
class Team
|
@@ -132,9 +133,12 @@ end
|
|
132
133
|
|
133
134
|
team = Team.find(1)
|
134
135
|
|
135
|
-
ImportUserCSV.new(path: "tmp/my_file.csv"
|
136
|
+
ImportUserCSV.new(path: "tmp/my_file.csv") do
|
137
|
+
model team.users
|
138
|
+
end
|
136
139
|
```
|
137
140
|
|
141
|
+
|
138
142
|
### Define columns and their mapping
|
139
143
|
|
140
144
|
This is where the fun begins.
|
@@ -157,37 +161,19 @@ Now, email could also be spelled "e-mail", or "mail", or even "courriel"
|
|
157
161
|
|
158
162
|
|
159
163
|
```ruby
|
160
|
-
class ImportUserCSV
|
161
|
-
include CSVImporter
|
162
|
-
|
163
|
-
model User
|
164
|
-
|
165
164
|
column :email, as: [/e.?mail/i, "courriel"]
|
166
|
-
end
|
167
165
|
```
|
168
166
|
|
169
167
|
Nice, emails should be downcased though, so let's do this.
|
170
168
|
|
171
169
|
```ruby
|
172
|
-
class ImportUserCSV
|
173
|
-
include CSVImporter
|
174
|
-
|
175
|
-
model User
|
176
|
-
|
177
170
|
column :email, as: [/e.?mail/i, "courriel"], to: ->(email) { email.downcase if email }
|
178
|
-
end
|
179
171
|
```
|
180
172
|
|
181
173
|
If you need to do more advanced stuff, you've got access to the model:
|
182
174
|
|
183
175
|
```ruby
|
184
|
-
class ImportUserCSV
|
185
|
-
include CSVImporter
|
186
|
-
|
187
|
-
model User
|
188
|
-
|
189
176
|
column :email, as: [/e.?mail/i, "courriel"], to: ->(email, user) { user.email = email.downcase; model.super_user! if email[/@brewhouse.io\z/] }
|
190
|
-
end
|
191
177
|
```
|
192
178
|
|
193
179
|
Now, what if the user does not provide the email column? It's not worth
|
@@ -275,9 +261,38 @@ easy:
|
|
275
261
|
|
276
262
|
```ruby
|
277
263
|
team = Team.find(1)
|
278
|
-
import = ImportUserCSV.new(file: my_file
|
264
|
+
import = ImportUserCSV.new(file: my_file) do
|
265
|
+
model team.users
|
266
|
+
end
|
267
|
+
```
|
268
|
+
|
269
|
+
### Preset attributes
|
270
|
+
|
271
|
+
You can preset attributes (or perform any changes to the model) at
|
272
|
+
configuration or runtime using `after_build`
|
273
|
+
|
274
|
+
```ruby
|
275
|
+
|
276
|
+
class ImportUserCSV
|
277
|
+
model User
|
278
|
+
|
279
|
+
column :email
|
280
|
+
|
281
|
+
after_build do |user|
|
282
|
+
user.name = email.split('@').first if email
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# assuming `current_user` is available
|
287
|
+
|
288
|
+
import = ImportUserCSV.new(file: my_file) do
|
289
|
+
after_build do |user|
|
290
|
+
user.created_by_user = current_user
|
291
|
+
end
|
292
|
+
end
|
279
293
|
```
|
280
294
|
|
295
|
+
|
281
296
|
### Validate the header
|
282
297
|
|
283
298
|
On a web application, as soon as a CSV file is uploaded, you can check
|
data/lib/csv_importer.rb
CHANGED
@@ -32,8 +32,9 @@ module CSVImporter
|
|
32
32
|
|
33
33
|
def self.included(klass)
|
34
34
|
klass.extend(Dsl)
|
35
|
-
klass.
|
36
|
-
|
35
|
+
klass.include(Dsl)
|
36
|
+
klass.define_singleton_method(:config) do
|
37
|
+
@config ||= Config.new
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
@@ -45,11 +46,12 @@ module CSVImporter
|
|
45
46
|
# .new(file: my_csv_file)
|
46
47
|
# .new(path: "subscribers.csv", model: newsletter.subscribers)
|
47
48
|
#
|
48
|
-
def initialize(*args)
|
49
|
+
def initialize(*args, &block)
|
49
50
|
@csv = CSVReader.new(*args)
|
50
|
-
@config = self.class.
|
51
|
+
@config = self.class.config.dup
|
51
52
|
@config.attributes = args.last
|
52
53
|
@report = Report.new
|
54
|
+
instance_exec(&block) if block
|
53
55
|
end
|
54
56
|
|
55
57
|
attr_reader :csv, :report, :config
|
@@ -61,8 +63,10 @@ module CSVImporter
|
|
61
63
|
|
62
64
|
# Initialize and return the `Row`s for the current CSV file
|
63
65
|
def rows
|
64
|
-
csv.rows.map
|
65
|
-
|
66
|
+
csv.rows.map do |row_array|
|
67
|
+
Row.new(header: header, row_array: row_array, model_klass: config.model,
|
68
|
+
identifier: config.identifier, after_build_blocks: config.after_build_blocks)
|
69
|
+
end
|
66
70
|
end
|
67
71
|
|
68
72
|
def valid_header?
|
data/lib/csv_importer/config.rb
CHANGED
@@ -7,6 +7,11 @@ module CSVImporter
|
|
7
7
|
attribute :column_definitions, Array[ColumnDefinition], default: proc { [] }
|
8
8
|
attribute :identifier, Symbol
|
9
9
|
attribute :when_invalid, Symbol, default: proc { :skip }
|
10
|
+
attribute :after_build_blocks, Array[Proc], default: []
|
11
|
+
|
12
|
+
def after_build(block)
|
13
|
+
self.after_build_blocks << block
|
14
|
+
end
|
10
15
|
end
|
11
16
|
end
|
12
17
|
|
data/lib/csv_importer/dsl.rb
CHANGED
@@ -3,19 +3,23 @@ module CSVImporter
|
|
3
3
|
# It is a thin proxy to the Config object
|
4
4
|
module Dsl
|
5
5
|
def model(model_klass)
|
6
|
-
|
6
|
+
config.model = model_klass
|
7
7
|
end
|
8
8
|
|
9
9
|
def column(name, options={})
|
10
|
-
|
10
|
+
config.column_definitions << options.merge(name: name)
|
11
11
|
end
|
12
12
|
|
13
13
|
def identifier(identifier)
|
14
|
-
|
14
|
+
config.identifier = identifier
|
15
15
|
end
|
16
16
|
|
17
17
|
def when_invalid(action)
|
18
|
-
|
18
|
+
config.when_invalid = action
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_build(&block)
|
22
|
+
config.after_build(block)
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
data/lib/csv_importer/row.rb
CHANGED
@@ -9,17 +9,13 @@ module CSVImporter
|
|
9
9
|
attribute :header, Header
|
10
10
|
attribute :row_array, Array[String]
|
11
11
|
attribute :model_klass
|
12
|
-
attribute :identifier
|
12
|
+
attribute :identifier, Symbol
|
13
|
+
attribute :after_build_blocks, Array[Proc], default: []
|
13
14
|
|
14
15
|
# The model to be persisted
|
15
16
|
def model
|
16
17
|
@model ||= begin
|
17
|
-
model =
|
18
|
-
find_or_build_model
|
19
|
-
else
|
20
|
-
build_model
|
21
|
-
end
|
22
|
-
|
18
|
+
model = find_or_build_model
|
23
19
|
set_attributes(model)
|
24
20
|
end
|
25
21
|
end
|
@@ -33,13 +29,20 @@ module CSVImporter
|
|
33
29
|
def set_attributes(model)
|
34
30
|
header.columns.each do |column|
|
35
31
|
value = csv_attributes[column.name]
|
36
|
-
|
32
|
+
begin
|
33
|
+
value = value.dup if value
|
34
|
+
rescue TypeError
|
35
|
+
# can't dup Symbols, Integer etc...
|
36
|
+
end
|
37
37
|
|
38
|
+
column_definition = column.definition
|
38
39
|
next if column_definition.nil?
|
39
40
|
|
40
41
|
set_attribute(model, column_definition, value)
|
41
42
|
end
|
42
43
|
|
44
|
+
after_build_blocks.each { |block| block.call(model) }
|
45
|
+
|
43
46
|
model
|
44
47
|
end
|
45
48
|
|
@@ -78,10 +81,17 @@ module CSVImporter
|
|
78
81
|
end
|
79
82
|
|
80
83
|
def find_or_build_model
|
84
|
+
find_model || build_model
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_model
|
88
|
+
return nil if identifier.nil?
|
89
|
+
|
81
90
|
model = build_model
|
82
91
|
set_attributes(model)
|
83
|
-
value = model.public_send(identifier)
|
84
|
-
|
92
|
+
if value = model.public_send(identifier)
|
93
|
+
model_klass.public_send("find_by_#{identifier}", value)
|
94
|
+
end
|
85
95
|
end
|
86
96
|
|
87
97
|
def build_model
|
data/lib/csv_importer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv-importer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Philippe Creux
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: virtus
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- ".gitignore"
|
63
63
|
- ".rspec"
|
64
64
|
- ".travis.yml"
|
65
|
+
- CHANGELOG.md
|
65
66
|
- CODE_OF_CONDUCT.md
|
66
67
|
- Gemfile
|
67
68
|
- Guardfile
|