dbmlite3 2.0.0.pre.alpha.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,217 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "README";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="file_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: README</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <h1 id="label-Simple+DBM-style+key-value+database+using+SQLite3">Simple DBM-style key-value database using SQLite3</h1>
62
+
63
+ <h2 id="label-Description">Description</h2>
64
+
65
+ <p><code>dbmlite3</code> is a simple key-value store built on top of SQLite3 that provides a Hash-like interface. It is a drop-in replacement for <code>DBM</code> or <code>YAML::DBM</code> that uses SQLite3 to do the underlying storage.</p>
66
+
67
+ <h2 id="label-Why-3F">Why?</h2>
68
+
69
+ <p>Because DBM is really simple and SQLite3 is solid, reliable, ubiquitous, and file-format-compatible across all platforms. This gem gives you the best of both worlds.</p>
70
+
71
+ <h2 id="label-Synopsis">Synopsis</h2>
72
+
73
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>dbmlite3</span><span class='tstring_end'>&#39;</span></span>
74
+
75
+ <span class='comment'># Open a table in a database
76
+ </span><span class='id identifier rubyid_settings'>settings</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Lite3/DBM.html#initialize-instance_method" title="Lite3::DBM#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>settings</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span>
77
+
78
+ <span class='comment'># You use it like a hash
79
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>88</span>
80
+ <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>date</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='const'>Date</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='int'>1955</span><span class='comma'>,</span> <span class='int'>11</span><span class='comma'>,</span> <span class='int'>5</span><span class='rparen'>)</span> <span class='comment'># Most Ruby types are allowed
81
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>power_threshold</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='float'>2.2</span>
82
+
83
+ <span class='id identifier rubyid_puts'>puts</span> <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>power_threshold</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span>
84
+
85
+ <span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_each'>each</span><span class='lbrace'>{</span><span class='op'>|</span><span class='id identifier rubyid_k'>k</span><span class='comma'>,</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span> <span class='id identifier rubyid_puts'>puts</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>setting: </span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_k'>k</span><span class='embexpr_end'>}</span><span class='tstring_content'> = </span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_v'>v</span><span class='embexpr_end'>}</span><span class='tstring_end'>&quot;</span></span> <span class='rbrace'>}</span>
86
+
87
+ <span class='comment'># But you also have transactions
88
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_transaction'>transaction</span><span class='lbrace'>{</span>
89
+ <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>*</span> <span class='int'>2</span>
90
+ <span class='rbrace'>}</span>
91
+
92
+ <span class='comment'># You can open other tables in the same database if you want, as above
93
+ </span><span class='comment'># or with a block
94
+ </span><span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_open'><span class='object_link'><a href="Lite3/DBM.html#open-class_method" title="Lite3::DBM.open (method)">open</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_stats'>stats</span><span class='op'>|</span>
95
+ <span class='id identifier rubyid_stats'>stats</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>42</span>
96
+
97
+ <span class='comment'># You can even open multiple handles to the same table if you need to
98
+ </span> <span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_open'><span class='object_link'><a href="Lite3/DBM.html#open-class_method" title="Lite3::DBM.open (method)">open</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_stats2'>stats2</span><span class='op'>|</span>
99
+ <span class='id identifier rubyid_stats2'>stats2</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
100
+ <span class='rbrace'>}</span>
101
+
102
+ <span class='id identifier rubyid_puts'>puts</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats=</span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_stats'>stats</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span><span class='embexpr_end'>}</span><span class='tstring_end'>&quot;</span></span>
103
+ <span class='rbrace'>}</span>
104
+
105
+ <span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_close'>close</span>
106
+ </code></pre>
107
+
108
+ <p>Complete documentation is available in the accompanying rdoc.</p>
109
+
110
+ <h2 id="label-Installation">Installation</h2>
111
+
112
+ <p><code>dbmlite3</code> is available as a gem:</p>
113
+
114
+ <pre class="code ruby"><code class="ruby">$ [sudo] gem install dbmlite3
115
+ </code></pre>
116
+
117
+ <p>Alternately, you can fetch the source code from GitLab and build it yourself:</p>
118
+
119
+ <pre class="code ruby"><code class="ruby">$ git clone https://gitlab.com/suetanvil/dbmlite3
120
+ $ cd dbmlite3
121
+ $ rake
122
+ </code></pre>
123
+
124
+ <p>It depends on the gem <code>sequel</code>; previously, it used <code>sqlite3</code>.</p>
125
+
126
+ <h2 id="label-Quirks+and+Hints">Quirks and Hints</h2>
127
+
128
+ <h3 id="label-Remember+that+a+DBM+is+a+-28potentially-29+shared+file">Remember that a <code>DBM</code> is a (potentially) shared file</h3>
129
+
130
+ <p>It is important to keep in mind that while <code>Lite3::DBM</code> objects look like Hashes, they are accessing files on disk that other processes could modify at any time.</p>
131
+
132
+ <p>For example, an innocuous-looking expression like</p>
133
+
134
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+</span> <span class='int'>1</span>
135
+ </code></pre>
136
+
137
+ <p>or its shorter equivalent</p>
138
+
139
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
140
+ </code></pre>
141
+
142
+ <p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others’ result.</p>
143
+
144
+ <p>There are two ways to deal with this. You can wrap the read-modify-write cycle in a transaction:</p>
145
+
146
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_transaction'>transaction</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span> <span class='rbrace'>}</span>
147
+ </code></pre>
148
+
149
+ <p>Or, of course, you could just design your script or program so that only one program accesses the table at a time.</p>
150
+
151
+ <h3 id="label-Keys+must+be+strings">Keys must be strings</h3>
152
+
153
+ <p>While values may be any serializable type, keys <em>must</em> be strings. As a special exception, Symbols are also allowed but are transparently converted to Strings first. This means that while something like this will work:</p>
154
+
155
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='symbol'>:foo</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>42</span>
156
+ </code></pre>
157
+
158
+ <p>a subseqent</p>
159
+
160
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_keys'>keys</span><span class='period'>.</span><span class='id identifier rubyid_include?'>include?</span><span class='lparen'>(</span><span class='symbol'>:foo</span><span class='rparen'>)</span> <span class='kw'>or</span> <span class='id identifier rubyid_raise'>raise</span> <span class='const'>AbjectFailure</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span>
161
+ </code></pre>
162
+
163
+ <p>will raise an exception because the key <code>:foo</code> was turned into a string before being used. You will need to do this instead:</p>
164
+
165
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_keys'>keys</span><span class='period'>.</span><span class='id identifier rubyid_include?'>include?</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='kw'>or</span> <span class='id identifier rubyid_raise'>raise</span> <span class='const'>AbjectFailure</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span>
166
+ </code></pre>
167
+
168
+ <p>However, this</p>
169
+
170
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_has_key?'>has_key?</span><span class='lparen'>(</span><span class='symbol'>:foo</span><span class='rparen'>)</span>
171
+ </code></pre>
172
+
173
+ <p>will work because <code>has_key?</code> does the conversion for us.</p>
174
+
175
+ <h3 id="label-Transactions+and+performance">Transactions and performance</h3>
176
+
177
+ <p>If you need to do a large number of accesses in a short amount of time (e.g. loading data from a file), it is significantly faster to do these in batches in one or more transactions.</p>
178
+
179
+ <h3 id="label-Serialization+Safety">Serialization Safety</h3>
180
+
181
+ <p><code>Lite3::DBM</code> stores Ruby data by first serializing values using the <code>Marshal</code> or <code>Psych</code> modules. This can pose a security risk if an untrusted third party has direct access to the underlying SQLite3 database. This tends to be pretty rare most of the time but if it is a concern, you can always configure <code>Lite3::DBM</code> to store its values as plain strings.</p>
182
+
183
+ <h3 id="label-Forking+safely">Forking safely</h3>
184
+
185
+ <p>It is a documented limitation of SQLite3 that database objects cannot be carried across a process fork. Either the parent or the child process will keep the open handle and the other one must forget it completely.</p>
186
+
187
+ <p>For this reason, if you need both the parent and child process to be able to use <code>Lite3::DBM</code> after a <code>fork</code>, you must first call <code>Lite3::SQL.close_all</code>. Not only will this make it safe but it also lets the child and parent use the same <code>Lite3::DBM</code> objects.</p>
188
+
189
+ <h3 id="label-Lite3-3A-3ADBM+objects+act+like+file+handles+but+are+not"><code>Lite3::DBM</code> objects act like file handles but are not</h3>
190
+
191
+ <p>While it is generally safe to treat <code>Lite3::DBM</code> as a wrapper around a file handle (i.e. <code>open</code> and <code>close</code> work as expected), you should be aware that this is not precisely the way things actually work. Instead, the gem maintains a pool of database handles, one per file, and associates them with <code>Lite3::DBM</code> instances as needed. This is necessary for transactions to work correctly.</p>
192
+
193
+ <p>Mostly, you don’t need to care about this. However, it affects you in the following ways:</p>
194
+ <ol><li>
195
+ <p>Transactions are done at the file level and not the table level. This means that you can access separate tables in the same transaction, which is a Very Good Thing.</p>
196
+ </li><li>
197
+ <p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you've invoked <code>Lite3::SQL.close_all</code> before the fork. This will have closed the actual database handles (which can't tolerate being carried across a fork) and opens new ones the next time they're needed.</p>
198
+ </li></ol>
199
+
200
+ <p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it’s almost always a bad idea to not explicitly call <code>close</code> first.</p>
201
+
202
+ <h3 id="label-Under+the+hood">Under the hood</h3>
203
+
204
+ <p>Currently, <code>Lite3::DBM</code> uses <a href="https://sequel.jeremyevans.net">Sequel</a> to access the <code>sqlite3</code> library. On JRuby, it goes through the <code>jdbc</code> interface. The previous version (1.0.0) used <a href="https://github.com/sparklemotion/sqlite3-ruby">sqlite3</a> and only worked on MRI. However, you should make no assumptions about the underlying database libraries this gem uses. It may change in a future release.</p>
205
+
206
+ <p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don’t make global changes or mix transactions across interfaces. However, I make no guarantees.</p>
207
+ </div></div>
208
+
209
+ <div id="footer">
210
+ Generated on Sat Aug 19 13:22:01 2023 by
211
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
212
+ 0.9.34 (ruby-3.2.1).
213
+ </div>
214
+
215
+ </div>
216
+ </body>
217
+ </html>
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="utf-8" />
6
+
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" />
8
+
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" />
10
+
11
+
12
+
13
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
14
+
15
+ <script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
16
+
17
+
18
+ <title>File List</title>
19
+ <base id="base_target" target="_parent" />
20
+ </head>
21
+ <body>
22
+ <div id="content">
23
+ <div class="fixed_header">
24
+ <h1 id="full_list_header">File List</h1>
25
+ <div id="full_list_nav">
26
+
27
+ <span><a target="_self" href="class_list.html">
28
+ Classes
29
+ </a></span>
30
+
31
+ <span><a target="_self" href="method_list.html">
32
+ Methods
33
+ </a></span>
34
+
35
+ <span><a target="_self" href="file_list.html">
36
+ Files
37
+ </a></span>
38
+
39
+ </div>
40
+
41
+ <div id="search">Search: <input type="text" /></div>
42
+ </div>
43
+
44
+ <ul id="full_list" class="file">
45
+
46
+
47
+ <li id="object_README" class="odd">
48
+ <div class="item"><span class="object_link"><a href="index.html" title="README">README</a></span></div>
49
+ </li>
50
+
51
+
52
+
53
+ </ul>
54
+ </div>
55
+ </body>
56
+ </html>
data/doc/frames.html ADDED
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Documentation by YARD 0.9.34</title>
6
+ </head>
7
+ <script type="text/javascript">
8
+ var match = unescape(window.location.hash).match(/^#!(.+)/);
9
+ var name = match ? match[1] : 'index.html';
10
+ name = name.replace(/^(\w+):\/\//, '').replace(/^\/\//, '');
11
+ window.top.location = name;
12
+ </script>
13
+ <noscript>
14
+ <h1>Oops!</h1>
15
+ <h2>YARD requires JavaScript!</h2>
16
+ </noscript>
17
+ </html>
data/doc/index.html ADDED
@@ -0,0 +1,217 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "README";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: README</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <h1 id="label-Simple+DBM-style+key-value+database+using+SQLite3">Simple DBM-style key-value database using SQLite3</h1>
62
+
63
+ <h2 id="label-Description">Description</h2>
64
+
65
+ <p><code>dbmlite3</code> is a simple key-value store built on top of SQLite3 that provides a Hash-like interface. It is a drop-in replacement for <code>DBM</code> or <code>YAML::DBM</code> that uses SQLite3 to do the underlying storage.</p>
66
+
67
+ <h2 id="label-Why-3F">Why?</h2>
68
+
69
+ <p>Because DBM is really simple and SQLite3 is solid, reliable, ubiquitous, and file-format-compatible across all platforms. This gem gives you the best of both worlds.</p>
70
+
71
+ <h2 id="label-Synopsis">Synopsis</h2>
72
+
73
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>dbmlite3</span><span class='tstring_end'>&#39;</span></span>
74
+
75
+ <span class='comment'># Open a table in a database
76
+ </span><span class='id identifier rubyid_settings'>settings</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Lite3/DBM.html#initialize-instance_method" title="Lite3::DBM#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>settings</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span>
77
+
78
+ <span class='comment'># You use it like a hash
79
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>88</span>
80
+ <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>date</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='const'>Date</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='int'>1955</span><span class='comma'>,</span> <span class='int'>11</span><span class='comma'>,</span> <span class='int'>5</span><span class='rparen'>)</span> <span class='comment'># Most Ruby types are allowed
81
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>power_threshold</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='float'>2.2</span>
82
+
83
+ <span class='id identifier rubyid_puts'>puts</span> <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>power_threshold</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span>
84
+
85
+ <span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_each'>each</span><span class='lbrace'>{</span><span class='op'>|</span><span class='id identifier rubyid_k'>k</span><span class='comma'>,</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span> <span class='id identifier rubyid_puts'>puts</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>setting: </span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_k'>k</span><span class='embexpr_end'>}</span><span class='tstring_content'> = </span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_v'>v</span><span class='embexpr_end'>}</span><span class='tstring_end'>&quot;</span></span> <span class='rbrace'>}</span>
86
+
87
+ <span class='comment'># But you also have transactions
88
+ </span><span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_transaction'>transaction</span><span class='lbrace'>{</span>
89
+ <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_settings'>settings</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>speed</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>*</span> <span class='int'>2</span>
90
+ <span class='rbrace'>}</span>
91
+
92
+ <span class='comment'># You can open other tables in the same database if you want, as above
93
+ </span><span class='comment'># or with a block
94
+ </span><span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_open'><span class='object_link'><a href="Lite3/DBM.html#open-class_method" title="Lite3::DBM.open (method)">open</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_stats'>stats</span><span class='op'>|</span>
95
+ <span class='id identifier rubyid_stats'>stats</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>42</span>
96
+
97
+ <span class='comment'># You can even open multiple handles to the same table if you need to
98
+ </span> <span class='const'><span class='object_link'><a href="Lite3.html" title="Lite3 (module)">Lite3</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Lite3/DBM.html" title="Lite3::DBM (class)">DBM</a></span></span><span class='period'>.</span><span class='id identifier rubyid_open'><span class='object_link'><a href="Lite3/DBM.html#open-class_method" title="Lite3::DBM.open (method)">open</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>config.sqlite3</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_stats2'>stats2</span><span class='op'>|</span>
99
+ <span class='id identifier rubyid_stats2'>stats2</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
100
+ <span class='rbrace'>}</span>
101
+
102
+ <span class='id identifier rubyid_puts'>puts</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>stats=</span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_stats'>stats</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>max</span><span class='tstring_end'>&quot;</span></span><span class='rbracket'>]</span><span class='embexpr_end'>}</span><span class='tstring_end'>&quot;</span></span>
103
+ <span class='rbrace'>}</span>
104
+
105
+ <span class='id identifier rubyid_settings'>settings</span><span class='period'>.</span><span class='id identifier rubyid_close'>close</span>
106
+ </code></pre>
107
+
108
+ <p>Complete documentation is available in the accompanying rdoc.</p>
109
+
110
+ <h2 id="label-Installation">Installation</h2>
111
+
112
+ <p><code>dbmlite3</code> is available as a gem:</p>
113
+
114
+ <pre class="code ruby"><code class="ruby">$ [sudo] gem install dbmlite3
115
+ </code></pre>
116
+
117
+ <p>Alternately, you can fetch the source code from GitLab and build it yourself:</p>
118
+
119
+ <pre class="code ruby"><code class="ruby">$ git clone https://gitlab.com/suetanvil/dbmlite3
120
+ $ cd dbmlite3
121
+ $ rake
122
+ </code></pre>
123
+
124
+ <p>It depends on the gem <code>sequel</code>; previously, it used <code>sqlite3</code>.</p>
125
+
126
+ <h2 id="label-Quirks+and+Hints">Quirks and Hints</h2>
127
+
128
+ <h3 id="label-Remember+that+a+DBM+is+a+-28potentially-29+shared+file">Remember that a <code>DBM</code> is a (potentially) shared file</h3>
129
+
130
+ <p>It is important to keep in mind that while <code>Lite3::DBM</code> objects look like Hashes, they are accessing files on disk that other processes could modify at any time.</p>
131
+
132
+ <p>For example, an innocuous-looking expression like</p>
133
+
134
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+</span> <span class='int'>1</span>
135
+ </code></pre>
136
+
137
+ <p>or its shorter equivalent</p>
138
+
139
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
140
+ </code></pre>
141
+
142
+ <p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others’ result.</p>
143
+
144
+ <p>There are two ways to deal with this. You can wrap the read-modify-write cycle in a transaction:</p>
145
+
146
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_transaction'>transaction</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span> <span class='rbrace'>}</span>
147
+ </code></pre>
148
+
149
+ <p>Or, of course, you could just design your script or program so that only one program accesses the table at a time.</p>
150
+
151
+ <h3 id="label-Keys+must+be+strings">Keys must be strings</h3>
152
+
153
+ <p>While values may be any serializable type, keys <em>must</em> be strings. As a special exception, Symbols are also allowed but are transparently converted to Strings first. This means that while something like this will work:</p>
154
+
155
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='symbol'>:foo</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='int'>42</span>
156
+ </code></pre>
157
+
158
+ <p>a subseqent</p>
159
+
160
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_keys'>keys</span><span class='period'>.</span><span class='id identifier rubyid_include?'>include?</span><span class='lparen'>(</span><span class='symbol'>:foo</span><span class='rparen'>)</span> <span class='kw'>or</span> <span class='id identifier rubyid_raise'>raise</span> <span class='const'>AbjectFailure</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span>
161
+ </code></pre>
162
+
163
+ <p>will raise an exception because the key <code>:foo</code> was turned into a string before being used. You will need to do this instead:</p>
164
+
165
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_keys'>keys</span><span class='period'>.</span><span class='id identifier rubyid_include?'>include?</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>foo</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='kw'>or</span> <span class='id identifier rubyid_raise'>raise</span> <span class='const'>AbjectFailure</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span>
166
+ </code></pre>
167
+
168
+ <p>However, this</p>
169
+
170
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='period'>.</span><span class='id identifier rubyid_has_key?'>has_key?</span><span class='lparen'>(</span><span class='symbol'>:foo</span><span class='rparen'>)</span>
171
+ </code></pre>
172
+
173
+ <p>will work because <code>has_key?</code> does the conversion for us.</p>
174
+
175
+ <h3 id="label-Transactions+and+performance">Transactions and performance</h3>
176
+
177
+ <p>If you need to do a large number of accesses in a short amount of time (e.g. loading data from a file), it is significantly faster to do these in batches in one or more transactions.</p>
178
+
179
+ <h3 id="label-Serialization+Safety">Serialization Safety</h3>
180
+
181
+ <p><code>Lite3::DBM</code> stores Ruby data by first serializing values using the <code>Marshal</code> or <code>Psych</code> modules. This can pose a security risk if an untrusted third party has direct access to the underlying SQLite3 database. This tends to be pretty rare most of the time but if it is a concern, you can always configure <code>Lite3::DBM</code> to store its values as plain strings.</p>
182
+
183
+ <h3 id="label-Forking+safely">Forking safely</h3>
184
+
185
+ <p>It is a documented limitation of SQLite3 that database objects cannot be carried across a process fork. Either the parent or the child process will keep the open handle and the other one must forget it completely.</p>
186
+
187
+ <p>For this reason, if you need both the parent and child process to be able to use <code>Lite3::DBM</code> after a <code>fork</code>, you must first call <code>Lite3::SQL.close_all</code>. Not only will this make it safe but it also lets the child and parent use the same <code>Lite3::DBM</code> objects.</p>
188
+
189
+ <h3 id="label-Lite3-3A-3ADBM+objects+act+like+file+handles+but+are+not"><code>Lite3::DBM</code> objects act like file handles but are not</h3>
190
+
191
+ <p>While it is generally safe to treat <code>Lite3::DBM</code> as a wrapper around a file handle (i.e. <code>open</code> and <code>close</code> work as expected), you should be aware that this is not precisely the way things actually work. Instead, the gem maintains a pool of database handles, one per file, and associates them with <code>Lite3::DBM</code> instances as needed. This is necessary for transactions to work correctly.</p>
192
+
193
+ <p>Mostly, you don’t need to care about this. However, it affects you in the following ways:</p>
194
+ <ol><li>
195
+ <p>Transactions are done at the file level and not the table level. This means that you can access separate tables in the same transaction, which is a Very Good Thing.</p>
196
+ </li><li>
197
+ <p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you've invoked <code>Lite3::SQL.close_all</code> before the fork. This will have closed the actual database handles (which can't tolerate being carried across a fork) and opens new ones the next time they're needed.</p>
198
+ </li></ol>
199
+
200
+ <p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it’s almost always a bad idea to not explicitly call <code>close</code> first.</p>
201
+
202
+ <h3 id="label-Under+the+hood">Under the hood</h3>
203
+
204
+ <p>Currently, <code>Lite3::DBM</code> uses <a href="https://sequel.jeremyevans.net">Sequel</a> to access the <code>sqlite3</code> library. On JRuby, it goes through the <code>jdbc</code> interface. The previous version (1.0.0) used <a href="https://github.com/sparklemotion/sqlite3-ruby">sqlite3</a> and only worked on MRI. However, you should make no assumptions about the underlying database libraries this gem uses. It may change in a future release.</p>
205
+
206
+ <p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don’t make global changes or mix transactions across interfaces. However, I make no guarantees.</p>
207
+ </div></div>
208
+
209
+ <div id="footer">
210
+ Generated on Sat Aug 19 13:22:01 2023 by
211
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
212
+ 0.9.34 (ruby-3.2.1).
213
+ </div>
214
+
215
+ </div>
216
+ </body>
217
+ </html>