git.s-ol.nu mmm / b866465
rename LFS to FS store s-ol 3 years ago
6 changed file(s) with 273 addition(s) and 274 deletion(s). Raw diff Collapse all Expand all
1515 RUN tup init && tup generate --config tup.docker.config build-static.sh && ./build-static.sh
1616
1717 EXPOSE 8000
18 ENTRYPOINT ["moon", "build/server.moon", "/db.sqlite3", "0.0.0.0", "8000"]
18 ENTRYPOINT ["moon", "build/server.moon", "sql:/db.sqlite3", "0.0.0.0", "8000"]
0 lfs = require 'lfs'
1
2 -- split filename into dirname + basename
3 dir_base = (path) ->
4 dir, base = path\match '(.-)([^/]-)$'
5 if dir and #dir > 0
6 dir = dir\sub 1, #dir - 1
7
8 dir, base
9
10 class FSStore
11 new: (opts = {}) =>
12 opts.root or= 'root'
13 opts.verbose or= false
14
15 if not opts.verbose
16 @log = ->
17
18 -- ensure path doesnt end with a slash
19 @root = opts.root\match '^(.-)/?$'
20 @log "opening '#{opts.root}'..."
21
22 log: (...) =>
23 print "[DB]", ...
24
25 -- fileders
26 list_fileders_in: (path='') =>
27 coroutine.wrap ->
28 for entry_name in lfs.dir @root .. path
29 continue if '.' == entry_name\sub 1, 1
30 entry_path = @root .. "#{path}/#{entry_name}"
31 if 'directory' == lfs.attributes entry_path, 'mode'
32 coroutine.yield "#{path}/#{entry_name}"
33
34 list_all_fileders: (path='') =>
35 coroutine.wrap ->
36 for path in @list_fileders_in path
37 coroutine.yield path
38 for p in @list_all_fileders path
39 coroutine.yield p
40
41 create_fileder: (parent, name) =>
42 @log "creating fileder #{path}"
43 path = "#{parent}/#{name}"
44 assert lfs.mkdir @root .. path
45 path
46
47 remove_fileder: (path) =>
48 @log "removing fileder #{path}"
49
50 rmdir = (path) ->
51 for file in lfs.dir path
52 continue if '.' == file\sub 1, 1
53
54 file_path = "#{path}/#{file}"
55 switch lfs.attributes file_path, 'mode'
56 when 'file'
57 assert os.remove file_path
58 when 'directory'
59 assert rmdir file_path
60
61 lfs.rmdir path
62
63 rmdir @root .. path
64
65 rename_fileder: (path, next_name) =>
66 @log "renaming fileder #{path} -> '#{next_name}'"
67 parent, name = dir_base path
68 assert os.rename path, @root .. "#{parent}/#{next_name}"
69
70 move_fileder: (path, next_parent) =>
71 @log "moving fileder #{path} -> #{next_parent}/"
72 parent, name = dir_base path
73 assert os.rename @root .. path, @root .. "#{next_parent}/#{name}"
74
75 -- facets
76 list_facets: (path) =>
77 coroutine.wrap ->
78 for entry_name in lfs.dir @root .. path
79 entry_path = "#{@root .. path}/#{entry_name}"
80 if 'file' == lfs.attributes entry_path, 'mode'
81 entry_name = (entry_name\match '(.*)%.%w+') or entry_name
82 entry_name = entry_name\gsub '%$', '/'
83 name, type = entry_name\match '(%w+): *(.+)'
84 if not name
85 name = ''
86 type = entry_name
87
88 coroutine.yield name, type
89
90 tofp: (path, name, type) =>
91 type = "#{name}: #{type}" if #name > 0
92 type = type\gsub '%/', '$'
93 @root .. "#{path}/#{type}"
94
95 locate: (path, name, type) =>
96 return unless lfs.attributes @root .. path, 'mode'
97
98 type = type\gsub '%/', '$'
99 name = "#{name}: " if #name > 0
100 name = name .. type
101 name = name\gsub '([^%w])', '%%%1'
102
103 local file_name
104 for entry_name in lfs.dir @root .. path
105 if (entry_name\match "^#{name}$") or entry_name\match "^#{name}%.%w+$"
106 if file_name
107 error "two files match #{name}: #{file_name} and #{entry_name}!"
108 file_name = entry_name
109
110
111 file_name and @root .. "#{path}/#{file_name}"
112
113 load_facet: (path, name, type) =>
114 filepath = @locate path, name, type
115 return unless filepath
116 file = assert (io.open filepath, 'rb'), "couldn't open facet file '#{filepath}'"
117 with file\read '*all'
118 file\close!
119
120 create_facet: (path, name, type, blob) =>
121 @log "creating facet #{path} | #{name}: #{type}"
122 assert blob, "cant create facet without value!"
123
124 filepath = @tofp path, name, type
125 if lfs.attributes filepath, 'mode'
126 error "facet file already exists!"
127
128 file = assert (io.open filepath, 'wb'), "couldn't open facet file '#{filepath}'"
129 file\write blob
130 file\close!
131
132 remove_facet: (path, name, type) =>
133 @log "removing facet #{path} | #{name}: #{type}"
134
135 filepath = @locate path, name, type
136 assert filepath, "couldn't locate facet!"
137 assert os.remove filepath
138
139 rename_facet: (path, name, type, next_name) =>
140 @log "renaming facet #{path} | #{name}: #{type} -> #{next_name}"
141 filepath = @locate path, name, type
142 assert filepath, "couldn't locate facet!"
143 assert os.rename filepath, @tofp path, next_name, type
144
145 update_facet: (path, name, type, blob) =>
146 @log "updating facet #{path} | #{name}: #{type}"
147 filepath = @locate path, name, type
148 assert filepath, "couldn't locate facet!"
149 file = assert (io.open filepath, 'wb'), "couldn't open facet file '#{filepath}'"
150 file\write blob
151 file\close!
152
153 {
154 :FSStore
155 }
00 require = relative ..., 0
11
22 -- instantiate a store from a CLI arg
3 -- e.g.: sql, lfs:/path/to/root, sql:MEMORY, sql:db.sqlite3
3 -- e.g.: sql, fs:/path/to/root, sql:MEMORY, sql:db.sqlite3
44 get_store = (args='sql', opts={verbose: true}) ->
55 type, arg = args\match '(%w+):(.*)'
66 type = arg unless type
1212 if arg == 'MEMORY'
1313 opts.memory = true
1414 else
15 opts.name = arg
15 opts.file = arg
1616
1717 SQLStore opts
1818
19 when 'lfs'
20 import LFSStore from require '.lfs'
19 when 'fs'
20 import LFSStore from require '.fs'
2121
2222 opts.root = arg
2323
2424 LFSStore opts
2525
2626 else
27 warn "unknown or missing value for STORE: valid types values are sql, lfs"
27 warn "unknown or missing value for STORE: valid types values are sql, fs"
2828 os.exit 1
2929
3030 {
+0
-157
mmm/mmmfs/stores/lfs.moon less more
0 lfs = require 'lfs'
1
2 -- split filename into dirname + basename
3 dir_base = (path) ->
4 dir, base = path\match '(.-)([^/]-)$'
5 if dir and #dir > 0
6 dir = dir\sub 1, #dir - 1
7
8 dir, base
9
10
11 class LFSStore
12 new: (opts = {}) =>
13 opts.root or= 'root'
14 opts.verbose or= false
15
16 if not opts.verbose
17 @log = ->
18
19 -- ensure path doesnt end with a slash
20 @root = opts.root\match '^(.-)/?$'
21 @log "opening '#{opts.root}'..."
22
23 log: (...) =>
24 print "[DB]", ...
25
26 -- fileders
27 list_fileders_in: (path='') =>
28 coroutine.wrap ->
29 for entry_name in lfs.dir @root .. path
30 continue if '.' == entry_name\sub 1, 1
31 entry_path = @root .. "#{path}/#{entry_name}"
32 if 'directory' == lfs.attributes entry_path, 'mode'
33 coroutine.yield "#{path}/#{entry_name}"
34
35 list_all_fileders: (path='') =>
36 coroutine.wrap ->
37 for path in @list_fileders_in path
38 coroutine.yield path
39 for p in @list_all_fileders path
40 coroutine.yield p
41
42 create_fileder: (parent, name) =>
43 @log "creating fileder #{path}"
44 path = "#{parent}/#{name}"
45 assert lfs.mkdir @root .. path
46 path
47
48 remove_fileder: (path) =>
49 @log "removing fileder #{path}"
50
51 rmdir = (path) ->
52 for file in lfs.dir path
53 continue if '.' == file\sub 1, 1
54
55 file_path = "#{path}/#{file}"
56 switch lfs.attributes file_path, 'mode'
57 when 'file'
58 assert os.remove file_path
59 when 'directory'
60 assert rmdir file_path
61
62 lfs.rmdir path
63
64 rmdir @root .. path
65
66 rename_fileder: (path, next_name) =>
67 @log "renaming fileder #{path} -> '#{next_name}'"
68 parent, name = dir_base path
69 assert os.rename path, @root .. "#{parent}/#{next_name}"
70
71 move_fileder: (path, next_parent) =>
72 @log "moving fileder #{path} -> #{next_parent}/"
73 parent, name = dir_base path
74 assert os.rename @root .. path, @root .. "#{next_parent}/#{name}"
75
76 -- facets
77 list_facets: (path) =>
78 coroutine.wrap ->
79 for entry_name in lfs.dir @root .. path
80 entry_path = "#{@root .. path}/#{entry_name}"
81 if 'file' == lfs.attributes entry_path, 'mode'
82 entry_name = (entry_name\match '(.*)%.%w+') or entry_name
83 entry_name = entry_name\gsub '%$', '/'
84 name, type = entry_name\match '(%w+): *(.+)'
85 if not name
86 name = ''
87 type = entry_name
88
89 coroutine.yield name, type
90
91 tofp: (path, name, type) =>
92 type = "#{name}: #{type}" if #name > 0
93 type = type\gsub '%/', '$'
94 @root .. "#{path}/#{type}"
95
96 locate: (path, name, type) =>
97 return unless lfs.attributes @root .. path, 'mode'
98
99 type = type\gsub '%/', '$'
100 name = "#{name}: " if #name > 0
101 name = name .. type
102 name = name\gsub '([^%w])', '%%%1'
103
104 local file_name
105 for entry_name in lfs.dir @root .. path
106 if (entry_name\match "^#{name}$") or entry_name\match "^#{name}%.%w+$"
107 if file_name
108 error "two files match #{name}: #{file_name} and #{entry_name}!"
109 file_name = entry_name
110
111
112 file_name and @root .. "#{path}/#{file_name}"
113
114 load_facet: (path, name, type) =>
115 filepath = @locate path, name, type
116 return unless filepath
117 file = assert (io.open filepath, 'rb'), "couldn't open facet file '#{filepath}'"
118 with file\read '*all'
119 file\close!
120
121 create_facet: (path, name, type, blob) =>
122 @log "creating facet #{path} | #{name}: #{type}"
123 assert blob, "cant create facet without value!"
124
125 filepath = @tofp path, name, type
126 if lfs.attributes filepath, 'mode'
127 error "facet file already exists!"
128
129 file = assert (io.open filepath, 'wb'), "couldn't open facet file '#{filepath}'"
130 file\write blob
131 file\close!
132
133 remove_facet: (path, name, type) =>
134 @log "removing facet #{path} | #{name}: #{type}"
135
136 filepath = @locate path, name, type
137 assert filepath, "couldn't locate facet!"
138 assert os.remove filepath
139
140 rename_facet: (path, name, type, next_name) =>
141 @log "renaming facet #{path} | #{name}: #{type} -> #{next_name}"
142 filepath = @locate path, name, type
143 assert filepath, "couldn't locate facet!"
144 assert os.rename filepath, @tofp path, next_name, type
145
146 update_facet: (path, name, type, blob) =>
147 @log "updating facet #{path} | #{name}: #{type}"
148 filepath = @locate path, name, type
149 assert filepath, "couldn't locate facet!"
150 file = assert (io.open filepath, 'wb'), "couldn't open facet file '#{filepath}'"
151 file\write blob
152 file\close!
153
154 {
155 :LFSStore
156 }
+0
-111
spec/driver_spec.moon less more
0 sort2 = (a, b) ->
1 {ax, ay}, {bx, by} = a, b
2 "#{ax}//#{ay}" < "#{bx}//#{by}"
3
4 toseq = (iter) ->
5 with v = [x for x in iter]
6 table.sort v
7
8 toseq2 = (iter) ->
9 with v = [{x, y} for x, y in iter]
10 table.sort v, sort2
11
12 test_driver = (ts) ->
13 randomize false
14
15 it "starts out empty", ->
16 assert.are.same {}, toseq ts\list_fileders_in!
17 assert.are.same {}, toseq ts\list_all_fileders!
18
19 it "can't create fileders without missing parents", ->
20 assert.has_error ->
21 ts\create_fileder '/hello', 'world'
22
23 it "can create root fileders", ->
24 assert.are.same '/hello', ts\create_fileder '', 'hello'
25 assert.are.same {'/hello'}, toseq ts\list_all_fileders!
26
27 it "can create and list child fileders recursively", ->
28 assert.are.same '/hello/world',
29 ts\create_fileder '/hello', 'world'
30 assert.are.same '/hello/world/again',
31 ts\create_fileder '/hello/world', 'again'
32
33 assert.are.same {'/hello', '/hello/world', '/hello/world/again'},
34 toseq ts\list_all_fileders!
35
36 it "can list immediate children", ->
37 assert.are.same {"/hello"}, toseq ts\list_fileders_in!
38 assert.are.same {"/hello/world"}, toseq ts\list_fileders_in "/hello"
39 assert.are.same {"/hello/world/again"}, toseq ts\list_fileders_in "/hello/world"
40
41 describe "can create and list facets", ->
42 ts\create_facet '/hello', 'name', 'alpha', 'hello'
43 ts\create_facet '/hello/world', 'name', 'alpha', 'world'
44 ts\create_facet '/hello/world', '', 'text/markdown', '# Helau World!'
45
46 it "but can't create facet for nonexistant fileders", ->
47 assert.has_error -> ts\create_facet '/hello/orldw', 'name', 'alpha', 'foo'
48
49 it "but can't create facet without value", ->
50 assert.has_error -> ts\create_facet '/hello/world', 'other', 'alpha', nil
51
52 it "but can't create facet for duplicate keys", ->
53 assert.has_error -> ts\create_facet '/hello/world', 'name', 'alpha', 'foo'
54
55 assert.are.same {{'name', 'alpha'}}, toseq2 ts\list_facets '/hello'
56 assert.are.same {{'', 'text/markdown'}, {'name', 'alpha'}},
57 toseq2 ts\list_facets '/hello/world'
58
59 it "can load facets", ->
60 assert.are.equal 'hello', ts\load_facet '/hello', 'name', 'alpha'
61 assert.are.equal 'world', ts\load_facet '/hello/world', 'name', 'alpha'
62 assert.are.equal '# Helau World!', ts\load_facet '/hello/world', '', 'text/markdown'
63 assert.is_falsy ts\load_facet '/hello', 'nonexistant', 'facet'
64
65 it "can rename facets", ->
66 ts\rename_facet '/hello/world', 'name', 'alpha', 'gnome'
67 assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
68 toseq2 ts\list_facets '/hello/world'
69 assert.are.equal 'world', ts\load_facet '/hello/world', 'gnome', 'alpha'
70
71 it "can update facets", ->
72 ts\update_facet '/hello/world', '', 'text/markdown', '# Hello World!'
73 assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
74 toseq2 ts\list_facets '/hello/world'
75 assert.are.equal '# Hello World!', ts\load_facet '/hello/world', '', 'text/markdown'
76
77 it "can remove facets", ->
78 ts\remove_facet '/hello/world', 'gnome', 'alpha'
79 assert.are.same {{'', 'text/markdown'}}, toseq2 ts\list_facets '/hello/world'
80
81 assert.has_error -> ts\remove_facet '/hello/world', 'gnome', 'alpha'
82
83 it "can delete fileders", ->
84 ts\remove_fileder '/hello/world'
85 assert.is_falsy ts\load_facet '/hello/world', 'gnome', 'alpha'
86 assert.are.same {'/hello'}, toseq ts\list_all_fileders!
87
88 ts\remove_fileder '/hello'
89 assert.are.same {}, toseq ts\list_all_fileders!
90
91 describe "SQL driver", ->
92 import SQLStore from require 'mmm.mmmfs.drivers.sql'
93
94 test_driver SQLStore memory: true
95
96 describe "LFS driver", ->
97 import LFSStore from require 'mmm.mmmfs.drivers.lfs'
98
99 lfs = require 'lfs'
100
101 root = os.tmpname!
102
103 setup ->
104 assert os.remove root
105 assert lfs.mkdir root
106
107 test_driver LFSStore :root
108
109 teardown ->
110 assert lfs.rmdir root
0 sort2 = (a, b) ->
1 {ax, ay}, {bx, by} = a, b
2 "#{ax}//#{ay}" < "#{bx}//#{by}"
3
4 toseq = (iter) ->
5 with v = [x for x in iter]
6 table.sort v
7
8 toseq2 = (iter) ->
9 with v = [{x, y} for x, y in iter]
10 table.sort v, sort2
11
12 test_store = (ts) ->
13 randomize false
14
15 it "starts out empty", ->
16 assert.are.same {}, toseq ts\list_fileders_in!
17 assert.are.same {}, toseq ts\list_all_fileders!
18
19 it "can't create fileders without missing parents", ->
20 assert.has_error ->
21 ts\create_fileder '/hello', 'world'
22
23 it "can create root fileders", ->
24 assert.are.same '/hello', ts\create_fileder '', 'hello'
25 assert.are.same {'/hello'}, toseq ts\list_all_fileders!
26
27 it "can create and list child fileders recursively", ->
28 assert.are.same '/hello/world',
29 ts\create_fileder '/hello', 'world'
30 assert.are.same '/hello/world/again',
31 ts\create_fileder '/hello/world', 'again'
32
33 assert.are.same {'/hello', '/hello/world', '/hello/world/again'},
34 toseq ts\list_all_fileders!
35
36 it "can list immediate children", ->
37 assert.are.same {"/hello"}, toseq ts\list_fileders_in!
38 assert.are.same {"/hello/world"}, toseq ts\list_fileders_in "/hello"
39 assert.are.same {"/hello/world/again"}, toseq ts\list_fileders_in "/hello/world"
40
41 describe "can create and list facets", ->
42 ts\create_facet '/hello', 'name', 'alpha', 'hello'
43 ts\create_facet '/hello/world', 'name', 'alpha', 'world'
44 ts\create_facet '/hello/world', '', 'text/markdown', '# Helau World!'
45
46 it "but can't create facet for nonexistant fileders", ->
47 assert.has_error -> ts\create_facet '/hello/orldw', 'name', 'alpha', 'foo'
48
49 it "but can't create facet without value", ->
50 assert.has_error -> ts\create_facet '/hello/world', 'other', 'alpha', nil
51
52 it "but can't create facet for duplicate keys", ->
53 assert.has_error -> ts\create_facet '/hello/world', 'name', 'alpha', 'foo'
54
55 assert.are.same {{'name', 'alpha'}}, toseq2 ts\list_facets '/hello'
56 assert.are.same {{'', 'text/markdown'}, {'name', 'alpha'}},
57 toseq2 ts\list_facets '/hello/world'
58
59 it "can load facets", ->
60 assert.are.equal 'hello', ts\load_facet '/hello', 'name', 'alpha'
61 assert.are.equal 'world', ts\load_facet '/hello/world', 'name', 'alpha'
62 assert.are.equal '# Helau World!', ts\load_facet '/hello/world', '', 'text/markdown'
63 assert.is_falsy ts\load_facet '/hello', 'nonexistant', 'facet'
64
65 it "can rename facets", ->
66 ts\rename_facet '/hello/world', 'name', 'alpha', 'gnome'
67 assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
68 toseq2 ts\list_facets '/hello/world'
69 assert.are.equal 'world', ts\load_facet '/hello/world', 'gnome', 'alpha'
70
71 it "can update facets", ->
72 ts\update_facet '/hello/world', '', 'text/markdown', '# Hello World!'
73 assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
74 toseq2 ts\list_facets '/hello/world'
75 assert.are.equal '# Hello World!', ts\load_facet '/hello/world', '', 'text/markdown'
76
77 it "can remove facets", ->
78 ts\remove_facet '/hello/world', 'gnome', 'alpha'
79 assert.are.same {{'', 'text/markdown'}}, toseq2 ts\list_facets '/hello/world'
80
81 assert.has_error -> ts\remove_facet '/hello/world', 'gnome', 'alpha'
82
83 it "can delete fileders", ->
84 ts\remove_fileder '/hello/world'
85 assert.is_falsy ts\load_facet '/hello/world', 'gnome', 'alpha'
86 assert.are.same {'/hello'}, toseq ts\list_all_fileders!
87
88 ts\remove_fileder '/hello'
89 assert.are.same {}, toseq ts\list_all_fileders!
90
91 describe "SQL spec", ->
92 import SQLStore from require 'mmm.mmmfs.stores.sql'
93
94 test_store SQLStore memory: true
95
96 describe "FS store", ->
97 import FSStore from require 'mmm.mmmfs.stores.fs'
98
99 lfs = require 'lfs'
100
101 root = os.tmpname!
102
103 setup ->
104 assert os.remove root
105 assert lfs.mkdir root
106
107 test_store LFSStore :root
108
109 teardown ->
110 assert lfs.rmdir root