sgfa 0.1.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/LICENSE.txt +674 -0
- data/README.txt +14 -0
- data/bin/sgfa +15 -0
- data/data/sgfa_web.css +240 -0
- data/lib/sgfa/binder.rb +627 -0
- data/lib/sgfa/binder_fs.rb +203 -0
- data/lib/sgfa/cli/binder.rb +160 -0
- data/lib/sgfa/cli/jacket.rb +299 -0
- data/lib/sgfa/cli.rb +36 -0
- data/lib/sgfa/demo/web_binders.rb +111 -0
- data/lib/sgfa/demo/web_css.rb +60 -0
- data/lib/sgfa/entry.rb +697 -0
- data/lib/sgfa/error.rb +95 -0
- data/lib/sgfa/history.rb +445 -0
- data/lib/sgfa/jacket.rb +556 -0
- data/lib/sgfa/jacket_fs.rb +136 -0
- data/lib/sgfa/lock_fs.rb +141 -0
- data/lib/sgfa/state_fs.rb +342 -0
- data/lib/sgfa/store_fs.rb +214 -0
- data/lib/sgfa/web/base.rb +225 -0
- data/lib/sgfa/web/binder.rb +1190 -0
- data/lib/sgfa.rb +37 -0
- metadata +68 -0
data/lib/sgfa/binder.rb
ADDED
@@ -0,0 +1,627 @@
|
|
1
|
+
#
|
2
|
+
# Simple Group of Filing Applications
|
3
|
+
# Binder
|
4
|
+
#
|
5
|
+
# Copyright (C) 2015 by Graham A. Field.
|
6
|
+
#
|
7
|
+
# See LICENSE.txt for licensing information.
|
8
|
+
#
|
9
|
+
# This program is distributed WITHOUT ANY WARRANTY; without even the
|
10
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
11
|
+
|
12
|
+
require 'json'
|
13
|
+
|
14
|
+
require_relative 'error'
|
15
|
+
require_relative 'jacket'
|
16
|
+
require_relative 'entry'
|
17
|
+
require_relative 'history'
|
18
|
+
|
19
|
+
module Sgfa
|
20
|
+
|
21
|
+
#####################################################################
|
22
|
+
# The basic administrative unit in the Sgfa system. A binder provides
|
23
|
+
# access control for a collection of {Jacket}s. In addition it allows
|
24
|
+
# values to be set for the Binder itself, to use in managing a collection
|
25
|
+
# of Binders.
|
26
|
+
class Binder
|
27
|
+
|
28
|
+
|
29
|
+
#########################################################
|
30
|
+
# @!group Limit checks
|
31
|
+
|
32
|
+
# Maximum characters in jacket name
|
33
|
+
LimJacketMax = 64
|
34
|
+
|
35
|
+
# Invalid chars in jacket name
|
36
|
+
LimJacketInv = /[[:cntrl:]]|[\/\\\*\?]|(^_)/
|
37
|
+
|
38
|
+
private_constant :LimJacketMax, :LimJacketInv
|
39
|
+
|
40
|
+
|
41
|
+
#####################################
|
42
|
+
# Limits checks, jacket name
|
43
|
+
def self.limits_jacket(str)
|
44
|
+
Error.limits(str, 1, LimJacketMax, LimJacketInv, 'Jacket name')
|
45
|
+
end # def self.limits_jacket()
|
46
|
+
|
47
|
+
|
48
|
+
# Max characters in jacket title
|
49
|
+
LimTitleMax = 128
|
50
|
+
|
51
|
+
# Invalid chars in jacket title
|
52
|
+
LimTitleInv = /[[:cntrl:]]/
|
53
|
+
|
54
|
+
private_constant :LimTitleMax, :LimTitleInv
|
55
|
+
|
56
|
+
#####################################
|
57
|
+
# Limits checks, jacket title
|
58
|
+
def self.limits_title(str)
|
59
|
+
Error.limits(str, 1, LimTitleMax, LimTitleInv, 'Jacket title')
|
60
|
+
end # def self.limits_title()
|
61
|
+
|
62
|
+
|
63
|
+
# Max chars in permission
|
64
|
+
LimPermMax = 64
|
65
|
+
|
66
|
+
# Invalid chars in permission
|
67
|
+
LimPermInv = /[[:cntrl:]\/\\\*\?,]|(^_)/
|
68
|
+
|
69
|
+
private_constant :LimPermMax, :LimPermInv
|
70
|
+
|
71
|
+
#####################################
|
72
|
+
# Limits checks, permission array
|
73
|
+
def self.limits_perms(ary)
|
74
|
+
if !ary.is_a?(Array)
|
75
|
+
raise Error::Limits, 'Permission array required'
|
76
|
+
end
|
77
|
+
ary.each do |prm|
|
78
|
+
Error.limits(prm, 1, LimPermMax, LimPermInv, 'Permission')
|
79
|
+
end
|
80
|
+
end # def self.limits_perms()
|
81
|
+
|
82
|
+
|
83
|
+
# Max chars in value name
|
84
|
+
LimValueMax = 64
|
85
|
+
|
86
|
+
# Invalid chars in value name
|
87
|
+
LimValueInv = /[[:cntrl:]]/
|
88
|
+
|
89
|
+
private_constant :LimValueMax, :LimValueInv
|
90
|
+
|
91
|
+
#####################################
|
92
|
+
# Limits checks, value name
|
93
|
+
def self.limits_value(str)
|
94
|
+
stc = str.is_a?(Symbol) ? str.to_s : str
|
95
|
+
Error.limits(stc, 1, LimValueMax, LimValueInv, 'Value name')
|
96
|
+
end # def self.limits_value()
|
97
|
+
|
98
|
+
|
99
|
+
# Max chars in value setting
|
100
|
+
LimSettingMax = 128
|
101
|
+
|
102
|
+
# Invalid chars in value setting
|
103
|
+
LimSettingInv = /[[:cntrl:]]/
|
104
|
+
|
105
|
+
private_constant :LimSettingMax, :LimSettingInv
|
106
|
+
|
107
|
+
|
108
|
+
#####################################
|
109
|
+
# Limits checks, value setting
|
110
|
+
def self.limits_setting(str)
|
111
|
+
Error.limits(str, 1, LimSettingMax, LimSettingInv, 'Value setting')
|
112
|
+
end # def self.limits_setting()
|
113
|
+
|
114
|
+
|
115
|
+
#####################################
|
116
|
+
# Limits check, create values
|
117
|
+
def self.limits_create(info)
|
118
|
+
if !info.is_a?(Hash)
|
119
|
+
raise Error::Limits, 'Binder create info is not a hash'
|
120
|
+
end
|
121
|
+
|
122
|
+
if !info[:jackets].is_a?(Array)
|
123
|
+
raise Error::Limits, 'Binder create info :jackets is not an array'
|
124
|
+
end
|
125
|
+
info[:jackets].each do |jin|
|
126
|
+
if !jin.is_a?(Hash)
|
127
|
+
raise Error::Limits, 'Binder create info jacket not a hash'
|
128
|
+
end
|
129
|
+
Binder.limits_jacket(jin[:name])
|
130
|
+
Binder.limits_title(jin[:title])
|
131
|
+
Binder.limits_perms(jin[:perms])
|
132
|
+
end
|
133
|
+
|
134
|
+
if !info[:users].is_a?(Array)
|
135
|
+
raise Error::Limits, 'Binder create info :users is not an array'
|
136
|
+
end
|
137
|
+
info[:users].each do |uin|
|
138
|
+
if !uin.is_a?(Hash)
|
139
|
+
raise Error::Limits, 'Binder create info user not a hash'
|
140
|
+
end
|
141
|
+
History.limits_user(uin[:name])
|
142
|
+
Binder.limits_perms(uin[:perms])
|
143
|
+
end
|
144
|
+
|
145
|
+
if !info[:values].is_a?(Array)
|
146
|
+
raise Error::Limits, 'Binder create info :values is not an array'
|
147
|
+
end
|
148
|
+
info[:values].each do |ary|
|
149
|
+
if !ary.is_a?(Array)
|
150
|
+
raise Error::Limits, 'Binder create info :value items are not arrays'
|
151
|
+
end
|
152
|
+
vn, vs = ary
|
153
|
+
Binder.limits_value(vn)
|
154
|
+
Binder.limits_setting(vs)
|
155
|
+
end
|
156
|
+
|
157
|
+
Jacket.limits_id(info[:id_text]) if info[:id_text]
|
158
|
+
end # def limits_create()
|
159
|
+
|
160
|
+
|
161
|
+
#########################################################
|
162
|
+
# @!group Binder
|
163
|
+
|
164
|
+
|
165
|
+
#####################################
|
166
|
+
# Create a jacket
|
167
|
+
#
|
168
|
+
# @param tr [Hash] Common transaction info
|
169
|
+
# @option tr [String] :jacket Jacket name
|
170
|
+
# @option tr [String] :user User name
|
171
|
+
# @option tr [Array] :groups List of groups to which :user belongs
|
172
|
+
# @option tr [String] :title Title of the entry
|
173
|
+
# @option tr [String] :body Body of the entry
|
174
|
+
# @param title [String] Title of the jacket
|
175
|
+
# @param perms [Array] Permissions for the jacket
|
176
|
+
def jacket_create(tr, title, perms)
|
177
|
+
Binder.limits_title(title)
|
178
|
+
Binder.limits_perms(perms)
|
179
|
+
_control(tr) do |jck|
|
180
|
+
_perms(tr, ['manage'])
|
181
|
+
num = @jackets.size + 1
|
182
|
+
id_text, id_hash = _jacket_create(num)
|
183
|
+
ent = _control_jacket(tr, num, tr[:jacket], id_hash, id_text,
|
184
|
+
title, perms)
|
185
|
+
jck.write(tr[:user], [ent])
|
186
|
+
@jackets
|
187
|
+
end
|
188
|
+
end # def jacket_create()
|
189
|
+
|
190
|
+
|
191
|
+
#####################################
|
192
|
+
# Edit a jacket
|
193
|
+
#
|
194
|
+
# @param tr (see #jacket_create)
|
195
|
+
# @param name [String] New jacket name
|
196
|
+
# @param title [String] New jacket title
|
197
|
+
# @param perms [Array] New jacket permissions
|
198
|
+
def jacket_edit(tr, name, title, perms)
|
199
|
+
Binder.limits_jacket(name)
|
200
|
+
Binder.limits_title(title)
|
201
|
+
Binder.limits_perms(perms)
|
202
|
+
_control(tr) do |jck|
|
203
|
+
jnam = tr[:jacket]
|
204
|
+
raise Error::NonExistent, 'Jacket does not exist' if !@jackets[jnam]
|
205
|
+
jacket = @jackets[jnam]
|
206
|
+
num = jacket['num']
|
207
|
+
ent = _control_jacket(tr, num, name, jacket['id_hash'],
|
208
|
+
jacket['id_text'], title, perms)
|
209
|
+
jck.write(tr[:user], [ent])
|
210
|
+
@jackets
|
211
|
+
end
|
212
|
+
end # def jacket_edit()
|
213
|
+
|
214
|
+
|
215
|
+
#####################################
|
216
|
+
# Set user or group permissions
|
217
|
+
#
|
218
|
+
# @param tr (see #jacket_create)
|
219
|
+
# @param perms [Array] New user/group permissions
|
220
|
+
def binder_user(tr, user, perms)
|
221
|
+
History.limits_user(user)
|
222
|
+
Binder.limits_perms(perms)
|
223
|
+
_control(tr) do |jck|
|
224
|
+
ent = _control_user(tr, user, perms)
|
225
|
+
jck.write(tr[:user], [ent])
|
226
|
+
@users
|
227
|
+
end
|
228
|
+
end # def binder_user()
|
229
|
+
|
230
|
+
|
231
|
+
#####################################
|
232
|
+
# Set values
|
233
|
+
#
|
234
|
+
# @param tr (see #jacket_create)
|
235
|
+
# @param vals [Hash] New values
|
236
|
+
def binder_values(tr, vals)
|
237
|
+
vals.each do |vn, vs|
|
238
|
+
Binder.limits_value(vn)
|
239
|
+
Binder.limits_setting(vs)
|
240
|
+
end
|
241
|
+
_control(tr) do |jck|
|
242
|
+
ent = _control_values(tr, vals)
|
243
|
+
jck.write(tr[:user], [ent])
|
244
|
+
@values
|
245
|
+
end
|
246
|
+
end # def binder_values()
|
247
|
+
|
248
|
+
|
249
|
+
#####################################
|
250
|
+
# Get info
|
251
|
+
#
|
252
|
+
# @param tr (see #jacket_create)
|
253
|
+
# @return [Hash] Containing :id_hash, :id_text, :jackets, :values, :users
|
254
|
+
def binder_info(tr)
|
255
|
+
_shared do
|
256
|
+
_perms(tr, ['info'])
|
257
|
+
{
|
258
|
+
:id_hash => @id_hash.dup,
|
259
|
+
:id_text => @id_text.dup,
|
260
|
+
:values => @values,
|
261
|
+
:jackets => @jackets,
|
262
|
+
:users => @users,
|
263
|
+
}
|
264
|
+
end
|
265
|
+
end # def binder_info()
|
266
|
+
|
267
|
+
|
268
|
+
#########################################################
|
269
|
+
# @!group Jacket
|
270
|
+
|
271
|
+
|
272
|
+
#####################################
|
273
|
+
# Read list of tags
|
274
|
+
#
|
275
|
+
# @param tr (see #jacket_create)
|
276
|
+
def read_list(tr)
|
277
|
+
_jacket(tr, 'info'){|jck| jck.read_list }
|
278
|
+
end # def read_list()
|
279
|
+
|
280
|
+
|
281
|
+
#####################################
|
282
|
+
# Read a tag
|
283
|
+
#
|
284
|
+
# @param tr (see #jacket_create)
|
285
|
+
# @param tag [String] Tag name
|
286
|
+
# @param offs [Integer] Offset to begin reading
|
287
|
+
# @param max [Integer] Maximum number of entries to read
|
288
|
+
def read_tag(tr, tag, offs, max)
|
289
|
+
_jacket(tr, 'read') do |jck|
|
290
|
+
size, ents = jck.read_tag(tag, offs, max)
|
291
|
+
lst = ents.map do |ent|
|
292
|
+
[ent.entry, ent.revision, ent.time, ent.title, ent.tags.size,
|
293
|
+
ent.attachments.size]
|
294
|
+
end
|
295
|
+
[size, lst]
|
296
|
+
end
|
297
|
+
end # def read_tag()
|
298
|
+
|
299
|
+
|
300
|
+
#####################################
|
301
|
+
# Read history log
|
302
|
+
#
|
303
|
+
# @param tr (see #jacket_create)
|
304
|
+
# @param offs [Integer] Offset to begin reading
|
305
|
+
# @param max [Integer] Maximum number of histories to read
|
306
|
+
def read_log(tr, offs, max)
|
307
|
+
_jacket(tr, 'info') do |jck|
|
308
|
+
cur = jck.read_history()
|
309
|
+
hmax = cur ? cur.history : 0
|
310
|
+
start = (offs <= hmax) ? hmax - offs : 0
|
311
|
+
stop = (start - max > 0) ? (start - (max-1)) : 1
|
312
|
+
ary = []
|
313
|
+
if start != 0
|
314
|
+
start.downto(stop) do |hnum|
|
315
|
+
hst = jck.read_history(hnum)
|
316
|
+
ary.push [hst.history, hst.time, hst.user, hst.entries.size,
|
317
|
+
hst.attachments.size]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
[hmax, ary]
|
321
|
+
end
|
322
|
+
end # def read_log()
|
323
|
+
|
324
|
+
|
325
|
+
#####################################
|
326
|
+
# Read an entry
|
327
|
+
#
|
328
|
+
# @param tr (see #jacket_create)
|
329
|
+
# @param enum [Integer] Entry number
|
330
|
+
# @param rnum [Integer] Revision number
|
331
|
+
# @return [Entry] the Requested entry
|
332
|
+
def read_entry(tr, enum, rnum=0)
|
333
|
+
_jacket(tr, 'read') do |jck|
|
334
|
+
cur = jck.read_entry(enum, 0)
|
335
|
+
pl = cur.perms
|
336
|
+
_perms(tr, pl) if !pl.empty?
|
337
|
+
if rnum == 0
|
338
|
+
cur
|
339
|
+
else
|
340
|
+
jck.read_entry(enum, rnum)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end # def read_entry()
|
344
|
+
|
345
|
+
|
346
|
+
#####################################
|
347
|
+
# Read a history item
|
348
|
+
#
|
349
|
+
# @param tr (see #jacket_create)
|
350
|
+
# @param hnum [Integer] History number
|
351
|
+
# @return [History] History item requested
|
352
|
+
def read_history(tr, hnum)
|
353
|
+
_jacket(tr, 'info'){|jck| jck.read_history(hnum) }
|
354
|
+
end # def read_history()
|
355
|
+
|
356
|
+
|
357
|
+
#####################################
|
358
|
+
# Read an attachment
|
359
|
+
#
|
360
|
+
# @param tr (see #jacket_create)
|
361
|
+
# @param enum [Integer] Entry number
|
362
|
+
# @param anum [Integer] Attachment number
|
363
|
+
# @param hnum [Integer] History number
|
364
|
+
# @return [File] Attachment
|
365
|
+
def read_attach(tr, enum, anum, hnum)
|
366
|
+
_jacket(tr, 'read') do |jck|
|
367
|
+
cur = jck.read_entry(enum, 0)
|
368
|
+
pl = cur.perms
|
369
|
+
_perms(tr, pl) if !pl.empty?
|
370
|
+
jck.read_attach(enum, anum, hnum)
|
371
|
+
end
|
372
|
+
end # def read_attach()
|
373
|
+
|
374
|
+
|
375
|
+
#####################################
|
376
|
+
# Write entries
|
377
|
+
#
|
378
|
+
# @param tr (see #jacket_create)
|
379
|
+
# @param ents [Array] List of entries to write
|
380
|
+
# @return (see Jacket#write)
|
381
|
+
# @raise [Error::Permission] if user lacks require permissions
|
382
|
+
# @raise [Error::Conflict] if entry revision is not one up from current
|
383
|
+
def write(tr, ents)
|
384
|
+
olde = ents.select{|ent| ent.entry }
|
385
|
+
enums = olde.map{|ent| ent.entry }
|
386
|
+
_jacket(tr, 'write') do |jck|
|
387
|
+
cur = jck.read_array(enums)
|
388
|
+
pl = []
|
389
|
+
cur.each{|ent| pl.concat ent.perms }
|
390
|
+
_perms(tr, pl) if !pl.empty?
|
391
|
+
enums.each_index do |idx|
|
392
|
+
if cur[idx].revision + 1 != olde[idx].revision
|
393
|
+
raise Error::Conflict, 'Entry revision conflict'
|
394
|
+
end
|
395
|
+
end
|
396
|
+
jck.write(tr[:user], ents)
|
397
|
+
end
|
398
|
+
end # def write()
|
399
|
+
|
400
|
+
|
401
|
+
private
|
402
|
+
|
403
|
+
|
404
|
+
#####################################
|
405
|
+
# Shared creation stuff
|
406
|
+
#
|
407
|
+
# @param ctl [Jacket] Control jacket
|
408
|
+
# @param tr (see #jacket_create)
|
409
|
+
# @param info [Hash] New binder creation options
|
410
|
+
# @option info [Array] :jackets List of jackets [name, title, perms]
|
411
|
+
# @option info [Array] :users List of users [name, perms]
|
412
|
+
# @option info [Hash] :values List of values name => setting
|
413
|
+
def _create(ctl, tr, info)
|
414
|
+
|
415
|
+
# check all the values are okay
|
416
|
+
History.limits_user(tr[:user])
|
417
|
+
Entry.limits_title(tr[:title])
|
418
|
+
Entry.limits_body(tr[:body])
|
419
|
+
Binder.limits_create(info)
|
420
|
+
|
421
|
+
ents = []
|
422
|
+
|
423
|
+
# jackets
|
424
|
+
num = 0
|
425
|
+
info[:jackets].each do |jin|
|
426
|
+
num += 1
|
427
|
+
trj = {
|
428
|
+
:title => 'Create binder initial jacket \'%s\'' % jin[:name],
|
429
|
+
:jacket => jin[:name],
|
430
|
+
:body => "Create binder initial jacket\n\n",
|
431
|
+
}
|
432
|
+
id_text, id_hash = _jacket_create(num)
|
433
|
+
ents.push _control_jacket(trj, num, jin[:name], id_hash, id_text,
|
434
|
+
jin[:title], jin[:perms])
|
435
|
+
end
|
436
|
+
|
437
|
+
# users
|
438
|
+
info[:users].each do |uin|
|
439
|
+
tru = {
|
440
|
+
:title => 'Create binder initial user \'%s\'' % uin[:name],
|
441
|
+
:body => "Create binder initial user\n\n",
|
442
|
+
}
|
443
|
+
ents.push _control_user(tr, uin[:name], uin[:perms])
|
444
|
+
end
|
445
|
+
|
446
|
+
# values
|
447
|
+
ents.push _control_values(tr, info[:values])
|
448
|
+
ctl.write(tr[:user], ents)
|
449
|
+
end # def _create()
|
450
|
+
|
451
|
+
|
452
|
+
#####################################
|
453
|
+
# Permission check
|
454
|
+
#
|
455
|
+
# @param tr (see #jacket_create)
|
456
|
+
# @param plst [Array] Permissions required
|
457
|
+
# @raise [Error::Permissions] if require permissions not met
|
458
|
+
def _perms(tr, plst)
|
459
|
+
if tr[:perms]
|
460
|
+
usr_has = tr[:perms]
|
461
|
+
else
|
462
|
+
usr = tr[:user]
|
463
|
+
grp = tr[:groups]
|
464
|
+
usr_has = []
|
465
|
+
usr_has.concat(@users[usr]) if @users[usr]
|
466
|
+
grp.each{|gr| usr_has.concat(@users[gr]) if @users[gr] }
|
467
|
+
if usr_has.include?('write')
|
468
|
+
usr_has.concat ['read', 'info']
|
469
|
+
elsif usr_has.include?('read') || usr_has.include?('manage')
|
470
|
+
usr_has.push 'info'
|
471
|
+
end
|
472
|
+
usr_has.uniq!
|
473
|
+
tr[:perms] = usr_has
|
474
|
+
end
|
475
|
+
|
476
|
+
miss = []
|
477
|
+
plst.each{|pr| miss.push(pr) if !usr_has.include?(pr) }
|
478
|
+
|
479
|
+
if !miss.empty?
|
480
|
+
raise Error::Permission, 'User lacks permission(s): ' + miss.join(', ')
|
481
|
+
end
|
482
|
+
end # def _perms()
|
483
|
+
|
484
|
+
|
485
|
+
#####################################
|
486
|
+
# Access a jacket
|
487
|
+
#
|
488
|
+
# @param tr (see #jacket_create)
|
489
|
+
# @param perm [String] Basic permission needed (write, read, info)
|
490
|
+
def _jacket(tr, perm)
|
491
|
+
ret = nil
|
492
|
+
_shared do
|
493
|
+
jnam = tr[:jacket]
|
494
|
+
raise Error::NonExistent, 'Jacket does not exist' if !@jackets[jnam]
|
495
|
+
pl = [perm].concat @jackets[jnam][:perms]
|
496
|
+
_perms(tr, pl)
|
497
|
+
jck = _jacket_open(@jackets[jnam][:num])
|
498
|
+
begin
|
499
|
+
ret = yield(jck)
|
500
|
+
ensure
|
501
|
+
jck.close
|
502
|
+
end
|
503
|
+
end
|
504
|
+
return ret
|
505
|
+
end # def _jacket()
|
506
|
+
|
507
|
+
|
508
|
+
#####################################
|
509
|
+
# Edit control jacket
|
510
|
+
#
|
511
|
+
# @param tr (see #jacket_create)
|
512
|
+
def _control(tr)
|
513
|
+
ret = nil
|
514
|
+
@lock.do_ex do
|
515
|
+
_cache_read()
|
516
|
+
_perms(tr, ['manage'])
|
517
|
+
begin
|
518
|
+
ctl = _jacket_open(0)
|
519
|
+
begin
|
520
|
+
ret = yield(ctl)
|
521
|
+
ensure
|
522
|
+
ctl.close()
|
523
|
+
end
|
524
|
+
_cache_write()
|
525
|
+
ensure
|
526
|
+
_cache_clear()
|
527
|
+
end
|
528
|
+
end # @lock_do
|
529
|
+
return ret
|
530
|
+
end # def _control()
|
531
|
+
|
532
|
+
|
533
|
+
#####################################
|
534
|
+
# Shared access to the binder
|
535
|
+
def _shared
|
536
|
+
ret = nil
|
537
|
+
@lock.do_sh do
|
538
|
+
_cache_read()
|
539
|
+
begin
|
540
|
+
ret = yield
|
541
|
+
ensure
|
542
|
+
_cache_clear()
|
543
|
+
end
|
544
|
+
end # @lock.do_sh
|
545
|
+
return ret
|
546
|
+
end # def _shared
|
547
|
+
|
548
|
+
|
549
|
+
#####################################
|
550
|
+
# Set jacket info
|
551
|
+
def _control_jacket(tr, num, name, id_hash, id_text, title, perms)
|
552
|
+
info = {
|
553
|
+
num: num,
|
554
|
+
name: name,
|
555
|
+
id_hash: id_hash,
|
556
|
+
id_text: id_text,
|
557
|
+
title: title,
|
558
|
+
perms: perms,
|
559
|
+
}
|
560
|
+
json = JSON.pretty_generate(info)
|
561
|
+
|
562
|
+
ent = Entry.new
|
563
|
+
ent.tag( 'jacket: %d' % num )
|
564
|
+
ent.title = tr[:title]
|
565
|
+
ent.body = tr[:body] + "\n" + json + "\n"
|
566
|
+
|
567
|
+
@jackets.delete(tr[:jacket])
|
568
|
+
@jackets[name] = info
|
569
|
+
|
570
|
+
return ent
|
571
|
+
end # def _control_jacket()
|
572
|
+
|
573
|
+
|
574
|
+
#####################################
|
575
|
+
# Set user permissions in the control jacket
|
576
|
+
def _control_user(tr, user, perms)
|
577
|
+
info = {
|
578
|
+
name: user,
|
579
|
+
perms: perms,
|
580
|
+
}
|
581
|
+
json = JSON.pretty_generate(info)
|
582
|
+
|
583
|
+
ent = Entry.new
|
584
|
+
ent.tag( 'user: %s' % user )
|
585
|
+
ent.title = tr[:title]
|
586
|
+
ent.body = tr[:body] + "\n" + json + "\n"
|
587
|
+
|
588
|
+
@users[user.dup] = perms.map{|pr| pr.dup }
|
589
|
+
|
590
|
+
return ent
|
591
|
+
end # def _control_user()
|
592
|
+
|
593
|
+
|
594
|
+
#####################################
|
595
|
+
# Set binder values in the control jacket
|
596
|
+
def _control_values(tr, vals)
|
597
|
+
json = JSON.pretty_generate(vals)
|
598
|
+
|
599
|
+
ent = Entry.new
|
600
|
+
ent.tag( 'values' )
|
601
|
+
ent.title = tr[:title]
|
602
|
+
ent.body = tr[:body] + "\n" + json + "\n"
|
603
|
+
|
604
|
+
vals.each do |val, sta|
|
605
|
+
vas = val.is_a?(Symbol) ? val : val.to_s
|
606
|
+
if sta
|
607
|
+
@values[val] = sta
|
608
|
+
else
|
609
|
+
@values.delete(val)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
return ent
|
614
|
+
end # def _control_values()
|
615
|
+
|
616
|
+
|
617
|
+
#####################################
|
618
|
+
# Clear cache
|
619
|
+
def _cache_clear
|
620
|
+
@jackets = nil
|
621
|
+
@users = nil
|
622
|
+
@values = nil
|
623
|
+
end # def _cache_clear
|
624
|
+
|
625
|
+
end # class Binder
|
626
|
+
|
627
|
+
end # module Sgfa
|