honey_format 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +19 -9
- data/exe/honey_format +58 -0
- data/honey_format.gemspec +2 -1
- data/lib/honey_format/header.rb +13 -0
- data/lib/honey_format/row.rb +31 -41
- data/lib/honey_format/row_builder.rb +50 -20
- data/lib/honey_format/rows.rb +3 -2
- data/lib/honey_format/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 220883bb92b35808697b428d346d1f171d2d06d47d4a9ed79404b824a7e65781
|
4
|
+
data.tar.gz: 9260762827e1fa50c1669696edfbc625a9e94ffc04773db81f27d6c8d9e7dd88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 245594826d582fdbbe02fb303a004197849f9ae17b184ecd4d57e833f98b220e28543bd3aaa1706221b9196b548d7adbd2b73f38ce96e24685f361645d86e307
|
7
|
+
data.tar.gz: 3c907de2398718ae946aeb3984991b9273b359e667004c331e86a359e513c38e2d09836c52fba230a2a98e47cdb3e93a5e964b7583c48e6d43ce739018806bd8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
# v0.11.0
|
2
|
+
|
3
|
+
* Add CLI: `honey_format` executable
|
4
|
+
* Swap `RowBuilder` <> `Row` class names [[#PR12](https://github.com/buren/honey_format/pull/12)]
|
5
|
+
|
1
6
|
# v0.10.0
|
2
7
|
|
3
8
|
* Add support for filtering what rows are included in `#to_csv` [[#PR11](https://github.com/buren/honey_format/pull/11)]
|
data/README.md
CHANGED
@@ -1,8 +1,19 @@
|
|
1
|
-
# HoneyFormat [![Build Status](https://travis-ci.org/buren/honey_format.svg)](https://travis-ci.org/buren/honey_format) [![Code Climate](https://codeclimate.com/github/buren/honey_format/badges/gpa.svg)](https://codeclimate.com/github/buren/honey_format) ![
|
1
|
+
# HoneyFormat [![Build Status](https://travis-ci.org/buren/honey_format.svg)](https://travis-ci.org/buren/honey_format) [![Code Climate](https://codeclimate.com/github/buren/honey_format/badges/gpa.svg)](https://codeclimate.com/github/buren/honey_format) [![Inline docs](http://inch-ci.org/github/buren/honey_format.svg)](http://inch-ci.org/github/buren/honey_format)
|
2
2
|
|
3
3
|
Convert CSV to an array of objects with with ease.
|
4
4
|
|
5
|
-
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Proper objects for CSV header and rows
|
8
|
+
- Convert column values with custom row builder
|
9
|
+
- Convert header column names
|
10
|
+
- Customize what columns and rows are included in CSV output
|
11
|
+
- Has no dependencies other than Ruby stdlib
|
12
|
+
- Supports Ruby >= 2.3
|
13
|
+
|
14
|
+
## Examples
|
15
|
+
|
16
|
+
See [examples/](https://github.com/buren/honey_format/tree/master/examples) for more examples.
|
6
17
|
|
7
18
|
```ruby
|
8
19
|
csv_string = "Id,Username\n1,buren"
|
@@ -13,8 +24,6 @@ user.id # => "1"
|
|
13
24
|
user.username # => "buren"
|
14
25
|
```
|
15
26
|
|
16
|
-
:information_source: Supports Ruby >= 2.3, has no dependencies other than Ruby stdlib.
|
17
|
-
|
18
27
|
## Installation
|
19
28
|
|
20
29
|
Add this line to your application's Gemfile:
|
@@ -164,10 +173,10 @@ Errors
|
|
164
173
|
begin
|
165
174
|
HoneyFormat::CSV.new(csv_string)
|
166
175
|
rescue HoneyFormat::HeaderError => e
|
167
|
-
puts 'there
|
176
|
+
puts 'there was a problem with the header'
|
168
177
|
raise(e)
|
169
178
|
rescue HoneyFormat::RowError => e
|
170
|
-
puts 'there
|
179
|
+
puts 'there was a problem with a row'
|
171
180
|
raise(e)
|
172
181
|
end
|
173
182
|
```
|
@@ -193,13 +202,14 @@ $ bin/benchmark file.csv
|
|
193
202
|
HoneyFormat::CSV: 49.6 i/s - 1.05x slower
|
194
203
|
```
|
195
204
|
|
196
|
-
|
205
|
+
2MB (10k lines)
|
197
206
|
|
198
207
|
```
|
199
|
-
stdlib CSV:
|
200
|
-
HoneyFormat::CSV:
|
208
|
+
stdlib CSV: 4.6 i/s
|
209
|
+
HoneyFormat::CSV: 4.2 i/s - 1.08x slower
|
201
210
|
```
|
202
211
|
|
212
|
+
|
203
213
|
## Development
|
204
214
|
|
205
215
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/exe/honey_format
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# for dev purposes
|
4
|
+
require 'bundler/setup' if ENV['HONEY_FORMAT_GEM_DEV']
|
5
|
+
require 'honey_format'
|
6
|
+
|
7
|
+
input_path = ARGV.first
|
8
|
+
|
9
|
+
columns = nil
|
10
|
+
output_path = nil
|
11
|
+
delimiter = ','
|
12
|
+
|
13
|
+
require 'optparse'
|
14
|
+
|
15
|
+
OptionParser.new do |parser|
|
16
|
+
parser.banner = "Usage: honey_format [file.csv] [options]"
|
17
|
+
parser.default_argv = ARGV
|
18
|
+
|
19
|
+
parser.on("--csv=input.csv", String, "CSV file") do |value|
|
20
|
+
input_path = value
|
21
|
+
end
|
22
|
+
|
23
|
+
parser.on("--columns=id,name", Array, "Select columns.") do |value|
|
24
|
+
columns = value
|
25
|
+
end
|
26
|
+
|
27
|
+
parser.on("--output=output.csv", String, "CSV output (STDOUT otherwise)") do |value|
|
28
|
+
output_path = value
|
29
|
+
end
|
30
|
+
|
31
|
+
parser.on("--delimiter=,", String, "CSV delimiter (default: ,)") do |value|
|
32
|
+
delimiter = value
|
33
|
+
end
|
34
|
+
|
35
|
+
parser.on("-h", "--help", "How to use") do
|
36
|
+
puts parser
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
parser.on_tail('--version', 'Show version') do
|
41
|
+
puts "HoneyFormat version #{HoneyFormat::VERSION}"
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
# No argument, shows at tail. This will print an options summary.
|
46
|
+
parser.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts parser
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
end.parse!
|
51
|
+
|
52
|
+
csv = HoneyFormat::CSV.new(File.read(input_path), delimiter: delimiter)
|
53
|
+
csv_string = csv.to_csv(columns: columns.map(&:to_sym))
|
54
|
+
if output_path
|
55
|
+
File.write(output_path, csv_string)
|
56
|
+
else
|
57
|
+
puts csv_string
|
58
|
+
end
|
data/honey_format.gemspec
CHANGED
@@ -14,7 +14,8 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = 'https://github.com/buren/honey_format'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
|
18
|
+
spec.bindir = 'exe'
|
18
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
20
|
spec.require_paths = ['lib']
|
20
21
|
|
data/lib/honey_format/header.rb
CHANGED
@@ -87,17 +87,27 @@ module HoneyFormat
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
# Convert the column value
|
91
|
+
# @param [Object] column the CSV header column value
|
92
|
+
# @param [Integer] index the CSV header column index
|
93
|
+
# @return [Object] the converted object
|
90
94
|
def convert_column(column, index)
|
91
95
|
return @converter.call(column) if converter_arity == 1
|
92
96
|
@converter.call(column, index)
|
93
97
|
end
|
94
98
|
|
99
|
+
# Returns the converter#call method arity
|
100
|
+
# @return [Integer] the converter#call method arity
|
95
101
|
def converter_arity
|
96
102
|
# procs and lambdas respond to #arity
|
97
103
|
return @converter.arity if @converter.respond_to?(:arity)
|
98
104
|
@converter.method(:call).arity
|
99
105
|
end
|
100
106
|
|
107
|
+
# Raises an error if header column is unknown
|
108
|
+
# @param [Object] column the CSV header column
|
109
|
+
# @param [Array<Symbol, String>] valid CSV columns
|
110
|
+
# @raise [Errors::UnknownHeaderColumnError]
|
101
111
|
def maybe_raise_unknown_column!(column, valid)
|
102
112
|
return if valid.empty?
|
103
113
|
return if valid.include?(column)
|
@@ -106,6 +116,9 @@ module HoneyFormat
|
|
106
116
|
raise(Errors::UnknownHeaderColumnError, err_msg)
|
107
117
|
end
|
108
118
|
|
119
|
+
# Raises an error if header column is missing/empty
|
120
|
+
# @param [Object] column the CSV header column
|
121
|
+
# @raise [Errors::MissingHeaderColumnError]
|
109
122
|
def maybe_raise_missing_column!(column)
|
110
123
|
return if column && !column.empty?
|
111
124
|
|
data/lib/honey_format/row.rb
CHANGED
@@ -1,49 +1,39 @@
|
|
1
|
-
require 'honey_format/row_builder'
|
2
|
-
|
3
1
|
module HoneyFormat
|
4
|
-
#
|
5
|
-
class Row
|
6
|
-
#
|
7
|
-
# @return [
|
8
|
-
# @
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
2
|
+
# Default row builder
|
3
|
+
class Row < Struct
|
4
|
+
# Create a row
|
5
|
+
# @return [Struct] returns an instantiated Struct representing a row
|
6
|
+
# @example
|
7
|
+
# row_klass = Row.new(:id, :username)
|
8
|
+
# row = row_klass.call('1', 'buren')
|
9
|
+
# # => #<struct id="1", username="buren">
|
10
|
+
def self.call(row)
|
11
|
+
new(*row)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Represent row as CSV
|
15
|
+
# @param columns [Array<Symbol>, Set<Symbol>, NilClass] the columns to output, nil means all columns (default: nil)
|
16
|
+
# @return [String] CSV-string representation.
|
17
|
+
def to_csv(columns: nil)
|
18
|
+
attributes = members
|
19
|
+
attributes = columns & attributes if columns
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
row = attributes.map { |column| to_csv_value(column) }
|
22
|
+
|
23
|
+
::CSV.generate_line(row)
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
# @
|
30
|
-
# @
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
return built_row unless @builder
|
36
|
-
@builder.call(built_row)
|
37
|
-
rescue ArgumentError => e
|
38
|
-
raise unless e.message == 'struct size differs'
|
26
|
+
private
|
27
|
+
|
28
|
+
# Returns the column in CSV format
|
29
|
+
# @param [Symbol] column name
|
30
|
+
# @return [String] column value as CSV string
|
31
|
+
def to_csv_value(column)
|
32
|
+
value = public_send(column)
|
33
|
+
return if value.nil?
|
34
|
+
return value.to_csv if value.respond_to?(:to_csv)
|
39
35
|
|
40
|
-
|
41
|
-
"Row length #{row.length}",
|
42
|
-
"column length #{@columns.length}",
|
43
|
-
"row: #{row.inspect}",
|
44
|
-
"orignal message: '#{e.message}'"
|
45
|
-
].join(', ')
|
46
|
-
raise(Errors::InvalidRowLengthError, err_msg)
|
36
|
+
value.to_s
|
47
37
|
end
|
48
38
|
end
|
49
39
|
end
|
@@ -1,28 +1,58 @@
|
|
1
|
+
require 'honey_format/row'
|
2
|
+
|
1
3
|
module HoneyFormat
|
2
|
-
#
|
3
|
-
class RowBuilder
|
4
|
-
#
|
5
|
-
# @return [
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# Holds data for a single row.
|
5
|
+
class RowBuilder
|
6
|
+
# Returns a new instance of RowBuilder.
|
7
|
+
# @return [RowBuilder] a new instance of RowBuilder.
|
8
|
+
# @param [Array<Symbol>] columns an array of symbols.
|
9
|
+
# @param builder [#call, #to_csv] optional row builder
|
10
|
+
# @raise [RowError] super class of errors raised when there is a row error.
|
11
|
+
# @raise [EmptyRowColumnsError] raised when there are no columns.
|
12
|
+
# @raise [InvalidRowLengthError] raised when row has more columns than header columns.
|
13
|
+
# @example Create new row
|
14
|
+
# RowBuilder.new!([:id])
|
15
|
+
def initialize(columns, builder: nil)
|
16
|
+
if columns.empty?
|
17
|
+
err_msg = 'Expected array with at least one element, but was empty.'
|
18
|
+
raise(Errors::EmptyRowColumnsError, err_msg)
|
19
|
+
end
|
9
20
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
attributes = members
|
15
|
-
attributes = columns & attributes if columns
|
21
|
+
@row_klass = Row.new(*columns)
|
22
|
+
@builder = builder
|
23
|
+
@columns = columns
|
24
|
+
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
# Returns a Struct.
|
27
|
+
# @return [Row, Object] a new instance of built row.
|
28
|
+
# @param row [Array] the row array.
|
29
|
+
# @raise [InvalidRowLengthError] raised when there are more row elements longer than columns
|
30
|
+
# @example Build new row
|
31
|
+
# r = RowBuilder.new([:id])
|
32
|
+
# r.build(['1']).id #=> '1'
|
33
|
+
def build(row)
|
34
|
+
row = @row_klass.call(row)
|
35
|
+
return row unless @builder
|
36
|
+
@builder.call(row)
|
37
|
+
rescue ArgumentError => e
|
38
|
+
raise unless e.message == 'struct size differs'
|
39
|
+
raise_invalid_row_length!(e, row)
|
40
|
+
end
|
21
41
|
|
22
|
-
|
23
|
-
end
|
42
|
+
private
|
24
43
|
|
25
|
-
|
44
|
+
# Raises invalid row length error
|
45
|
+
# @param [StandardError] e the raised error
|
46
|
+
# @param [Object] row
|
47
|
+
# @raise [Errors::InvalidRowLengthError]
|
48
|
+
def raise_invalid_row_length!(e, row)
|
49
|
+
err_msg = [
|
50
|
+
"Row length #{row.length}",
|
51
|
+
"column length #{@columns.length}",
|
52
|
+
"row: #{row.inspect}",
|
53
|
+
"orignal message: '#{e.message}'"
|
54
|
+
].join(', ')
|
55
|
+
raise(Errors::InvalidRowLengthError, err_msg)
|
26
56
|
end
|
27
57
|
end
|
28
58
|
end
|
data/lib/honey_format/rows.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
-
require 'honey_format/
|
2
|
+
require 'honey_format/row_builder'
|
3
3
|
|
4
4
|
module HoneyFormat
|
5
5
|
# Represents rows.
|
@@ -14,7 +14,8 @@ module HoneyFormat
|
|
14
14
|
# @raise [EmptyRowColumnsError] raised when there are no columns.
|
15
15
|
# @raise [InvalidRowLengthError] raised when row has more columns than header columns.
|
16
16
|
def initialize(rows, columns, builder: nil)
|
17
|
-
|
17
|
+
builder = RowBuilder.new(columns, builder: builder)
|
18
|
+
@rows = prepare_rows(builder, rows)
|
18
19
|
end
|
19
20
|
|
20
21
|
# @yield [row] The given block will be passed for every row.
|
data/lib/honey_format/version.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honey_format
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Burenstam
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2018-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
@@ -99,7 +99,8 @@ description: Convert CSV to an array of objects with with ease. Create objects f
|
|
99
99
|
stdlib.
|
100
100
|
email:
|
101
101
|
- burenstam@gmail.com
|
102
|
-
executables:
|
102
|
+
executables:
|
103
|
+
- honey_format
|
103
104
|
extensions: []
|
104
105
|
extra_rdoc_files: []
|
105
106
|
files:
|
@@ -115,6 +116,7 @@ files:
|
|
115
116
|
- bin/benchmark
|
116
117
|
- bin/console
|
117
118
|
- bin/setup
|
119
|
+
- exe/honey_format
|
118
120
|
- honey_format.gemspec
|
119
121
|
- lib/honey_format.rb
|
120
122
|
- lib/honey_format/convert_header_value.rb
|