ruby_ex 0.1.2 → 0.2.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.
- data/ChangeLog +693 -0
- data/NEWS +74 -0
- data/SPEC.dyn.yml +6 -6
- data/SPEC.gemspec +14 -0
- data/SPEC.yml +4 -4
- data/lib/abstract.rb +2 -4
- data/lib/abstract_node.rb +1 -2
- data/lib/algorithms/simulated_annealing.rb +50 -29
- data/lib/attributed_class.rb +50 -21
- data/lib/auto_object.rb +102 -0
- data/lib/blank_slate.rb +102 -0
- data/lib/cache.rb +1 -2
- data/lib/choose.rb +165 -163
- data/lib/commands.rb +2 -3
- data/lib/commands/command.rb +47 -20
- data/lib/commands/datas.rb +1 -1
- data/lib/commands/datas/composite.rb +5 -1
- data/lib/commands/datas/data.rb +102 -5
- data/lib/commands/datas/factory.rb +13 -6
- data/lib/commands/datas/temp.rb +3 -5
- data/lib/commands/factory.rb +1 -1
- data/lib/commands/helpers.rb +1 -1
- data/lib/commands/pipe.rb +10 -1
- data/lib/commands/runners.rb +1 -1
- data/lib/commands/runners/exec.rb +1 -1
- data/lib/commands/runners/fork.rb +3 -16
- data/lib/commands/runners/mock.rb +67 -0
- data/lib/commands/runners/runner.rb +5 -3
- data/lib/commands/runners/system.rb +1 -1
- data/lib/commands/seq.rb +2 -1
- data/lib/config_file.rb +10 -2
- data/lib/const_regexp.rb +1 -2
- data/lib/{dlogger.rb → d_logger.rb} +1 -2
- data/lib/daemon.rb +1 -2
- data/lib/diff.rb +1 -2
- data/lib/drb/drb_observable.rb +1 -2
- data/lib/drb/drb_observable_pool.rb +2 -2
- data/lib/drb/drb_service.rb +1 -2
- data/lib/drb/drb_undumped_attributes.rb +1 -2
- data/lib/drb/drb_undumped_indexed_object.rb +1 -2
- data/lib/drb/insecure_protected_methods.rb +1 -2
- data/lib/drb_ex.rb +2 -2
- data/lib/file_type.rb +466 -0
- data/lib/generate_id.rb +12 -6
- data/lib/genpasswd.rb +22 -0
- data/lib/hash_eval.rb +83 -0
- data/lib/histogram.rb +1 -2
- data/lib/hookable.rb +3 -4
- data/lib/hooker.rb +1 -3
- data/lib/html_encode.rb +191 -0
- data/lib/indexed_node.rb +0 -1
- data/lib/io_marshal.rb +4 -2
- data/lib/ioo.rb +3 -2
- data/lib/kill_all.rb +46 -0
- data/lib/labeled_node.rb +0 -1
- data/lib/logger_observer.rb +8 -4
- data/lib/md5sum.rb +3 -3
- data/lib/meta_factory.rb +99 -0
- data/lib/method_call.rb +87 -0
- data/lib/mocks.rb +12 -0
- data/lib/mocks/assertions.rb +50 -0
- data/lib/mocks/method_logger.rb +40 -0
- data/lib/mocks/mock.rb +64 -0
- data/lib/mocks/object.rb +47 -0
- data/lib/mocks/observer.rb +38 -0
- data/lib/module/autoload_tree.rb +30 -29
- data/lib/module/hierarchy.rb +176 -171
- data/lib/module/instance_method_visibility.rb +39 -38
- data/lib/node.rb +0 -1
- data/lib/object_monitor.rb +1 -2
- data/lib/object_monitor_activity.rb +1 -2
- data/lib/observable.rb +1 -2
- data/lib/observable_pool.rb +1 -2
- data/lib/{orderedhash.rb → ordered_hash.rb} +41 -5
- data/lib/pp_hierarchy.rb +7 -2
- data/lib/r_path.rb +307 -0
- data/lib/random_generators.rb +7 -20
- data/lib/random_generators/random_generator.rb +2 -4
- data/lib/random_generators/ruby.rb +4 -2
- data/lib/regex_path.rb +124 -0
- data/lib/ruby_ex.rb +28 -98
- data/lib/safe_eval.rb +1 -2
- data/lib/sendmail.rb +14 -17
- data/lib/service_manager.rb +1 -2
- data/lib/shuffle.rb +6 -2
- data/lib/spring.rb +1 -2
- data/lib/spring_set.rb +1 -2
- data/lib/{symtbl.rb → sym_tbl.rb} +90 -5
- data/lib/sym_tbl_gsub.rb +227 -0
- data/lib/{synflow.rb → syn_flow.rb} +1 -2
- data/lib/text.rb +218 -0
- data/lib/timeout_ex.rb +1 -2
- data/lib/trace.rb +9 -8
- data/lib/uri/druby.rb +1 -2
- data/lib/uri/file.rb +1 -1
- data/lib/uri/ftp_ex.rb +1 -1
- data/lib/uri/http_ex.rb +1 -1
- data/lib/uri/mysql.rb +121 -0
- data/lib/uri/pgsql.rb +19 -38
- data/lib/uri/svn.rb +1 -2
- data/lib/uri_ex.rb +45 -3
- data/lib/verbose_object.rb +181 -38
- data/lib/yaml/chop_header.rb +19 -11
- data/lib/yaml/transform.rb +17 -11
- data/lib/yaml/yregexpath.rb +11 -5
- data/test/algorithms/simulated_annealing_test.rb +2 -2
- data/test/resources/foo.tar.gz +0 -0
- data/test/resources/tar.gz.log +49 -0
- data/test/sanity-suite.yml +5 -7
- data/test/sanity/multiple-requires.yml +17 -7
- data/test/sanity/single-requires.yml +38 -20
- data/test/stress-tests/threads_and_exceptions.yml +13 -0
- data/test/test-unit-setup.rb +3 -1
- data/test/unit-suite.yml +7 -8
- metadata +42 -31
- data/lib/algorithms.rb +0 -12
- data/lib/ask.rb +0 -100
- data/lib/checkout.rb +0 -12
- data/lib/dumpable_proc.rb +0 -57
- data/lib/filetype.rb +0 -229
- data/lib/thread_mutex.rb +0 -11
- data/lib/yaml/basenode_ext.rb +0 -63
data/lib/commands/helpers.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: helpers.rb
|
|
4
|
+
# Revision:: $Id: helpers.rb 255 2005-06-01 00:08:46Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
data/lib/commands/pipe.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: pipe.rb
|
|
4
|
+
# Revision:: $Id: pipe.rb 341 2005-09-07 00:01:43Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -11,6 +11,7 @@ module Commands
|
|
|
11
11
|
|
|
12
12
|
def initialize ( *cmds )
|
|
13
13
|
@cmds = cmds.map { |x| x.dup }
|
|
14
|
+
@input, @output, @error = nil, nil, nil
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def run ( *a )
|
|
@@ -56,6 +57,14 @@ module Commands
|
|
|
56
57
|
@cmds.last.error
|
|
57
58
|
end
|
|
58
59
|
|
|
60
|
+
%w[ args command ].each do |meth|
|
|
61
|
+
class_eval %Q{
|
|
62
|
+
def #{meth} ( *a, &b )
|
|
63
|
+
raise ArgumentError, "no method `#{meth}' on a pipe"
|
|
64
|
+
end
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
59
68
|
def to_sh
|
|
60
69
|
strs = @cmds.map { |cmd| "(#{cmd.to_sh})" }
|
|
61
70
|
"(#{strs.join(' | ')})#{sh_args}"
|
data/lib/commands/runners.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: runners.rb
|
|
4
|
+
# Revision:: $Id: runners.rb 255 2005-06-01 00:08:46Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: exec.rb
|
|
4
|
+
# Revision:: $Id: exec.rb 255 2005-06-01 00:08:46Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: fork.rb
|
|
4
|
+
# Revision:: $Id: fork.rb 358 2005-09-16 09:54:04Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -30,6 +30,7 @@ module Commands
|
|
|
30
30
|
def run_impl ( aCommand, data )
|
|
31
31
|
data.pid = Kernel.fork do
|
|
32
32
|
begin
|
|
33
|
+
TempPath.fork_init
|
|
33
34
|
hook_trigger :son, aCommand, data
|
|
34
35
|
super
|
|
35
36
|
rescue Exception => ex
|
|
@@ -80,21 +81,7 @@ module Commands
|
|
|
80
81
|
class Error < Exception
|
|
81
82
|
end
|
|
82
83
|
def failure ( data )
|
|
83
|
-
|
|
84
|
-
output = data.output.read rescue nil
|
|
85
|
-
error = data.error.read rescue nil
|
|
86
|
-
output.gsub!(/^/, ' ')
|
|
87
|
-
error.gsub!(/^/, ' ')
|
|
88
|
-
|
|
89
|
-
raise Error, <<end
|
|
90
|
-
---
|
|
91
|
-
Command failed:
|
|
92
|
-
exit: #{data.status.exitstatus}
|
|
93
|
-
output:
|
|
94
|
-
#{output}
|
|
95
|
-
error:
|
|
96
|
-
#{error}
|
|
97
|
-
end
|
|
84
|
+
raise Error, { 'Command failed' => data }.to_yaml
|
|
98
85
|
end
|
|
99
86
|
end
|
|
100
87
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
|
+
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
|
+
# License:: GNU General Public License (GPL).
|
|
4
|
+
# Revision:: $Id: mock.rb 302 2005-06-23 21:03:51Z ertai $
|
|
5
|
+
|
|
6
|
+
require 'commands'
|
|
7
|
+
|
|
8
|
+
module Commands
|
|
9
|
+
|
|
10
|
+
module Runners
|
|
11
|
+
|
|
12
|
+
class Mock < Runner
|
|
13
|
+
concrete
|
|
14
|
+
|
|
15
|
+
attr_reader :log
|
|
16
|
+
|
|
17
|
+
def initialize ( *a, &b )
|
|
18
|
+
@log = []
|
|
19
|
+
@contents = b
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run_impl ( aCommand, data )
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run ( aCommand )
|
|
27
|
+
d = super
|
|
28
|
+
d.output.open('w', &@contents)
|
|
29
|
+
d.status = 0
|
|
30
|
+
d
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def display_command ( m )
|
|
34
|
+
@log << m
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end # class Mock
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
test_section __FILE__ do
|
|
42
|
+
|
|
43
|
+
class MockTest < Test::Unit::TestCase
|
|
44
|
+
|
|
45
|
+
def setup
|
|
46
|
+
assert_nothing_raised { @runner = Mock.new }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_0_initialize
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_simple
|
|
53
|
+
assert_nothing_raised do
|
|
54
|
+
@cmd = 'foo bar baz'.to_cmd
|
|
55
|
+
@data = @runner.run(@cmd)
|
|
56
|
+
end
|
|
57
|
+
assert_kind_of(Commands::Datas::Data, @data)
|
|
58
|
+
assert_equal([@cmd], @runner.log)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end # class SystemTest
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end # module Runners
|
|
66
|
+
|
|
67
|
+
end # module Commands
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: runner.rb
|
|
4
|
+
# Revision:: $Id: runner.rb 343 2005-09-08 01:32:57Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -11,8 +11,7 @@ module Commands
|
|
|
11
11
|
|
|
12
12
|
class Runner
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
include Abstract
|
|
16
15
|
include Hookable
|
|
17
16
|
include Hooker
|
|
18
17
|
|
|
@@ -42,6 +41,7 @@ module Commands
|
|
|
42
41
|
|
|
43
42
|
def initialize
|
|
44
43
|
@command_data_factory = Datas::Factory.new
|
|
44
|
+
@open_mode = :w
|
|
45
45
|
hooker_subscribe self
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -58,6 +58,7 @@ module Commands
|
|
|
58
58
|
hook_trigger :display_command, aCommand
|
|
59
59
|
|
|
60
60
|
data = @command_data_factory.create
|
|
61
|
+
data.open_mode = @open_mode
|
|
61
62
|
data.input = aCommand.input unless aCommand.input.nil?
|
|
62
63
|
data.output = aCommand.output unless aCommand.output.nil?
|
|
63
64
|
data.error = aCommand.error unless aCommand.error.nil?
|
|
@@ -70,6 +71,7 @@ module Commands
|
|
|
70
71
|
|
|
71
72
|
def run_impl ( aCommand, data )
|
|
72
73
|
hook_trigger :before_open, data
|
|
74
|
+
aCommand = aCommand.instanciate_args
|
|
73
75
|
STDIN.reopen(data.input.to_io_for_commands) unless data.input.nil?
|
|
74
76
|
STDOUT.reopen(data.output.to_io_for_commands) unless data.output.nil?
|
|
75
77
|
STDERR.reopen(data.error.to_io_for_commands) unless data.error.nil?
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: system.rb
|
|
4
|
+
# Revision:: $Id: system.rb 255 2005-06-01 00:08:46Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
data/lib/commands/seq.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
|
4
|
-
# Revision:: $Id: seq.rb
|
|
4
|
+
# Revision:: $Id: seq.rb 341 2005-09-07 00:01:43Z ertai $
|
|
5
5
|
|
|
6
6
|
require 'commands'
|
|
7
7
|
|
|
@@ -11,6 +11,7 @@ module Commands
|
|
|
11
11
|
|
|
12
12
|
def initialize ( *cmds )
|
|
13
13
|
@cmds = cmds
|
|
14
|
+
@input, @output, @error = nil, nil, nil
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def run ( *a )
|
data/lib/config_file.rb
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id: config_file.rb
|
|
6
|
+
# $Id: config_file.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
require 'ruby_ex'
|
|
10
9
|
require 'pathname'
|
|
11
10
|
require 'yaml'
|
|
12
11
|
require 'delegate'
|
|
@@ -68,6 +67,15 @@ require 'tempfile'
|
|
|
68
67
|
|
|
69
68
|
class ConfigFileTest < Test::Unit::TestCase
|
|
70
69
|
|
|
70
|
+
def setup
|
|
71
|
+
@verbose = $VERBOSE
|
|
72
|
+
$VERBOSE = false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def teardown
|
|
76
|
+
$VERBOSE = @verbose
|
|
77
|
+
end
|
|
78
|
+
|
|
71
79
|
def test_config_file
|
|
72
80
|
tmp_file = nil
|
|
73
81
|
Tempfile.open('config_file') do |f|
|
data/lib/const_regexp.rb
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id:
|
|
6
|
+
# $Id: d_logger.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
|
-
require 'ruby_ex'
|
|
9
8
|
require 'logger'
|
|
10
9
|
|
|
11
10
|
# This Logger subclass permit to use more debug levels
|
data/lib/daemon.rb
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id: daemon.rb
|
|
6
|
+
# $Id: daemon.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
require 'pathname'
|
|
10
10
|
require 'singleton'
|
|
11
11
|
|
|
12
|
-
require 'ruby_ex'
|
|
13
12
|
require 'observable'
|
|
14
13
|
require 'observable_pool'
|
|
15
14
|
require 'service_manager'
|
data/lib/diff.rb
CHANGED
data/lib/drb/drb_observable.rb
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id: drb_observable.rb
|
|
6
|
+
# $Id: drb_observable.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
require 'ruby_ex'
|
|
10
9
|
require 'drb/observer'
|
|
11
10
|
|
|
12
11
|
require 'observable'
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# Author: Nicolas Despres <polrop@lrde.epita.fr>.
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
|
-
# $LastChangedBy:
|
|
6
|
-
# $Id: drb_observable_pool.rb
|
|
5
|
+
# $LastChangedBy: polrop $
|
|
6
|
+
# $Id: drb_observable_pool.rb 97 2005-01-13 18:20:49Z polrop $
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
require 'observable_pool'
|
data/lib/drb/drb_service.rb
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id: drb_undumped_attributes.rb
|
|
6
|
+
# $Id: drb_undumped_attributes.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
require 'ruby_ex'
|
|
10
9
|
require 'drb/drb'
|
|
11
10
|
|
|
12
11
|
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
# License: Gnu General Public License.
|
|
4
4
|
|
|
5
5
|
# $LastChangedBy: ertai $
|
|
6
|
-
# $Id: insecure_protected_methods.rb
|
|
6
|
+
# $Id: insecure_protected_methods.rb 339 2005-09-06 23:27:27Z ertai $
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
require 'ruby_ex'
|
|
10
9
|
require 'drb/drb'
|
|
11
10
|
|
|
12
11
|
|
data/lib/drb_ex.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
|
2
2
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
3
3
|
# License:: Gnu General Public License.
|
|
4
|
-
# Revision:: $Id: drb_ex.rb
|
|
4
|
+
# Revision:: $Id: drb_ex.rb 344 2005-09-08 01:44:18Z ertai $
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
AutoloadTree.import!
|
|
7
7
|
|
|
8
8
|
module DRb
|
|
9
9
|
|
data/lib/file_type.rb
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
require 'abstract'
|
|
2
|
+
require 'commands'
|
|
3
|
+
require 'active_support/class_attribute_accessors'
|
|
4
|
+
require 'active_support/class_inheritable_attributes'
|
|
5
|
+
|
|
6
|
+
module FileType
|
|
7
|
+
|
|
8
|
+
@@subclasses = []
|
|
9
|
+
|
|
10
|
+
def self.register ( klass )
|
|
11
|
+
@@subclasses << klass
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
GZIP = 'gzip'.to_cmd
|
|
16
|
+
BZIP2 = 'bzip2'.to_cmd
|
|
17
|
+
UNZIP = 'unzip'.to_cmd
|
|
18
|
+
TAR = 'tar'.to_cmd
|
|
19
|
+
GEM = 'gem'.to_cmd
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Generic
|
|
23
|
+
abstract
|
|
24
|
+
|
|
25
|
+
attr_reader :path, :base, :ext
|
|
26
|
+
|
|
27
|
+
def initialize ( path )
|
|
28
|
+
@path = Pathname.new(path)
|
|
29
|
+
re = self.class.extension
|
|
30
|
+
raise ArgumentError, "bad class #{self.class}" if re.nil?
|
|
31
|
+
unless @path.to_s =~ re
|
|
32
|
+
raise ArgumentError, "#{@path} do not match /#{re.source}/"
|
|
33
|
+
end
|
|
34
|
+
@base, @ext = $`, $&
|
|
35
|
+
if @ext.empty?
|
|
36
|
+
@base = self
|
|
37
|
+
else
|
|
38
|
+
@base = FileType.guess(@base)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def extsplit
|
|
43
|
+
[@base, @ext]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.inherited ( klass )
|
|
47
|
+
FileType.register(klass)
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
@path.to_s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.match_type ( path, max, best )
|
|
56
|
+
ext_re = self.extension
|
|
57
|
+
if path.to_s =~ ext_re
|
|
58
|
+
cur = $&.size * self.priority
|
|
59
|
+
return [cur, self] if cur > max
|
|
60
|
+
end
|
|
61
|
+
return [max, best]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def + ( arg )
|
|
65
|
+
@path + arg.to_s
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.extractable ( aCommand )
|
|
69
|
+
include Extractable
|
|
70
|
+
self.extract_command = aCommand
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.extractable_dir ( aCommand )
|
|
74
|
+
include ExtractableDir
|
|
75
|
+
self.extract_command = aCommand
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def extractable?
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def installable?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.filetype_extension ( anObject, priority=1 )
|
|
87
|
+
class_eval do
|
|
88
|
+
self.extension = anObject
|
|
89
|
+
self.priority = priority
|
|
90
|
+
concrete
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
cattr_accessor :runner
|
|
95
|
+
class_inheritable_accessor :extension
|
|
96
|
+
class_inheritable_accessor :priority # 0 .. +inf
|
|
97
|
+
|
|
98
|
+
self.runner = Commands::Runners::System.new
|
|
99
|
+
|
|
100
|
+
end # class Generic
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Unknown < Generic
|
|
105
|
+
filetype_extension(/(\.[^.\/]+)?$/, 0)
|
|
106
|
+
end # class Unknown
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ExtractError < Exception
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
module Extractable
|
|
116
|
+
|
|
117
|
+
def self.included ( aClass )
|
|
118
|
+
|
|
119
|
+
aClass.class_inheritable_accessor :extract_command
|
|
120
|
+
|
|
121
|
+
aClass.module_eval do
|
|
122
|
+
|
|
123
|
+
def extractable?
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def mk_cmd ( out )
|
|
128
|
+
extract_command < @path > out
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def extract
|
|
132
|
+
base, ext = @base.extsplit
|
|
133
|
+
@tmp = TempPath.new(base.path.basename, ext)
|
|
134
|
+
cmd = mk_cmd(@tmp)
|
|
135
|
+
data = cmd.run(self.class.runner)
|
|
136
|
+
if data.status != 0
|
|
137
|
+
@tmp.rmtree if @tmp.exist?
|
|
138
|
+
raise ExtractError, "Cannot extract a file:
|
|
139
|
+
| path: #{to_s}
|
|
140
|
+
| type: #{self.class}
|
|
141
|
+
| command: #{cmd}
|
|
142
|
+
| exit status: #{data.status.exitstatus}".head_cut!
|
|
143
|
+
end
|
|
144
|
+
FileType.guess(@tmp)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
alias :default :extract
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end # module Extractable
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# FIXME Use the zlib if gzip not available.
|
|
158
|
+
class Gz < Generic
|
|
159
|
+
filetype_extension(/(\.(gz|z|Z)|-gz|-z|_z)$/)
|
|
160
|
+
extractable GZIP + %w[-d -c %i]
|
|
161
|
+
end # class Gz
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Bz2 < Generic
|
|
166
|
+
filetype_extension(/\.bz2?$/)
|
|
167
|
+
extractable BZIP2 + %w[-d -c %i]
|
|
168
|
+
end # class Bz2
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class Zip < Generic
|
|
173
|
+
filetype_extension(/\.zip$/)
|
|
174
|
+
extractable UNZIP + %w[-p %i]
|
|
175
|
+
end # class Zip
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class CorruptedTarballError < Exception
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
module ExtractableDir
|
|
183
|
+
|
|
184
|
+
def self.included ( aClass )
|
|
185
|
+
|
|
186
|
+
aClass.module_eval do
|
|
187
|
+
|
|
188
|
+
include Extractable
|
|
189
|
+
|
|
190
|
+
remove_method :mk_cmd
|
|
191
|
+
|
|
192
|
+
def mk_cmd ( out )
|
|
193
|
+
@log = TempPath.new('log')
|
|
194
|
+
cmd = extract_command[@tmp] < @path > @log
|
|
195
|
+
@tmp.mkpath
|
|
196
|
+
cmd
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
alias :extract_extractable :extract
|
|
200
|
+
|
|
201
|
+
def extract
|
|
202
|
+
dir = extract_extractable
|
|
203
|
+
FileType.guess(dir.path + longest_common_path(@log).join('/'))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# We want the longest common path
|
|
211
|
+
def longest_common_path ( log )
|
|
212
|
+
longest = nil
|
|
213
|
+
raise CorruptedTarballError, 'tar output empty' if log.zero?
|
|
214
|
+
log.each_line do |line|
|
|
215
|
+
line.chomp!
|
|
216
|
+
path = line.split(/\//)
|
|
217
|
+
longest = path if longest.nil?
|
|
218
|
+
longest &= path
|
|
219
|
+
raise CorruptedTarballError, log.read if longest.empty?
|
|
220
|
+
end
|
|
221
|
+
raise CorruptedTarballError, log.read if longest.nil?
|
|
222
|
+
longest
|
|
223
|
+
end
|
|
224
|
+
module_function :longest_common_path
|
|
225
|
+
|
|
226
|
+
end # module ExtractableDir
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class Tar < Generic
|
|
231
|
+
filetype_extension(/\.tar$/)
|
|
232
|
+
extractable_dir TAR + %w[xvf %i -C]
|
|
233
|
+
end # class Tar
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class TarGz < Generic
|
|
238
|
+
filetype_extension(/\.(tar(\.(gz|z|Z)|-gz|-z|_z)|tgz)$/)
|
|
239
|
+
extractable_dir TAR + %w[xvzf %i -C]
|
|
240
|
+
end # class TarGz
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class TarBz2 < Generic
|
|
245
|
+
filetype_extension(/\.(tar\.bz2|tbz2|tbz)$/)
|
|
246
|
+
# FIXME Use this pipe beacause -j is not portable
|
|
247
|
+
# extractable_dir (BZIP2 + %w[-c -d]) | (TAR + %w[xvf - -C])
|
|
248
|
+
# problem: a pipe command doesn't properly implement `[]'
|
|
249
|
+
extractable_dir TAR + %w[xvjf %i -C]
|
|
250
|
+
end # class TarBz2
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class Ruby < Generic
|
|
255
|
+
filetype_extension(/\.rbw?$/)
|
|
256
|
+
|
|
257
|
+
def load
|
|
258
|
+
path.load
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def require
|
|
262
|
+
path.require
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
end # class Ruby
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class Yaml < Generic
|
|
270
|
+
filetype_extension(/\.ya?ml$/)
|
|
271
|
+
|
|
272
|
+
def load
|
|
273
|
+
YAML::load(path.read)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
end # class Yaml
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Gem < Generic
|
|
281
|
+
filetype_extension(/\.gem$/)
|
|
282
|
+
|
|
283
|
+
def installable?
|
|
284
|
+
true
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def install ( install_dir=nil )
|
|
288
|
+
cmd = GEM + 'install' + path
|
|
289
|
+
cmd << '--install-dir' << install_dir unless install_dir.nil?
|
|
290
|
+
cmd.run(runner)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
end # class Gem
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class Directory < Generic
|
|
298
|
+
filetype_extension(/()$/)
|
|
299
|
+
|
|
300
|
+
def self.match_type ( path, max, best )
|
|
301
|
+
if path.exist? and path.directory?
|
|
302
|
+
return [path.to_s.size, self]
|
|
303
|
+
end
|
|
304
|
+
return [max, best]
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
end # class Directory
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def self.guess_class ( path )
|
|
312
|
+
lazy_init
|
|
313
|
+
max = -1
|
|
314
|
+
best = Unknown
|
|
315
|
+
path = path.to_path
|
|
316
|
+
@@subclasses.each do |klass|
|
|
317
|
+
max, best = klass.match_type(path, max, best)
|
|
318
|
+
end
|
|
319
|
+
return best
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def self.guess ( path )
|
|
323
|
+
guess_class(path).new(path)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def self.lazy_init
|
|
327
|
+
return if defined? @@init
|
|
328
|
+
@@init = true
|
|
329
|
+
@@subclasses.delete_if do |klass|
|
|
330
|
+
klass.abstract? or not (klass.is_a? Class) # and constants.include? klass)
|
|
331
|
+
end
|
|
332
|
+
@@subclasses.each do |klass|
|
|
333
|
+
ext = klass.extension
|
|
334
|
+
raise ArgumentError, "Bad extension #{ext}" unless ext.is_a? Regexp
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
end # module FileType
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
test_section __FILE__ do
|
|
342
|
+
|
|
343
|
+
class FileTypeTest < Test::Unit::TestCase
|
|
344
|
+
|
|
345
|
+
def setup
|
|
346
|
+
@tmp = TempPath.new
|
|
347
|
+
(@tmp + 'foo').mkpath
|
|
348
|
+
@mr = Commands::Runners::Mock.new do |out|
|
|
349
|
+
out.write("#@tmp/foo/bar/baz\n#@tmp/foo\n#@tmp/foo/f/f/foo/baz\n")
|
|
350
|
+
end
|
|
351
|
+
FileType::Generic.runner = @mr
|
|
352
|
+
@res_dir = __FILE__.to_path.dirname.parent + 'test/resources'
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def assert_guess ( type, *files )
|
|
356
|
+
files.flatten.each do |file|
|
|
357
|
+
assert_nothing_raised do
|
|
358
|
+
@file = FileType.guess(file)
|
|
359
|
+
end
|
|
360
|
+
assert_kind_of(FileType.const_get(type), @file)
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def assert_extract ( type, command, file )
|
|
365
|
+
assert_nothing_raised { @inp = FileType.guess(file) }
|
|
366
|
+
assert(@inp.extractable?, "#@inp not extractable")
|
|
367
|
+
assert_nothing_raised { @res = @inp.extract }
|
|
368
|
+
assert_kind_of(FileType.const_get(type), @res)
|
|
369
|
+
assert_match(command, @mr.log.last.to_sh)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def assert_real_extract ( type, file )
|
|
373
|
+
FileType::Generic.runner = Commands::Runners::System.new
|
|
374
|
+
assert_nothing_raised { @inp = FileType.guess(file) }
|
|
375
|
+
assert(@inp.extractable?, "#@inp not extractable")
|
|
376
|
+
assert_nothing_raised { @res = @inp.extract }
|
|
377
|
+
assert_kind_of(FileType.const_get(type), @res)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def assert_install ( command, file, *args )
|
|
381
|
+
assert_nothing_raised { @inp = FileType.guess(file) }
|
|
382
|
+
assert(@inp.installable?, "#@inp not installable")
|
|
383
|
+
assert_nothing_raised { @res = @inp.install(*args) }
|
|
384
|
+
assert_match(command, @mr.log.last.to_sh)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def test_gz
|
|
389
|
+
assert_guess :Gz, %w[ foo.gz foo.z foo.Z foo-z foo-gz foo_z ]
|
|
390
|
+
end
|
|
391
|
+
def test_bz2
|
|
392
|
+
assert_guess :Bz2, %w[ foo.bz2 foo.bz ]
|
|
393
|
+
end
|
|
394
|
+
def test_zip
|
|
395
|
+
assert_guess :Zip, %w[ foo.zip ]
|
|
396
|
+
end
|
|
397
|
+
def test_tar_bz2
|
|
398
|
+
assert_guess :TarBz2, %w[ foo.tar.bz2 foo.tbz2 foo/bar.tbz ]
|
|
399
|
+
end
|
|
400
|
+
def test_tar_gz
|
|
401
|
+
assert_guess :TarGz, %w[ foo.tar.gz foo.tgz foo/bar.tar.gz foo.tar.Z ]
|
|
402
|
+
end
|
|
403
|
+
def test_tar
|
|
404
|
+
assert_guess :Tar, %w[ foo.tar /foo/bar.tar ]
|
|
405
|
+
end
|
|
406
|
+
def test_ruby
|
|
407
|
+
assert_guess :Ruby, 'foo.rb', 'foo.rbw'
|
|
408
|
+
end
|
|
409
|
+
def test_yaml
|
|
410
|
+
assert_guess :Yaml, 'foo.yml', 'foo.yaml'
|
|
411
|
+
end
|
|
412
|
+
def test_gem
|
|
413
|
+
assert_guess :Gem, 'foo.gem'
|
|
414
|
+
end
|
|
415
|
+
def test_unknown
|
|
416
|
+
assert_guess :Unknown, 'foo.dne', 'foo', '.foo'
|
|
417
|
+
assert(! @file.extractable?, "#@file is extractable")
|
|
418
|
+
end
|
|
419
|
+
def test_dir
|
|
420
|
+
TempPath.new do |tmp|
|
|
421
|
+
tmp.mkpath
|
|
422
|
+
assert_guess :Directory, tmp, '.', '/', '..'
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def test_extract_gz
|
|
427
|
+
assert_extract :Ruby, /^"gzip" "-d" "-c" "[^%]*" > "[^%]*"$/, 'foo.rb.gz'
|
|
428
|
+
end
|
|
429
|
+
def test_extract_bz2
|
|
430
|
+
assert_extract :Unknown, /^"bzip2" "-d" "-c" "[^%]*" > "[^%]*"$/, 'foo.bz2'
|
|
431
|
+
end
|
|
432
|
+
def test_extract_zip
|
|
433
|
+
assert_extract :Gz, /^"unzip" "-p" "[^%]*" > "[^%]*"$/, 'foo.gz.zip'
|
|
434
|
+
end
|
|
435
|
+
def test_extract_tar_bz2
|
|
436
|
+
assert_extract :Directory,
|
|
437
|
+
# /^"bzip2" -d -c "foo.tar.bz2" | "tar" "xvf" "-" "-C" "[^%]*" > "[^%]*"$/,
|
|
438
|
+
/^"tar" "xvjf" "foo.tar.bz2" "-C" "[^%]*" > "[^%]*"$/, 'foo.tar.bz2'
|
|
439
|
+
end
|
|
440
|
+
def test_extract_tar_gz
|
|
441
|
+
assert_extract :Directory,
|
|
442
|
+
/^"tar" "xvzf" "foo.tar.gz" "-C" ".*foo.*" > ".*log.*"$/, 'foo.tar.gz'
|
|
443
|
+
assert_real_extract :Directory, @res_dir + 'foo.tar.gz'
|
|
444
|
+
end
|
|
445
|
+
def test_extract_tar
|
|
446
|
+
assert_extract :Directory,
|
|
447
|
+
/^"tar" "xvf" "foo.tar" "-C" "[^%]*" > "[^%]*"$/, 'foo.tar'
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def test_install_gem
|
|
451
|
+
assert_install(/^"gem" "install" "foo.gem"$/, 'foo.gem')
|
|
452
|
+
assert_install(/^"gem" "install" "foo.gem" "--install-dir" "\/tmp"$/,
|
|
453
|
+
'foo.gem', '/tmp')
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def test_longest_common_path
|
|
457
|
+
assert_nothing_raised do
|
|
458
|
+
res = @res_dir + 'tar.gz.log'
|
|
459
|
+
@longest = FileType::ExtractableDir.longest_common_path(res)
|
|
460
|
+
end
|
|
461
|
+
assert_equal(['core_ex-0.1.4'], @longest)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
end # class FileTypeTest
|
|
465
|
+
|
|
466
|
+
end
|