spreadsheetx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.3.0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.2"
12
+ gem "rcov", ">= 0"
13
+ gem "zipruby", "~> 0.3.6"
14
+ end
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.6.3)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.9.2)
11
+ rcov (0.9.9)
12
+ rspec (2.3.0)
13
+ rspec-core (~> 2.3.0)
14
+ rspec-expectations (~> 2.3.0)
15
+ rspec-mocks (~> 2.3.0)
16
+ rspec-core (2.3.1)
17
+ rspec-expectations (2.3.0)
18
+ diff-lcs (~> 1.1.2)
19
+ rspec-mocks (2.3.0)
20
+ zipruby (0.3.6)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (~> 1.0.0)
27
+ jeweler (~> 1.6.2)
28
+ rcov
29
+ rspec (~> 2.3.0)
30
+ zipruby (~> 0.3.6)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Craig Ulliott
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,27 @@
1
+ = spreadsheetx
2
+
3
+ This gem facilitates a templatized approach to working with Excel files. It only supports Microsoft's new .xlsx format (because it's xml based and somewhat sane to work with).
4
+
5
+ We use this gem to generate reports for clients, where the reports have substantial charts and styling throughout.
6
+
7
+ The work flow goes a little something like this:
8
+ * Create a report in Excel that has all your charts and styling
9
+ * Save the report with placeholder data
10
+ * Programmatically replace or add rows and cells
11
+ * When the new file is opened, formatting and charts are preserved
12
+
13
+ == Contributing to spreadsheetx
14
+
15
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
16
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
17
+ * Fork the project
18
+ * Start a feature/bugfix branch
19
+ * Commit and push until you are happy with your contribution
20
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
21
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
22
+
23
+ == Copyright
24
+
25
+ Copyright (c) 2011 Craig Ulliott. See LICENSE.txt for
26
+ further details.
27
+
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "spreadsheetx"
18
+ gem.homepage = "http://github.com/craigulliott/spreadsheetx"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Facilitates opening and modifying existing xlsx excel spreadsheets}
21
+ gem.description = %Q{Using an existing xlsx file as a template, it allows you to modify cell values and add rows and columns. Facilitating a templateized approach to creating a new xlsx spreadsheet}
22
+ gem.email = "craigulliott@gmail.com"
23
+ gem.authors = ["Craig Ulliott"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "spreadsheetx #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,21 @@
1
+ # zipruby is nice as it can modify an existing zip file, perfect for our usecase
2
+ require 'zipruby'
3
+ # we use this because it comes with ruby
4
+ require 'rexml/document'
5
+ # for copying files
6
+ require 'fileutils'
7
+ #
8
+ require 'spreadsheetx/workbook'
9
+ require 'spreadsheetx/worksheet'
10
+
11
+ module SpreadsheetX
12
+
13
+ class << self
14
+
15
+ def open(path)
16
+ SpreadsheetX::Workbook.new(path)
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,55 @@
1
+ module SpreadsheetX
2
+
3
+ # This class represents an XLSX Document on disk
4
+ class Workbook
5
+
6
+ attr_reader :path
7
+ attr_reader :worksheets
8
+
9
+ # return a Workbook object which relates to an existing xlsx file on disk
10
+ def initialize(path)
11
+ @path = path
12
+ Zip::Archive.open(path) do |archive|
13
+
14
+ # open the workbook
15
+ archive.fopen('xl/workbook.xml') do |f|
16
+
17
+ # read contents of this file
18
+ file_contents = f.read
19
+
20
+ #parse the XML and build the worksheets
21
+ @worksheets = []
22
+ REXML::Document.new(file_contents).elements.each('workbook/sheets/sheet') do |node|
23
+ sheet_id = node.attributes['sheetId'].to_i
24
+ r_id = node.attributes['r:id'].gsub('rId','').to_i
25
+ name = node.attributes['name'].to_s
26
+ @worksheets.push SpreadsheetX::Worksheet.new(archive, sheet_id, r_id, name)
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+
34
+ # saves the binary form of the complete xlsx file to a new xlsx file
35
+ def save(destination_path)
36
+
37
+ # copy the xlsx file to the destination
38
+ FileUtils.cp(@path, destination_path)
39
+
40
+ # replace the xlsx files with the new workbooks
41
+ Zip::Archive.open(destination_path) do |ar|
42
+
43
+ # replace with the new worksheets
44
+ @worksheets.each do |worksheet|
45
+ ar.replace_buffer("xl/worksheets/sheet#{worksheet.r_id}.xml", worksheet.to_s)
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,88 @@
1
+ module SpreadsheetX
2
+
3
+ # Workbooks are made up of N Worksheets, this class represents a specific Worksheet.
4
+ class Worksheet
5
+
6
+ attr_reader :sheet_id
7
+ attr_reader :r_id
8
+ attr_reader :name
9
+
10
+ # return a Worksheet object which relates to a specific Worksheet
11
+ def initialize(archive, sheet_id, r_id, name)
12
+ @sheet_id = sheet_id
13
+ @r_id = r_id
14
+ @name = name
15
+
16
+ # open the workbook
17
+ archive.fopen("xl/worksheets/sheet#{@r_id}.xml") do |f|
18
+
19
+ # read contents of this file
20
+ file_contents = f.read
21
+ #parse the XML and hold the doc
22
+ @xml_doc = REXML::Document.new(file_contents)
23
+
24
+ end
25
+
26
+ end
27
+
28
+ # update the value of a particular cell, if the row or cell doesnt exist in the XML, then it will be created
29
+ def update_cell(col_number, row_number, val)
30
+
31
+ cell_id = SpreadsheetX::Worksheet.cell_id(col_number, row_number)
32
+
33
+ rows = @xml_doc.get_elements("worksheet/sheetData/row[@r=#{row_number}]")
34
+ # was this row found
35
+ if rows.empty?
36
+ # build a new row
37
+ row = @xml_doc.elements['worksheet'].elements['sheetData'].add_element('row', {'r' => row_number})
38
+ else
39
+ # x path returns an array, but we know there is only one row with this number
40
+ row = rows.first
41
+ end
42
+
43
+ cells = row.get_elements("c[@r='#{cell_id}']")
44
+ if cells.empty?
45
+ cell = row.add_element('c', {'r' => cell_id})
46
+ else
47
+ # x path returns an array, but we know there is only one row with this number
48
+ cell = cells.first
49
+ end
50
+
51
+ # first clear out any existing values
52
+ cell.delete_element('*')
53
+
54
+ # now we put the value in the cell
55
+ if val.kind_of? String
56
+ cell.attributes['t'] = 'inlineStr'
57
+ cell.add_element('is').add_element('t').add_text(val)
58
+ else
59
+ cell.attributes['t'] = nil
60
+ cell.add_element('v').add_text(val.to_s)
61
+ end
62
+
63
+ end
64
+
65
+ # the number of rows containing data this sheet has
66
+ # NOTE: this is the count of those rows, not the length of the document
67
+ def row_count
68
+ count = 0
69
+ @xml_doc.elements.each('worksheet/sheetData/row'){ count+=1 }
70
+ count
71
+ end
72
+
73
+ # returns the xml representation of this worksheet
74
+ def to_s
75
+ @xml_doc.to_s
76
+ end
77
+
78
+ # turns a cell address into its excel name, 1,1 = A1 2,3 = C2 etc.
79
+ def self.cell_id(col_number, row_number)
80
+ letter = 'A'
81
+ # some day, speed this up
82
+ (col_number.to_i-1).times{letter = letter.succ}
83
+ "#{letter}#{row_number}"
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'spreadsheetx'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Spreadsheetx" do
4
+
5
+ it "opens xlsx files successfully" do
6
+
7
+ # a valid xlsx file used for testing
8
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
9
+ workbook = SpreadsheetX.open(empty_xlsx_file)
10
+
11
+ end
12
+
13
+ it "allow accessing worksheets" do
14
+
15
+ # a valid xlsx file used for testing
16
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
17
+ workbook = SpreadsheetX.open(empty_xlsx_file)
18
+
19
+ workbook.worksheets.length.should == 2
20
+ workbook.worksheets.last.name.should == 'Test'
21
+
22
+ end
23
+
24
+ it "allow accessing row counts" do
25
+
26
+ # a valid xlsx file used for testing
27
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
28
+ workbook = SpreadsheetX.open(empty_xlsx_file)
29
+
30
+ workbook.worksheets.last.row_count.should == 4
31
+
32
+ end
33
+
34
+ it "can be saved" do
35
+
36
+ # a valid xlsx file used for testing
37
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
38
+ workbook = SpreadsheetX.open(empty_xlsx_file)
39
+
40
+ new_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec_out.xlsx"
41
+ workbook.save(new_xlsx_file)
42
+
43
+ end
44
+
45
+ it "can convert an address of a cell to a cell name" do
46
+
47
+ SpreadsheetX::Worksheet.cell_id(1, 1).should == 'A1'
48
+ SpreadsheetX::Worksheet.cell_id(2, 1).should == 'B1'
49
+ SpreadsheetX::Worksheet.cell_id(27, 9).should == 'AA9'
50
+ SpreadsheetX::Worksheet.cell_id(26, 4).should == 'Z4'
51
+ SpreadsheetX::Worksheet.cell_id(820, 496).should == 'AEN496'
52
+
53
+
54
+ end
55
+
56
+ it "allows cell values to be updated" do
57
+
58
+ # a valid xlsx file used for testing
59
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
60
+ workbook = SpreadsheetX.open(empty_xlsx_file)
61
+
62
+ workbook.worksheets.last.update_cell(1, 1, 9)
63
+ workbook.worksheets.last.update_cell(1, 2, 'A')
64
+ workbook.worksheets.last.update_cell(1, 3, nil)
65
+
66
+ new_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec_changed_out.xlsx"
67
+ workbook.save(new_xlsx_file)
68
+
69
+ end
70
+
71
+ it "allows cells to be added" do
72
+
73
+ # a valid xlsx file used for testing
74
+ empty_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec.xlsx"
75
+ workbook = SpreadsheetX.open(empty_xlsx_file)
76
+
77
+ workbook.worksheets.last.update_cell(9, 9, 9)
78
+ workbook.worksheets.last.update_cell(9, 10, 'A')
79
+
80
+ new_xlsx_file = "#{File.dirname(__FILE__)}/../templates/spec_added_out.xlsx"
81
+ workbook.save(new_xlsx_file)
82
+
83
+ end
84
+
85
+ end
Binary file
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreadsheetx
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Craig Ulliott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-02 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.0
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: jeweler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.2
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rcov
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: zipruby
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 0.3.6
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ description: Using an existing xlsx file as a template, it allows you to modify cell values and add rows and columns. Facilitating a templateized approach to creating a new xlsx spreadsheet
72
+ email: craigulliott@gmail.com
73
+ executables: []
74
+
75
+ extensions: []
76
+
77
+ extra_rdoc_files:
78
+ - LICENSE.txt
79
+ - README.rdoc
80
+ files:
81
+ - .document
82
+ - .rspec
83
+ - Gemfile
84
+ - Gemfile.lock
85
+ - LICENSE.txt
86
+ - README.rdoc
87
+ - Rakefile
88
+ - VERSION
89
+ - lib/spreadsheetx.rb
90
+ - lib/spreadsheetx/workbook.rb
91
+ - lib/spreadsheetx/worksheet.rb
92
+ - spec/spec_helper.rb
93
+ - spec/spreadsheetx_spec.rb
94
+ - templates/spec.xlsx
95
+ has_rdoc: true
96
+ homepage: http://github.com/craigulliott/spreadsheetx
97
+ licenses:
98
+ - MIT
99
+ post_install_message:
100
+ rdoc_options: []
101
+
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: -715639084265841052
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project:
122
+ rubygems_version: 1.6.2
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: Facilitates opening and modifying existing xlsx excel spreadsheets
126
+ test_files: []
127
+