excel_walker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+
6
+ env:
7
+ - "RAILS_VERSION=4.0"
8
+ - "RAILS_VERSION=3.2"
9
+ - "RAILS_VERSION=3.1"
10
+ - "RAILS_VERSION=3.0"
11
+
12
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in excel_walker.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'growl'
8
+ gem 'guard'
9
+ gem 'guard-rspec'
10
+ gem 'redis', :require => false
11
+ gem 'simplecov', :require => false
12
+ end
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+ interactor :simple
4
+
5
+ guard 'rspec' do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
+ watch('spec/spec_helper.rb') { "spec" }
9
+ end
10
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Shadab Ahmed
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.md ADDED
@@ -0,0 +1,29 @@
1
+ # ExcelWalker
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'excel_walker'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install excel_walker
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'excel_walker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'excel_walker'
8
+ spec.version = ExcelWalker::VERSION
9
+ spec.authors = ['Shadab Ahmed']
10
+ spec.email = ['shadab.ansari@gmail.com']
11
+ spec.description = %q{A declarative parser and builder for Excel Files}
12
+ spec.summary = %q{This gem chooses a different approach to Excel Parsing since excel can contain many regions of interest.}
13
+ spec.homepage = 'https://github.com/shadabahmed/excel_walker'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
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 'creek', '~>1.0.4'
22
+ spec.add_dependency 'axlsx', '~>2.0.1'
23
+ spec.add_dependency 'activesupport', '> 3.0.0'
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'excel_walker/version'
3
+ require 'excel_walker/errors'
4
+ require 'excel_walker/reader'
5
+ require 'excel_walker/writer'
6
+
7
+ module ExcelWalker
8
+ # Your code goes here...
9
+ end
@@ -0,0 +1,7 @@
1
+ module ExcelWalker
2
+ class StopIteration < ::StopIteration
3
+ end
4
+
5
+ class ArgumentError < ::ArgumentError
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'creek'
2
+ require 'excel_walker/reader/hook'
3
+ require 'excel_walker/reader/reader'
4
+
5
+ module ExcelWalker
6
+ module Reader
7
+ def self.create(file_path)
8
+ Reader.new(file_path)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ require 'set'
2
+ module ExcelWalker
3
+ module Reader
4
+ class Hook
5
+ def initialize(condition)
6
+ @matcher = case true
7
+ when condition.respond_to?(:call)
8
+ condition
9
+ when condition.is_a?(Array), condition.is_a?(Range)
10
+ proc { |row_num| condition.include?(row_num) }
11
+ when condition.is_a?(Fixnum)
12
+ proc { |row_num| condition === row_num }
13
+ end
14
+ end
15
+
16
+ def columns(cols_condition = nil, &block)
17
+ cols_condition = block if block_given?
18
+ @cols_extractor =
19
+ case cols_condition.class.name
20
+ when 'Array'
21
+ cols_set = Set.new(cols_condition)
22
+ proc { |row| row.values.select.with_index { |_, idx| cols_set.include?(idx + 1) } }
23
+ when 'Fixnum'
24
+ proc { |row| row.values[cols_condition - 1] }
25
+ when 'Range'
26
+ proc { |row| row.values[(cols_condition.min - 1)..(cols_condition.max - 1)] }
27
+ when 'Proc'
28
+ proc { |row| cols_condition[row.values] }
29
+ end
30
+ self
31
+ end
32
+
33
+ alias pluck_columns columns
34
+
35
+ def run(&block)
36
+ @run_block = block
37
+ end
38
+
39
+ def match?(row_num, sheet_num)
40
+ @matcher[row_num, sheet_num]
41
+ end
42
+
43
+ def call(row, row_num, sheet, sheet_num)
44
+ data = extract_columns(row, row_num, sheet, sheet_num)
45
+ @run_block[data, row_num, sheet, sheet_num]
46
+ end
47
+
48
+ protected
49
+
50
+ def extract_columns(*args)
51
+ @cols_extractor[*args]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,76 @@
1
+ module ExcelWalker
2
+ module Reader
3
+ class Reader
4
+ def initialize(file_path)
5
+ @xl = Creek::Book.new(file_path)
6
+ @hooks = {}
7
+ @max_rows = {}
8
+ @current_sheet = 1
9
+ @max_sheets = 1
10
+ end
11
+
12
+ def for_sheet(sheet_num)
13
+ @current_sheet = sheet_num
14
+ @max_sheets = sheet_num if sheet_num > @max_sheets
15
+ self
16
+ end
17
+
18
+ alias set_sheet for_sheet
19
+
20
+ def max_rows(max)
21
+ @max_rows[@current_sheet] = max
22
+ self
23
+ end
24
+
25
+ def on_row(condition = nil, &block)
26
+ condition = block if block_given?
27
+ Hook.new(condition).tap do |hook|
28
+ @hooks[@current_sheet] ||= []
29
+ @hooks[@current_sheet] << hook
30
+ end
31
+ end
32
+
33
+ alias on_rows on_row
34
+
35
+ def hooks
36
+ @hooks[@current_sheet]
37
+ end
38
+
39
+ def start
40
+ sheet_num = 0
41
+ sheets_done = []
42
+ begin
43
+ @xl.sheets.each do |sheet|
44
+ sheet_num += 1
45
+ break if sheet_num > @max_sheets
46
+ process_rows(sheet, sheet_num)
47
+ sheets_done << sheet.name
48
+ end
49
+ rescue StopIteration
50
+ end
51
+ sheets_done
52
+ ensure
53
+ @xl.close
54
+ end
55
+
56
+ alias walk start
57
+
58
+ def exit
59
+ raise StopIteration.new
60
+ end
61
+
62
+ protected
63
+
64
+ def process_rows(sheet, sheet_num)
65
+ row_num = 0
66
+ sheet.rows.each do |row|
67
+ row_num += 1
68
+ break if @max_rows[sheet_num] && row_num > @max_rows[sheet_num]
69
+ @hooks[sheet_num].each do |hook|
70
+ hook.call(row, row_num, sheet, sheet_num) if hook.match?(row_num, sheet_num)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module ExcelWalker
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ require 'axlsx'
2
+ require 'excel_walker/writer/cells'
3
+ require 'excel_walker/writer/hook'
4
+ require 'excel_walker/writer/sheet_builder'
5
+
6
+ module ExcelWalker
7
+ module Writer
8
+
9
+ def self.create(file_path)
10
+ Writer.new(file_path)
11
+ end
12
+
13
+ class Writer
14
+ def initialize(file_path)
15
+
16
+ end
17
+
18
+ def new_sheet(sheet_name)
19
+
20
+ end
21
+
22
+ def save
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ module ExcelWalker
2
+ module Writer
3
+
4
+ class Cells
5
+ attr_accessor :width, :default_style, :data, :styles
6
+
7
+ def initialize(default_style)
8
+ @default_style = default_style
9
+ @data, @styles, @width = [], [], 0
10
+ end
11
+
12
+ def set_data_at(range, cell_data)
13
+ range = [range] if range.is_a?(Fixnum)
14
+ range.each do |i|
15
+ data[i] = cell_data
16
+ end
17
+ end
18
+
19
+ def set_style_at(range, cell_style)
20
+ range = [range] if range.is_a?(Fixnum)
21
+ range.each do |i|
22
+ styles[i] = cell_style
23
+ end
24
+ end
25
+
26
+ def build
27
+ build_data
28
+ build_styles
29
+ end
30
+
31
+ private
32
+
33
+ def build_data
34
+ if data.empty? && @width > 0
35
+ @data = [nil]*@width
36
+ end
37
+ end
38
+
39
+ def build_styles
40
+ final_styles = [@default_style]*@data.length
41
+ 0.upto(styles.length - 1).each do |idx|
42
+ final_styles[idx] = @styles[idx] if @styles[idx]
43
+ end
44
+ @styles = final_styles
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ module ExcelWalker
2
+ module Writer
3
+
4
+ class Hook
5
+ attr_reader :max, :offset
6
+ attr_accessor :style
7
+
8
+ def initialize(condition)
9
+ @matcher = case true
10
+ when condition.is_a?(Range), condition.is_a?(Array)
11
+ @max = condition.max
12
+ proc { |row_num| condition.include?(row_num) }
13
+ when condition.is_a?(Fixnum)
14
+ @max = condition
15
+ proc { |row_num| condition === row_num }
16
+ else
17
+ raise ArgumentException.new('Can only take Range, Integers or Arrays here')
18
+ end
19
+ @row_index = 0
20
+ end
21
+
22
+ def match?(row_num)
23
+ @matcher[row_num]
24
+ end
25
+
26
+ def after_column(offset)
27
+ @offset = offset
28
+ self
29
+ end
30
+
31
+ def fill(&block)
32
+ @filler = block
33
+ end
34
+
35
+ def run(row_num)
36
+ cells = Cells.new(style)
37
+ @filler[cells, @row_index, row_num]
38
+ cells.build
39
+ @row_index += 1
40
+ cells
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,56 @@
1
+ module ExcelWalker
2
+ module Writer
3
+ class SheetBuilder
4
+ def initialize(workbook, name)
5
+ @workbook = workbook
6
+ @name = name
7
+ @sheet = @workbook.add_worksheet(:name => @name)
8
+ @hooks = []
9
+ @max_rows = 0
10
+ end
11
+
12
+ delegate :add_style, to: '@workbook.styles'
13
+ delegate :pane, to: '@sheet.sheet_view'
14
+
15
+ def create_pane(x , y)
16
+ @sheet.sheet_view.pane do |pane|
17
+ pane.top_left_cell = "#{(64 + x).chr}#{y}"
18
+ pane.state = :frozen_split
19
+ pane.y_split = x - 1
20
+ pane.x_split = y - 1
21
+ pane.active_pane = :bottom_right
22
+ end
23
+ end
24
+
25
+ def on_rows(range, opts = {style: nil})
26
+ Hook.new(range).tap do |hook|
27
+ hook.style = opts[:style]
28
+ @max_rows = hook.max if hook.max > @max_rows
29
+ @hooks << hook
30
+ end
31
+ end
32
+
33
+ alias on_row on_rows
34
+
35
+ def merge_array(arr1, arr2, offset)
36
+ offset.upto(arr2.length - 1 + offset).with_index do |offset_idx, idx|
37
+ arr1[offset_idx] = arr2[idx]
38
+ end
39
+ end
40
+
41
+ def build
42
+ 1.upto(@max_rows) do |row_num|
43
+ row, styles = [], []
44
+ @hooks.each do |hook|
45
+ if hook.match?(row_num)
46
+ cells = hook.run(row_num)
47
+ merge_array(row, cells.data, hook.offset)
48
+ merge_array(styles, cells.styles, hook.offset)
49
+ end
50
+ end
51
+ @sheet.add_row row, style: styles
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end