cowtech-lib 1.9.0

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,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
+