winexcel 0.0.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,37 @@
1
+ module CommonHelpers
2
+ def get_command_output
3
+ strip_color_codes(File.read(@stdout)).chomp
4
+ end
5
+
6
+ def strip_color_codes(text)
7
+ text.gsub(/\e\[\d+m/, '')
8
+ end
9
+
10
+ def in_tmp_folder(&block)
11
+ FileUtils.chdir(@tmp_root, &block)
12
+ end
13
+
14
+ def in_project_folder(&block)
15
+ project_folder = @active_project_folder || @tmp_root
16
+ FileUtils.chdir(project_folder, &block)
17
+ end
18
+
19
+ def in_home_folder(&block)
20
+ FileUtils.chdir(@home_path, &block)
21
+ end
22
+
23
+ def force_local_lib_override(project_name = @project_name)
24
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
25
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
26
+ f << "$:.unshift('#{@lib_path}')\n"
27
+ f << rakefile
28
+ end
29
+ end
30
+
31
+ def setup_active_project_folder project_name
32
+ @active_project_folder = File.join(@tmp_root, project_name)
33
+ @project_name = project_name
34
+ end
35
+ end
36
+
37
+ World(CommonHelpers)
@@ -0,0 +1,14 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + "/../../lib"))
2
+ require 'winexcel.rb'
3
+ require 'bundler/setup'
4
+
5
+ Before do
6
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
7
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
8
+ @lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
9
+ FileUtils.rm_rf @tmp_root
10
+ FileUtils.mkdir_p @home_path
11
+ ENV['HOME'] = @home_path
12
+ ENV['CUCUMBER_RUNNING'] = 'yes'
13
+ end
14
+
@@ -0,0 +1,9 @@
1
+ module Matchers
2
+ RSpec::Matchers.define :contain do |expected_text|
3
+ match do |text|
4
+ text.index expected_text
5
+ end
6
+ end
7
+ end
8
+
9
+ World(Matchers)
@@ -0,0 +1,12 @@
1
+ module WinExcel
2
+
3
+ require 'winexcel/core_ext/object'
4
+ require 'winexcel/core_ext/ordered_hash'
5
+ require 'fileutils'
6
+ require 'winexcel/fileutils_ext/create_dir_if_missing'
7
+ require 'winexcel/fileutils_ext/backup_file'
8
+
9
+ require 'winexcel/excel/xl_file_format'
10
+ require 'winexcel/version'
11
+ require 'winexcel/excel_file'
12
+ end
@@ -0,0 +1,34 @@
1
+ class Object
2
+ # Returns true if the object is nil or empty. It works on most popular objects
3
+ # like hashes, arrays and strings. Main purpose is to avoid double checking statements
4
+ # (and to eliminate bugs caused by single checking) like in code below:
5
+ # if some_obj == nil or some_obj.empty?
6
+ # # do something
7
+ # end
8
+ # Remarks: When this funcion is applied to a String, false is returned when
9
+ # the string contains white space characters only.
10
+ def unempty?
11
+ if [nil, [], {}].include?(self) or (self.kind_of?(String) and self.strip == '')
12
+ return false
13
+ else
14
+ return true
15
+ end
16
+ end
17
+
18
+ def in?(arr)
19
+ return true if arr.unempty? and arr.include?(self)
20
+ end
21
+
22
+ def has?(obj)
23
+ if [nil].include?(self)
24
+ return false
25
+ else
26
+ return self.include?(obj)
27
+ end
28
+ end
29
+
30
+ def eq?(obj)
31
+ return self.to_s.upcase == obj.to_s.upcase
32
+ end
33
+
34
+ end
@@ -0,0 +1,100 @@
1
+ module WinExcel
2
+ module CoreExt #:nodoc:
3
+
4
+ if RUBY_VERSION >= '1.9'
5
+ class OrderedHash < ::Hash
6
+ end
7
+ else
8
+ # This class is based on the Ruby 1.9 ordered hashes.
9
+ #
10
+ # It keeps the semantics and most of the efficiency of normal hashes
11
+ # while also keeping track of the order in which elements were set.
12
+ #
13
+ class OrderedHash #:nodoc:
14
+ include Enumerable
15
+
16
+ Node = Struct.new(:key, :value, :next, :prev)
17
+
18
+ def initialize
19
+ @hash = {}
20
+ end
21
+
22
+ def [](key)
23
+ @hash[key] && @hash[key].value
24
+ end
25
+
26
+ def []=(key, value)
27
+ if node = @hash[key]
28
+ node.value = value
29
+ else
30
+ node = Node.new(key, value)
31
+
32
+ if @first.nil?
33
+ @first = @last = node
34
+ else
35
+ node.prev = @last
36
+ @last.next = node
37
+ @last = node
38
+ end
39
+ end
40
+
41
+ @hash[key] = node
42
+ value
43
+ end
44
+
45
+ def delete(key)
46
+ if node = @hash[key]
47
+ prev_node = node.prev
48
+ next_node = node.next
49
+
50
+ next_node.prev = prev_node if next_node
51
+ prev_node.next = next_node if prev_node
52
+
53
+ @first = next_node if @first == node
54
+ @last = prev_node if @last == node
55
+
56
+ value = node.value
57
+ end
58
+
59
+ @hash.delete(key)
60
+ value
61
+ end
62
+
63
+ def keys
64
+ self.map { |k, v| k }
65
+ end
66
+
67
+ def values
68
+ self.map { |k, v| v }
69
+ end
70
+
71
+ def each
72
+ return unless @first
73
+ yield [@first.key, @first.value]
74
+ node = @first
75
+ yield [node.key, node.value] while node = node.next
76
+ self
77
+ end
78
+
79
+ def merge(other)
80
+ hash = self.class.new
81
+
82
+ self.each do |key, value|
83
+ hash[key] = value
84
+ end
85
+
86
+ other.each do |key, value|
87
+ hash[key] = value
88
+ end
89
+
90
+ hash
91
+ end
92
+
93
+ def empty?
94
+ @hash.empty?
95
+ end
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,74 @@
1
+ module WinExcel
2
+ module Excel
3
+ APPLICATION_NAME = 'Excel.Application'
4
+ LOAD_ALL_CONSTANTS = false # for performance reasons it's been disabled
5
+
6
+ #
7
+ # XlFileFormat is much more faster replacement for LOAD_ALL_CONSTANTS
8
+ #
9
+ module XlFileFormat
10
+ # Excel::XlFileFormat::
11
+
12
+ # Name = Value # Description
13
+ XlAddIn = 18 # Microsoft Excel 97-2003 Add-In
14
+ XlAddIn8 = 18 # Microsoft Excel 97-2003 Add-In
15
+ XlCSV = 6 # CSV
16
+ XlCSVMac = 22 # Macintosh CSV
17
+ XlCSVMSDOS = 24 # MSDOS CSV
18
+ XlCSVWindows = 23 # Windows CSV
19
+ XlCurrentPlatformText = -4158 # Current Platform Text
20
+ XlDBF2 = 7 # DBF2
21
+ XlDBF3 = 8 # DBF3
22
+ XlDBF4 = 11 # DBF4
23
+ XlDIF = 9 # DIF
24
+ XlExcel12 = 50 # Excel12
25
+ XlExcel2 = 16 # Excel2
26
+ XlExcel2FarEast = 27 # Excel2 FarEast
27
+ XlExcel3 = 29 # Excel3
28
+ XlExcel4 = 33 # Excel4
29
+ XlExcel4Workbook = 35 # Excel4 Workbook
30
+ XlExcel5 = 39 # Excel5
31
+ XlExcel7 = 39 # Excel7
32
+ XlExcel8 = 56 # Excel8
33
+ XlExcel9795 = 43 # Excel9795
34
+ XlHtml = 44 # HTML format
35
+ XlIntlAddIn = 26 # International Add-In
36
+ XlIntlMacro = 25 # International Macro
37
+ XlOpenDocumentSpreadsheet = 60 # OpenDocument Spreadsheet
38
+ XlOpenXMLAddIn = 55 # Open XML Add-In
39
+ XlOpenXMLTemplate = 54 # Open XML Template
40
+ XlOpenXMLTemplateMacroEnabled = 53 # Open XML Template Macro Enabled
41
+ XlOpenXMLWorkbook = 51 # Open XML Workbook
42
+ XlOpenXMLWorkbookMacroEnabled = 52 # Open XML Workbook Macro Enabled
43
+ XlSYLK = 2 # SYLK
44
+ XlTemplate = 17 # Template
45
+ XlTemplate8 = 17 # Template 8
46
+ XlTextMac = 19 # Macintosh Text
47
+ XlTextMSDOS = 21 # MSDOS Text
48
+ XlTextPrinter = 36 # Printer Text
49
+ XlTextWindows = 20 # Windows Text
50
+ XlUnicodeText = 42 # Unicode Text
51
+ XlWebArchive = 45 # Web Archive
52
+ XlWJ2WD1 = 14 # WJ2WD1
53
+ XlWJ3 = 40 # WJ3
54
+ XlWJ3FJ3 = 41 # WJ3FJ3
55
+ XlWK1 = 5 # WK1
56
+ XlWK1ALL = 31 # WK1ALL
57
+ XlWK1FMT = 30 # WK1FMT
58
+ XlWK3 = 15 # WK3
59
+ XlWK3FM3 = 32 # WK3FM3
60
+ XlWK4 = 38 # WK4
61
+ XlWKS = 4 # Worksheet
62
+ XlWorkbookDefault = 51 # Workbook default
63
+ XlWorkbookNormal = -4143 # Workbook normal
64
+ XlWorks2FarEast = 28 # Works2 FarEast
65
+ XlWQ1 = 34 # WQ1
66
+ XlXMLSpreadsheet = 46 # XML Spreadsheet
67
+ end
68
+
69
+ # not yet implemented
70
+ RunModes = {}
71
+ RunModes[:open_new_in_isolated_excel] = :open_new_in_isolated_excel
72
+ RunModes[:find_in_existing_not_isolated_workbooks] = :find_in_existing_not_isolated_workbooks
73
+ end
74
+ end
@@ -0,0 +1,255 @@
1
+ # excel_file.rb
2
+ #
3
+ # File based on Xls.rb being part of 'wwatf' project
4
+ # Copyright (C) 2010-2011 Sobieraj Kamil <ksob@dslowl.com>.
5
+ #
6
+ # This file is published under New BSD License
7
+ # You can redistribute and/or
8
+ # modify it under the terms of the New BSD License.
9
+ #
10
+ # New BSD License claims:
11
+ # Redistribution and use in source and binary forms, with or without
12
+ # modification, are permitted provided that the following conditions
13
+ # are met:
14
+ #
15
+ # 1. Redistributions of source code must retain the above copyright notice,
16
+ # this list of conditions and the following disclaimer.
17
+ #
18
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
19
+ # this list of conditions and the following disclaimer in the documentation
20
+ # and/or other materials provided with the distribution.
21
+ #
22
+ # 3. Neither the name of Zend Technologies USA, Inc. nor the names of its
23
+ # contributors may be used to endorse or promote products derived from this
24
+ # software without specific prior written permission.
25
+ #
26
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+
37
+
38
+ require 'logger'
39
+ require 'win32ole'
40
+
41
+ require 'rubygems'
42
+ if RUBY_VERSION >= '1.9'
43
+ require 'win32olerot'
44
+ else
45
+ require 'winexcel/win32olerot_ext/win32olerot'
46
+ end
47
+
48
+ require 'winexcel/excel_file/write_2D_array'
49
+ require 'winexcel/excel_file/common_methods'
50
+ require 'winexcel/excel_file/other_methods'
51
+
52
+ # Excel interface class.
53
+ # This class provides many simple methods for using Excel spreadsheets.
54
+ #
55
+ # Inner working:
56
+ # If the File is already open in excel, data is read from the open file
57
+ # and left open after the call to the close/finalize method.
58
+ # If the file is not open, It will be opened in the background
59
+ # and closed when the close/finalize method is called.
60
+ #
61
+ # For examples look at the examples folder as well as Cucumber/RSpec
62
+ # files in features and spec directories
63
+ #
64
+ # Information for developers:
65
+ # It is helpful to use COM/Win32OLE tracking tool like "oakland ActiveX Inspector"
66
+ # to clearly see what Excel instances are beeing created
67
+ #
68
+ module WinExcel
69
+
70
+ class ExcelFile
71
+ # Singleton Class to store Excel Constant Variables
72
+ class ExcelConst;
73
+ end
74
+
75
+ attr_accessor :excel
76
+ attr_accessor :workbook
77
+ attr_accessor :records
78
+ attr_accessor :fileName
79
+
80
+ @@automationInstance = nil
81
+
82
+ XlsAutomationSecurity = 3 # msoAutomationSecurityForceDisable
83
+ XlsDisplayAlerts = false
84
+ XlsVisible = false
85
+ XlsScreenUpdating = false
86
+ XlsInteractive = false
87
+
88
+ def setSettings excel
89
+ excel.Application.AutomationSecurity = XlsAutomationSecurity
90
+ excel.DisplayAlerts = XlsDisplayAlerts
91
+ excel.Visible = XlsVisible
92
+ excel.ScreenUpdating = XlsScreenUpdating
93
+ excel.Interactive = XlsInteractive
94
+ end
95
+
96
+ #
97
+ # initilize creates an ExcelFile instance for a given .xls file.
98
+ # If the File is already open in excel, data is read from the open file and left open after the call to the close/finalize method.
99
+ # If the file is not open, It will be opened in the background and closed when the close/finalize method is called.
100
+ #
101
+ # For examples look at the end of the file as well as Cucumber/RSpec files
102
+ #
103
+ # Information for developers:
104
+ # It is helpful to use Win32OLE tracking tool like "oakland ActiveX Inspector"
105
+ # to clearly see what Excel instances are beeing created
106
+ #
107
+ def initialize(file, debug = false, template = nil)
108
+ @excelOpen=false
109
+ @connectedToOpenWorkBook=false
110
+ @log = Logger.new(STDERR)
111
+ @log.level = Logger::WARN
112
+ @log.level = Logger::DEBUG if debug
113
+
114
+ if not @@automationInstance
115
+ ExcelFile.killExcelAutomationProcesses
116
+ end
117
+
118
+ connectToOpenWorkbook = lambda {
119
+ # open existing file in a traditional way connecting to running Excel instance
120
+ workbookObj = WIN32OLE.connect(file)
121
+ @excel = workbookObj.Application
122
+ @excelOpen = true
123
+ basename = File.basename(file)
124
+ @workbook = @excel.Workbooks(basename)
125
+ @connectedToOpenWorkBook=true
126
+ @log.info(self.class) { "Attached to open Workbook: #{basename}" }
127
+ }
128
+
129
+ openAutomationInstance = lambda {
130
+ if not @@automationInstance
131
+ @@automationInstance = WIN32OLE::new(Excel::APPLICATION_NAME)
132
+ setSettings @@automationInstance
133
+ @log.info(self.class) { "Excel Automation Instance Created" }
134
+ end
135
+ @excel = @@automationInstance
136
+ }
137
+
138
+ makeFileFromTemplateAndOpen = lambda {
139
+ # copy the template on place of the file (that's been just moved)
140
+ openAutomationInstance.call
141
+ @workbook = @@automationInstance.Workbooks.Add(template)
142
+ @workbook.SaveAs(file.gsub("/", "\\"), Excel::XlFileFormat::XlWorkbookNormal)
143
+ @log.info(self.class) { "File: '#{file}' created from template '#{template}'." }
144
+ }
145
+
146
+ openFileInAutomationInstance = lambda {
147
+ # create isolated Excel and open that workbook/file inside of it
148
+ openAutomationInstance.call
149
+ @excelOpen = true
150
+ begin
151
+ basename = File.basename(file)
152
+ @workbook = @excel.Workbooks(basename)
153
+ @log.info(self.class) { "Attached to open Workbook: '#{basename}'." }
154
+ rescue
155
+ if File.exists?(file)
156
+ @workbook = @excel.Workbooks.Open(file)
157
+ @log.info(self.class) { "File: '#{file}' opened." }
158
+ else
159
+ raise "File: '#{file}' does not exist."
160
+ end
161
+ end
162
+ }
163
+
164
+ # check in ROT if workbook is open if so use WIN32OLE::connect(file) otherwise open in isolation
165
+ rot = WIN32OLE::RunningObjectTable.new
166
+ if rot.nil? then
167
+ throw 'Cannot access WIN32OLE::RunningObjectTable'
168
+ end
169
+ begin
170
+ isFileRunning = rot.is_running?(file)
171
+ if isFileRunning and template
172
+ connectToOpenWorkbook.call
173
+
174
+ # save file, close it and move to backup
175
+ save
176
+ sleep 1
177
+ @workbook.close
178
+ while rot.is_running?(file) do
179
+ sleep 1
180
+ end
181
+ @excel.quit
182
+ @excel = nil
183
+ GC.start
184
+ FileUtils.moveFileToBackupDir file
185
+ @log.info(self.class) { "File: #{file} successfully saved, closed and moved to BACKUP folder." }
186
+
187
+ makeFileFromTemplateAndOpen.call
188
+ elsif isFileRunning and not template
189
+ connectToOpenWorkbook.call
190
+ elsif not isFileRunning and template
191
+ makeFileFromTemplateAndOpen.call
192
+ elsif not isFileRunning and not template
193
+ openFileInAutomationInstance.call
194
+ end
195
+ rescue
196
+ @log.error(self.class) { "Error attaching: wasFileOpenedByUser: #{@connectedToOpenWorkBook}, file: #{file}, error: #{$!}" }
197
+ raise
198
+ end
199
+
200
+ @records = {}
201
+
202
+ if Excel::LOAD_ALL_CONSTANTS
203
+ # Get the standard set of Excel constants
204
+ WIN32OLE.const_load(@excel, ExcelConst) if ExcelConst.constants == []
205
+ end
206
+ end
207
+
208
+ def self.killExcelAutomationProcesses
209
+ begin
210
+ wmi = WIN32OLE.connect("winmgmts://")
211
+ processes = wmi.ExecQuery("select * from win32_process where commandline like '%excel.exe\"% /automation %'")
212
+ processes.each do |process|
213
+ Process.kill('KILL', process.ProcessID)
214
+ end
215
+ rescue
216
+ end
217
+ end
218
+
219
+ # not used
220
+ # do not work yet
221
+ def self.killInvisibleExcelProcesses
222
+ 2.times do
223
+ excelObj = WIN32OLE::connect(Excel::APPLICATION_NAME)
224
+ if not excelObj.Visible
225
+ finalize(excelObj)
226
+ end
227
+ end
228
+ end
229
+
230
+ def self.finalize()
231
+ if not @@automationInstance.nil?
232
+ begin
233
+ @@automationInstance.Workbooks.each do |wb|
234
+ begin
235
+ wb.Close(false)
236
+ rescue
237
+ end
238
+ wb = nil
239
+ end
240
+ GC.start
241
+ sleep(3)
242
+ rescue
243
+ end
244
+ @@automationInstance.quit
245
+ sleep 1
246
+ @@automationInstance = nil
247
+ GC.start
248
+ sleep(3)
249
+ end
250
+ end
251
+
252
+
253
+ end
254
+
255
+ end