csv_piper 0.1.7 → 0.1.8

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/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rubocop', require: false
4
+
3
5
  # Specify your gem's dependencies in csv_piper.gemspec
4
6
  gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Gem Version](https://badge.fury.io/rb/csv_piper.svg)](http://badge.fury.io/rb/csv_piper) [![Build Status](https://travis-ci.org/jazzarati/csv_piper.svg?branch=master)](https://travis-ci.org/jazzarati/csv_piper)
1
+ [![Gem Version](https://badge.fury.io/rb/csv_piper.svg)](http://badge.fury.io/rb/csv_piper) [![Build Status](https://travis-ci.org/jazzarati/csv_piper.svg?branch=master)](https://travis-ci.org/jazzarati/csv_piper) [![Code Climate](https://codeclimate.com/github/jazzarati/csv_piper/badges/gpa.svg)](https://codeclimate.com/github/jazzarati/csv_piper) [![Test Coverage](https://codeclimate.com/github/jazzarati/csv_piper/badges/coverage.svg)](https://codeclimate.com/github/jazzarati/csv_piper/coverage) [![Dependency Status](https://gemnasium.com/jazzarati/csv_piper.svg)](https://gemnasium.com/jazzarati/csv_piper)
2
2
 
3
3
  # CsvPiper
4
4
 
@@ -144,12 +144,24 @@ If you return `nil` instead of `[transformed, errors]` all further processing of
144
144
 
145
145
  _Return value_ is what will be passed into _source_ and _errors_ of the next pre-processor (and processors). Final pre-processor value of _source_ will be passed to each processor as a frozen hash. Final pre-processor value of _errors_ will be passed to the first processor.
146
146
 
147
- #### Row Errors
148
- This object is passed into each processor (which must pass it on) and is used to accumulate any and all errors for the particular row. You can access the row number through `row_index`.
147
+ ## Error Handling
148
+
149
+ #### Built-in
150
+
151
+ The `Errors::Row` object is passed into each processor as the last parameter to process (which must pass it on) and is used to accumulate any and all errors for the particular row being processed. This is useful to collect all errors for display to your users rather than just failing on first error (if this mode matches your use case). You can add the built-in `CollectErrors` processor as one of the final processors and this will allow you to grab all the errors ever occured once processing all rows have finished if desired.
149
152
 
150
153
  Add errors using `errors.add(error_key, error)`.
151
154
 
152
- #### Builder Options
155
+ You can access the row number being processed through `row_index` which can be useful for displaying or logging errors.
156
+
157
+ #### Do-it-yourself
158
+
159
+ You can ignore the `Errors::Row` passed in to each processor and just handle error cases anyway you feel like. You can pass in a logger object in the construction of each of your processors if you want to use it during processing rows to handle errors.
160
+
161
+ ## Builder
162
+
163
+ CsvPiper provides a builder class to allow nicer creation of the piper object.
164
+
153
165
  All builder options utilise the _fluent interface pattern_ and should be followed by a call to `build` to get the piper instance and then `process` to process the csv.
154
166
 
155
167
  Eg. `CsvPiper::Builder.new.from(io).with_processors(processors).build.process`
data/Rakefile CHANGED
@@ -1,6 +1,14 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
5
7
 
6
- task :default => :spec
8
+ task :default => [:spec, :quality]
9
+
10
+ task :quality => :rubocop
11
+
12
+ task :codeclimate do
13
+ exec('codeclimate analyze')
14
+ end
data/csv_piper.gemspec CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "bundler", "~> 1.10"
24
24
  spec.add_development_dependency "rake", "~> 10.0"
25
25
  spec.add_development_dependency "rspec", "~> 3"
26
+ spec.add_development_dependency "codeclimate-test-reporter", '~> 0.4'
27
+ spec.add_development_dependency "rubocop", '~> 0.34'
26
28
  spec.add_development_dependency 'sqlite3', '~> 1.3'
27
29
  spec.add_development_dependency 'activerecord', '~> 4.2'
28
30
  end
@@ -1,4 +1,7 @@
1
1
  module CsvPiper
2
+ ###
3
+ # Catch exceptions on a per processor/row basis (allow other rows to continue processing)?
4
+ ###
2
5
  class Piper
3
6
  HEADER_LINE_INDEX = 1
4
7
  FIRST_DATA_LINE_INDEX = 2
@@ -37,23 +40,23 @@ module CsvPiper
37
40
 
38
41
  def process_csv_body
39
42
  csv.each.with_index(FIRST_DATA_LINE_INDEX) do |row, index|
40
- processed_data, row_errors = process_row(row.to_hash, Errors::Row.new(index))
43
+ process_row(row.to_hash, Errors::Row.new(index))
41
44
  end
42
45
  end
43
46
 
44
47
  def process_row(row, row_errors)
45
48
  pre_processed_row, row_errors = pre_processors.reduce([row, row_errors]) do |memo, processor|
46
49
  output = processor.process(*memo)
47
- return if output.nil?
50
+ return nil if output.nil?
48
51
  output
49
52
  end
50
53
 
51
54
  frozen_row = pre_processed_row.freeze
52
55
 
53
56
  processed_data = {}
54
- processed_data, row_errors = processors.reduce([processed_data, row_errors]) do |memo, processor|
57
+ processors.reduce([processed_data, row_errors]) do |memo, processor|
55
58
  output = processor.process(frozen_row, *memo)
56
- return if output.nil?
59
+ return nil if output.nil?
57
60
  output
58
61
  end
59
62
  end
@@ -1,12 +1,16 @@
1
1
  module CsvPiper
2
2
  module Processors
3
+ # Collects errors for use after processing.
4
+ # Instantiate and keep a reference, then once processing complete retrieve errors through #errors
3
5
  class CollectErrors
6
+ # @return[Hash] Holds all of the errors for each row that was processed
7
+ # { row_index => { errors_key => array_of_errors } }
4
8
  attr_reader :errors
5
9
  def initialize
6
10
  @errors = {}
7
11
  end
8
12
 
9
- def process(source, transformed, row_errors)
13
+ def process(_source, transformed, row_errors)
10
14
  @errors[row_errors.row_index] = row_errors.errors unless row_errors.empty?
11
15
  [transformed, errors]
12
16
  end
@@ -1,13 +1,17 @@
1
1
  module CsvPiper
2
2
  module Processors
3
+ # Collects transformed objects for use after processing.
4
+ # Instantiate and keep a reference, then once processing complete retrieve transformed objects through #output
3
5
  class CollectOutput
6
+ # @return[Array] Holds all of the transformed objects for each row that was processed
7
+ # { row_index => { errors_key => array_of_error } }
4
8
  attr_reader :output
5
9
  def initialize(collect_when_invalid: true)
6
10
  @output = []
7
11
  @collect_when_invalid = collect_when_invalid
8
12
  end
9
13
 
10
- def process(source, transformed, errors)
14
+ def process(_source, transformed, errors)
11
15
  @output << transformed if @collect_when_invalid || errors.empty?
12
16
  [transformed, errors]
13
17
  end
@@ -1,6 +1,12 @@
1
1
  module CsvPiper
2
2
  module Processors
3
+ # Use to copy data from source row to transformed hash. Does not add any errors.
3
4
  class Copy
5
+ # @param mapping: [nil, Array, Hash{source_key => new_key}] (Defaults to +nil+)
6
+ # - When +nil+: All contents of the source hash will be copied across to the transformed hash
7
+ # - When an +Array+: Only the matching keys will be copied to the transformed hash
8
+ # - When a +Hash+: Only the matching keys will be copied to the transformed hash but they will be copied onto the transformed hash with a new key value (mapping = +{ source_key => new_key }+ )
9
+ #
4
10
  def initialize(mapping = nil)
5
11
  mapping = Hash[ mapping.map { |val| [val, val] } ] if mapping.is_a?(Array)
6
12
  @mapping = mapping
@@ -5,15 +5,16 @@ module CsvPiper
5
5
  @model_class = model_class
6
6
  end
7
7
 
8
- def process(source, transformed, errors)
8
+ def process(_source, transformed, errors)
9
9
  model = @model_class.new(transformed)
10
10
 
11
11
  model.save if model.valid? && errors.empty?
12
12
 
13
- errors.errors.merge!(model.errors.to_hash) do |key, old_val, new_val|
13
+ errors.errors.merge!(model.errors.to_hash) do |_key, old_val, new_val|
14
14
  old_val + new_val
15
15
  end
16
16
 
17
+ transformed["#{@model_class.name.underscore}_model".to_sym] = model
17
18
  [transformed, errors]
18
19
  end
19
20
  end
@@ -0,0 +1,31 @@
1
+ module CsvPiper
2
+ module Processors
3
+ # Used to convert the values in the transformed hash according a provided mapping hash. { key => { 'value' => 'new_value', 'value2' => 'new_value2' } }
4
+ class Translate
5
+
6
+ # @param mapping: [Hash] Mapping to use for translation: { key => { 'value' => 'new_value', 'value2' => 'new_value2' } }
7
+ # @param add_error_on_missing_translation: [Boolean] By default errors are not added when there is no matching
8
+ # value found in the mapping. When set to +true+ errors will be added to the key if no matching value found.
9
+ # @param pass_through_on_no_match: By default when there is no matching value the value will become +nil+.
10
+ # When set to +true+, the value will remain unchanged if there is no matching value to translate using.
11
+ def initialize(mapping: , add_error_on_missing_translation: false, pass_through_on_no_match: false)
12
+ @add_error = add_error_on_missing_translation
13
+ @pass_through = pass_through_on_no_match
14
+ @mapping = mapping
15
+ end
16
+
17
+ def process(_source, transformed, errors)
18
+ mappings_to_apply = @mapping.select { |key,_| transformed.has_key?(key) }
19
+
20
+ transformed = mappings_to_apply.each_with_object(transformed) do |(key, translation), memo|
21
+ new_value = translation[memo[key]]
22
+ errors.add(key, "No mapping for value #{memo[key]}") if @add_error && new_value.nil?
23
+ new_value = new_value || memo[key] if @pass_through
24
+ memo[key] = new_value
25
+ end
26
+
27
+ [transformed, errors]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module CsvPiper
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_piper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarrod Sibbison
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-09-19 00:00:00.000000000 Z
11
+ date: 2015-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codeclimate-test-reporter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.34'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.34'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: sqlite3
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -87,8 +115,10 @@ executables: []
87
115
  extensions: []
88
116
  extra_rdoc_files: []
89
117
  files:
118
+ - ".codeclimate.yml"
90
119
  - ".gitignore"
91
120
  - ".rspec"
121
+ - ".rubocop.yml"
92
122
  - ".travis.yml"
93
123
  - Gemfile
94
124
  - LICENSE.txt
@@ -106,6 +136,7 @@ files:
106
136
  - lib/csv_piper/processors/collect_output.rb
107
137
  - lib/csv_piper/processors/copy.rb
108
138
  - lib/csv_piper/processors/create_active_model.rb
139
+ - lib/csv_piper/processors/translate.rb
109
140
  - lib/csv_piper/test_support/csv_mock_file.rb
110
141
  - lib/csv_piper/version.rb
111
142
  homepage: https://github.com/jazzarati/csv_piper
@@ -133,3 +164,4 @@ signing_key:
133
164
  specification_version: 4
134
165
  summary: CSV processing pipeline
135
166
  test_files: []
167
+ has_rdoc: