vex 0.2
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/Manifest +112 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/config/README +2 -0
- data/config/dependencies.rb +10 -0
- data/config/gem.yml +7 -0
- data/init.rb +36 -0
- data/lib/nokogiri/nokogiri_ext.rb +96 -0
- data/lib/vex.rb +5 -0
- data/lib/vex/action_controller.rb +4 -0
- data/lib/vex/action_controller/verify_action.rb +97 -0
- data/lib/vex/action_controller/whitelisted_actions.rb +45 -0
- data/lib/vex/active_record.rb +3 -0
- data/lib/vex/active_record/__init__.rb +0 -0
- data/lib/vex/active_record/advisory_lock.rb +11 -0
- data/lib/vex/active_record/advisory_lock/mysql_adapter.rb +16 -0
- data/lib/vex/active_record/advisory_lock/sqlite_adapter.rb +78 -0
- data/lib/vex/active_record/belongs_to_many.rb +143 -0
- data/lib/vex/active_record/find_by_extension.rb +70 -0
- data/lib/vex/active_record/gem.rb +8 -0
- data/lib/vex/active_record/lite_table.rb +139 -0
- data/lib/vex/active_record/lite_view.rb +140 -0
- data/lib/vex/active_record/mass_load.rb +65 -0
- data/lib/vex/active_record/mysql_backup.rb +21 -0
- data/lib/vex/active_record/plugins/default_value_for/LICENSE.TXT +19 -0
- data/lib/vex/active_record/plugins/default_value_for/README.rdoc +421 -0
- data/lib/vex/active_record/plugins/default_value_for/Rakefile +6 -0
- data/lib/vex/active_record/plugins/default_value_for/init.rb +91 -0
- data/lib/vex/active_record/plugins/default_value_for/test.rb +279 -0
- data/lib/vex/active_record/plugins/default_value_for/test.sqlite3 +0 -0
- data/lib/vex/active_record/random_id.rb +56 -0
- data/lib/vex/active_record/resolver.rb +49 -0
- data/lib/vex/active_record/serialize_hash.rb +125 -0
- data/lib/vex/active_record/to_html.rb +53 -0
- data/lib/vex/active_record/validate.rb +76 -0
- data/lib/vex/active_record/validation_error_ext.rb +68 -0
- data/lib/vex/base.rb +2 -0
- data/lib/vex/base/app.rb +75 -0
- data/lib/vex/base/array/at_random.rb +17 -0
- data/lib/vex/base/array/cross.rb +26 -0
- data/lib/vex/base/array/each_batch.rb +32 -0
- data/lib/vex/base/array/parallel_map.rb +98 -0
- data/lib/vex/base/deprecation.rb +41 -0
- data/lib/vex/base/enumerable/deep.rb +95 -0
- data/lib/vex/base/enumerable/enumerable_ext.rb +59 -0
- data/lib/vex/base/enumerable/progress.rb +71 -0
- data/lib/vex/base/filesystem/fast_copy.rb +61 -0
- data/lib/vex/base/filesystem/grep.rb +34 -0
- data/lib/vex/base/filesystem/lock.rb +43 -0
- data/lib/vex/base/filesystem/lock.rb.test.lck +0 -0
- data/lib/vex/base/filesystem/lock.rb.test.pid +1 -0
- data/lib/vex/base/filesystem/make_dirs.rb +94 -0
- data/lib/vex/base/filesystem/parse_filename.rb +36 -0
- data/lib/vex/base/filesystem/tmp_file.rb +87 -0
- data/lib/vex/base/filesystem/write.rb +43 -0
- data/lib/vex/base/hash/compact.rb +38 -0
- data/lib/vex/base/hash/cross.rb +117 -0
- data/lib/vex/base/hash/easy_access.rb +141 -0
- data/lib/vex/base/hash/ensure_keys.rb +18 -0
- data/lib/vex/base/hash/extract.rb +71 -0
- data/lib/vex/base/hash/extras.rb +62 -0
- data/lib/vex/base/hash/inspect.rb +17 -0
- data/lib/vex/base/hash/simple_access_methods.rb +74 -0
- data/lib/vex/base/invalid_argument/invalid_argument.rb +97 -0
- data/lib/vex/base/local_conf.rb +35 -0
- data/lib/vex/base/net/http_ext.rb +227 -0
- data/lib/vex/base/net/socket_ext.rb +43 -0
- data/lib/vex/base/object/insp.rb +123 -0
- data/lib/vex/base/object/multiple_attributes.rb +58 -0
- data/lib/vex/base/object/singleton_methods.rb +23 -0
- data/lib/vex/base/object/with_benchmark.rb +110 -0
- data/lib/vex/base/range_array.rb +40 -0
- data/lib/vex/base/range_ext.rb +28 -0
- data/lib/vex/base/safe_token.rb +156 -0
- data/lib/vex/base/string/string_ext.rb +136 -0
- data/lib/vex/base/thread/deferred.rb +52 -0
- data/lib/vex/base/thread/sleep.rb +11 -0
- data/lib/vex/base/time/date_ext.rb +12 -0
- data/lib/vex/boot.rb +40 -0
- data/lib/vex/boot/array.rb +22 -0
- data/lib/vex/boot/blank.rb +41 -0
- data/lib/vex/boot/string.rb +60 -0
- data/migration/create_request_log.rb +28 -0
- data/r.rb +35 -0
- data/script/console +19 -0
- data/script/rebuild +7 -0
- data/tasks/echoe.rake +52 -0
- data/tasks/validate_db.rake +14 -0
- data/test/ar.rb +30 -0
- data/test/auto.rb +31 -0
- data/test/base-tests/local_conf.rb +25 -0
- data/test/base.rb +2 -0
- data/test/boot.rb +3 -0
- data/test/config/local.defaults.yml +4 -0
- data/test/config/local.yml +8 -0
- data/test/test.sqlite3 +0 -0
- data/test/test.sqlite3.Class#create.lck +0 -0
- data/test/test.sqlite3.Class#create.lck.lck +0 -0
- data/test/test.sqlite3.Class#create.lck.pid +1 -0
- data/test/test.sqlite3.Class#create.pid +1 -0
- data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck +0 -0
- data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.lck +0 -0
- data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.pid +1 -0
- data/test/test.sqlite3.LiteView.make.holders__view_dummy.pid +1 -0
- data/test/test.sqlite3.vex.lck +0 -0
- data/test/test_helper.rb +49 -0
- data/test/tmp/copy.dat +1 -0
- data/test/tmp/lock.sqlite3 +0 -0
- data/test/tmp/lock.sqlite3.etest.lck +0 -0
- data/test/tmp/lock.sqlite3.etest.pid +1 -0
- data/test/tmp/somedata.dat +61 -0
- data/vex.gemspec +49 -0
- data/vex.tmproj +186 -0
- metadata +305 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module File::Grep
|
|
2
|
+
def grep(rex, *files)
|
|
3
|
+
files = files.flatten
|
|
4
|
+
|
|
5
|
+
unless block_given?
|
|
6
|
+
results = []
|
|
7
|
+
grep(rex, files) do |line, file, *args|
|
|
8
|
+
results << [ line, file ]
|
|
9
|
+
end
|
|
10
|
+
return results
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
files.each do |file|
|
|
14
|
+
File.readlines(file).each do |line|
|
|
15
|
+
next unless matches = (rex.match(line))
|
|
16
|
+
yield line, file, matches
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
File.extend File::Grep
|
|
23
|
+
|
|
24
|
+
module File::Grep::Etest
|
|
25
|
+
def test_grep
|
|
26
|
+
assert_equal 4, File.grep(/Etest/, __FILE__).length
|
|
27
|
+
assert_equal 5, File.grep(/ETEST/i, __FILE__).length
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_greps
|
|
31
|
+
assert_equal 8, File.grep(/Etest/, [ __FILE__, __FILE__ ]).length
|
|
32
|
+
assert_equal 8, File.grep(/Etest/, __FILE__, __FILE__ ).length
|
|
33
|
+
end
|
|
34
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module File::Lock
|
|
2
|
+
#
|
|
3
|
+
# File.locked implements recursive locking based on lock files.
|
|
4
|
+
def locked(path, &block)
|
|
5
|
+
file = File.open "#{path}.lck", "w+"
|
|
6
|
+
begin
|
|
7
|
+
# First try to lock the file nonblockingly.
|
|
8
|
+
# Failing that it might be already locked by *this* process.
|
|
9
|
+
# Otherwise it is locked by someone else.
|
|
10
|
+
if locked = file.flock(File::LOCK_EX | File::LOCK_NB)
|
|
11
|
+
File.write("#{path}.pid", Thread.uid)
|
|
12
|
+
elsif File.read("#{path}.pid") == Thread.uid
|
|
13
|
+
# already locked by us.
|
|
14
|
+
else
|
|
15
|
+
locked = file.flock(File::LOCK_EX)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
yield
|
|
19
|
+
ensure
|
|
20
|
+
file.flock(File::LOCK_UN) if locked
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
File.extend File::Lock
|
|
26
|
+
|
|
27
|
+
module File::Lock::Etest
|
|
28
|
+
TESTFILE = "#{__FILE__}.test"
|
|
29
|
+
|
|
30
|
+
def test_lock
|
|
31
|
+
i = 1
|
|
32
|
+
File.locked TESTFILE do
|
|
33
|
+
File.locked TESTFILE do
|
|
34
|
+
i = 2
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
assert_equal(2, i)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_lock_unsuccessful
|
|
42
|
+
end
|
|
43
|
+
end if VEX_TEST == "base"
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7394.106710
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Dir::MakeDirs
|
|
2
|
+
def exists?(path)
|
|
3
|
+
begin
|
|
4
|
+
Dir.open(path) && true
|
|
5
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
|
6
|
+
false
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def mkdirs(path)
|
|
11
|
+
paths = path.split("/")
|
|
12
|
+
paths.each_with_index do |path, idx|
|
|
13
|
+
p = paths[0..idx].join("/")
|
|
14
|
+
next if p.empty? # This is root
|
|
15
|
+
next if exists?(p)
|
|
16
|
+
mkdir(p)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def rmdirs(path)
|
|
21
|
+
Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).sort.reverse.each do |file|
|
|
22
|
+
if File.directory?(file)
|
|
23
|
+
next if file =~ /\/\.\.?$/
|
|
24
|
+
Dir.rmdir(file)
|
|
25
|
+
else
|
|
26
|
+
File.unlink(file)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Dir.rmdir(path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def tmp(do_unlink = true, &block)
|
|
34
|
+
path = "#{App.tmpdir}/#{$$}_#{Thread.current.object_id}"
|
|
35
|
+
Dir.mkdirs path
|
|
36
|
+
|
|
37
|
+
r = yield(path)
|
|
38
|
+
ensure
|
|
39
|
+
Dir.rmdirs(path) if path && do_unlink
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Dir.extend Dir::MakeDirs
|
|
44
|
+
|
|
45
|
+
module Dir::MakeDirs::Etest
|
|
46
|
+
def test_mkdirs
|
|
47
|
+
base = File.dirname(__FILE__) + "/dirtest"
|
|
48
|
+
|
|
49
|
+
assert !Dir.exists?(base)
|
|
50
|
+
Dir.mkdirs "#{base}/a/b/c"
|
|
51
|
+
Dir.mkdirs "#{base}/a/b/.dot"
|
|
52
|
+
assert Dir.exists?(base)
|
|
53
|
+
assert Dir.exists?("#{base}/a/b/c")
|
|
54
|
+
|
|
55
|
+
File.touch "#{base}/a/b/x.y"
|
|
56
|
+
File.touch "#{base}/a/b/.x.y"
|
|
57
|
+
File.touch "#{base}/a/b/..x.y"
|
|
58
|
+
File.touch "#{base}/a/b/.dot/a"
|
|
59
|
+
|
|
60
|
+
Dir.rmdirs("#{base}")
|
|
61
|
+
assert !Dir.exists?(base)
|
|
62
|
+
assert !Dir.exists?("#{base}/a/b/c")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_exists
|
|
66
|
+
assert Dir.exists?(File.dirname(__FILE__))
|
|
67
|
+
assert !Dir.exists?(__FILE__)
|
|
68
|
+
assert !Dir.exists?(__FILE__ + ".unknown")
|
|
69
|
+
assert !Dir.exists?(__FILE__ + ":invalid")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_tmpdir
|
|
73
|
+
p = nil
|
|
74
|
+
Dir.tmp do |pdir|
|
|
75
|
+
p = pdir
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
assert p.starts_with?("#{App.tmpdir}/")
|
|
79
|
+
assert_file_doesnt_exist p
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_tmpdir_unlinks_on_raise
|
|
83
|
+
p = nil
|
|
84
|
+
assert_raise(RuntimeError) {
|
|
85
|
+
Dir.tmp do |pdir|
|
|
86
|
+
p = pdir
|
|
87
|
+
raise RuntimeError
|
|
88
|
+
end
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
assert p.starts_with?("#{App.tmpdir}/")
|
|
92
|
+
assert_file_doesnt_exist p
|
|
93
|
+
end
|
|
94
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class File
|
|
2
|
+
def self.basename_wo_ext(s)
|
|
3
|
+
bn = File.basename(s)
|
|
4
|
+
if bn =~ /^(.*)\.([^.]*)$/
|
|
5
|
+
$1
|
|
6
|
+
else
|
|
7
|
+
bn
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# File.extname_wo_dot("x.y") => "y"
|
|
13
|
+
# File.extname_wo_dot("x.") => ""
|
|
14
|
+
# File.extname_wo_dot(".y") => ""
|
|
15
|
+
# File.extname_wo_dot("x") => ""
|
|
16
|
+
#
|
|
17
|
+
def self.extname_wo_dot(s)
|
|
18
|
+
File.extname(s) =~ /^\.([^.]*)$/ ? $1 : ""
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module File::Etest
|
|
23
|
+
def test_extname_wo_dot
|
|
24
|
+
assert_equal "y", File.extname_wo_dot("x.y")
|
|
25
|
+
assert_equal "", File.extname_wo_dot("x.")
|
|
26
|
+
assert_equal "", File.extname_wo_dot(".y")
|
|
27
|
+
assert_equal "", File.extname_wo_dot("x")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_basename_wo_ext
|
|
31
|
+
assert_equal "x", File.basename_wo_ext("x.y")
|
|
32
|
+
assert_equal "x", File.basename_wo_ext("x.")
|
|
33
|
+
assert_equal "", File.basename_wo_ext(".y")
|
|
34
|
+
assert_equal "x", File.basename_wo_ext("x")
|
|
35
|
+
end
|
|
36
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module FileUtils::TmpFile
|
|
2
|
+
def self.counter
|
|
3
|
+
Thread.current["tmpfiles"] ||= 0
|
|
4
|
+
Thread.current["tmpfiles"] += 1
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# tmpfile("xx.jpg") do |dest|
|
|
9
|
+
# Net.download("http://xx.yy.zz/a.jpg", dest)
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# the block gets the temp file name, which is guaranteed to be
|
|
13
|
+
# unique amongst all running processes.
|
|
14
|
+
#
|
|
15
|
+
# If the path parameter is set the temporary file will be fastcopied
|
|
16
|
+
# to that output file.
|
|
17
|
+
def tmpfile(path=nil, &block)
|
|
18
|
+
raise ArgumentError, "This no longer supports Symbol parameters" if path.is_a?(Symbol)
|
|
19
|
+
ext = "#{Thread.uid}_#{FileUtils::TmpFile.counter}"
|
|
20
|
+
|
|
21
|
+
case path
|
|
22
|
+
when nil
|
|
23
|
+
tmp = "#{App.tmpdir}/data.#{ext}"
|
|
24
|
+
else
|
|
25
|
+
Dir.mkdirs(File.dirname(path))
|
|
26
|
+
tmp = "#{path}.tmp#{ext}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
result = yield(tmp)
|
|
31
|
+
if result != false && path && File.exist?(tmp)
|
|
32
|
+
FileUtils.fast_copy(tmp, path)
|
|
33
|
+
end
|
|
34
|
+
return result
|
|
35
|
+
ensure
|
|
36
|
+
File.unlink(tmp) if File.exists?(tmp)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
FileUtils.extend FileUtils::TmpFile
|
|
42
|
+
|
|
43
|
+
module FileUtils::TmpFile::Etest
|
|
44
|
+
def test_tmpfile
|
|
45
|
+
assert File.exist?(__FILE__)
|
|
46
|
+
|
|
47
|
+
# so something successfully via a tmp file
|
|
48
|
+
FileUtils.fast_copy __FILE__, "tmp/copy.dat"
|
|
49
|
+
assert File.exist?("tmp/copy.dat")
|
|
50
|
+
|
|
51
|
+
FileUtils.tmpfile "tmp/copy.dat" do |dest|
|
|
52
|
+
false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
assert_equal File.size(__FILE__), File.size("tmp/copy.dat")
|
|
56
|
+
|
|
57
|
+
FileUtils.tmpfile "tmp/copy.dat" do |dest|
|
|
58
|
+
File.write(dest, "hey")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
assert_equal 3, File.size("tmp/copy.dat")
|
|
62
|
+
|
|
63
|
+
FileUtils.fast_copy __FILE__, "tmp/copy.dat"
|
|
64
|
+
assert_equal File.size(__FILE__), File.size("tmp/copy.dat")
|
|
65
|
+
|
|
66
|
+
FileUtils.tmpfile "tmp/copy.dat" do |dest|
|
|
67
|
+
File.write(dest, "hey")
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
assert_equal File.size(__FILE__), File.size("tmp/copy.dat")
|
|
72
|
+
|
|
73
|
+
FileUtils.tmpfile "tmp/copy.dat" do |dest|
|
|
74
|
+
File.write(dest, "hey")
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
assert_equal 3, File.size("tmp/copy.dat")
|
|
79
|
+
|
|
80
|
+
r = FileUtils.tmpfile "tmp/copy.dat" do |dest|
|
|
81
|
+
File.write(dest, "hey")
|
|
82
|
+
"fourfour"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
assert_equal "fourfour", r
|
|
86
|
+
end
|
|
87
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module File::Write
|
|
2
|
+
def touch(*files)
|
|
3
|
+
files.each do |file|
|
|
4
|
+
File.open(file, "w") do |f|
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def write(path, data)
|
|
10
|
+
File.open(path, "w+") do |file|
|
|
11
|
+
file.write(data)
|
|
12
|
+
end
|
|
13
|
+
path
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
File.extend File::Write
|
|
18
|
+
|
|
19
|
+
module File::Write::Etest
|
|
20
|
+
TESTFILE = "#{__FILE__}.test"
|
|
21
|
+
|
|
22
|
+
def test_touches
|
|
23
|
+
assert !File.exist?(TESTFILE)
|
|
24
|
+
File.touch TESTFILE
|
|
25
|
+
assert File.exist?(TESTFILE)
|
|
26
|
+
File.touch TESTFILE
|
|
27
|
+
assert File.exist?(TESTFILE)
|
|
28
|
+
File.unlink TESTFILE
|
|
29
|
+
assert !File.exist?(TESTFILE)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_writes
|
|
33
|
+
assert !File.exist?(TESTFILE)
|
|
34
|
+
File.write TESTFILE, "blabber"
|
|
35
|
+
assert_equal("blabber", File.read(TESTFILE))
|
|
36
|
+
File.write TESTFILE, "bla"
|
|
37
|
+
assert_equal("bla", File.read(TESTFILE))
|
|
38
|
+
File.write TESTFILE, ""
|
|
39
|
+
assert_equal("", File.read(TESTFILE))
|
|
40
|
+
File.unlink TESTFILE
|
|
41
|
+
assert !File.exist?(TESTFILE)
|
|
42
|
+
end
|
|
43
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Hash::Compact
|
|
2
|
+
def compact
|
|
3
|
+
dup.compact!
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def compact!
|
|
7
|
+
empty = []
|
|
8
|
+
each { |k,v| empty << k if v.nil? }
|
|
9
|
+
empty.each do |k| delete(k) end
|
|
10
|
+
self
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Hash
|
|
15
|
+
include Compact
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module Hash::Compact::Etest
|
|
19
|
+
def test_compact_no
|
|
20
|
+
h = { 1 => 2 }
|
|
21
|
+
assert_equal(h.compact, h)
|
|
22
|
+
assert_not_equal(h.compact.object_id, h.object_id)
|
|
23
|
+
assert_equal(h.compact!.object_id, h.object_id)
|
|
24
|
+
assert_equal(h.compact!, h.compact)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_compact
|
|
28
|
+
h = { 1 => nil }
|
|
29
|
+
assert_equal(h.compact, {})
|
|
30
|
+
h.compact!
|
|
31
|
+
assert_equal(h, {})
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_compact_2
|
|
35
|
+
h = { nil => 1 }
|
|
36
|
+
assert_equal(h.compact, { nil => 1})
|
|
37
|
+
end
|
|
38
|
+
end if VEX_TEST == "base"
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
module Hash::Cross
|
|
2
|
+
#
|
|
3
|
+
# { :a => [ 1, 2], :b => [ "bb", "cc"], :c => :cc }.cross =>
|
|
4
|
+
#
|
|
5
|
+
# [
|
|
6
|
+
# { :a => 1, :b => "bb", :c => :cc },
|
|
7
|
+
# { :a => 2, :b => "bb", :c => :cc },
|
|
8
|
+
# { :a => 1, :b => "cc", :c => :cc },
|
|
9
|
+
# { :a => 2, :b => "cc", :c => :cc }
|
|
10
|
+
# ]
|
|
11
|
+
#
|
|
12
|
+
def cross(*keys)
|
|
13
|
+
dup.send :do_cross, *keys
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def do_cross(*keys)
|
|
19
|
+
keys = self.keys if keys.empty?
|
|
20
|
+
keys = keys.select { |key| self[key].is_a?(Array) }
|
|
21
|
+
|
|
22
|
+
crossing = self.extract! *keys
|
|
23
|
+
|
|
24
|
+
array = [ self ]
|
|
25
|
+
|
|
26
|
+
keys.each do |key|
|
|
27
|
+
r = []
|
|
28
|
+
|
|
29
|
+
values = crossing[key]
|
|
30
|
+
array.each do |obj|
|
|
31
|
+
values.each do |value|
|
|
32
|
+
# Note: the following (instead of the obj.dup.update() results in a ~10%
|
|
33
|
+
# faster cross implementation
|
|
34
|
+
o = obj.dup
|
|
35
|
+
o[key] = value
|
|
36
|
+
r << o
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
array = r
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
array
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Hash
|
|
47
|
+
include Cross
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module Hash::Cross::Etest
|
|
51
|
+
def assert_equal_sets(a, b)
|
|
52
|
+
return assert_equal_sets(Set.new(a), b) unless a.is_a?(Set)
|
|
53
|
+
return assert_equal_sets(a, Set.new(b)) unless b.is_a?(Set)
|
|
54
|
+
|
|
55
|
+
assert_equal(a, b)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_cross_1
|
|
59
|
+
uncrossed = { :a => [ 1, 2], :b => [ "bb", "cc"], :c => :cc }
|
|
60
|
+
uncrossed_orig = uncrossed.dup
|
|
61
|
+
|
|
62
|
+
crossed = [
|
|
63
|
+
{ :a => [1, 2], :b => "bb", :c => :cc },
|
|
64
|
+
{ :a => [1, 2], :b => "cc", :c => :cc }
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
assert_equal_sets(crossed, uncrossed.cross(:b))
|
|
68
|
+
assert_equal(uncrossed_orig, uncrossed)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_cross_2
|
|
72
|
+
uncrossed = { :a => [ 1, 2], :b => [ "bb", "cc"], :c => :cc }
|
|
73
|
+
uncrossed_orig = uncrossed.dup
|
|
74
|
+
|
|
75
|
+
crossed = [
|
|
76
|
+
{ :a => 1, :b => "bb", :c => :cc },
|
|
77
|
+
{ :a => 2, :b => "bb", :c => :cc },
|
|
78
|
+
{ :a => 1, :b => "cc", :c => :cc },
|
|
79
|
+
{ :a => 2, :b => "cc", :c => :cc }
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
assert_equal_sets(crossed, uncrossed.cross)
|
|
83
|
+
assert_equal(uncrossed_orig, uncrossed)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_cross_2_w_order
|
|
87
|
+
uncrossed = { :a => [ 1, 2], :b => [ "bb", "cc"], :c => :cc }
|
|
88
|
+
uncrossed_orig = uncrossed.dup
|
|
89
|
+
|
|
90
|
+
crossed = [
|
|
91
|
+
{ :a => 1, :b => "bb", :c => :cc },
|
|
92
|
+
{ :a => 1, :b => "cc", :c => :cc },
|
|
93
|
+
{ :a => 2, :b => "bb", :c => :cc },
|
|
94
|
+
{ :a => 2, :b => "cc", :c => :cc }
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
assert_equal(crossed, uncrossed.cross(:a, :b))
|
|
98
|
+
assert_equal(uncrossed_orig, uncrossed)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_cross_simple
|
|
102
|
+
uncrossed = { :a => :b }
|
|
103
|
+
assert_equal([{:a=>:b}], uncrossed.cross)
|
|
104
|
+
|
|
105
|
+
uncrossed = { :a => [ 1, :b ] }
|
|
106
|
+
assert_equal([{:a=>1}, {:a=>:b}], uncrossed.cross)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# def xtest_benchmark_1
|
|
110
|
+
# benchmark do
|
|
111
|
+
# 5000.times do
|
|
112
|
+
# uncrossed = { :a => [ 1, 2], :b => [ "bb", "cc"], :c => :cc }
|
|
113
|
+
# uncrossed.cross
|
|
114
|
+
# end
|
|
115
|
+
# end
|
|
116
|
+
# end
|
|
117
|
+
end if VEX_TEST == "base"
|