cowtech-lib 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,374 @@
1
+ # encoding: utf-8
2
+ #
3
+ # cowtech-lib
4
+ # Author: Shogun <shogun_panda@me.com>
5
+ # Copyright © 2011 and above Shogun
6
+ # Released under the MIT License, which follows.
7
+ #
8
+ # The MIT License
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in
17
+ # all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ # THE SOFTWARE.
26
+ #
27
+
28
+ require "rexml/document"
29
+ require "open4"
30
+ require "find"
31
+ require "fileutils"
32
+
33
+ module CowtechLib
34
+ module Lib
35
+ # A class which provides some useful method for interaction with files and processes.
36
+ # @author Shogun
37
+ class Shell
38
+ # Run a command.
39
+ #
40
+ # Arguments:
41
+ # * <em>msg</em>: The message to show
42
+ # * <em>cmd</em>: The command to run
43
+ # * <em>show_msg</em>: If show the exit message
44
+ # * <em>show_exit</em>: If show the exit code
45
+ # * <em>fatal</em>: If abort on failure
46
+ #
47
+ # Returns: An object which the status attribute is the exit status of the process, and the output attribute is the output of the command
48
+ def run(msg, cmd, show_msg = true, show_exit = true, fatal = false)
49
+ rv = {:status => 0, :output => []}
50
+ command = args[:command]
51
+
52
+ @console.write(:begin => args[:msg]) if args[:msg]
53
+
54
+ if @console.show_commands then
55
+ @console.warn("Will run command: \"#{command}\"", :dots => false)
56
+ @command.status(:ok)
57
+ end
58
+
59
+ unless @console.skip_commands == true then
60
+ rv[:status] = Open4::open4(cmd + " 2>&1") { |pid, stdin, stdout, stderr|
61
+ stdout.each_line do |line|
62
+ rv[:output] << line
63
+ print line if @console.show_outputs
64
+ end
65
+ }.exitstatus
66
+ end
67
+ rv[:output] = rv[:output].join("\n")
68
+
69
+ self.status(rv[:status] == 0 ? :ok : :fail) if args[:show_exit]
70
+ rv
71
+ end
72
+
73
+ # Opens a file.
74
+ #
75
+ # Arguments:
76
+ # * <em>name</em>: The file path
77
+ # * <em>mode</em>: Open mode, like the one in File.new
78
+ # * <em>codec</em>: The encoding used to open the file. <b>UNUSED FOR NOW!</b>
79
+ #
80
+ # Returns: A new <em>File</em> object
81
+ def open_file(*args)
82
+ begin
83
+ File.new(args[:name], args[:mode] || "r" + (args[:codec] : ? ":#{args[:codce]}" : ""))
84
+ rescue Exception => e
85
+ @console.write(:error => "Unable to open file #{name}: #{e}")
86
+ nil
87
+ end
88
+ end
89
+
90
+ # Perform a check on a file or directory.
91
+ #
92
+ # Arguments:
93
+ # * <em>name</em>: The file/directory path
94
+ # * <em>tests</em>: A list of tests to execute
95
+ #
96
+ # Valid tests are:
97
+ #
98
+ # * <em>:fc_exists</em>: Check if the file exists
99
+ # * <em>:fc_read</em>: Check if the file is readable
100
+ # * <em>:fc_write</em>: Check if the file writeable
101
+ # * <em>:fc_exec</em>: Check if the file executable
102
+ # * <em>:fc_dir</em>: Check if the file is a directory
103
+ # * <em>:fc_symlink</em>: Check if the file is symbolic link
104
+ #
105
+ # Returns: <em>true</em> if any of tests had success, <em>false</em> otherwise
106
+ def file_check?(*args)
107
+ rv = false
108
+ tests = args[:tests].to_a
109
+
110
+ if args[:file] then
111
+ rv = true
112
+ tests.each do |test|
113
+ rv = rv && FileTest.try((test.to_s + "?").to_sym, args[:file])
114
+ end
115
+ end
116
+
117
+ rv
118
+ end
119
+
120
+ # Delete files/directories.
121
+ #
122
+ # Arguments:
123
+ # * <em>files</em>: List of entries to delete
124
+ # * <em>exit_on_fail</em>: Whether abort on failure
125
+ # * <em>show_error</em>: Whether show errors occurred
126
+ #
127
+ # Returns: <em>true</em> if operation had success, <em>false</em> otherwise.
128
+ def delete_files!(*args)
129
+ rv = true
130
+ files = args[:files].to_a
131
+
132
+ begin
133
+ FileUtils.rm_r(files, {:noop => @console.skip_commands, :verbose => @console.skip_commands, :secure => true})
134
+ rescue StandardError => e
135
+ if args[:show_errors] == true then
136
+ if e.message =~ /^Permission denied - (.+)/ then
137
+ @console.error("Cannot remove following non writable entry: #{$1}", :dots => false, :fatal => args[:fatal])
138
+ elsif e.message =~ /^No such file or directory - (.+)/) then
139
+ @console.error("Cannot remove following non existent entry: #{$1}", :dots => false, :fatal => args[:fatal])
140
+ end
141
+ end
142
+
143
+ rv = false
144
+ rescue Exception => e
145
+ if args[:show_error] == true then
146
+ @console.error("Cannot remove following entries:", :dots => false)
147
+ @console.indent_region(3) do
148
+ files.each do |afile|
149
+ @console.write(:msg => afile, :dots => false)
150
+ end
151
+ end
152
+ @console.write(:msg => "#{@console.indentator * @console.indent_level}due to an error: #{e}\n", :dots => false, :fatal => args[:fatal])
153
+ end
154
+
155
+ rv = false
156
+ end
157
+
158
+ rv ? rv : self.status(:fail, :fatal => args[:fatal])
159
+ end
160
+
161
+ # Create directories (and any missing parent directory).
162
+ #
163
+ # Arguments:
164
+ # * <em>files</em>: List of directories to create
165
+ # * <em>mode</em>: Octal mode for newly created directories
166
+ # * <em>exit_on_fail</em>: Whether abort on failure
167
+ # * <em>show_error</em>: Whether show errors occurred
168
+ #
169
+ # Returns: <em>true</em> if operation had success, <em>false</em> otherwise.
170
+ def create_directories(*args)
171
+ rv = true
172
+ files = args[:files].to_a
173
+
174
+ files.each do |file|
175
+ if self.file_check?(:files => file, :tests => :exists) then
176
+ if self.file_check?(:files => file, :tests => :directory) == true then
177
+ @console.error("Cannot create following directory <text style=\"bold white\">#{file}</text> because it already exists.", :dots => false, :fatal => args[:fatal])
178
+ else
179
+ @console.error("Cannot create following directory <text style=\"bold white\">#{file}</text> because it already exists as a file", :dots => false, :fatal => args[:fatal])
180
+ end
181
+
182
+ rv = false
183
+ end
184
+
185
+ if rv then
186
+ begin
187
+ FileUtils.makedirs(file, {:mode => args[:mode] || 0755, :noop => @console.skip_commands, :verbose => @console.skip_commands})
188
+ rescue StandardError => e
189
+ if args[:show_errors] && e.message =~ /^Permission denied - (.+)/ then
190
+ @console.error("Cannot create following directory in non writable parent: <text style=\"bold white\">#{$1}</text>.", :dots => false, :fatal => args[:fatal])
191
+ end
192
+
193
+ rv = false
194
+ rescue Exception => e
195
+ if args[:show_error] == true then
196
+ @console.error("Cannot create following directory:", :dots => false)
197
+ @console.indent_region(3) do
198
+ files.each do |afile|
199
+ @console.write(:msg => afile, :dots => false)
200
+ end
201
+ end
202
+ @console.write("#{@console.indentator * @console.indent_level}due to an error: #{e}\n", :dots => false, :fatal => args[:fatal])
203
+ end
204
+
205
+ rv = false
206
+ end
207
+ end
208
+
209
+ break unless rv
210
+ end
211
+
212
+ rv ? rv : self.status(:fail, :fatal => args[:fatal])
213
+ end
214
+
215
+ # Copy files to a destination directory.
216
+ #
217
+ # Arguments:
218
+ # * <em>files</em>: List of entries to copy/move
219
+ # * <em>dest</em>: Destination file/directory
220
+ # * <em>must_move</em>: Move instead of copy
221
+ # * <em>dest_is_dir</em>: Whether destination is a directory
222
+ # * <em>exit_on_fail</em>: Whether abort on failure
223
+ # * <em>show_error</em>: Whether show errors occurred
224
+ #
225
+ # Returns: <em>true</em> if operation had success, <em>false</em> otherwise.
226
+ def copy(*args)
227
+ rv = true
228
+ move = args[:move]
229
+
230
+ # If we are copy or moving to a directory
231
+ if args[:destination_is_directory] then
232
+ files = (args[:files] || []).to_a
233
+ dest = args[:dest]
234
+ dest += "/" if self.file_check?(dest, :directory) and dest !~ /\/$/
235
+
236
+ files.each do |file|
237
+ begin
238
+ if move == true then
239
+ FileUtils.move(file, dest, {:noop => @console.skip_commands, :verbose => @console.skip_commands})
240
+ else
241
+ FileUtils.cp_r(file, dest, {:noop => @console.skip_commands, :verbose => @console.skip_commands, :remove_destination => true})
242
+ end
243
+ rescue StandardError => e
244
+ if args[:show_errors] && e.message =~ /^Permission denied - (.+)/ then
245
+ self.error("Cannot #{if must_move then "move" else "copy" end} entry <text style=\"bold white\">#{file}</text> to non-writable entry <text style=\"bold white\">#{dest}</text>", false, false, false) if m != nil
246
+ end
247
+
248
+ rv = false
249
+ rescue Exception => e
250
+ if args[:show_errors] then
251
+ self.error(, false)
252
+ @console.error("Cannot #{if move then "move" else "copy" end} following entries to <text style=\"bold white\">#{dest}</text>:", :dots => false)
253
+ @console.indent_region(3) do
254
+ files.each do |afile|
255
+ @console.write(:msg => afile, :dots => false)
256
+ end
257
+ end
258
+ @console.write("#{@console.indentator * @console.indent_level}due to an error: #{e}\n", :dots => false, :fatal => args[:fatal])
259
+ end
260
+
261
+ rv = false
262
+ end
263
+
264
+ break unless rv
265
+ end
266
+ else # If we are copying or moving to a file
267
+ unless files.kind_of?(String) == true and dest.kind_of?(String) == true then
268
+ self.error("Cowtech::Lib::Shell#copy: To copy a single file, both files and dest arguments must be a string.", :dots => false, :fatal => args[:fatal])
269
+ rv = false
270
+ else
271
+ dst_dir = File.dirname(dest)
272
+
273
+ unless self.file_check?(:file => dst_dir, :tests => [:exists, :directory]) then
274
+ self.create_directories(:files => dst_dir, :mode => 0755, :fatal => args[:fatal], :show_errors => args[:show_errors])
275
+ end
276
+
277
+ begin
278
+ if move == true then
279
+ FileUtils.move(files, dest, {:noop => @console.skip_commands, :verbose => @console.skip_commands})
280
+ else
281
+ FileUtils.cp(files, dest, {:noop => @console.skip_commands, :verbose => @console.skip_commands})
282
+ end
283
+ rescue StandardError => e
284
+ self.error("Cannot #{if move then "move" else "copy" end} entry <text style=\"bold white\">#{files}</text> to non-writable entry<text style=\"bold white\"> #{dest}</text>", :dots => false, :fatal => args[:fatal]) if args[:show_errors] && (e.message =~ /^Permission denied - (.+)/)
285
+ rv = false
286
+ rescue Exception => e
287
+ self.error("Cannot #{if move then "move" else "copy" end} <text style=\"bold white\">#{files}</text> to <text style=\"bold_white\">#{dest}</text> due to an error: <text style=\"bold red\">#{e}</text>", :dots => false, :fatal => args[:fatal]) if args[:show_errors]
288
+ rv = false
289
+ end
290
+ end
291
+ end
292
+
293
+ rv ? rv : self.status(:fail, :fatal => args[:fatal])
294
+ end
295
+
296
+ # Rename a file.
297
+ #
298
+ # Arguments:
299
+ # * <em>src</em>: The file to rename
300
+ # * <em>dst</em>: The new name
301
+ # * <em>exit_on_fail</em>: Whether abort on failure
302
+ # * <em>show_error</em>: Whether show errors occurred
303
+ #
304
+ # Returns: <em>true</em> if operation had success, <em>false</em> otherwise.
305
+ def rename(*args)
306
+ rv = true
307
+
308
+ if src.is_a?(String) and dst.is_a?(String) then
309
+ rv = self.copy(:from => src, :to => dst, :show_errors => args[:show_errors], :fatal => args[:fatal])
310
+ else
311
+ @console.error("Cowtech::Lib::Shell#rename: Both :src and :dst arguments must be a string.", :dots => false, :fatal => args[:fatal]) if args[:show_error]
312
+ rv = false
313
+ end
314
+
315
+ rv ? rv : self.status(:fail, :fatal => args[:fatal])
316
+ end
317
+
318
+ # Returns a list of files in specified paths which matchs one of requested patterns.
319
+ #
320
+ # Arguments:
321
+ # * <em>paths</em>: List of path in which seach
322
+ # * <em>patterns</em>: List of requested patterns
323
+ #
324
+ # Returns: List of found files.
325
+ def find_by_pattern(*args)
326
+ # TODO: E se patterns è vuoto?
327
+ rv = []
328
+
329
+ paths = args[:paths].to_a
330
+ string_patterns = args[:patterns].to_a
331
+
332
+ if paths.length > 0 then
333
+ # Convert patterns to regexp
334
+ patterns = string_patterns.collect do |pattern|
335
+ pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern, Regexp::IGNORECASE)
336
+ end
337
+
338
+ # For each path
339
+ paths.each do |path|
340
+ Find.find(path) do |file| # Find files
341
+ matchs = false
342
+
343
+ patterns.each do |pattern| # Match patterns
344
+ if file =~ pattern then
345
+ matchs = true
346
+ break
347
+ end
348
+ end
349
+
350
+ rv << file if matchs
351
+ end
352
+ end
353
+
354
+ rv
355
+ end
356
+ end
357
+
358
+ # Returns a list of files in specified paths which have one of requested extensions.
359
+ #
360
+ # Arguments:
361
+ # * <em>paths</em>: List of path in which seach
362
+ # * <em>extensions</em>: List of requested extensions
363
+ #
364
+ # Returns: List of found files.
365
+ def find_by_extension(*args)
366
+ args[:patterns] = (args[:extensions] || "").to_a.collect do |extension|
367
+ Regexp.new(extension + "$", Regexp::IGNORECASE)
368
+ end
369
+
370
+ self.find_by_pattern(*args)
371
+ end
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,12 @@
1
+ module Cowtech
2
+ module Lib
3
+ module Version
4
+ MAJOR = 1
5
+ MINOR = 9
6
+ PATCH = 0
7
+ BUILD = nil
8
+
9
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ #
3
+ # cowtech-lib
4
+ # Author: Shogun <shogun_panda@me.com>
5
+ # Copyright © 2011 and above Shogun
6
+ # Released under the MIT License, which follows.
7
+ #
8
+ # This program is free software; you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License
10
+ # as published by the Free Software Foundation; either version 2
11
+ # of the License, or (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
+ #
22
+ # A copy of the GNU General Public License is available at: http://www.gnu.org/licenses/gpl.txt
23
+ #
24
+
25
+ require "rubygems"
26
+ require "CowtechLib/Console"
27
+ require "CowtechLib/OptionParser"
28
+ require "CowtechLib/Shell"
29
+ require "CowtechLib/Script"
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cowtech-lib
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 9
8
+ - 0
9
+ version: 1.9.0
10
+ platform: ruby
11
+ authors:
12
+ - Shogun
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-21 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: jeweler
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 5
30
+ - 1
31
+ version: 1.5.1
32
+ type: :development
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rcov
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *id002
48
+ description: A general purpose utility library.
49
+ email: shogun_panda@me.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE.txt
56
+ - README.rdoc
57
+ files:
58
+ - .document
59
+ - Gemfile
60
+ - Gemfile.lock
61
+ - LICENSE.txt
62
+ - README.rdoc
63
+ - Rakefile
64
+ - cowtech-lib.gemspec
65
+ - lib/cowtech-lib.rb
66
+ - lib/cowtech-lib/console.rb
67
+ - lib/cowtech-lib/option_parser.rb
68
+ - lib/cowtech-lib/script.rb
69
+ - lib/cowtech-lib/shell.rb
70
+ - lib/cowtech-lib/version.rb
71
+ has_rdoc: true
72
+ homepage: http://github.com/ShogunPanda/cowtech-lib
73
+ licenses:
74
+ - MIT
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ requirements: []
97
+
98
+ rubyforge_project:
99
+ rubygems_version: 1.3.7
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: A general purpose utility library.
103
+ test_files: []
104
+