gold_mine 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +29 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +4 -0
- data/README.md +87 -0
- data/fortunes/fortunes +9711 -0
- data/fortunes/fortunes.dat +0 -0
- data/gold_mine.gemspec +23 -0
- data/lib/gold_mine.rb +18 -0
- data/lib/gold_mine/db.rb +75 -0
- data/lib/gold_mine/fortune.rb +48 -0
- data/lib/gold_mine/idb.rb +19 -0
- data/lib/gold_mine/index_reader.rb +80 -0
- data/lib/gold_mine/index_writer.rb +196 -0
- data/lib/gold_mine/version.rb +3 -0
- data/spec/gold_mine/db_spec.rb +72 -0
- data/spec/gold_mine/fortune_spec.rb +68 -0
- data/spec/gold_mine/index_reader.rb +63 -0
- data/spec/gold_mine/index_writer_spec.rb +202 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/temp_file.rb +25 -0
- metadata +115 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoldMine::DB do
|
4
|
+
add_temp_file
|
5
|
+
subject { GoldMine::DB.new(path: temp_file.path) }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
subject { GoldMine::DB.new }
|
9
|
+
let(:path) { subject.instance_variable_get("@path") }
|
10
|
+
let(:options) { subject.instance_variable_get("@options") }
|
11
|
+
|
12
|
+
context "without a :path option" do
|
13
|
+
it "assigns built-in database path to the @path" do
|
14
|
+
default_path = File.expand_path("../../../fortunes/fortunes", __FILE__)
|
15
|
+
expect(path).to eq(default_path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "without a :delim option" do
|
20
|
+
it "assigns default delimiter to the :delim option" do
|
21
|
+
expect(options[:delim]).to eq("%")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#random" do
|
27
|
+
temp_file <<-EOF.gsub(/^ {6}/, "")
|
28
|
+
"His mind is like a steel trap ― full of mice."
|
29
|
+
― Foghorn Leghorn
|
30
|
+
%
|
31
|
+
"Humor is a drug which it's the fashion to abuse."
|
32
|
+
― William Gilbert
|
33
|
+
EOF
|
34
|
+
|
35
|
+
it "returns the instance of fortune class" do
|
36
|
+
expect(subject.random).to be_a(GoldMine::Fortune)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns the random fortune" do
|
40
|
+
results = []
|
41
|
+
1000.times { results << subject.random }
|
42
|
+
expect(results.uniq.size).to be > 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#fortunes" do
|
47
|
+
temp_file <<-EOF.gsub(/^ {6}/, "")
|
48
|
+
"Deliver yesterday, code today, think tomorrow."
|
49
|
+
%
|
50
|
+
"Die? I should say not, dear fellow. No Barrymore would allow such a
|
51
|
+
conventional thing to happen to him."
|
52
|
+
― John Barrymore's dying words
|
53
|
+
%
|
54
|
+
"Do not stop to ask what is it;
|
55
|
+
Let us go and make our visit."
|
56
|
+
― T. S. Eliot, "The Love Song of J. Alfred Prufrock"
|
57
|
+
EOF
|
58
|
+
|
59
|
+
it "returns the array with fortune instances" do
|
60
|
+
array = subject.fortunes.map { |f| f.is_a?(GoldMine::Fortune) }
|
61
|
+
expect(array).to_not include(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns properly formated fortunes" do
|
65
|
+
expect(subject.fortunes.first.content).to eq("\"Deliver yesterday, code today, think tomorrow.\"")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns all fortunes from a database" do
|
69
|
+
expect(subject.fortunes.size).to eq(3)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoldMine::Fortune do
|
4
|
+
describe "#initialize" do
|
5
|
+
context "with content" do
|
6
|
+
subject do
|
7
|
+
GoldMine::Fortune.new <<-EOF.gsub(/^ {10}/, "")
|
8
|
+
I hate quotations.
|
9
|
+
― Ralph Waldo Emerson
|
10
|
+
EOF
|
11
|
+
end
|
12
|
+
|
13
|
+
it "assigns a text without attribution to the @content" do
|
14
|
+
expect(subject.instance_variable_get("@content")).to eq("I hate quotations.\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "assigns an attribution to the @attribution" do
|
18
|
+
expect(subject.instance_variable_get("@attribution")).to eq("Ralph Waldo Emerson")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#to_s" do
|
24
|
+
shared_examples "a formatting" do |format|
|
25
|
+
it "returns the string in the correct format" do
|
26
|
+
expect(subject.to_s).to eq(format)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when a content is empty and an attribution is empty" do
|
31
|
+
it "returns the empty string" do
|
32
|
+
expect(subject.to_s).to eq("")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when a content is empty and an attribution is not empty" do
|
37
|
+
before { subject.instance_variable_set("@attribution", "Yogi Berra") }
|
38
|
+
|
39
|
+
it "returns the empty string" do
|
40
|
+
expect(subject.to_s).to eq("")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when a content is not empty and an attribution is empty" do
|
45
|
+
before { subject.instance_variable_set("@content", "You can observe a lot just by watching.") }
|
46
|
+
|
47
|
+
include_examples "a formatting", <<-EOS.gsub(/^ {8}/, "")
|
48
|
+
|
49
|
+
You can observe a lot just by watching.
|
50
|
+
|
51
|
+
EOS
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when an content is not empty and an attribution is not empty" do
|
55
|
+
before do
|
56
|
+
subject.instance_variable_set("@attribution", "Yogi Berra")
|
57
|
+
subject.instance_variable_set("@content", "You can observe a lot just by watching.")
|
58
|
+
end
|
59
|
+
|
60
|
+
include_examples "a formatting", <<-EOS.gsub(/^ {8}/, "")
|
61
|
+
|
62
|
+
You can observe a lot just by watching.
|
63
|
+
― Yogi Berra
|
64
|
+
|
65
|
+
EOS
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoldMine::IndexReader do
|
4
|
+
add_temp_file
|
5
|
+
subject { described_class.new(temp_file.path) }
|
6
|
+
|
7
|
+
INDEX_HEADER = {
|
8
|
+
version: 2,
|
9
|
+
numstr: 3,
|
10
|
+
longlen: 612,
|
11
|
+
shortlen: 28,
|
12
|
+
flags: 0,
|
13
|
+
delim: 37
|
14
|
+
}
|
15
|
+
|
16
|
+
temp_file (INDEX_HEADER.values + [0, 12, 154]).pack("N*")
|
17
|
+
|
18
|
+
describe "#initialize" do
|
19
|
+
it "assigns a path to the @path" do
|
20
|
+
expect(subject.instance_variable_get("@path")).to eq(temp_file.path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#header_fields" do
|
25
|
+
it "returns an array of each value extracted" do
|
26
|
+
expect(subject.header_fields).to be_instance_of(Array)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns an array including six elements" do
|
30
|
+
expect(subject.header_fields.size).to eq(6)
|
31
|
+
end
|
32
|
+
|
33
|
+
INDEX_HEADER.each_with_index do |(element, value), index|
|
34
|
+
it "returns an array where element at #{index+1} position is a #{element}" do
|
35
|
+
expect(subject.header_fields[index]).to eq(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#get_pointers" do
|
41
|
+
it "returns an array instance" do
|
42
|
+
expect(subject.get_pointers).to be_instance_of(Array)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "extracts pointers from index file" do
|
46
|
+
expect(subject.get_pointers).to eq([0, 12, 154])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#get_pointer_at" do
|
51
|
+
it "returns pointer by index" do
|
52
|
+
expect(subject.get_pointer_at(1)).to eq(12)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#random_pointer" do
|
57
|
+
it "returns random pointer" do
|
58
|
+
results = []
|
59
|
+
1000.times { results << subject.random_pointer }
|
60
|
+
expect(results.uniq.size).to be > 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
describe GoldMine::IndexWriter do
|
5
|
+
add_temp_file
|
6
|
+
subject { described_class.new(temp_file.path) }
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
let(:options) { subject.instance_variable_get("@options") }
|
10
|
+
|
11
|
+
context "without an options" do
|
12
|
+
it "assigns default options to @options" do
|
13
|
+
default_options = {
|
14
|
+
version: 2,
|
15
|
+
delim: "%",
|
16
|
+
randomized: false,
|
17
|
+
ordered: false,
|
18
|
+
rotated: false,
|
19
|
+
comments: false
|
20
|
+
}
|
21
|
+
expect(options).to eq(default_options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with an options" do
|
26
|
+
subject { described_class.new(temp_file.path, version: 12) }
|
27
|
+
|
28
|
+
it "merged them with the default options" do
|
29
|
+
expect(options[:version]).to eq(12)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "assings to @shortlen very high number" do
|
34
|
+
shortlen = subject.instance_variable_get("@shortlen")
|
35
|
+
expect(shortlen).to be >= 99999
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with an :ordered option and a :randomized option" do
|
39
|
+
subject { described_class.new(temp_file.path, randomized: true, ordered: true) }
|
40
|
+
|
41
|
+
it "excludes :randomized" do
|
42
|
+
flags = subject.instance_variable_get("@flags")
|
43
|
+
expect(flags[:randomized]).to be_false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#load_pointers" do
|
49
|
+
temp_file <<-EOF.gsub(/^ {6}/, "")
|
50
|
+
%
|
51
|
+
I Like Facebook
|
52
|
+
%
|
53
|
+
"I see fire"
|
54
|
+
-- Ed Sheeran
|
55
|
+
%
|
56
|
+
CTSG
|
57
|
+
EOF
|
58
|
+
|
59
|
+
let(:load_pointers) { subject.load_pointers }
|
60
|
+
|
61
|
+
context "when :comments option is true" do
|
62
|
+
subject { described_class.new(temp_file.path, comments: true) }
|
63
|
+
|
64
|
+
temp_file <<-EOF.gsub(/^ {8}/, "")
|
65
|
+
%% It's new!
|
66
|
+
"Be or not to be"
|
67
|
+
%% What?!
|
68
|
+
-- Shakespeare
|
69
|
+
%
|
70
|
+
%% Todo: Change it!
|
71
|
+
EOF
|
72
|
+
|
73
|
+
describe "omits lines starting with double delimeter" do
|
74
|
+
it { expect(load_pointers.size).to eq(1) }
|
75
|
+
it { expect(load_pointers[0][0]).to eq(13) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns the array with correct size" do
|
80
|
+
expect(load_pointers.size).to eq(3)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "assings end of file position to @eof" do
|
84
|
+
expect(subject.instance_variable_get("@eof")).to eq(62)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "assings length of shortest fortune to @shortlen" do
|
88
|
+
expect(subject.instance_variable_get("@shortlen")).to eq(5)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "assings length of longest fortune to @longlen" do
|
92
|
+
expect(subject.instance_variable_get("@longlen")).to eq(35)
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "loads a fortune as an array" do
|
96
|
+
it { expect(load_pointers.sample).to be_a(Array) }
|
97
|
+
|
98
|
+
it "where first element is a correct pointer" do
|
99
|
+
expect(load_pointers[1][0]).to eq(20)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "where second element is a first character" do
|
103
|
+
expect(load_pointers[0][1]).to eq("I")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "even when is first and preceded by a delimeter" do
|
107
|
+
expect(load_pointers[0][0]).to eq(2)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "even when is located at the bottom of the file" do
|
111
|
+
expect(load_pointers[2][0]).to eq(57)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#order_pointers!" do
|
117
|
+
it "organizes @pointers alphabetically" do
|
118
|
+
subject.instance_variable_set("@pointers", [[23, "S"], [43, "A"], [61, "Z"]])
|
119
|
+
subject.order_pointers!
|
120
|
+
expect(subject.instance_variable_get("@pointers")).to eq([[43, "A"], [23, "S"], [61, "Z"]])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#shuffle_pointers!" do
|
125
|
+
it "shuffles @pointers" do
|
126
|
+
input = [[0, "A"], [1, "B"], [2, "C"]]
|
127
|
+
subject.instance_variable_set("@pointers", input)
|
128
|
+
subject.shuffle_pointers!
|
129
|
+
expect(subject.instance_variable_get("@pointers")).to_not eq(input)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#write" do
|
134
|
+
context "when @index_path is presence" do
|
135
|
+
before do
|
136
|
+
subject.instance_variable_set("@index_path", Dir.tmpdir + "/custom.dat")
|
137
|
+
end
|
138
|
+
|
139
|
+
it "saves a file at @index_path" do
|
140
|
+
subject.write
|
141
|
+
expect(File.exists?(subject.instance_variable_get("@index_path"))).to be_true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when @index_path is not presence" do
|
146
|
+
it "savas a file at @path.dat" do
|
147
|
+
subject.write
|
148
|
+
expect(File.exists?(subject.instance_variable_get('@path') + ".dat")).to be_true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#offsets" do
|
154
|
+
it "returns offsets from @pointers" do
|
155
|
+
subject.instance_variable_set("@pointers", [[0, "A"], [1, "B"]])
|
156
|
+
expect(subject.offsets).to eq([0, 1])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "#packed_pointers" do
|
161
|
+
describe "packs offsets into binary sequence" do
|
162
|
+
it "32-bit unsigned and big-endian" do
|
163
|
+
subject.instance_variable_set("@pointers", [[0, "A"], [22, "B"], [30, "C"]])
|
164
|
+
expect(subject.packed_pointers).to eq("\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x1E")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "#packed_eof" do
|
170
|
+
describe "packs @eof into binary sequence" do
|
171
|
+
it "as 32-bit unsigned big-endian" do
|
172
|
+
subject.instance_variable_set("@eof", 100)
|
173
|
+
expect(subject.packed_eof).to eq("\x00\x00\x00d")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#packed_header" do
|
179
|
+
before do
|
180
|
+
subject.instance_variable_set("@version", 2)
|
181
|
+
subject.instance_variable_set("@numstr", 23)
|
182
|
+
subject.instance_variable_set("@longlen", 31)
|
183
|
+
subject.instance_variable_set("@shortlen", 7)
|
184
|
+
subject.instance_variable_set("@flags", 0)
|
185
|
+
subject.instance_variable_set("@delim", "%")
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "packs header into binary sequence with big-endian convention where" do
|
189
|
+
let(:unpacked_header) { subject.packed_header.unpack("N*") }
|
190
|
+
|
191
|
+
["version", "numstr", "longlen", "shortlen", "flags"].each_with_index do |e, i|
|
192
|
+
it "#{i+1} element is a 32-bit unsigned integer representing #{e}" do
|
193
|
+
expect(unpacked_header[i]).to eq(subject.instance_variable_get("@#{e}"))
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
it "6 element is a 32-bit integer representing delim" do
|
198
|
+
expect(unpacked_header[5]).to eq(subject.instance_variable_get("@delim").ord << 24)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
|
3
|
+
module TempFile
|
4
|
+
def self.included(example_group)
|
5
|
+
example_group.extend(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_temp_file
|
9
|
+
let(:temp_file) do
|
10
|
+
Tempfile.new("gold_mine")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def temp_file(content = "")
|
15
|
+
before do
|
16
|
+
temp_file.open
|
17
|
+
temp_file.write(content)
|
18
|
+
temp_file.close
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
temp_file.unlink
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|