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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2e473bcdd6155c530c3b6c8006b51468aee8c36
4
- data.tar.gz: 4801594f03a9fa3bff2ff0513ba627992db5e473
3
+ metadata.gz: 7ef35d5c77f6bc1eba14f38b6d68737e393ebb69
4
+ data.tar.gz: c670a2d4fa3b6dda957170eaeb1e46931ba57521
5
5
  SHA512:
6
- metadata.gz: a497a2962a71fbdc9de86c616322542a39786e119a0ddb3e63aa7e6962212abff1f24efe9eb0865d20fb7ccc234d9c9b853ae471c90a9ea042cd4689da841d67
7
- data.tar.gz: 8de23f9e3c71ca2c05b85c9de92a97a84457374d9e6b50b8b4a2622dcd4490f82359a0fa41ac5e64ae492ed298b8feebe7ebe2b4a9ea6e145e0257cdcd7b838c
6
+ metadata.gz: 6efcbaf329d95b552f8a43201d816c2f0649ef20d511084e00f6a211f94eaa8f6042f9e04de7892ca3f2892d95f9a1dfa1e819558d56a9d7f0e3f188344eb774
7
+ data.tar.gz: d0d251d39386bef710e3773fed1a0d32a3ee3d3a11db5485682748a6ebf128ab045f2d6522d4143982a65473347f36ed67c3a79f0ca8934b500b21b5cf78cbc6
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  .DS_Store
11
+ /spec/examples.txt
@@ -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
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in csv-importer.gemspec
4
4
  gemspec
5
5
 
6
- gem 'rspec'
6
+ gem 'rspec', '~> 3.3.0'
7
7
  gem 'guard-rspec'
8
8
  gem 'activemodel'
9
9
  gem 'simplecov', require: nil
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
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
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 import associated records.
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", model: team.users)
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, model: team.users)
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
@@ -32,8 +32,9 @@ module CSVImporter
32
32
 
33
33
  def self.included(klass)
34
34
  klass.extend(Dsl)
35
- klass.define_singleton_method(:csv_importer_config) do
36
- @csv_importer_config ||= Config.new
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.csv_importer_config.dup
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 { |row_array| Row.new(header: header, row_array: row_array,
65
- model_klass: config.model, identifier: config.identifier) }
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?
@@ -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
 
@@ -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
- csv_importer_config.model = model_klass
6
+ config.model = model_klass
7
7
  end
8
8
 
9
9
  def column(name, options={})
10
- csv_importer_config.column_definitions << options.merge(name: name)
10
+ config.column_definitions << options.merge(name: name)
11
11
  end
12
12
 
13
13
  def identifier(identifier)
14
- csv_importer_config.identifier = identifier
14
+ config.identifier = identifier
15
15
  end
16
16
 
17
17
  def when_invalid(action)
18
- csv_importer_config.when_invalid = action
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
@@ -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 = if identifier
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
- column_definition = column.definition
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
- model_klass.public_send("find_by_#{identifier}", value) || build_model
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
@@ -1,3 +1,3 @@
1
1
  module CSVImporter
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
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.2
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-16 00:00:00.000000000 Z
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