pile 0.1.0

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.
@@ -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: