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.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +96 -0
- data/Rakefile +12 -0
- data/data/example.png +0 -0
- data/data/griddle.rb +47 -0
- data/data/po.csv +49 -0
- data/griddle.gemspec +25 -0
- data/lib/griddle.rb +4 -0
- data/lib/griddle/data_grid.rb +105 -0
- data/lib/griddle/point.rb +74 -0
- data/lib/griddle/rectangle.rb +15 -0
- data/lib/griddle/version.rb +3 -0
- data/spec/lib/data_grid_spec.rb +79 -0
- data/spec/lib/point_spec.rb +82 -0
- data/spec/spec_helper.rb +17 -0
- metadata +139 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.3-p392@griddle.com --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.markdown
ADDED
@@ -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
|
+

|
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.
|
data/Rakefile
ADDED
@@ -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
|
data/data/example.png
ADDED
Binary file
|
data/data/griddle.rb
ADDED
@@ -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
|
data/data/po.csv
ADDED
@@ -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]",,,,,,
|
data/griddle.gemspec
ADDED
@@ -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
|
data/lib/griddle.rb
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|