grid-csv 0.1.1

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,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .irbrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p392@griddle.com --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in griddle.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 bjh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ Griddle
2
+ =======
3
+
4
+ **note:** someone has the gem name **griddle** registered already so I am calling the gem **grid-csv**.
5
+
6
+ This gem was created to help process CSV files that are not really the usual *[row, col]* data.
7
+ I have lost many hours of my life in the black hole known as the *coporate intranet*, where windows documents multiply at an obscene rate.
8
+ I came up with this idea to help someone handle this particular nastiness.
9
+
10
+ This is one for my **windows brothers**, of course if it is useful to anyone else I would be delighted.
11
+
12
+ On To Business
13
+ ==============
14
+
15
+ An example of the source material in question would be parsing Excel data that comes from a formatted template. One where the structure is more like a document than a database table.
16
+ The template in question may look like this
17
+
18
+ ![](data/example.png)
19
+
20
+ If you exported the Excel data as CSV it may look something like [this](data/po.csv)
21
+
22
+ Modus Operandi
23
+ ==============
24
+
25
+ The main idea to keep in mind is that you are not processing rows and columns of data.
26
+ You are cutting out **rectangles** or *grids* of data to process further.
27
+
28
+ Let's say you have to process a bunch of excel files that captured form data.
29
+ The template that was used has a section for the user's contact information.
30
+ Instead of ripping through rows and columns you can either:
31
+ - search for a known string and use that as the starting point to cut out a **rectangle**
32
+ - cut out a rectangle from a know location, *i.e.* the contact info starts at row 4, column 3 and is 1 column wide and 4 rows high
33
+
34
+ The hope is that you can access data in a more intuitive way than just doing a nested for loop and crossing your fingers.
35
+
36
+ But Why?
37
+ =======
38
+
39
+ Because Microsoft has empowered business people to do **terrible, terrible** things with data.
40
+
41
+ Small Examples
42
+ ==============
43
+
44
+ ```ruby
45
+ $: << File.expand_path(File.dirname(__FILE__) + './lib')
46
+ require 'griddle.rb'
47
+
48
+ grid = Griddle::DataGrid.new.populate_from_csv('po.csv')
49
+
50
+ # I know where the Vendor data is from looking at the Purchase Order
51
+ # so I cut a rectangle that contains that data
52
+ puts "<Vendor>"
53
+ candy = grid.cut(11, 1, 1, 5)
54
+ puts candy.to_s
55
+ puts
56
+
57
+ # the Ship To data might move down a line or two
58
+ # so search for it's location based off of it's column header
59
+ puts "<Ship To>"
60
+ shipto = grid.find("SHIP TO").last
61
+ puts "ship to header location: #{shipto}"
62
+ puts grid.cut(shipto.row+1, shipto.col, 1, 5).to_s
63
+ puts
64
+
65
+ # iterate over the line item rows returned in a Rectangle
66
+ puts "<line items>"
67
+ grid.cut(21, 1, 7, 2).each do |row|
68
+ puts row.join(":")
69
+ end
70
+ puts
71
+
72
+ # search for two different points and use them to make a Rectangle
73
+ puts "<rectangle from points>"
74
+ tl = grid.find("TAX RATE").last
75
+ br = grid.find("TOTAL").last
76
+ puts grid.cut_rectangle(Griddle::Rectangle.create(tl, br)).to_s
77
+
78
+
79
+ # try out the Point.move functionality
80
+ # use the previous Ship To example
81
+ puts
82
+ puts "<Ship To with move>"
83
+ shipto = grid.find("SHIP TO").last
84
+ shipto.move(down:1)
85
+ puts grid.cut(shipto.row, shipto.col, 1, 5).to_s
86
+
87
+
88
+ # show cash money info
89
+ puts
90
+ puts "<want mo money!>"
91
+ puts grid.cut(38, 6, 2, 4).to_s
92
+ ```
93
+
94
+
95
+ **NOTE:** This code was originally written to accompany the article [here](http://pregnantfist.tumblr.com/post/42406731034/eating-glue-or-how-to-parse-csv-like-a-pre-schooler),
96
+ It is no longer as up to date as the gem version but it does explain the **why** of it all.
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ # If you want to make this the default task
7
+ task default: :spec
8
+
9
+ desc 'load gem files into IRB'
10
+ task :console do
11
+ exec 'irb -Ilib -rgriddle'
12
+ end
Binary file
@@ -0,0 +1,47 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + './lib')
2
+ require 'griddle.rb'
3
+
4
+ grid = Griddle::DataGrid.new.populate_from_csv('po.csv')
5
+
6
+ # I know where the Vendor data is from looking at the Purchase Order
7
+ # so I cut a rectangle that contains that data
8
+ puts "<Vendor>"
9
+ candy = grid.cut(11, 1, 1, 5)
10
+ puts candy.to_s
11
+ puts
12
+
13
+ # the Ship To data might move down a line or two
14
+ # so search for it's location based off of it's column header
15
+ puts "<Ship To>"
16
+ shipto = grid.find("SHIP TO").last
17
+ puts "ship to header location: #{shipto}"
18
+ puts grid.cut(shipto.row+1, shipto.col, 1, 5).to_s
19
+ puts
20
+
21
+ # iterate over the line item rows returned in a Rectangle
22
+ puts "<line items>"
23
+ grid.cut(21, 1, 7, 2).each do |row|
24
+ puts row.join(":")
25
+ end
26
+ puts
27
+
28
+ # search for two different points and use them to make a Rectangle
29
+ puts "<rectangle from points>"
30
+ tl = grid.find("TAX RATE").last
31
+ br = grid.find("TOTAL").last
32
+ puts grid.cut_rectangle(Griddle::Rectangle.create(tl, br)).to_s
33
+
34
+
35
+ # try out the Point.move functionality
36
+ # use the previous Ship To example
37
+ puts
38
+ puts "<Ship To with move>"
39
+ shipto = grid.find("SHIP TO").last
40
+ shipto.move(down:1)
41
+ puts grid.cut(shipto.row, shipto.col, 1, 5).to_s
42
+
43
+
44
+ # show cash money info
45
+ puts
46
+ puts "<want mo money!>"
47
+ puts grid.cut(38, 6, 2, 4).to_s
@@ -0,0 +1,49 @@
1
+ [Company Name],,,PURCHASE ORDER,,,
2
+ [Company Slogan],,,,,DATE:,2/1/2013
3
+ ,,,,,P.O. #,[123456]
4
+ ,,,,,,
5
+ [Street Address],,,,,,
6
+ "[City, ST ZIP]",,,,,,
7
+ Phone: [000-000-0000],,,,,,
8
+ Fax: [000-000-0000],,,,,,
9
+ ,,,,,,
10
+ VENDOR,,,SHIP TO,,,
11
+ [Name],,,[Attn: Name],,,
12
+ [Company Name],,,[Company Name],,,
13
+ [Street Address],,,[Street Address],,,
14
+ "[City, ST ZIP]",,,"[City, ST ZIP]",,,
15
+ [Phone],,,[Phone],,,
16
+ ,,,,,,
17
+ REQUISITIONER,SHIP VIA,,F.O.B.,SHIPPING TERMS,,
18
+ ,,,,,,
19
+ ,,,,,,
20
+ ITEM #,DESCRIPTION,,,QTY,UNIT PRICE,TOTAL
21
+ [23423423],Product XYZ,,,15,150.00,"2,250.00"
22
+ [45645645],Product ABC,,,1,75.00,75.00
23
+ ,,,,,,0.00
24
+ ,,,,,,0.00
25
+ ,,,,,,0.00
26
+ ,,,,,,0.00
27
+ ,,,,,,0.00
28
+ ,,,,,,0.00
29
+ ,,,,,,0.00
30
+ ,,,,,,0.00
31
+ ,,,,,,0.00
32
+ ,,,,,,0.00
33
+ ,,,,,,0.00
34
+ ,,,,,,0.00
35
+ ,,,,,,0.00
36
+ ,,,,[42],SUBTOTAL,"$2,325.00"
37
+ Other Comments or Special Instructions,,,,,TAX RATE,6.875%
38
+ ,,,,,TAX,$159.84
39
+ ,,,,,S & H,$0.00
40
+ ,,,,,OTHER,$0.00
41
+ ,,,,,TOTAL,"$2,484.84"
42
+ ,,,,,,
43
+ ,,,,,,
44
+ ,,,,,,
45
+ ,,,,,,
46
+ ,,,Authorized by,,,Date
47
+ ,,,,,,
48
+ "If you have any questions about this purchase order, please contact",,,,,,
49
+ "[Name, Phone #, E-mail, Phone, Fax]",,,,,,
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'griddle/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grid-csv"
8
+ spec.version = Griddle::VERSION
9
+ spec.authors = ["bjh"]
10
+ spec.email = ["fake@fake.com"]
11
+ spec.summary = %q{treat CSV like a grid}
12
+ spec.description = %q{access rectangles of data instead of rows and columns}
13
+ spec.homepage = "https://github.com/bjh/griddle"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "terminal-table"
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,4 @@
1
+ require 'griddle/version'
2
+ require 'griddle/point'
3
+ require 'griddle/rectangle'
4
+ require 'griddle/data_grid'
@@ -0,0 +1,105 @@
1
+ require 'csv'
2
+ require 'terminal-table'
3
+ require_relative 'point'
4
+
5
+ module Griddle
6
+ class DataGrid
7
+ include Enumerable
8
+ attr_accessor :case_sensitive, :offset, :grid
9
+
10
+ def self.from_csv(csv)
11
+ DataGrid.new.populate_from_csv(csv)
12
+ end
13
+
14
+ def initialize(start_counting_at_zero=false)
15
+ @offset = start_counting_at_zero ? 0 : 1
16
+ @grid = []
17
+ @case_sensitive = false
18
+ end
19
+
20
+ def to_rectangle
21
+ Rectangle.new(0, 0, width, height)
22
+ end
23
+
24
+ def width
25
+ grid[0].size
26
+ end
27
+
28
+ def height
29
+ grid.size
30
+ end
31
+
32
+ # Enumerable
33
+ def each(&block)
34
+ grid.each do |row|
35
+ block.call(row)
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ Terminal::Table.new(rows: grid)
41
+ end
42
+
43
+ def populate_from_csv(csv_file_path)
44
+ CSV.foreach(csv_file_path) {|row| self.grid << row}
45
+ self
46
+ end
47
+
48
+ def populate_from_data(data)
49
+ self.grid = data
50
+ self
51
+ end
52
+
53
+ def to_regex(query)
54
+ return query if query.is_a? Regexp
55
+
56
+ if case_sensitive
57
+ /#{Regexp.escape(query)}/
58
+ else
59
+ /#{Regexp.escape(query)}/i
60
+ end
61
+ end
62
+
63
+ def find(what)
64
+ what = to_regex(what)
65
+ matches = []
66
+
67
+ grid.each_with_index do |row, row_index|
68
+ row.each_index.select {|n| what.match(row[n])}.each do |column|
69
+ matches << Point.new(*offset_up(row_index, column))
70
+ end
71
+ end
72
+
73
+ matches
74
+ end
75
+
76
+ def cut(top, left, width, height)
77
+ top, left = offset_down(top, left)
78
+ selection = []
79
+
80
+ for row in top...(top+height)
81
+ row_data = []
82
+
83
+ for col in left...(left+width)
84
+ row_data << grid[row][col]
85
+ end
86
+
87
+ selection << row_data
88
+ end
89
+
90
+ DataGrid.new.populate_from_data(selection)
91
+ end
92
+
93
+ def cut_rectangle(rectangle)
94
+ cut(rectangle.top, rectangle.left, rectangle.width, rectangle.height)
95
+ end
96
+
97
+ def offset_down(*args)
98
+ args.collect {|n| n - offset}
99
+ end
100
+
101
+ def offset_up(*args)
102
+ args.collect {|n| n + offset}
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,74 @@
1
+ require_relative 'rectangle'
2
+
3
+ module Griddle
4
+ class Point < Struct.new(:row, :col)
5
+ include Comparable
6
+
7
+ # TODO: not sure this is really necessary,
8
+ # it seemed like a good idea at the time
9
+ def <=>(point)
10
+ [row, col] <=> [point.row, point.col]
11
+ end
12
+
13
+ def to_s
14
+ "[#{row}, #{col}]"
15
+ end
16
+
17
+ def nil?
18
+ row.nil? && col.nil?
19
+ end
20
+
21
+ def zero?
22
+ row.zero? && col.zero?
23
+ end
24
+
25
+ def delta(point)
26
+ Point.new(
27
+ (self.row - point.row).abs,
28
+ (self.col - point.col).abs
29
+ )
30
+ end
31
+
32
+ # `point` is used to calculate the width and height
33
+ # of the new rectangle
34
+ def to_rectangle(point)
35
+ d = delta(point)
36
+
37
+ Rectangle.new(
38
+ row,
39
+ col,
40
+ d.col + 1,
41
+ d.row + 1
42
+ )
43
+ end
44
+
45
+ def move(directions)
46
+ directions.each_pair do |direction, amount|
47
+ amount = amount.to_i
48
+
49
+ case direction.to_sym
50
+ when :up
51
+ self.row -= amount
52
+ when :down
53
+ self.row += amount
54
+ when :left
55
+ self.col -= amount
56
+ when :right
57
+ self.col += amount
58
+ end
59
+ end
60
+ end
61
+
62
+ # some sugar
63
+ [:up, :down, :left, :right].each do |direction|
64
+ define_method direction do |amount|
65
+ move(direction => amount)
66
+ end
67
+ end
68
+ end
69
+
70
+ # # return NullPoint instead of nil
71
+ # NullPoint = Point.new(nil, nil)
72
+ # # for no apparent reason
73
+ # ZeroPoint = Point.new(0, 0)
74
+ end
@@ -0,0 +1,15 @@
1
+ module Griddle
2
+ class Rectangle < Struct.new(:top, :left, :width, :height)
3
+ # include Comparable
4
+
5
+ def self.create(top_left, bottom_right)
6
+ top_left.to_rectangle(bottom_right)
7
+ end
8
+
9
+ # TODO: what makes a rectangle less than another?
10
+ # the width, the position, the height? the size?
11
+ # def <=>(rectangle)
12
+ # [top, left, width, height] <=> [rectangle.top, rectangle.left, rectangle.width, rectangle.height]
13
+ # end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Griddle
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper.rb'
2
+ require 'griddle'
3
+
4
+ describe Griddle::DataGrid do
5
+ let(:grid) { Griddle::DataGrid.from_csv('data/po.csv') }
6
+
7
+ describe '#find' do
8
+ context 'using a regular expression' do
9
+ it 'uses the regexp to match against' do
10
+ expect(grid.find(/ship to/i)).to have(1).item
11
+ end
12
+ end
13
+
14
+ context 'when a single match is found' do
15
+ it 'returns an array with a single Point' do
16
+ expect(grid.find('SHIP TO')).to have(1).item
17
+ end
18
+
19
+ it 'the matched point has the row and column of the found item' do
20
+ p = grid.find('SHIP TO').last
21
+ expect(p.row).to eq 10
22
+ expect(p.col).to eq 4
23
+ end
24
+ end
25
+
26
+ context 'when multiple matches are found' do
27
+ it 'returns an array of Points' do
28
+ expect(grid.find('[Street Address]')).to have(3).items
29
+ end
30
+ end
31
+
32
+ context 'when a match is NOT found' do
33
+ it 'returns an empty array' do
34
+ expect(grid.find('SHAZAM!!!')).to have(0).items
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#width' do
40
+ it 'returns the width of the data grid' do
41
+ r = grid.cut(11, 1, 1, 5).to_rectangle
42
+ expect(r.width).to eq 1
43
+ end
44
+ end
45
+
46
+ describe '#height' do
47
+ it 'the height of the data grid' do
48
+ r = grid.cut(11, 1, 1, 5).to_rectangle
49
+ expect(r.height).to eq 5
50
+ end
51
+ end
52
+
53
+ describe '#to_rectangle' do
54
+ it 'returns a rectangle with the width and height of the data' do
55
+ r = grid.cut(11, 1, 1, 5).to_rectangle
56
+ expect(r.width).to eq 1
57
+ expect(r.height).to eq 5
58
+ end
59
+ end
60
+
61
+ describe '#cut' do
62
+ it 'returns a matrix/grid the size of the passed in parameters' do
63
+ data = grid.cut(11, 1, 1, 5)
64
+ expect(data.grid).to have(5).items
65
+ end
66
+ end
67
+
68
+ describe '#offset_up' do
69
+ it 'adds one to every value passed in' do
70
+ expect(grid.offset_up(4, 4)).to eq [5, 5]
71
+ end
72
+ end
73
+
74
+ describe '#offset_down' do
75
+ it 'subtract one from every value passed in' do
76
+ expect(grid.offset_down(4, 4)).to eq [3, 3]
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper.rb'
2
+ require 'griddle'
3
+
4
+ describe Griddle::Point do
5
+ let(:point) { Griddle::Point.new(10, 10) }
6
+
7
+ describe '#delta' do
8
+ context 'when the points have the same values' do
9
+ it 'returns a point with [0,0]' do
10
+ p2 = Griddle::Point.new(10, 10)
11
+ expect(point.delta(p2).zero?).to be_true
12
+ end
13
+ end
14
+
15
+ context 'when the points have the different values' do
16
+ it 'returns a point with the difference' do
17
+ p2 = Griddle::Point.new(3, 3)
18
+ expect(point.delta(p2)).to eq Griddle::Point.new(7, 7)
19
+ end
20
+ end
21
+ end
22
+
23
+ # make sure the spaceship was implemented properly
24
+ describe '<=>' do
25
+ context 'when lhs == rhs' do
26
+ it 'returns true' do
27
+ rhs = Griddle::Point.new(10, 10)
28
+ expect(point == rhs).to be_true
29
+ end
30
+ end
31
+
32
+ context 'when lhs > rhs' do
33
+ it 'returns true' do
34
+ rhs = Griddle::Point.new(9, 0)
35
+ expect(point > rhs).to be_true
36
+ end
37
+ end
38
+
39
+ context 'when lhs < rhs' do
40
+ it 'returns true' do
41
+ rhs = Griddle::Point.new(11, 11)
42
+ expect(point < rhs).to be_true
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'movement methods' do
48
+ it 'can go up' do
49
+ expect(point).to respond_to :up
50
+ end
51
+
52
+ it 'can go down' do
53
+ expect(point).to respond_to :down
54
+ end
55
+
56
+ it 'can go left' do
57
+ expect(point).to respond_to :left
58
+ end
59
+
60
+ it 'can go right' do
61
+ expect(point).to respond_to :right
62
+ end
63
+
64
+ describe "#move" do
65
+ it "moves up" do
66
+ point.move(up: 2)
67
+ expect(point.row).to eq(8)
68
+ end
69
+
70
+ it "moves down" do
71
+ point.move(:down => 4)
72
+ expect(point.row).to eq(14)
73
+ end
74
+
75
+ it "moves up and right" do
76
+ point.move(up:5, right:3)
77
+ expect(point.row).to eq(5)
78
+ expect(point.col).to eq(13)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grid-csv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - bjh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: terminal-table
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: access rectangles of data instead of rows and columns
79
+ email:
80
+ - fake@fake.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .rspec
87
+ - .rvmrc
88
+ - Gemfile
89
+ - Gemfile.lock
90
+ - LICENSE.txt
91
+ - README.markdown
92
+ - Rakefile
93
+ - data/example.png
94
+ - data/griddle.rb
95
+ - data/po.csv
96
+ - griddle.gemspec
97
+ - lib/griddle.rb
98
+ - lib/griddle/data_grid.rb
99
+ - lib/griddle/point.rb
100
+ - lib/griddle/rectangle.rb
101
+ - lib/griddle/version.rb
102
+ - spec/lib/data_grid_spec.rb
103
+ - spec/lib/point_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: https://github.com/bjh/griddle
106
+ licenses:
107
+ - MIT
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ segments:
119
+ - 0
120
+ hash: 434941054811727487
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ segments:
128
+ - 0
129
+ hash: 434941054811727487
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 1.8.23
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: treat CSV like a grid
136
+ test_files:
137
+ - spec/lib/data_grid_spec.rb
138
+ - spec/lib/point_spec.rb
139
+ - spec/spec_helper.rb