s3db 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +74 -0
- data/bin/s3db +4 -0
- data/lib/s3db.rb +16 -0
- data/lib/s3db/backend.rb +4 -0
- data/lib/s3db/collection.rb +70 -0
- data/lib/s3db/database.rb +100 -0
- data/lib/s3db/file_backend.rb +492 -0
- data/lib/s3db/record.rb +195 -0
- data/lib/s3db/utils.rb +11 -0
- data/s3db.gemspec +40 -0
- data/spec/lib/s3db/collection_spec.rb +91 -0
- data/spec/lib/s3db/database_spec.rb +141 -0
- data/spec/lib/s3db/file_backend_spec.rb +612 -0
- data/spec/lib/s3db/record_spec.rb +286 -0
- data/spec/lib/s3db/utils_spec.rb +27 -0
- data/spec/spec_helper.rb +110 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3458f2b36ed8c03cf0a51b7974f6404f64d15207
|
4
|
+
data.tar.gz: c140dda6f8ee3f73ff1e0331fedaae242c8da6dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c53ba31f46f5fcfcb5a4e5282aa7a0981c52f24e137028ac7c3e425aa01c3aa4b24e1d3de9c270e8df521289894caba2b44ce6bf06ea04afaf71a3a1d36f429
|
7
|
+
data.tar.gz: 4816697a89e02e03ee9942c28392581e9cedc36dd6d401f7eeade1837cd395b25e2fba98a7f8f17c4f277fa22ff6371a374ca2476af875bc9ae18fdc14d5fb10
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (1.1.0)
|
5
|
+
diff-lcs (1.2.5)
|
6
|
+
docile (1.1.5)
|
7
|
+
ffi (1.9.10)
|
8
|
+
formatador (0.2.5)
|
9
|
+
guard (2.13.0)
|
10
|
+
formatador (>= 0.2.4)
|
11
|
+
listen (>= 2.7, <= 4.0)
|
12
|
+
lumberjack (~> 1.0)
|
13
|
+
nenv (~> 0.1)
|
14
|
+
notiffany (~> 0.0)
|
15
|
+
pry (>= 0.9.12)
|
16
|
+
shellany (~> 0.0)
|
17
|
+
thor (>= 0.18.1)
|
18
|
+
guard-compat (1.2.1)
|
19
|
+
guard-rspec (4.6.4)
|
20
|
+
guard (~> 2.1)
|
21
|
+
guard-compat (~> 1.1)
|
22
|
+
rspec (>= 2.99.0, < 4.0)
|
23
|
+
json (1.8.3)
|
24
|
+
listen (3.0.3)
|
25
|
+
rb-fsevent (>= 0.9.3)
|
26
|
+
rb-inotify (>= 0.9)
|
27
|
+
lumberjack (1.0.9)
|
28
|
+
method_source (0.8.2)
|
29
|
+
nenv (0.2.0)
|
30
|
+
notiffany (0.0.8)
|
31
|
+
nenv (~> 0.1)
|
32
|
+
shellany (~> 0.0)
|
33
|
+
pry (0.10.2)
|
34
|
+
coderay (~> 1.1.0)
|
35
|
+
method_source (~> 0.8.1)
|
36
|
+
slop (~> 3.4)
|
37
|
+
rb-fsevent (0.9.6)
|
38
|
+
rb-inotify (0.9.5)
|
39
|
+
ffi (>= 0.5.0)
|
40
|
+
rspec (3.3.0)
|
41
|
+
rspec-core (~> 3.3.0)
|
42
|
+
rspec-expectations (~> 3.3.0)
|
43
|
+
rspec-mocks (~> 3.3.0)
|
44
|
+
rspec-core (3.3.2)
|
45
|
+
rspec-support (~> 3.3.0)
|
46
|
+
rspec-expectations (3.3.1)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.3.0)
|
49
|
+
rspec-mocks (3.3.2)
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
+
rspec-support (~> 3.3.0)
|
52
|
+
rspec-support (3.3.0)
|
53
|
+
shellany (0.0.1)
|
54
|
+
simplecov (0.10.0)
|
55
|
+
docile (~> 1.1.0)
|
56
|
+
json (~> 1.8)
|
57
|
+
simplecov-html (~> 0.10.0)
|
58
|
+
simplecov-html (0.10.0)
|
59
|
+
slop (3.6.0)
|
60
|
+
thor (0.19.1)
|
61
|
+
uuidtools (2.1.5)
|
62
|
+
|
63
|
+
PLATFORMS
|
64
|
+
ruby
|
65
|
+
|
66
|
+
DEPENDENCIES
|
67
|
+
guard-rspec
|
68
|
+
json
|
69
|
+
rspec
|
70
|
+
simplecov
|
71
|
+
uuidtools
|
72
|
+
|
73
|
+
BUNDLED WITH
|
74
|
+
1.10.6
|
data/bin/s3db
ADDED
data/lib/s3db.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
Bundler.require(:default)
|
4
|
+
|
5
|
+
require_relative 's3db/utils'
|
6
|
+
require_relative 's3db/collection'
|
7
|
+
require_relative 's3db/database'
|
8
|
+
require_relative 's3db/record'
|
9
|
+
require_relative 's3db/backend'
|
10
|
+
require_relative 's3db/file_backend'
|
11
|
+
|
12
|
+
module S3DB
|
13
|
+
class << self
|
14
|
+
attr_accessor :backend
|
15
|
+
end
|
16
|
+
end
|
data/lib/s3db/backend.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module S3DB
|
2
|
+
class Collection
|
3
|
+
attr_reader :database, :name
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# Create a new collection.
|
7
|
+
#
|
8
|
+
# database - Database attached to collection. Required.
|
9
|
+
# name - String name of the collection. Required.
|
10
|
+
#
|
11
|
+
# returns a new Collection.
|
12
|
+
def create(database, name)
|
13
|
+
collection = new(database, name)
|
14
|
+
collection.save
|
15
|
+
|
16
|
+
collection
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Instantiate a new collection, without writing it to disk.
|
21
|
+
#
|
22
|
+
# database - Database attached to collection. Required.
|
23
|
+
# name - String name of the collection. Required.
|
24
|
+
#
|
25
|
+
# returns a new Collection, validated but unwritten.
|
26
|
+
def initialize(database, name)
|
27
|
+
|
28
|
+
# Store the database and collection name
|
29
|
+
@database = database
|
30
|
+
@name = Utils.sanitize(name)
|
31
|
+
|
32
|
+
# Sanity check the database and collection name
|
33
|
+
validate!
|
34
|
+
|
35
|
+
# Yield self for configs, if people want to.
|
36
|
+
yield self if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate a collection to ensure that it's sane.
|
40
|
+
#
|
41
|
+
# returns nil on success; raises an error on failure.
|
42
|
+
def validate!
|
43
|
+
unless @database.is_a?(S3DB::Database)
|
44
|
+
raise ArgumentError, 'database must be an S3DB::Database!'
|
45
|
+
end
|
46
|
+
|
47
|
+
unless @name.is_a?(String)
|
48
|
+
raise ArgumentError, 'name must be a String!'
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def list_records
|
55
|
+
@database.backend.list_records(@database.name, @name).map do |file|
|
56
|
+
@database.backend.read_record(@database.name, @name, file)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Write the collection skeleton to disk.
|
61
|
+
#
|
62
|
+
# Returns nil.
|
63
|
+
def save
|
64
|
+
@database.backend.write_collection(@database.name, @name)
|
65
|
+
@database.backend.write_schema(@database.name, @name, @schema.to_json)
|
66
|
+
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module S3DB
|
2
|
+
class Database
|
3
|
+
attr_reader :backend, :name
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# Create a new database.
|
7
|
+
#
|
8
|
+
# backend - S3DB::Backend subclass. Required.
|
9
|
+
# db_name - String name of the database to create. Required.
|
10
|
+
#
|
11
|
+
# returns a new S3DB::Database on success, raises an error on failure.
|
12
|
+
def create(backend, db_name)
|
13
|
+
begin
|
14
|
+
backend.write_db(db_name)
|
15
|
+
rescue Errno::EEXIST
|
16
|
+
raise ArgumentError, 'database exists!'
|
17
|
+
end
|
18
|
+
|
19
|
+
new(backend, db_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Drop a database.
|
23
|
+
#
|
24
|
+
# backend - S3DB::Backend subclass. Required.
|
25
|
+
# db_name - String name of database to drop. Required.
|
26
|
+
#
|
27
|
+
# returns the String database name on success, raises an error on failure.
|
28
|
+
def drop(backend, db_name)
|
29
|
+
backend.delete_db(db_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create a new DB instance.
|
34
|
+
#
|
35
|
+
# backend - S3DB::Backend subclass. Required.
|
36
|
+
# db_name - String name of database. Will be used in the storage path,
|
37
|
+
# so make sure it's something sane. Required.
|
38
|
+
#
|
39
|
+
# returns a new Database instance.
|
40
|
+
def initialize(backend, db_name)
|
41
|
+
@backend = backend
|
42
|
+
@name = db_name
|
43
|
+
|
44
|
+
yield self if block_given?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Save a DB instance to disk.
|
48
|
+
#
|
49
|
+
# Returns a Database instance.
|
50
|
+
def save
|
51
|
+
@backend.write_db(@name)
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# List all available collections in the database.
|
57
|
+
#
|
58
|
+
# returns sorted Array of Strings.
|
59
|
+
def show_collections
|
60
|
+
@backend.list_collections(@name).sort
|
61
|
+
end
|
62
|
+
|
63
|
+
# Create a collection under this database.
|
64
|
+
#
|
65
|
+
# collection - String name of collection to create. Will also be used as
|
66
|
+
# its filesystem path, so use something sane. Required.
|
67
|
+
# schema - Hash schema for this collection. Default: {}.
|
68
|
+
#
|
69
|
+
# returns true on success.
|
70
|
+
def create_collection(collection, schema = {})
|
71
|
+
Collection.create(self, collection)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Drop a collection from this database.
|
75
|
+
#
|
76
|
+
# collection - String name of collection to drop.
|
77
|
+
#
|
78
|
+
# returns the name of the collection on success.
|
79
|
+
def drop_collection(collection)
|
80
|
+
@backend.delete_collection(@name, collection)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Locate the storage location for the database.
|
84
|
+
#
|
85
|
+
# returns a String path of the database path. Will vary by backend adapter.
|
86
|
+
def path
|
87
|
+
@backend.db_path(@name)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Check to ensure that the database name is valid, from the perspective of
|
93
|
+
# the storage engine.
|
94
|
+
#
|
95
|
+
# returns a Bool.
|
96
|
+
def valid?
|
97
|
+
@backend.db_exist?(@name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,492 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module S3DB
|
4
|
+
class FileBackend < Backend
|
5
|
+
attr_reader :path, :errors
|
6
|
+
|
7
|
+
PATH_BLACKLIST = [
|
8
|
+
'bin',
|
9
|
+
'boot',
|
10
|
+
'cdrom',
|
11
|
+
'data',
|
12
|
+
'dev',
|
13
|
+
'docker',
|
14
|
+
'etc',
|
15
|
+
'home',
|
16
|
+
'lib',
|
17
|
+
'lib64',
|
18
|
+
'media',
|
19
|
+
'mnt',
|
20
|
+
'opt',
|
21
|
+
'proc',
|
22
|
+
'root',
|
23
|
+
'run',
|
24
|
+
'sbin',
|
25
|
+
'srv',
|
26
|
+
'sys',
|
27
|
+
'usr',
|
28
|
+
'var',
|
29
|
+
'badpath', #this is just to be VERY sure we don't trash a real dir in tests
|
30
|
+
]
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# Create a new base path for a file backend storage location. This method
|
34
|
+
# will create the basepath if it doesn't exist, but also use an existing
|
35
|
+
# path if it does exest.
|
36
|
+
#
|
37
|
+
# path - String base path. Required.
|
38
|
+
#
|
39
|
+
# returns a new FileBackend.
|
40
|
+
def create(path)
|
41
|
+
be = new(path)
|
42
|
+
be.save
|
43
|
+
|
44
|
+
be
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a new base path for a file backend storage location. This method
|
48
|
+
# will throw an error if the path already exists. This is safer.
|
49
|
+
#
|
50
|
+
# path - String base path. Required.
|
51
|
+
#
|
52
|
+
# returns a new FileBackend.
|
53
|
+
def create!(path)
|
54
|
+
be = new(path)
|
55
|
+
be.save!
|
56
|
+
|
57
|
+
be
|
58
|
+
end
|
59
|
+
|
60
|
+
# Destroy a base path for data storage. This method will raise an error
|
61
|
+
# if the directory is not empty.
|
62
|
+
#
|
63
|
+
# path - String base path. Required.
|
64
|
+
#
|
65
|
+
# returns the String path that was removed.
|
66
|
+
def destroy(path)
|
67
|
+
be = new(path).destroy
|
68
|
+
|
69
|
+
be
|
70
|
+
end
|
71
|
+
|
72
|
+
# Destroy a base path, whether or not it's empty. This is dangerous, and
|
73
|
+
# should be used with great care.
|
74
|
+
#
|
75
|
+
# path - String path to delete. Required.
|
76
|
+
#
|
77
|
+
# returns itself on success, and raises an error on failure.
|
78
|
+
def delete(path)
|
79
|
+
be = new(path)
|
80
|
+
|
81
|
+
if be.valid!
|
82
|
+
FileUtils.rm_rf(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
self
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a new FileBackend.
|
90
|
+
#
|
91
|
+
# path - String path to use as the base storage location.
|
92
|
+
#
|
93
|
+
# returns a new FileBackend.
|
94
|
+
def initialize(path)
|
95
|
+
@errors = []
|
96
|
+
|
97
|
+
@path = path.strip
|
98
|
+
end
|
99
|
+
|
100
|
+
# Check a path to ensure it does not violate basic sanity rules, such as
|
101
|
+
# being a linux system path, or having weird characters in the name.
|
102
|
+
#
|
103
|
+
# path - String path to check. Required.
|
104
|
+
#
|
105
|
+
# returns true if it checks out, false otherwise. It will raise an error
|
106
|
+
# for dangerous exceptions.
|
107
|
+
def validate_path
|
108
|
+
PATH_BLACKLIST.each do |p|
|
109
|
+
@errors << "`#{p}` is insane to use as a base path!" if @path =~ /#{p}/i
|
110
|
+
end
|
111
|
+
|
112
|
+
if @path !~ /^(\w|\/)+$/
|
113
|
+
@errors << "path does not match /^(\w|\/)+$/"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Check to see whether the backend is in a valid state.
|
118
|
+
#
|
119
|
+
# returns Bool.
|
120
|
+
def valid?
|
121
|
+
@errors = []
|
122
|
+
|
123
|
+
validate_path
|
124
|
+
|
125
|
+
!@errors.any?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Confirm that the backend is in a consistent state. Raises an error on
|
129
|
+
# failure.
|
130
|
+
#
|
131
|
+
# returns true on success, raises an error on failure.
|
132
|
+
def valid!
|
133
|
+
if !valid?
|
134
|
+
raise ArgumentError, @errors.join(', ')
|
135
|
+
end
|
136
|
+
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
# Save a FileBackend, which means writing its root path directory to the
|
141
|
+
# filesystem.
|
142
|
+
#
|
143
|
+
# returns itself on success, returns false on failure.
|
144
|
+
def save
|
145
|
+
return false unless valid?
|
146
|
+
|
147
|
+
FileUtils.mkdir_p(@path)
|
148
|
+
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Save a FileBackend, which means writing its root path directory to the
|
153
|
+
# filesystem.
|
154
|
+
#
|
155
|
+
# returns itself on success, raises an error on failure.
|
156
|
+
def save!
|
157
|
+
valid!
|
158
|
+
|
159
|
+
begin
|
160
|
+
Dir.mkdir(@path)
|
161
|
+
rescue Errno::EEXIST
|
162
|
+
raise ArgumentError, 'base path exists!'
|
163
|
+
end
|
164
|
+
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# Destroy a FileBackend basepath. The directory must be empty (which means
|
170
|
+
# you must destroy all collections/dbs in the basepath first).
|
171
|
+
#
|
172
|
+
# returns itself on success, false on failure.
|
173
|
+
def destroy
|
174
|
+
return false unless valid?
|
175
|
+
|
176
|
+
begin
|
177
|
+
Dir.rmdir(@path)
|
178
|
+
rescue Errno::ENOTEMPTY, Errno::ENOENT
|
179
|
+
return false
|
180
|
+
end
|
181
|
+
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
# Destroy a FileBackend basepath. The directory must be empty (which means
|
186
|
+
# you must destroy all collections/dbs in the basepath first).
|
187
|
+
#
|
188
|
+
# returns itself on success, raises an error on failure.
|
189
|
+
def destroy!
|
190
|
+
valid!
|
191
|
+
|
192
|
+
begin
|
193
|
+
Dir.rmdir(@path)
|
194
|
+
rescue Errno::ENOENT
|
195
|
+
raise ArgumentError, 'basepath does not exist!'
|
196
|
+
rescue Errno::ENOTEMPTY
|
197
|
+
raise ArgumentError, 'basepath not empty!'
|
198
|
+
end
|
199
|
+
|
200
|
+
self
|
201
|
+
end
|
202
|
+
|
203
|
+
# Build a full path from the base path and database name.
|
204
|
+
#
|
205
|
+
# db_name - String name of database. Required.
|
206
|
+
#
|
207
|
+
# returns a String path.
|
208
|
+
def db_path(db_name)
|
209
|
+
File.join(@path, db_name)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Build a full path to a collection from the base path, database name and
|
213
|
+
# collection name.
|
214
|
+
#
|
215
|
+
# db_name - String name of database. Required.
|
216
|
+
# collection_name - String name of collection. Required.
|
217
|
+
#
|
218
|
+
# returns a String path.
|
219
|
+
def collection_path(db_name, collection_name)
|
220
|
+
File.join(@path, db_name, collection_name)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Build a full path to a scema from the base path, database name and
|
224
|
+
# collection name.
|
225
|
+
#
|
226
|
+
# db_name - String name of database. Required.
|
227
|
+
# collection_name - String name of collection. Required.
|
228
|
+
#
|
229
|
+
# returns a String path.
|
230
|
+
def schema_path(db_name, collection_name)
|
231
|
+
File.join(@path, db_name, collection_name, 'schema.json')
|
232
|
+
end
|
233
|
+
|
234
|
+
# Build a full path to the data dir from the base path, database name and
|
235
|
+
# collection name.
|
236
|
+
#
|
237
|
+
# db_name - String name of database. Required.
|
238
|
+
# collection_name - String name of collection. Required.
|
239
|
+
#
|
240
|
+
# returns a String path.
|
241
|
+
def data_path(db_name, collection_name)
|
242
|
+
File.join(@path, db_name, collection_name, 'data')
|
243
|
+
end
|
244
|
+
|
245
|
+
# Build a full path to a record from the base path, database name,
|
246
|
+
# collection name, and filename.
|
247
|
+
#
|
248
|
+
# db_name - String name of database. Required.
|
249
|
+
# collection_name - String name of collection. Required.
|
250
|
+
#
|
251
|
+
# returns a String path.
|
252
|
+
def record_path(db_name, collection_name, filename)
|
253
|
+
File.join(@path, db_name, collection_name, 'data', filename)
|
254
|
+
end
|
255
|
+
|
256
|
+
# Write a directory to disk for the name of the database. Will fail if the
|
257
|
+
# database already exists.
|
258
|
+
#
|
259
|
+
# db_name - String name of database. Required.
|
260
|
+
#
|
261
|
+
# returns db_name on success; raises an error on failure.
|
262
|
+
def write_db(db_name)
|
263
|
+
FileUtils.mkdir_p(db_path(db_name))
|
264
|
+
|
265
|
+
db_name
|
266
|
+
end
|
267
|
+
|
268
|
+
# Write a directory to disk for the name of the collection. Will ignore the
|
269
|
+
# write if the directory exists.
|
270
|
+
#
|
271
|
+
# db_name - String name of database. Required.
|
272
|
+
# collection_name - String name of collection. Required.
|
273
|
+
#
|
274
|
+
# returns collection_name on success; raises an error on failure.
|
275
|
+
def write_collection(db_name, collection_name)
|
276
|
+
dir = collection_path(db_name, collection_name)
|
277
|
+
|
278
|
+
unless Dir.exist?(dir)
|
279
|
+
Dir.mkdir(dir)
|
280
|
+
end
|
281
|
+
|
282
|
+
dir = data_path(db_name, collection_name)
|
283
|
+
|
284
|
+
unless Dir.exist?(dir)
|
285
|
+
Dir.mkdir(dir)
|
286
|
+
end
|
287
|
+
|
288
|
+
collection_name
|
289
|
+
end
|
290
|
+
|
291
|
+
# Write a file to disk for the schema for a database/collection.
|
292
|
+
#
|
293
|
+
# db_name - String name of database. Required.
|
294
|
+
# collection_name - String name of collection. Required.
|
295
|
+
# schema - String schema contents. Required.
|
296
|
+
#
|
297
|
+
# returns the schema on success; raises an error on failure.
|
298
|
+
def write_schema(db_name, collection_name, schema)
|
299
|
+
File.open(schema_path(db_name, collection_name), 'w') do |f|
|
300
|
+
f.puts schema
|
301
|
+
end
|
302
|
+
|
303
|
+
schema
|
304
|
+
end
|
305
|
+
|
306
|
+
# Write a record to a database/collection. The record can be in any format.
|
307
|
+
# No parsing of the file is performed. Data must be sent as a string.
|
308
|
+
#
|
309
|
+
# db_name - String name of database. Required.
|
310
|
+
# collection_name - String name of collection. Required.
|
311
|
+
# filename - String name of file to write, w/extension. Required.
|
312
|
+
# data - String data to write to the file. Required.
|
313
|
+
#
|
314
|
+
# returns the data written.
|
315
|
+
def write_record(db_name, coll_name, filename, data)
|
316
|
+
raise ArgumentError, 'data must be a string!' unless data.is_a?(String)
|
317
|
+
|
318
|
+
File.open(record_path(db_name, coll_name, filename), 'w') do |f|
|
319
|
+
f.puts data
|
320
|
+
end
|
321
|
+
|
322
|
+
data
|
323
|
+
end
|
324
|
+
|
325
|
+
# List all available collections in a database.
|
326
|
+
#
|
327
|
+
# db_name - String name of database to look in. Required.
|
328
|
+
#
|
329
|
+
# returns an Array of String collection names.
|
330
|
+
def list_collections(db_name)
|
331
|
+
Dir.entries(db_path(db_name)).select do |dir|
|
332
|
+
!File.directory?(dir)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# List all available records for a database/collection.
|
337
|
+
#
|
338
|
+
# db_name - String name of database to look in. Required.
|
339
|
+
# collection_name - String name of collection to look in. Required.
|
340
|
+
#
|
341
|
+
# returns an Array of String record file names.
|
342
|
+
def list_records(db_name, coll_name)
|
343
|
+
output = []
|
344
|
+
|
345
|
+
Dir.open(data_path(db_name, coll_name)) do |dir|
|
346
|
+
dir.each do |file|
|
347
|
+
output << file unless File.directory?(file)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
output
|
352
|
+
end
|
353
|
+
|
354
|
+
# Read the contents of the schema file.
|
355
|
+
#
|
356
|
+
# db_name - String name of database to look in. Required.
|
357
|
+
# collection_name - String name of collection to look in. Required.
|
358
|
+
#
|
359
|
+
# returns the String contents of the schema file. Raises an error if the
|
360
|
+
# file is missing.
|
361
|
+
def read_schema(db_name, collection_name)
|
362
|
+
file = schema_path(db_name, collection_name)
|
363
|
+
|
364
|
+
begin
|
365
|
+
File.read(file)
|
366
|
+
rescue Errno::ENOENT
|
367
|
+
raise ArgumentError, 'schema does not exist!'
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# Read the contents of a record in a database/collection
|
372
|
+
#
|
373
|
+
# db_name - String name of database to look in. Required.
|
374
|
+
# collection_name - String name of collection to look in. Required.
|
375
|
+
# filename - String name of the file to read w/extension. Required.
|
376
|
+
#
|
377
|
+
# returns the String contents of the record file. Raises an error if the
|
378
|
+
# file is missing.
|
379
|
+
def read_record(db_name, coll_name, filename)
|
380
|
+
file = record_path(db_name, coll_name, filename)
|
381
|
+
|
382
|
+
begin
|
383
|
+
File.read(file)
|
384
|
+
rescue Errno::ENOENT
|
385
|
+
raise ArgumentError, 'record does not exist!'
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Delete a database directory from disk. Will only succed if the database is
|
390
|
+
# empty.
|
391
|
+
#
|
392
|
+
# db_name - String name of database to remove. Required.
|
393
|
+
#
|
394
|
+
# returns an Array of the databases deleted, or empty if the database did
|
395
|
+
# not exist.
|
396
|
+
def delete_db(db_name)
|
397
|
+
path = db_path(db_name)
|
398
|
+
|
399
|
+
if Dir.exist?(path)
|
400
|
+
begin
|
401
|
+
Dir.rmdir(path)
|
402
|
+
rescue Errno::ENOTEMPTY
|
403
|
+
raise ArgumentError, 'database is not empty!'
|
404
|
+
end
|
405
|
+
|
406
|
+
return [db_name]
|
407
|
+
end
|
408
|
+
|
409
|
+
[]
|
410
|
+
end
|
411
|
+
|
412
|
+
# Delete a collection directory from disk. Will only succed if the
|
413
|
+
# collection is empty.
|
414
|
+
#
|
415
|
+
# db_name - String name of database to remove. Required.
|
416
|
+
# collection_name - String name of collection to remove. Required.
|
417
|
+
#
|
418
|
+
# returns an Array of the databases deleted, or empty if the database did
|
419
|
+
# not exist.
|
420
|
+
def delete_collection(db_name, collection_name)
|
421
|
+
path = collection_path(db_name, collection_name)
|
422
|
+
|
423
|
+
if File.exist?(schema_path(db_name, collection_name))
|
424
|
+
File.delete(schema_path(db_name, collection_name))
|
425
|
+
end
|
426
|
+
|
427
|
+
if Dir.exist?(data_path(db_name, collection_name))
|
428
|
+
begin
|
429
|
+
Dir.rmdir(data_path(db_name, collection_name))
|
430
|
+
rescue Errno::ENOTEMPTY
|
431
|
+
raise ArgumentError, 'collection/data is not empty!'
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
if Dir.exist?(path)
|
436
|
+
begin
|
437
|
+
Dir.rmdir(path)
|
438
|
+
rescue Errno::ENOTEMPTY
|
439
|
+
raise ArgumentError, 'collection is not empty!'
|
440
|
+
end
|
441
|
+
|
442
|
+
return [collection_name]
|
443
|
+
end
|
444
|
+
|
445
|
+
[]
|
446
|
+
end
|
447
|
+
|
448
|
+
# Delete a collection directory from disk. Will only succed if the
|
449
|
+
# collection is empty.
|
450
|
+
#
|
451
|
+
# db_name - String name of database to remove. Required.
|
452
|
+
# collection_name - String name of collection to remove. Required.
|
453
|
+
#
|
454
|
+
# returns an Array of the databases deleted, or empty if the database did
|
455
|
+
# not exist.
|
456
|
+
def delete_record(db_name, collection_name, filename)
|
457
|
+
path = record_path(db_name, collection_name, filename)
|
458
|
+
|
459
|
+
begin
|
460
|
+
File.delete(path)
|
461
|
+
rescue Errno::ENOENT
|
462
|
+
return ''
|
463
|
+
end
|
464
|
+
|
465
|
+
filename
|
466
|
+
end
|
467
|
+
|
468
|
+
# Delete a collection directory from disk. Will only succed if the
|
469
|
+
# collection is empty.
|
470
|
+
#
|
471
|
+
# db_name - String name of database to remove. Required.
|
472
|
+
# collection_name - String name of collection to remove. Required.
|
473
|
+
#
|
474
|
+
# returns an Array of the databases deleted, or empty if the database did
|
475
|
+
# not exist.
|
476
|
+
def delete_record!(db_name, collection_name, filename)
|
477
|
+
path = record_path(db_name, collection_name, filename)
|
478
|
+
|
479
|
+
begin
|
480
|
+
File.delete(path)
|
481
|
+
rescue Errno::ENOENT
|
482
|
+
raise ArgumentError, 'record does not exist!'
|
483
|
+
end
|
484
|
+
|
485
|
+
filename
|
486
|
+
end
|
487
|
+
|
488
|
+
def db_exist?(db_name)
|
489
|
+
Dir.exist?(db_path(db_name))
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|