winexcel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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