s3db 0.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/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
|