oklahoma_mixer 0.2.0 → 0.3.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.rdoc +7 -0
- data/README.rdoc +4 -0
- data/Rakefile +1 -1
- data/TODO.rdoc +2 -3
- data/lib/oklahoma_mixer.rb +7 -20
- data/lib/oklahoma_mixer/array_list.rb +21 -7
- data/lib/oklahoma_mixer/array_list/c.rb +9 -1
- data/lib/oklahoma_mixer/b_tree_database.rb +25 -9
- data/lib/oklahoma_mixer/b_tree_database/c.rb +5 -0
- data/lib/oklahoma_mixer/cursor.rb +3 -0
- data/lib/oklahoma_mixer/cursor/c.rb +2 -0
- data/lib/oklahoma_mixer/error.rb +2 -0
- data/lib/oklahoma_mixer/extensible_string.rb +4 -2
- data/lib/oklahoma_mixer/extensible_string/c.rb +2 -0
- data/lib/oklahoma_mixer/fixed_length_database.rb +11 -3
- data/lib/oklahoma_mixer/fixed_length_database/c.rb +2 -0
- data/lib/oklahoma_mixer/hash_database.rb +24 -7
- data/lib/oklahoma_mixer/hash_database/c.rb +2 -0
- data/lib/oklahoma_mixer/hash_map.rb +42 -0
- data/lib/oklahoma_mixer/hash_map/c.rb +27 -0
- data/lib/oklahoma_mixer/query.rb +73 -0
- data/lib/oklahoma_mixer/query/c.rb +51 -0
- data/lib/oklahoma_mixer/table_database.rb +402 -0
- data/lib/oklahoma_mixer/table_database/c.rb +104 -0
- data/lib/oklahoma_mixer/utilities.rb +26 -1
- data/test/{b_tree_binary_data_test.rb → b_tree_database/b_tree_binary_data_test.rb} +2 -2
- data/test/{b_tree_tuning_test.rb → b_tree_database/b_tree_tuning_test.rb} +2 -2
- data/test/{cursor_based_iteration_test.rb → b_tree_database/cursor_based_iteration_test.rb} +2 -2
- data/test/{duplicate_storage_test.rb → b_tree_database/duplicate_storage_test.rb} +18 -12
- data/test/{binary_data_test.rb → core_database/binary_data_test.rb} +2 -2
- data/test/{file_system_test.rb → core_database/file_system_test.rb} +0 -0
- data/test/{getting_and_setting_keys_test.rb → core_database/getting_and_setting_keys_test.rb} +31 -60
- data/test/{iteration_test.rb → core_database/iteration_test.rb} +2 -2
- data/test/{transactions_test.rb → core_database/transactions_test.rb} +0 -0
- data/test/core_database/tuning_test.rb +31 -0
- data/test/{fixed_length_tuning_test.rb → fixed_length_database/fixed_length_tuning_test.rb} +0 -0
- data/test/{getting_and_setting_by_id_test.rb → fixed_length_database/getting_and_setting_by_id_test.rb} +8 -0
- data/test/{shared_binary_data.rb → shared/binary_data_tests.rb} +1 -1
- data/test/{tuning_test.rb → shared/hash_tuning_tests.rb} +18 -42
- data/test/{shared_iteration.rb → shared/iteration_tests.rb} +8 -7
- data/test/{key_range_test.rb → shared/key_range_test.rb} +0 -0
- data/test/{order_test.rb → shared/order_test.rb} +0 -0
- data/test/shared/storage_tests.rb +65 -0
- data/test/{top_level_interface_test.rb → shared/top_level_interface_test.rb} +39 -2
- data/test/{shared_tuning.rb → shared/tuning_tests.rb} +1 -1
- data/test/table_database/document_iteration_test.rb +22 -0
- data/test/table_database/document_storage_test.rb +225 -0
- data/test/table_database/index_test.rb +57 -0
- data/test/table_database/query_test.rb +866 -0
- data/test/table_database/table_tuning_test.rb +56 -0
- data/test/test_helper.rb +27 -0
- metadata +35 -36
File without changes
|
@@ -266,6 +266,14 @@ class TestGettingAndSettingByID < Test::Unit::TestCase
|
|
266
266
|
assert_equal(3, @db.length)
|
267
267
|
end
|
268
268
|
|
269
|
+
def test_empty_returns_true_while_no_pairs_are_in_the_database
|
270
|
+
assert(@db.empty?, "An empty database was not detected")
|
271
|
+
@db[42] = :value
|
272
|
+
assert(!@db.empty?, "A non-empty database was reported empty")
|
273
|
+
@db.delete(42)
|
274
|
+
assert(@db.empty?, "An empty database was not detected")
|
275
|
+
end
|
276
|
+
|
269
277
|
def test_each_key_iterates_over_ids
|
270
278
|
@db.update(1 => 100, 2 => 200, 3 => 300)
|
271
279
|
keys = [1, 2, 3]
|
@@ -1,69 +1,66 @@
|
|
1
|
-
require "
|
2
|
-
|
1
|
+
require "shared/tuning_tests"
|
2
|
+
|
3
|
+
module HashTuningTests
|
4
|
+
include TuningTests
|
3
5
|
|
4
|
-
class TestTuning < Test::Unit::TestCase
|
5
|
-
def teardown
|
6
|
-
remove_db_files
|
7
|
-
end
|
8
|
-
|
9
|
-
include SharedTuning
|
10
|
-
|
11
6
|
def test_a_bucket_array_size_can_be_set_with_other_tuning_defaults
|
12
7
|
size = rand(1_000) + 1
|
13
8
|
assert_option_calls([:tune, size, -1, -1, 0xFF], :bnum => size)
|
14
9
|
end
|
15
|
-
|
10
|
+
|
16
11
|
def test_bucket_array_size_is_converted_to_an_int
|
17
12
|
assert_option_calls([:tune, 42, -1, -1, 0xFF], :bnum => "42")
|
18
13
|
end
|
19
|
-
|
14
|
+
|
20
15
|
def test_a_record_alignment_power_can_be_set_with_other_tuning_defaults
|
21
16
|
pow = rand(10) + 1
|
22
17
|
assert_option_calls([:tune, 0, pow, -1, 0xFF], :apow => pow)
|
23
18
|
end
|
24
|
-
|
19
|
+
|
25
20
|
def test_record_alignment_power_is_converted_to_an_int
|
26
21
|
assert_option_calls([:tune, 0, 42, -1, 0xFF], :apow => "42")
|
27
22
|
end
|
28
|
-
|
23
|
+
|
29
24
|
def test_a_max_free_block_power_can_be_set_with_other_tuning_defaults
|
30
25
|
pow = rand(10) + 1
|
31
26
|
assert_option_calls([:tune, 0, -1, pow, 0xFF], :fpow => pow)
|
32
27
|
end
|
33
|
-
|
28
|
+
|
34
29
|
def test_max_free_block_power_is_converted_to_an_int
|
35
30
|
assert_option_calls([:tune, 0, -1, 42, 0xFF], :fpow => "42")
|
36
31
|
end
|
37
|
-
|
32
|
+
|
38
33
|
def test_options_can_be_set_with_other_tuning_defaults
|
39
34
|
assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "ld")
|
40
35
|
end
|
41
|
-
|
36
|
+
|
42
37
|
def test_options_is_a_string_of_characters_mapped_to_enums_and_ored_together
|
43
|
-
|
38
|
+
db_prefix = self.class.name.include?("Table") ? "T" : "H"
|
39
|
+
opts = { "l" => lib::OPTS["#{db_prefix}DBTLARGE".to_sym],
|
40
|
+
"b" => lib::OPTS["#{db_prefix}DBTBZIP".to_sym]}
|
44
41
|
assert_option_calls( [ :tune, 0, -1, -1,
|
45
42
|
opts.values.inject(0) { |o, v| o | v } ],
|
46
43
|
:opts => opts.keys.join )
|
47
44
|
end
|
48
|
-
|
45
|
+
|
49
46
|
def test_the_options_string_is_not_case_sensative
|
50
47
|
assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "ld")
|
51
48
|
assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "LD")
|
52
49
|
end
|
53
|
-
|
50
|
+
|
54
51
|
def test_unknown_options_are_ignored_with_a_warning
|
55
52
|
warning = capture_stderr do
|
56
53
|
assert_option_calls([:tune, 0, -1, -1, 1 | 8], :opts => "ltu")
|
57
54
|
end
|
58
55
|
assert(!warning.empty?, "A warning was not issued for an unknown option")
|
59
56
|
end
|
60
|
-
|
57
|
+
|
61
58
|
def test_multiple_tuning_parameters_can_be_set_at_the_same_time
|
62
59
|
size = rand(1_000) + 1
|
63
60
|
assert_option_calls( [:tune, size, -1, -1, 1 | 2],
|
64
61
|
:bnum => size, :opts => "ld" )
|
65
62
|
end
|
66
|
-
|
63
|
+
|
67
64
|
def test_optimize_allows_the_adjustment_of_tune_options_for_an_open_database
|
68
65
|
db do |db|
|
69
66
|
args = capture_args(lib, :optimize) do
|
@@ -73,25 +70,4 @@ class TestTuning < Test::Unit::TestCase
|
|
73
70
|
assert_equal([0, 42, -1, 1 | 2], args[1..-1])
|
74
71
|
end
|
75
72
|
end
|
76
|
-
|
77
|
-
def test_limit_for_cached_records_can_be_set
|
78
|
-
limit = rand(1_000) + 1
|
79
|
-
assert_option_calls([:setcache, limit], :rcnum => limit)
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_limit_for_cached_records_is_converted_to_an_int
|
83
|
-
assert_option_calls([:setcache, 42], :rcnum => "42")
|
84
|
-
end
|
85
|
-
|
86
|
-
#######
|
87
|
-
private
|
88
|
-
#######
|
89
|
-
|
90
|
-
def lib
|
91
|
-
OKMixer::HashDatabase::C
|
92
|
-
end
|
93
|
-
|
94
|
-
def db(*args, &block)
|
95
|
-
hdb(*args, &block)
|
96
|
-
end
|
97
73
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module IterationTests
|
2
2
|
def test_each_key_iterates_over_all_keys_in_the_database
|
3
3
|
@db.each_key do |key|
|
4
4
|
@keys.delete(key)
|
@@ -11,7 +11,7 @@ module SharedIteration
|
|
11
11
|
assert_instance_of(Array, key_value_array)
|
12
12
|
assert_equal(2, key_value_array.size)
|
13
13
|
key, value = key_value_array
|
14
|
-
assert_equal(key
|
14
|
+
assert_equal(@values[@keys.index(key)], value)
|
15
15
|
@keys.delete(key)
|
16
16
|
@values.delete(value)
|
17
17
|
end
|
@@ -58,16 +58,17 @@ module SharedIteration
|
|
58
58
|
assert_kind_of(Enumerable, @db)
|
59
59
|
|
60
60
|
# examples
|
61
|
-
assert_equal(
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
assert_equal( ["b", @values[@keys.index("b")]],
|
62
|
+
@db.find { |key, _| key == "b" } )
|
63
|
+
assert_nil(@db.find { |key, _| key == "d" })
|
64
|
+
assert_equal(%w[aaa bbb ccc], @db.map { |key, _| key * 3 }.sort)
|
65
|
+
assert( @db.any? { |_, value| value.include?("c") or value.include?("cc") },
|
65
66
|
"A value was not found during iteration" )
|
66
67
|
end
|
67
68
|
|
68
69
|
def test_delete_if_removes_all_keys_for_which_the_block_returns_true
|
69
70
|
@db.delete_if { |key, _| key != "a" }
|
70
|
-
assert_equal(
|
71
|
+
assert_equal(%w[a], @db.keys)
|
71
72
|
end
|
72
73
|
|
73
74
|
def test_iterators_return_self_to_match_hash_interface
|
File without changes
|
File without changes
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module StorageTests
|
2
|
+
def test_fetching_a_missing_value_fails_with_an_index_error
|
3
|
+
assert_raise(IndexError) do
|
4
|
+
@db.fetch(:missing)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_fetch_can_return_a_default_for_a_missing_value
|
9
|
+
assert_equal(42, @db.fetch(:missing, 42))
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_fetch_can_run_a_block_returning_its_result_for_a_missing_value
|
13
|
+
assert_equal(:missing, @db.fetch(:missing) { |key| key })
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_fetching_with_a_block_overrides_a_default_and_triggers_a_warning
|
17
|
+
warning = capture_stderr do
|
18
|
+
assert_equal(42, @db.fetch(:missing, 13) { 42 })
|
19
|
+
end
|
20
|
+
assert(!warning.empty?, "A warning was not issued for a default and block")
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_store_and_fetch_can_also_be_used_through_the_indexing_brackets
|
24
|
+
assert_equal({:a => 1, :b => 2}, @db[:doc] = {:a => 1, :b => 2})
|
25
|
+
assert_equal({"a" => "1", "b" => "2"}, @db[:doc])
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_indexing_returns_nil_instead_of_failing_with_index_error
|
29
|
+
assert_nil(@db[:missing])
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_a_default_can_be_set_for_indexing_to_return
|
33
|
+
@db.default = 42
|
34
|
+
assert_equal(42, @db[:missing])
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_the_indexing_default_will_be_run_if_it_is_a_proc
|
38
|
+
@db.default = lambda { |key| "is #{key}" }
|
39
|
+
assert_equal("is missing", @db[:missing])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_the_indexing_default_for_a_given_key_can_be_retrieved
|
43
|
+
@db.default = lambda { |key| "is #{key}" }
|
44
|
+
assert_equal("is ", @db.default)
|
45
|
+
assert_equal("is missing", @db.default(:missing))
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_the_indexing_default_can_be_changed
|
49
|
+
assert_nil(@db[:missing])
|
50
|
+
assert_equal(42, @db.default = 42)
|
51
|
+
assert_equal(42, @db[:missing])
|
52
|
+
proc = lambda { |key| fail RuntimeError, "%p not found" % [key]}
|
53
|
+
assert_equal(proc, @db.default = proc)
|
54
|
+
error = assert_raise(RuntimeError) { @db[:missing] }
|
55
|
+
assert_equal(":missing not found", error.message)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_delete_returns_nil_for_a_missing_key
|
59
|
+
assert_nil(@db.delete(:missing))
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_delete_can_be_passed_a_block_to_handle_missing_keys
|
63
|
+
assert_equal(42, @db.delete(:missing) { 42 })
|
64
|
+
end
|
65
|
+
end
|
@@ -5,10 +5,33 @@ class TestTopLevelInterface < Test::Unit::TestCase
|
|
5
5
|
remove_db_files
|
6
6
|
end
|
7
7
|
|
8
|
+
def test_ok_mixer_is_a_shortcut_for_oklahoma_mixer
|
9
|
+
assert_same(OklahomaMixer, OKMixer)
|
10
|
+
end
|
11
|
+
|
8
12
|
def test_the_version_constant_contains_the_current_version_number
|
9
13
|
assert_match(/\A\d\.\d\.\d\z/, OKMixer::VERSION)
|
10
14
|
end
|
11
15
|
|
16
|
+
def test_the_hash_database_interface_is_autoloaded_as_needed
|
17
|
+
assert_loads(%w[hash], %Q{OKMixer.open(#{db_path('tch').inspect}) { }})
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_the_b_tree_database_interface_is_autoloaded_as_needed
|
21
|
+
assert_loads( %w[b_tree hash],
|
22
|
+
%Q{OKMixer.open(#{db_path('tcb').inspect}) { }} )
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_the_fixed_length_database_interface_is_autoloaded_as_needed
|
26
|
+
assert_loads( %w[fixed_length hash],
|
27
|
+
%Q{OKMixer.open(#{db_path('tcf').inspect}) { }} )
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_the_table_database_interface_is_autoloaded_as_needed
|
31
|
+
assert_loads( %w[hash table],
|
32
|
+
%Q{OKMixer.open(#{db_path('tct').inspect}) { }} )
|
33
|
+
end
|
34
|
+
|
12
35
|
def test_open_of_a_tch_extension_file_creates_a_hash_database
|
13
36
|
OKMixer.open(db_path("tch")) do |db|
|
14
37
|
assert_instance_of(OKMixer::HashDatabase, db)
|
@@ -27,6 +50,12 @@ class TestTopLevelInterface < Test::Unit::TestCase
|
|
27
50
|
end
|
28
51
|
end
|
29
52
|
|
53
|
+
def test_open_of_a_tct_extension_file_creates_a_table_database
|
54
|
+
OKMixer.open(db_path("tct")) do |db|
|
55
|
+
assert_instance_of(OKMixer::TableDatabase, db)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
30
59
|
def test_open_of_an_unrecognized_extension_fails_with_an_error
|
31
60
|
assert_raise(ArgumentError) do
|
32
61
|
OKMixer.open("unrecognized")
|
@@ -53,7 +82,15 @@ class TestTopLevelInterface < Test::Unit::TestCase
|
|
53
82
|
db.close if db
|
54
83
|
end
|
55
84
|
|
56
|
-
|
57
|
-
|
85
|
+
private
|
86
|
+
|
87
|
+
def assert_loads(fields, ruby_code)
|
88
|
+
run_ruby <<-END_RUBY
|
89
|
+
loaded_dbs = lambda { $".grep(/\\b(\\w+)_database\\.rb\\z/) { $1 }.sort }
|
90
|
+
before = loaded_dbs.call
|
91
|
+
#{ruby_code}
|
92
|
+
puts loaded_dbs.call - before
|
93
|
+
END_RUBY
|
94
|
+
assert_equal(fields, @output.scan(/\w+/))
|
58
95
|
end
|
59
96
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "shared/iteration_tests"
|
3
|
+
|
4
|
+
class TestDocumentIteration < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@db = tdb
|
7
|
+
@keys = %w[a b c]
|
8
|
+
@values = @keys.map { |key|
|
9
|
+
Hash[*@keys.map { |k| [key + k, "abc".index(k).to_s] }.flatten]
|
10
|
+
}
|
11
|
+
@keys.zip(@values) do |key, value|
|
12
|
+
@db[key] = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
@db.close
|
18
|
+
remove_db_files
|
19
|
+
end
|
20
|
+
|
21
|
+
include IterationTests
|
22
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "shared/storage_tests"
|
3
|
+
|
4
|
+
class TestDocumentStorage < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@db = tdb
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
@db.close
|
11
|
+
remove_db_files
|
12
|
+
end
|
13
|
+
|
14
|
+
include StorageTests
|
15
|
+
|
16
|
+
def test_a_document_can_be_stored_and_fetched_from_the_database
|
17
|
+
document = {"a" => "1", "b" => "2"}
|
18
|
+
assert_equal(document, @db.store("document", document))
|
19
|
+
assert_equal(document, @db.fetch("document")) # later
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_both_all_keys_and_values_are_converted_to_strings
|
23
|
+
assert_equal({:a => 1, :b => 2}, @db.store(:document, {:a => 1, :b => 2}))
|
24
|
+
assert_equal( {"a" => "1", "b" => "2"},
|
25
|
+
@db.fetch("document") ) # effectively the same key
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_storing_with_keep_mode_adds_a_document_only_if_it_didnt_already_exist
|
29
|
+
original = {"a" => "1", "b" => "2"}
|
30
|
+
replacement = {"a" => "1", "c" => "3"}
|
31
|
+
assert(@db.store(:doc, original, :keep), "Failed to store a new key")
|
32
|
+
assert_equal(original, @db.fetch(:doc))
|
33
|
+
assert(!@db.store(:doc, replacement, :keep), "Replaced an existing key")
|
34
|
+
assert_equal(original, @db.fetch(:doc))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_storing_with_cat_mode_concatenates_new_columns_onto_a_document
|
38
|
+
original = {"a" => "1", "b" => "2"}
|
39
|
+
added = {"a" => "ignored", "c" => "new"}
|
40
|
+
assert_equal(original, @db.store(:cols, original, :cat))
|
41
|
+
assert_equal(original, @db.fetch(:cols))
|
42
|
+
assert_equal(added, @db.store(:cols, added, :cat))
|
43
|
+
assert_equal(original.merge("c" => "new"), @db.fetch(:cols))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_storing_with_add_mode_adds_to_an_existing_value
|
47
|
+
assert_equal(0, @db.store(:i, 0, :add))
|
48
|
+
assert_equal(1, @db.store(:i, 1, :add))
|
49
|
+
assert_equal(2, @db.store(:i, 1, :add))
|
50
|
+
assert_equal(1, @db.store(:i, -1, :add))
|
51
|
+
|
52
|
+
assert_in_delta(1.5, @db.store(:f, 1.5, :add), 2 ** -20)
|
53
|
+
assert_in_delta(3.5, @db.store(:f, 2.0, :add), 2 ** -20)
|
54
|
+
assert_in_delta(2.5, @db.store(:f, -1.0, :add), 2 ** -20)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_additions_are_stored_in_a_document
|
58
|
+
assert_equal(42, @db.store(:i, 42, :add))
|
59
|
+
assert_equal({"_num" => "42"}, @db.fetch(:i))
|
60
|
+
|
61
|
+
assert_equal(3.14, @db.store(:f, 3.14, :add))
|
62
|
+
assert_equal({"_num" => "3.14"}, @db.fetch(:f))
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_additions_can_be_added_to_a_document
|
66
|
+
int = {"type" => "Integer"}
|
67
|
+
assert_equal(int, @db.store(:i, int))
|
68
|
+
assert_equal(42, @db.store(:i, 42, :add))
|
69
|
+
assert_equal(int.merge("_num" => "42"), @db.fetch(:i))
|
70
|
+
|
71
|
+
flt = {"type" => "Float"}
|
72
|
+
assert_equal(flt, @db.store(:f, flt))
|
73
|
+
assert_equal(3.14, @db.store(:f, 3.14, :add))
|
74
|
+
assert_equal(flt.merge("_num" => "3.14"), @db.fetch(:f))
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_adding_to_a_non_added_number_clobbers_the_column
|
78
|
+
junk = {"_num" => "junk"}
|
79
|
+
assert_equal(junk, @db.store(:i, junk))
|
80
|
+
assert_equal(1, @db.store(:i, 1, :add))
|
81
|
+
assert_equal({"_num" => "1"}, @db.fetch(:i))
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_addition_types_can_be_switched_as_needed
|
85
|
+
assert_equal(1, @db.store(:num, 1, :add))
|
86
|
+
assert_equal(2.0, @db.store(:num, 1.0, :add))
|
87
|
+
assert_equal(2.1, @db.store(:num, 0.1, :add))
|
88
|
+
assert_equal(3, @db.store(:num, 1, :add)) # truncated
|
89
|
+
assert_equal(3.5, @db.store(:num, 0.4, :add))
|
90
|
+
assert_equal(4, @db.store(:num, 1, :add)) # truncated
|
91
|
+
assert_equal({"_num" => "4.5"}, @db.fetch(:num))
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_storing_with_a_block_allows_duplicate_resolution
|
95
|
+
@db[:key] = {:a => 1, :b => 2}
|
96
|
+
assert_equal( {:b => 2.5, :c => 3},
|
97
|
+
@db.store(:key, {:b => 2.5, :c => 3}) { |key, old, new|
|
98
|
+
old.merge(new).merge("" => key)
|
99
|
+
} )
|
100
|
+
assert_equal({"" => "key", "a" => "1", "b" => "2.5", "c" => "3"}, @db[:key])
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_storing_with_a_block_overrides_a_mode_and_triggers_a_warning
|
104
|
+
warning = capture_stderr do
|
105
|
+
assert_equal({ }, @db.store(:key, { }, :keep) { |key, old, new| })
|
106
|
+
end
|
107
|
+
assert(!warning.empty?, "A warning was not issued for a mode and block")
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_include_and_aliases_can_be_used_to_check_for_the_existance_of_a_key
|
111
|
+
@db[:exist] = {"I" => "exist"}
|
112
|
+
%w[include? has_key? key? member?].each do |query|
|
113
|
+
assert(@db.send(query, :exist), "Failed to detect an existing key")
|
114
|
+
assert(!@db.send(query, :missing), "Failed to detect an missing key")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_update_sets_multiple_documents_at_once_overwriting_old_documents
|
119
|
+
@db[:b] = {"b" => "old"}
|
120
|
+
assert_equal( @db, @db.update( :a => {"a" => "new"},
|
121
|
+
:b => {"b" => "new"},
|
122
|
+
:c => {"c" => "new"}) )
|
123
|
+
assert_equal( [ {"a" => "new"},
|
124
|
+
{"b" => "new"},
|
125
|
+
{"c" => "new"} ], @db.values_at(:a, :b, :c) )
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_update_can_be_passed_a_block_for_handling_duplicates
|
129
|
+
@db[:b] = {:keep => 1, :replace => 2}
|
130
|
+
assert_equal( @db, @db.update( :a => {:new => 1},
|
131
|
+
:b => {:replace => 2.5, :add => 3},
|
132
|
+
:c => {:new => 2} ) { |key, old, new|
|
133
|
+
old.merge(new).merge("" => key)
|
134
|
+
} )
|
135
|
+
assert_equal( [ {"new" => "1"},
|
136
|
+
{ "" => "b",
|
137
|
+
"keep" => "1",
|
138
|
+
"replace" => "2.5",
|
139
|
+
"add" => "3" },
|
140
|
+
{"new" => "2"} ], @db.values_at(:a, :b, :c) )
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_values_at_can_be_used_to_retrieve_multiple_documents_at_once
|
144
|
+
@db[:a] = {:a => 1}
|
145
|
+
@db[:c] = {:c => 2}
|
146
|
+
assert_equal([ ], @db.values_at)
|
147
|
+
assert_equal( [ {"a" => "1"},
|
148
|
+
nil,
|
149
|
+
{"c" => "2"} ], @db.values_at(:a, :b, :c) )
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_values_at_supports_defaults
|
153
|
+
@db.default = { }
|
154
|
+
@db[:a] = {:a => 1}
|
155
|
+
@db[:c] = {:c => 2}
|
156
|
+
assert_equal( [ {"a" => "1"},
|
157
|
+
{ },
|
158
|
+
{"c" => "2"} ], @db.values_at(:a, :b, :c))
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_keys_returns_all_keys_in_the_database
|
162
|
+
@db.update(:a => {:a => 1}, :b => {:b => 2}, :c => {:c => 3})
|
163
|
+
assert_equal(%w[a b c], @db.keys.sort)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_keys_can_take_a_prefix_which_all_returned_keys_must_start_with
|
167
|
+
@db.update(:a => {:a => 1}, :ab => {:ab => 2}, :c => {:c => 3})
|
168
|
+
assert_equal(%w[a ab], @db.keys(:prefix => :a).sort)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_keys_can_take_a_limit_of_keys_to_return
|
172
|
+
@db.update(:a => {:a => 1}, :b => {:b => 2}, :c => {:c => 3})
|
173
|
+
keys = @db.keys(:limit => 2)
|
174
|
+
assert_equal(2, keys.size)
|
175
|
+
keys.each do |key|
|
176
|
+
assert(%w[a b c].include?(key), "Key wasn't in known keys")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_values_returns_all_values_in_the_database
|
181
|
+
@db.update(:a => {:a => 1}, :b => {:b => 2}, :c => {:c => 3})
|
182
|
+
assert_equal( [ {"a" => "1"},
|
183
|
+
{"b" => "2"},
|
184
|
+
{"c" => "3"} ], @db.values.sort_by { |h| h.to_a } )
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_delete_removes_a_document_from_the_database
|
188
|
+
@db[:doc] = {"a" => "1"}
|
189
|
+
assert_equal({"a" => "1"}, @db.delete(:doc))
|
190
|
+
assert_nil(@db[:doc])
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_clear_removes_all_keys_from_the_database
|
194
|
+
@db.update(:a => {:a => 1}, :b => {:b => 2}, :c => {:c => 3})
|
195
|
+
assert_equal(@db, @db.clear)
|
196
|
+
assert_equal([nil, nil, nil], @db.values_at(:a, :b, :c))
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_size_and_length_return_the_count_of_key_value_pairs_in_the_database
|
200
|
+
assert_equal(0, @db.size)
|
201
|
+
assert_equal(0, @db.length)
|
202
|
+
@db.update(:a => {:a => 1}, :b => {:b => 2}, :c => {:c => 3})
|
203
|
+
assert_equal(3, @db.size)
|
204
|
+
assert_equal(3, @db.length)
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_empty_returns_true_while_no_pairs_are_in_the_database
|
208
|
+
assert(@db.empty?, "An empty database was not detected")
|
209
|
+
@db[:doc] = {"a" => "1"}
|
210
|
+
assert(!@db.empty?, "A non-empty database was reported empty")
|
211
|
+
@db.delete(:doc)
|
212
|
+
assert(@db.empty?, "An empty database was not detected")
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_generate_unique_id_and_uid_can_be_used_to_manage_increasing_ids
|
216
|
+
assert_equal(1, @db.generate_unique_id)
|
217
|
+
assert_equal([2, 3, 4, 5], Array.new(4) { @db.uid })
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_uids_are_not_stored_in_documents
|
221
|
+
assert_equal(0, @db.size)
|
222
|
+
assert_equal(1, @db.uid)
|
223
|
+
assert_equal(0, @db.size)
|
224
|
+
end
|
225
|
+
end
|