excel_walker 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +1 -6
- data/README.md +3 -3
- data/lib/excel_walker/reader/hook.rb +18 -14
- data/lib/excel_walker/reader/reader.rb +1 -1
- data/lib/excel_walker/version.rb +1 -1
- data/lib/excel_walker/writer/hook.rb +1 -1
- data/spec/excel_walker/reader/hook_spec.rb +91 -0
- data/spec/excel_walker/writer/cells_spec.rb +60 -0
- data/spec/excel_walker/writer/hook_spec.rb +77 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
M2Y0NTYwMjQyYTEwMzhjYjU5NGMxNTQzNTY1ZWY0ZjgzMmRhMDZlZg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODdiOWFhNmY0OGM4MjY0Y2IyZmE4YzRjYTQ0MjQ5OTcyMTMzYzBhOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YjFiYjM3ZmQwYmE0MzA3YmM4YWMzZDIwOTNmYjFlYzRmZDUzNDRlYTQ3YTMx
|
10
|
+
YzU2M2U1ZjgyYmNjMjhjOWM1NzUwMjBiZGJlMTFkNDgyOWIxMGRjMTZhZTEy
|
11
|
+
YTgwOWRlNjkzY2FkNjM3MTc5ZmQ4NTQ2MWZiNWYzY2Y0OTZjNGQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZWY3YzU0Y2E3ZTg1NGZlNjVjZTQyZThhYjJlMWI5ZGJjY2NlMjZmZDVkNmRl
|
14
|
+
NjdhNDQwN2JiYzBkNzRkNDYyZWI4NDVlNDhlMzk4NTEzNzI3ZjNmM2EwMmVh
|
15
|
+
NGJlNWMzZTQyZTcyODc1YjA3OGQ4YWYwYmQwZGM1OWY2ZDRmNjU=
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
# ExcelWalker
|
1
|
+
# ExcelWalker [![Gem Version](https://badge.fury.io/rb/excel_walker.png)](http://badge.fury.io/rb/excel_walker) [![Build Status](https://secure.travis-ci.org/shadabahmed/excel_walker.png)](https://secure.travis-ci.org/shadabahmed/excel_walker)
|
2
2
|
|
3
3
|
Excel Walker is a declarative Microsoft Excel file parser and generator. It uses [Axlsx](https://github.com/randym/axlsx) to write and [Creek](https://github.com/pythonicrubyist/creek) to read.
|
4
4
|
|
5
5
|
# Why I created this ?
|
6
6
|
|
7
|
-
While dealing with Excel files recently, I noticed that excel files are never a set of homogenous data, which you can iterate over directly. Rather there are regions of interest whether in the same worksheet or across worksheets, which
|
7
|
+
While dealing with Excel files recently, I noticed that excel files are never a set of homogenous data, which you can iterate over directly. Rather there are regions of interest whether in the same worksheet or across worksheets, which you want to work upon differently. For e.g. look at the spreadsheet below:
|
8
8
|
|
9
9
|
![currency_excel](https://cloud.githubusercontent.com/assets/830679/2937625/d333658e-d8bf-11e3-9619-658e20c425a0.png)
|
10
10
|
|
11
|
-
We are not interested in the top header, we just want the country, currency and USD exchange rate so that we can store that in our database in currencies table. The regions are marked in RED. If you were to go
|
11
|
+
We are not interested in the top header, we just want the country, currency and USD exchange rate so that we can store that in our database in currencies table. The regions are marked in RED. If you were to go iteratively over each row, then lot of custom logic would have to fit in.
|
12
12
|
|
13
13
|
What if you could just declare this - Start from row 3 and just give me 1st, 2nd and 4th column. Let's see this in code:
|
14
14
|
|
@@ -4,28 +4,32 @@ module ExcelWalker
|
|
4
4
|
class Hook
|
5
5
|
def initialize(condition)
|
6
6
|
@matcher = case true
|
7
|
-
when condition.
|
7
|
+
when condition.is_a?(Proc)
|
8
8
|
condition
|
9
9
|
when condition.is_a?(Array), condition.is_a?(Range)
|
10
10
|
proc { |row_num| condition.include?(row_num) }
|
11
11
|
when condition.is_a?(Fixnum)
|
12
12
|
proc { |row_num| condition === row_num }
|
13
|
+
else
|
14
|
+
raise ArgumentError.new('Can only take Array, Number, Range or a Block')
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
def columns(
|
17
|
-
|
18
|
+
def columns(cols_matcher = nil, &block)
|
19
|
+
cols_matcher = block if block_given?
|
18
20
|
@cols_extractor =
|
19
|
-
case
|
20
|
-
when
|
21
|
-
cols_set = Set.new(
|
22
|
-
proc { |row| row.
|
23
|
-
when
|
24
|
-
proc { |row| row
|
25
|
-
when
|
26
|
-
proc { |row| row
|
27
|
-
when
|
28
|
-
proc { |row|
|
21
|
+
case true
|
22
|
+
when cols_matcher.is_a?(Array)
|
23
|
+
cols_set = Set.new(cols_matcher)
|
24
|
+
proc { |row| row.select.with_index { |_, idx| cols_set.include?(idx + 1) } }
|
25
|
+
when cols_matcher.is_a?(Fixnum)
|
26
|
+
proc { |row| row[cols_matcher - 1] }
|
27
|
+
when cols_matcher.is_a?(Range)
|
28
|
+
proc { |row| row[(cols_matcher.min - 1)..(cols_matcher.max - 1)] }
|
29
|
+
when cols_matcher.is_a?(Proc)
|
30
|
+
proc { |row| cols_matcher[row] }
|
31
|
+
else
|
32
|
+
raise ArgumentError.new('Can only take Array, Number, Range or a Block')
|
29
33
|
end
|
30
34
|
self
|
31
35
|
end
|
@@ -36,7 +40,7 @@ module ExcelWalker
|
|
36
40
|
@run_block = block
|
37
41
|
end
|
38
42
|
|
39
|
-
def match?(row_num, sheet_num)
|
43
|
+
def match?(row_num, sheet_num = nil)
|
40
44
|
@matcher[row_num, sheet_num]
|
41
45
|
end
|
42
46
|
|
@@ -67,7 +67,7 @@ module ExcelWalker
|
|
67
67
|
row_num += 1
|
68
68
|
break if @max_rows[sheet_num] && row_num > @max_rows[sheet_num]
|
69
69
|
@hooks[sheet_num].each do |hook|
|
70
|
-
hook.call(row, row_num, sheet, sheet_num) if hook.match?(row_num, sheet_num)
|
70
|
+
hook.call(row.values, row_num, sheet, sheet_num) if hook.match?(row_num, sheet_num)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
data/lib/excel_walker/version.rb
CHANGED
@@ -14,7 +14,7 @@ module ExcelWalker
|
|
14
14
|
@max = condition
|
15
15
|
proc { |row_num| condition === row_num }
|
16
16
|
else
|
17
|
-
raise
|
17
|
+
raise ArgumentError.new('Can only take Range, Integers or Arrays')
|
18
18
|
end
|
19
19
|
@row_index = 0
|
20
20
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ExcelWalker::Reader
|
4
|
+
describe Hook do
|
5
|
+
subject(:hook) { Hook.new(proc { true }) }
|
6
|
+
it 'raises error on wrong initializaion' do
|
7
|
+
expect { Hook.new('string') }.to raise_error(ArgumentError, 'Can only take Array, Number, Range or a Block')
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#match?' do
|
11
|
+
context 'array condition for rows' do
|
12
|
+
subject(:hook) { Hook.new([1, 2, 3]) }
|
13
|
+
it 'gives correct output on match' do
|
14
|
+
expect(hook.match?(2)).to be_true
|
15
|
+
expect(hook.match?(0)).to be_false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'range condition for rows' do
|
20
|
+
subject(:hook) { Hook.new(1..3) }
|
21
|
+
it 'gives correct output on match' do
|
22
|
+
expect(hook.match?(2)).to be_true
|
23
|
+
expect(hook.match?(0)).to be_false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'number condition for rows' do
|
28
|
+
subject(:hook) { Hook.new(2) }
|
29
|
+
it 'gives correct output on match' do
|
30
|
+
expect(hook.match?(2)).to be_true
|
31
|
+
expect(hook.match?(3)).to be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'proc condition for rows' do
|
36
|
+
subject(:hook) { Hook.new(proc { |x, y| x == y && y == 2 }) }
|
37
|
+
it 'gives correct output on match' do
|
38
|
+
expect(hook.match?(2, 2)).to be_true
|
39
|
+
expect(hook.match?(2, 3)).to be_false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#run' do
|
45
|
+
let(:hook_lambda) { proc {} }
|
46
|
+
it 'assigns the block to run when hook is executed' do
|
47
|
+
hook.run(&hook_lambda)
|
48
|
+
expect(hook.instance_variable_get('@run_block')).to eq hook_lambda
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#call' do
|
53
|
+
let(:collection) { (1..100).to_a }
|
54
|
+
let(:hook_block) { double }
|
55
|
+
before { subject.run { |a, b, c, d| hook_block.call(a, b, c, d) } }
|
56
|
+
|
57
|
+
context 'array column numbers matcher' do
|
58
|
+
subject { hook.columns([1, 2, 4]) }
|
59
|
+
it 'sends correct data to the hooked block' do
|
60
|
+
hook_block.should_receive(:call).with([1, 2, 4], 'b', 'c', 'd')
|
61
|
+
subject.call(collection, 'b', 'c', 'd')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'range column numbers matcher' do
|
66
|
+
subject { hook.columns(6..9) }
|
67
|
+
it 'sends correct data to the hooked block' do
|
68
|
+
hook_block.should_receive(:call).with([6, 7, 8, 9], 'b', 'c', 'd')
|
69
|
+
subject.call(collection, 'b', 'c', 'd')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'number column matcher' do
|
74
|
+
subject { hook.columns(11) }
|
75
|
+
it 'sends correct data to the hooked block' do
|
76
|
+
hook_block.should_receive(:call).with(11, 'b', 'c', 'd')
|
77
|
+
subject.call(collection, 'b', 'c', 'd')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'proc column matcher' do
|
82
|
+
subject { hook.columns {|row| row[96] } }
|
83
|
+
it 'sends correct data to the hooked block' do
|
84
|
+
hook_block.should_receive(:call).with(97, 'b', 'c', 'd')
|
85
|
+
subject.call(collection, 'b', 'c', 'd')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ExcelWalker::Writer
|
4
|
+
describe Cells do
|
5
|
+
subject(:cells) { Cells.new(:style) }
|
6
|
+
|
7
|
+
describe '#set_data_at' do
|
8
|
+
it 'sets data at specified position' do
|
9
|
+
cells.set_data_at(2, :data)
|
10
|
+
expect(cells.data).to eq [nil, nil, :data]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'sets data at specified range' do
|
14
|
+
cells.set_data_at(4..6, :data)
|
15
|
+
expect(cells.data).to eq [nil, nil, nil, nil, :data, :data, :data]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#set_style_at' do
|
20
|
+
it 'sets style at specified position' do
|
21
|
+
cells.set_style_at(2, :style)
|
22
|
+
expect(cells.styles).to eq [nil, nil, :style]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'sets data at specified range' do
|
26
|
+
cells.set_style_at(4..6, :style)
|
27
|
+
expect(cells.styles).to eq [nil, nil, nil, nil, :style, :style, :style]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#build' do
|
32
|
+
|
33
|
+
context 'when no data is set but width is set' do
|
34
|
+
it 'maps style for each data cell' do
|
35
|
+
cells.width = 4
|
36
|
+
cells.build
|
37
|
+
expect(cells.styles).to eq [:style, :style, :style, :style]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when data is set' do
|
42
|
+
it 'maps style for each data cell' do
|
43
|
+
cells.set_data_at(3..5, :data)
|
44
|
+
cells.build
|
45
|
+
expect(cells.data).to eq [nil, nil, nil, :data, :data, :data]
|
46
|
+
expect(cells.styles).to eq [:style, :style, :style, :style, :style, :style]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when both data and styles are set' do
|
51
|
+
it 'maps style for each data cell' do
|
52
|
+
cells.set_data_at(3..5, :data)
|
53
|
+
cells.set_style_at([2, 3, 5], :new_style)
|
54
|
+
cells.build
|
55
|
+
expect(cells.styles).to eq [:style, :style, :new_style, :new_style, :style, :new_style]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ExcelWalker::Writer
|
4
|
+
describe Hook do
|
5
|
+
subject(:hook) { Hook.new(0..100) }
|
6
|
+
it 'raises error on wrong initializaion' do
|
7
|
+
expect { Hook.new('string') }.to raise_error(ArgumentError, 'Can only take Range, Integers or Arrays')
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#match?' do
|
11
|
+
context 'array condition for rows' do
|
12
|
+
subject(:hook) { Hook.new([1, 2, 3]) }
|
13
|
+
it 'gives correct output on match' do
|
14
|
+
expect(hook.match?(2)).to be_true
|
15
|
+
expect(hook.match?(0)).to be_false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'range condition for rows' do
|
20
|
+
subject(:hook) { Hook.new(1..3) }
|
21
|
+
it 'gives correct output on match' do
|
22
|
+
expect(hook.match?(2)).to be_true
|
23
|
+
expect(hook.match?(0)).to be_false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'number condition for rows' do
|
28
|
+
subject(:hook) { Hook.new(2) }
|
29
|
+
it 'gives correct output on match' do
|
30
|
+
expect(hook.match?(2)).to be_true
|
31
|
+
expect(hook.match?(3)).to be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#max' do
|
37
|
+
it 'gives the correct value of max row number for the hook' do
|
38
|
+
expect(Hook.new(0..100).max).to eq 100
|
39
|
+
expect(Hook.new([0, 1, 110]).max).to eq 110
|
40
|
+
expect(Hook.new(200).max).to eq 200
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#after_column' do
|
45
|
+
it 'assigns the block to run when hook is executed' do
|
46
|
+
hook.after_column(10)
|
47
|
+
expect(hook.instance_variable_get('@offset')).to eq 10
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
describe '#fill' do
|
53
|
+
let(:hook_lambda) { proc {} }
|
54
|
+
it 'assigns the block to run when hook is executed' do
|
55
|
+
hook.fill(&hook_lambda)
|
56
|
+
expect(hook.instance_variable_get('@filler')).to eq hook_lambda
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#run' do
|
61
|
+
let(:hook_block) { double }
|
62
|
+
let(:cells) { double(build: nil) }
|
63
|
+
before { subject.fill { |a, b, c| hook_block.call(a, b, c) } }
|
64
|
+
subject { hook.after_column(10) }
|
65
|
+
it 'sends correct data to the hooked block' do
|
66
|
+
Cells.should_receive(:new).times.exactly(2).and_return(cells)
|
67
|
+
#first run
|
68
|
+
hook_block.should_receive(:call).with(cells, 0, 5)
|
69
|
+
expect(subject.run(5)).to eq cells
|
70
|
+
#second run
|
71
|
+
hook_block.should_receive(:call).with(cells, 1, 6)
|
72
|
+
expect(subject.run(6)).to eq cells
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: excel_walker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shadab Ahmed
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: creek
|