pile 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19ad39412c2023f5a744a424e9b940facf3002d1
4
+ data.tar.gz: 5fc589e24e53245e1aeab8a6674a8264cd0496f6
5
+ SHA512:
6
+ metadata.gz: f07e56bc2906081397cc58329f34b78af792eab83c48c935c6537ac04eba53d4361dd0f00a65d73bf3581d441c2f8b56428cdee1945af04786668f831b49762f
7
+ data.tar.gz: 538ed3323cf0b90be97c0cddd29b0ae5fea8a50f96f35cd47707d58fd360db0dd8473e8a69e42a369a175fdb297fea4f8e26787d4b29854e0a935f9a3f43213b
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rspec/core/rake_task'
4
+ require 'yard'
5
+ require 'yard/rake/yardoc_task'
6
+
7
+ desc "Run rspec with formatting"
8
+ RSpec::Core::RakeTask.new(:test) do |t|
9
+ t.rspec_opts = ['--colour', '--format documentation']
10
+ t.pattern = '*_spec.rb'
11
+ end
12
+
13
+ desc "Generate YARD documentation"
14
+ YARD::Rake::YardocTask.new :doc do
15
+ end
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+
3
+ Dir["#{File.dirname(__FILE__)}/pile/*.rb"].each {|r| require_relative r }
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+
3
+ require 'csv'
4
+
5
+ require_relative 'record'
6
+
7
+ module Pile
8
+ # Header that contains the names of each column, used to refer to values of
9
+ # records by name.
10
+ #
11
+ # For example, given a CSV file containing the following header followed by
12
+ # multiple lines each containing a record,
13
+ #
14
+ # "ID,Name,Address Line"
15
+ #
16
+ # The parsed array from this row can be passed to +initialize+ after the
17
+ # +aliases+ hash, which can look like this, assuming +case_sensitive+ is
18
+ # false:
19
+ #
20
+ # \{'id' => ['identity', '#'], 'address line' => ['address']\}
21
+ class Header
22
+ # Construct a 'Header' from a CSV-formatted line.
23
+ def self.from_csv_row row, aliases = {}
24
+ self.new aliases, *row.parse_csv(converters: [:integer])
25
+ end
26
+
27
+ # @return [Hash<String, Array<String>>] aliases A hash of aliases from the column
28
+ # name to an array of names that contains aliases, each of which can be
29
+ # used to identify the same column. Without case sensitivity, each key
30
+ # in this hash is downcased.
31
+ attr_writer :aliases
32
+ def aliases= aliases
33
+ @aliases_downcased = nil
34
+
35
+ # Ensure each value is an array; create a singleton array for each one
36
+ # that isn't.
37
+ @aliases = {}
38
+ aliases.each_pair {|k, v| @aliases[k] = v.kind_of?(Array) ? v : [v]}
39
+ @aliases = aliases
40
+ end
41
+ def aliases
42
+ if case_sensitive
43
+ @aliases
44
+ elsif @aliases_downcased
45
+ @aliases_downcased
46
+ else
47
+ downcased = {}
48
+ @aliases.each_pair {|k, v| downcased[k.downcase] = v}
49
+ @aliases_downcased = downcased
50
+ end
51
+ end
52
+ # @param [Array<String>] indices The name of each value. Conventionally the
53
+ # first row in a CSV file.
54
+ attr_accessor :indices
55
+
56
+ # @return [Boolean] Whether indices are case sensitive; defaults to +false+.
57
+ def case_sensitive
58
+ @case_sensitive.nil? ? @case_sensitive = false : @case_sensitive
59
+ end
60
+ attr_writer :case_sensitive
61
+
62
+ # @return [CSV] (nil) Optional CSV object associated with this header; used
63
+ # for utility functions such as +write_header+.
64
+ attr_accessor :csv
65
+
66
+ #
67
+ # @param [Hash<String, Array<String>>] aliases A hash of aliases from the column
68
+ # name to an array of names that contains aliases, each of which can be
69
+ # used to identify the same column.
70
+ # @param [Array<String>] indices The name of each value.
71
+ def initialize(aliases, *indices)
72
+ @aliases = aliases
73
+ @indices = indices
74
+ end
75
+
76
+ # Return the integer position that +i+ refers to. This takes into
77
+ # account the name of each column and the alias hash.
78
+ def column_index i
79
+ if case_sensitive
80
+ position = indices.find_index {|column| column == i || (@aliases.has_key?(column) && @aliases[column].member?(i))}
81
+ else
82
+ position = indices.find_index {|column| column.downcase == i.to_s.downcase || (aliases.has_key?(column.downcase) && aliases[column.downcase].any? {|the_alias| the_alias.downcase == i.to_s.downcase})}
83
+ end
84
+
85
+ position ||= (i.kind_of?(Fixnum) ? i : nil)
86
+ end
87
+
88
+ # Write this header to the header's CSV object, if present.
89
+ #
90
+ # @param [CSV] csv (nil) If present, the header will be written to the
91
+ # passed CSV object rather than the header's.
92
+ def write_header csv = nil
93
+ csv << indices
94
+ end
95
+
96
+ def ==(other)
97
+ self.aliases == other.aliases && self.indices == other.indices && self.csv == other.csv
98
+ end
99
+
100
+ def eql?(other)
101
+ self.aliases.eql?(other.aliases) && self.indices.eql?(other.indices) && self.csv.eql?(other.csv)
102
+ end
103
+
104
+ # Enumerate the record after converting to an array with +to_a+.
105
+ def each
106
+ to_a.each
107
+ end
108
+
109
+ # Enumerate each column header.
110
+ def to_a
111
+ indices
112
+ end
113
+
114
+ include Enumerable
115
+ end
116
+ end
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+
3
+ require 'csv'
4
+ require 'matrix'
5
+
6
+ require_relative 'header'
7
+ require_relative 'record'
8
+
9
+ module Pile
10
+ # A database of +Record+s, as an array of records coupled with their header.
11
+ class List
12
+ # Construct a +List+ from the contents of a CSV-formatted file.
13
+ #
14
+ # @param [String, Array<String>] contents The contents of a file, or an
15
+ # array of the lines of the file.
16
+ # @param [Hash<String, Array<String>>] aliases ({})
17
+ def self.from_string contents, aliases = {}
18
+ lines = contents.kind_of?(Array) ? contents : contents.lines
19
+ header = Header.from_csv_row lines[0], aliases
20
+ self.new header, lines[1..-1].map{|l| Record.from_csv_row l, header}
21
+ end
22
+
23
+ # Construct a +List+ from a matrix. Inverse of +render_rows+.
24
+ #
25
+ # @param [Array<Array<String>>, Matrix<String>] matrix
26
+ def self.from_matrix matrix, aliases = {}
27
+ matrix = matrix.to_a
28
+ header = Header.new aliases, *(matrix[0] || [])
29
+ if matrix.length <= 1
30
+ # Header only.
31
+ self.new header, []
32
+ else
33
+ self.new header, matrix[1..-1].map{|row| Record.new header, *row}
34
+ end
35
+ end
36
+
37
+ # Read in filepath_from and write to filepath_to, which must refer to a
38
+ # different file, yielding to the block given with the header as the first
39
+ # argument and the record as the second; the block should return a new
40
+ # +Record+.
41
+ #
42
+ # The +Header+ must be the same for each +Record+, but it can be changed if
43
+ # the same one is returned for each record.
44
+ #
45
+ # No value is returned from this method.
46
+ def self.map_csv_file filepath_from, filepath_to, aliases
47
+ return to_enum :map_csv_file, filepath_from, filepath_to, aliases unless block_given?
48
+
49
+ # write
50
+ CSV.open(filepath_to, 'wb') do |csv|
51
+ header = nil
52
+ empty = true
53
+
54
+ # read
55
+ CSV.foreach(filepath_from, converters: [:integer]) do |row|
56
+ if !header
57
+ # Header.
58
+ header = Header.new aliases, *row
59
+ else
60
+ # Record.
61
+ record = Record.new header, *row
62
+ record = yield header, record
63
+
64
+ if empty
65
+ empty = false
66
+ record.write_header csv
67
+ end
68
+
69
+ record.write_record csv
70
+ end
71
+ end
72
+
73
+ # We wait until we check for the first record before writing the header
74
+ # in case it changed. Write the original one if there are no records.
75
+ if empty
76
+ empty = false
77
+ record.write_header csv
78
+ end
79
+ end
80
+ end
81
+
82
+ # Like +map_csv_file+, but operates on strings instead of files.
83
+ def self.map_csv_contents contents, aliases
84
+ return to_enum :map_csv_contents, contents, aliases unless block_given?
85
+
86
+ # write
87
+ s = CSV.generate do |csv|
88
+ header = nil
89
+ empty = true
90
+
91
+ # read
92
+ CSV.parse contents, converters: [:integer] do |row|
93
+ if !header
94
+ # Header.
95
+ header = Header.new aliases, *row
96
+ else
97
+ # Record.
98
+ record = Record.new header, *row
99
+ record = yield header, record
100
+
101
+ if empty
102
+ empty = false
103
+ record.write_header csv
104
+ end
105
+
106
+ record.write_record csv
107
+ end
108
+ end
109
+
110
+ # We wait until we check for the first record before writing the header
111
+ # in case it changed. Write the original one if there are no records.
112
+ if empty
113
+ empty = false
114
+ record.write_header csv
115
+ end
116
+ end
117
+ end
118
+
119
+ attr_accessor :header
120
+ attr_accessor :records
121
+
122
+ # @param [Pile::Header] header
123
+ # @param [Array<Pile::Record>] records
124
+ def initialize(header, records)
125
+ @header = header
126
+ @records = records
127
+ end
128
+
129
+ # Map each record.
130
+ #
131
+ # The +Header+ must be the same for each +Record+, but, unlike +map+, it
132
+ # can be changed if the same one is returned for each record.
133
+ def map_records &block
134
+ records.map &block
135
+ header = records[0].header unless records.empty?
136
+ end
137
+
138
+ # Generate a CVS-formatted string encoding this list that can be written to
139
+ # a file.
140
+ #
141
+ # @return [String]
142
+ def csv_string
143
+ CSV.generate do |csv|
144
+ header.write_header csv
145
+ records.each {|r| r.write_record csv}
146
+ end
147
+ end
148
+
149
+ # Return an unwrapped matrix containing the header and each record.
150
+ #
151
+ # @return [Array<Array<String>>]
152
+ def render_rows
153
+ [header.indices, *(records.map &:values)]
154
+ end
155
+
156
+ # Returns a matrix containing the header and each record.
157
+ #
158
+ # @return [Matrix<String>]
159
+ def render_matrix
160
+ Matrix.rows render_rows
161
+ end
162
+
163
+ def ==(other)
164
+ self.header == other.header && self.records == other.records
165
+ end
166
+
167
+ def eql?(other)
168
+ self.header.eql?(other.aliases) && self.records.eql?(other.records)
169
+ end
170
+
171
+ # Enumerate the list records after converting to an array with +to_a+.
172
+ def each
173
+ to_a.each
174
+ end
175
+
176
+ # Enumerate each record. Note that the header is not returned.
177
+ def to_a
178
+ records
179
+ end
180
+
181
+ # Send everything that the +header+ object recognized to it. Can be used
182
+ # for +column_index+, etc.
183
+ def method_missing method, *args, &block
184
+ header.send method, *args, &block
185
+ end
186
+
187
+ include Enumerable
188
+
189
+ # When used as an array, operates on the records.
190
+ def [](i)
191
+ records[i]
192
+ end
193
+
194
+ # When used as an array, operates on the records.
195
+ def []=(i, v)
196
+ records[i] = v
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ require 'csv'
4
+
5
+ module Pile
6
+ # Individual record in list of contributors, as an array of values coupled
7
+ # with its header.
8
+ #
9
+ # @param [Pile::Header] header The header defining the structure of
10
+ # each record; used to determine the type of each entry in the record
11
+ # by its position.
12
+ class Record
13
+ # Construct a 'Header' from a CSV-formatted line.
14
+ def self.from_csv_row row, header
15
+ self.new header, *row.parse_csv(converters: [:integer])
16
+ end
17
+
18
+ # @return [Pile::Header] The header associated with this record.
19
+ attr_accessor :header
20
+ # @return [Array<Object>] The values associated with this record. See
21
+ # below for helper methods that operate on a record's values.
22
+ attr_accessor :values
23
+ # @return [CSV] An optional CSV object, which some helper methods use; e.g.
24
+ # see +add_record_to_csv+
25
+ attr_accessor :csv
26
+
27
+ # @param [Pile::Header] header The header associated with this record,
28
+ # defining the structure of the record, and by what names (e.g. 'id' and
29
+ # 'name') values can be indexed.
30
+ # @param [Array<Object>] values The values in the row of the record.
31
+ def initialize header, *values
32
+ @header = header
33
+ @values = values
34
+ end
35
+
36
+ # Send everything that the +header+ object recognized to it. Can be used
37
+ # for +column_index+, etc.
38
+ def method_missing method, *args, &block
39
+ header.send method, *args, &block
40
+ end
41
+
42
+ # Retrieve a value in the record by its position, or by the column name.
43
+ # Aliases are recognized.
44
+ def [](i)
45
+ values[column_index i]
46
+ end
47
+
48
+ # Set a value in the record by its position, or by the column name.
49
+ # Aliases are recognized.
50
+ def []=(i, v)
51
+ values[column_index i] = v
52
+ end
53
+
54
+ # Write this record to its CSV object, if present.
55
+ #
56
+ # @param [CSV] csv (nil) If present, the values will be written to the
57
+ # passed CSV object rather than the header's.
58
+ def write_record csv = nil
59
+ csv ||= self.csv
60
+ raise 'Record#add_record_to_csv: no associated CSV object.' unless csv
61
+
62
+ csv << values
63
+ end
64
+
65
+ def ==(other)
66
+ self.header == other.header && self.values == other.values && self.csv == other.csv
67
+ end
68
+
69
+ def eql?(other)
70
+ self.header.eql?(other.header) && self.values.eql?(other.values) && self.csv.eql?(other.csv)
71
+ end
72
+
73
+ # Enumerate the record after converting to an array with +to_a+.
74
+ def each
75
+ to_a.each
76
+ end
77
+
78
+ # Enumerate each value.
79
+ def to_a
80
+ values
81
+ end
82
+
83
+ include Enumerable
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module Pile
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ require 'csv'
4
+
5
+ require_relative '../lib/pile/header.rb'
6
+ include Pile
7
+
8
+ require_relative 'spec_helper'
9
+
10
+ describe Header, 'column_index' do
11
+ include Pile::Helpers
12
+
13
+ it 'should return the same integer indices' do
14
+ header = new_example_header
15
+
16
+ header.column_index(2).should == 2
17
+ header.column_index(0).should == 0
18
+ end
19
+
20
+ it 'should recognized column names' do
21
+ header = new_example_header
22
+
23
+ header.column_index('name').should == 1
24
+ end
25
+
26
+ it 'should recognize aliases' do
27
+ header = new_example_header
28
+
29
+ header.column_index('id').should == 0
30
+ header.column_index('identity').should == 0
31
+
32
+ header.column_index('address line').should == 2
33
+ header.column_index('address').should == 2
34
+ end
35
+
36
+ it 'should respect case sensitivity' do
37
+ header = new_example_header
38
+
39
+ header.column_index('address line').should == 2
40
+ header.case_sensitive = true
41
+ header.column_index('address line').should == nil
42
+ header.column_index('Address Line').should == 2
43
+ header.case_sensitive = false
44
+ header.column_index('address line').should == 2
45
+ end
46
+ end
47
+
48
+ describe Header, 'write_header' do
49
+ include Pile::Helpers
50
+
51
+ it 'writes the example header that matches our string' do
52
+ header = new_example_header
53
+
54
+ read_write_tempfile 'csv-spec' do |file, step|
55
+ case step
56
+ when :write
57
+ file.write (CSV.generate {|csv| header.write_header csv})
58
+ when :read
59
+ file.read.should == "ID,Name,Address Line\n"
60
+ end
61
+ end
62
+ end
63
+
64
+ it 'is the right-inverse of from_csv_row' do
65
+ header = new_example_header
66
+
67
+ read_write_tempfile 'csv-spec' do |file, step|
68
+ case step
69
+ when :write
70
+ file.write (CSV.generate {|csv| header.write_header csv})
71
+ when :read
72
+ header2 = Header.from_csv_row file.read, header.aliases
73
+ header2.should == header
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe Header, '==' do
80
+ include Pile::Helpers
81
+
82
+ it 'should not consider headers with different indices the same' do
83
+ header = new_example_header
84
+ header2 = new_example_header
85
+ header2.indices[3] = 'Country'
86
+
87
+ header2.should_not == header
88
+ end
89
+
90
+ it 'should not consider headers with different indices the same' do
91
+ header = new_example_header
92
+ header2 = new_example_header
93
+ header2.aliases['name'] = ['handle', 'nick']
94
+
95
+ header2.should_not == header
96
+ end
97
+
98
+ it 'should consider headers with the same indices and aliases as equal' do
99
+ header = new_example_header
100
+ header2 = new_example_header
101
+
102
+ header2.should == header
103
+ end
104
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative '../lib/pile/list.rb'
4
+ include Pile
5
+
6
+ require_relative 'spec_helper'
7
+
8
+ describe List, '::map_csv_contents' do
9
+ include Pile::Helpers
10
+
11
+ it 'responds to mappings as we expect' do
12
+ file_contents = "ID,Name,Address Line\n1,Alice,123 1st St\n2,Bob,234 2nd St\n3,Charles,345 3rd St\n"
13
+ aliases = {'Address Line' => ['address']}
14
+
15
+ updated_contents = List.map_csv_contents file_contents, aliases do |header, record|
16
+ record['id'] += 1
17
+ record
18
+ end
19
+
20
+ updated_contents.should == "ID,Name,Address Line\n2,Alice,123 1st St\n3,Bob,234 2nd St\n4,Charles,345 3rd St\n"
21
+ end
22
+ end
23
+
24
+ describe List, '::csv_string' do
25
+ it 'returns the contents we expect' do
26
+ file_contents = example_list_csv_string
27
+
28
+ list = List.from_string file_contents
29
+ output = list.csv_string
30
+
31
+ list.csv_string.should == "ID,Name,Address Line\n1,Alice,123 1st St\n2,Bob,234 2nd St\n3,Charles,345 3rd St\n"
32
+ end
33
+
34
+ it 'is the right-inverse of from_string' do
35
+ list = List.from_string example_list_csv_string
36
+
37
+ read_write_tempfile 'csv-list-spec' do |file, step|
38
+ case step
39
+ when :write
40
+ file.write list.csv_string
41
+ when :read
42
+ list2 = List.from_string file.read
43
+
44
+ list2.should == list
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe List, '::render_rows' do
51
+ it 'returns the output we expect' do
52
+ file_contents = example_list_csv_string
53
+
54
+ list = List.from_string file_contents
55
+ output = list.render_rows
56
+
57
+ output.should == [["ID", "Name", "Address Line"], [1, "Alice", "123 1st St"], [2, "Bob", "234 2nd St"], [3, "Charles", "345 3rd St"]]
58
+ end
59
+
60
+ it 'is the right-inverse of from_matrix' do
61
+ list = List.from_string example_list_csv_string
62
+ list2 = List.from_matrix list.render_rows
63
+
64
+ list2.should == list
65
+ end
66
+ end
67
+
68
+ describe List, '::render_matrix' do
69
+ it 'is the right-inverse of from_matrix' do
70
+ list = List.from_string example_list_csv_string
71
+ list2 = List.from_matrix list.render_matrix
72
+
73
+ list2.should == list
74
+ end
75
+ end
76
+
77
+ describe List, '==' do
78
+ it 'should not consider lists with different headers the same' do
79
+ list = new_example_list
80
+ list2 = new_example_list
81
+ list2.header.indices[0] = 'ID#'
82
+
83
+ list2.should_not == list
84
+ end
85
+
86
+ it 'should consider lists with the same headers and records as equal' do
87
+ list = new_example_list
88
+ list2 = new_example_list
89
+
90
+ list2.should == list
91
+ end
92
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ require 'csv'
4
+
5
+ require_relative '../lib/pile/record.rb'
6
+ include Pile
7
+ include Helpers
8
+
9
+ require_relative 'spec_helper'
10
+
11
+ describe Record, '[]' do
12
+ it 'should return the appropriate values' do
13
+ record = new_example_record
14
+
15
+ record[1].should == 'Bob Smith'
16
+ record['name'].should == 'Bob Smith'
17
+ record['address'].should == '123 1st St'
18
+ end
19
+ end
20
+
21
+ describe Record, '[]=' do
22
+ it 'should update values' do
23
+ record = new_example_record
24
+
25
+ record[1].should == 'Bob Smith'
26
+ record['name'].should == 'Bob Smith'
27
+ record['address'].should == '123 1st St'
28
+ end
29
+ end
30
+
31
+ describe Record, 'write_record' do
32
+ it 'writes the example record that matches our string' do
33
+ record = new_example_record
34
+
35
+ read_write_tempfile 'csv-record-spec' do |file, step|
36
+ case step
37
+ when :write
38
+ contents = CSV.generate do |csv|
39
+ record.write_header csv
40
+ record.write_record csv
41
+ end
42
+ file.write contents
43
+ when :read
44
+ file.read.should == "ID,Name,Address Line\n3,Bob Smith,123 1st St\n"
45
+ end
46
+ end
47
+ end
48
+
49
+ it 'is the right-inverse of from_csv_row' do
50
+ record = new_example_record
51
+
52
+ read_write_tempfile 'csv-record-spec' do |file, step|
53
+ case step
54
+ when :write
55
+ contents = CSV.generate do |csv|
56
+ record.write_header csv
57
+ record.write_record csv
58
+ end
59
+ file.write contents
60
+ when :read
61
+ lines = file.readlines
62
+
63
+ header2 = Header.from_csv_row lines[0], record.aliases
64
+ record2 = Record.from_csv_row lines[1], header2
65
+
66
+ record2.should == record
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe Record, '==' do
73
+ it 'should not consider records with different values the same' do
74
+ record = new_example_record
75
+ record2 = new_example_record
76
+ record2[1] = 'Bob Johnson'
77
+
78
+ record2.should_not == record
79
+ end
80
+
81
+ it 'should consider records with the same indices and aliases as equal' do
82
+ record = new_example_record
83
+ record2 = new_example_record
84
+
85
+ record2.should == record
86
+ end
87
+ end
@@ -0,0 +1,48 @@
1
+ module Pile
2
+ module Helpers
3
+ require 'tempfile'
4
+
5
+ # Construct a new header as specified in the example in the documentation
6
+ # of the +Header+ class.
7
+ def new_example_header
8
+ header = Header.new ({'id' => ['identity', '#'], 'address line' => ['address']}),
9
+ 'ID', 'Name', 'Address Line'
10
+ end
11
+
12
+ # Open and close a +Tempfile+ around a block yielded to with the +Tempfile+
13
+ # object.
14
+ def with_tempfile name
15
+ file = Tempfile.new name
16
+ begin
17
+ yield file
18
+ ensure
19
+ file.close
20
+ file.unlink
21
+ end
22
+ end
23
+
24
+ # Calls the block twice: first with +:write+ as the second argument, and
25
+ # then second with +:read+ as the second argument. The file is rewound to
26
+ # the beginning in between.
27
+ def read_write_tempfile name
28
+ with_tempfile name do |file|
29
+ yield file, :write
30
+ file.rewind
31
+ yield file, :read
32
+ end
33
+ end
34
+
35
+ def new_example_record
36
+ Record.new new_example_header, 3, 'Bob Smith', '123 1st St'
37
+ end
38
+
39
+ def new_example_list
40
+ header = new_example_header
41
+ List.new header, [Record.new(header), *new_example_record.values]
42
+ end
43
+
44
+ def example_list_csv_string
45
+ "ID,Name,Address Line\n1,Alice,123 1st St\n2,Bob,234 2nd St\n3,Charles,345 3rd St\n"
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pile
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Byron Johnson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: pile provides classes for updating, reading, and writing CSV files that
28
+ consist of a header and a number of records.
29
+ email:
30
+ - byron@byronjohnson.net
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Rakefile
36
+ - lib/pile/version.rb
37
+ - lib/pile/record.rb
38
+ - lib/pile/list.rb
39
+ - lib/pile/header.rb
40
+ - lib/pile.rb
41
+ - spec/spec_helper.rb
42
+ - spec/record_spec.rb
43
+ - spec/list_spec.rb
44
+ - spec/header_spec.rb
45
+ homepage: https://github.com/bairyn/pile
46
+ licenses:
47
+ - BSD3
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.0.6
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: CSV file manipulation library.
69
+ test_files:
70
+ - spec/spec_helper.rb
71
+ - spec/record_spec.rb
72
+ - spec/list_spec.rb
73
+ - spec/header_spec.rb
74
+ has_rdoc: