csv-importer 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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