rubko 0.1
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/.gitignore +1 -0
- data/bin/rubko-deploy +40 -0
- data/bin/rubko-init +6 -0
- data/lib/rubko/app.rb +79 -0
- data/lib/rubko/asset/fileStream.rb +18 -0
- data/lib/rubko/asset/gzipStream.rb +26 -0
- data/lib/rubko/asset.rb +71 -0
- data/lib/rubko/base.rb +123 -0
- data/lib/rubko/controller.rb +17 -0
- data/lib/rubko/model.rb +5 -0
- data/lib/rubko/plugin.rb +7 -0
- data/lib/rubko/plugins/cookie.rb +45 -0
- data/lib/rubko/plugins/db.rb +389 -0
- data/lib/rubko/plugins/facebook.rb +50 -0
- data/lib/rubko/plugins/log.rb +49 -0
- data/lib/rubko/plugins/session.rb +88 -0
- data/lib/rubko/plugins/storage/db.rb +6 -0
- data/lib/rubko/plugins/storage/disk.rb +50 -0
- data/lib/rubko/plugins/storage/memory.rb +60 -0
- data/lib/rubko/plugins/url.rb +91 -0
- data/lib/rubko/socket.rb +23 -0
- data/lib/rubko/storage.rb +27 -0
- data/lib/rubko.rb +32 -0
- data/rubko.gemspec +22 -0
- data/stamp/.gitignore +3 -0
- data/stamp/config/db.rb +8 -0
- data/stamp/config/facebook.rb +8 -0
- data/stamp/config/session.rb +7 -0
- data/stamp/config/url.rb +7 -0
- data/stamp/config.ru +38 -0
- data/stamp/controllers/error404.rb +19 -0
- data/stamp/controllers/files.rb +20 -0
- data/stamp/controllers/welcome.rb +6 -0
- data/stamp/models/user.rb +14 -0
- data/stamp/public/css/.private/default.sass +31 -0
- data/stamp/public/img/favicon.png +0 -0
- data/stamp/public/js/jquery/iframize.js +25 -0
- data/stamp/public/js/jquery/translate.js +112 -0
- data/stamp/public/js/merge.sh +10 -0
- data/stamp/tarok.yml +17 -0
- data/stamp/views/welcome.haml +11 -0
- metadata +100 -0
@@ -0,0 +1,389 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
class DbPlugin < Rubko::Plugin
|
4
|
+
def init
|
5
|
+
@host = ''
|
6
|
+
@port = 5432
|
7
|
+
@user = 'postgres'
|
8
|
+
@password = ''
|
9
|
+
@db = 'postgres'
|
10
|
+
|
11
|
+
@pool = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle
|
15
|
+
unless @handle
|
16
|
+
config if @handle.nil?
|
17
|
+
if @pool
|
18
|
+
@sig = [@host, @port, @user, @password, @db]
|
19
|
+
@handle = ( @pool[:db, *@sig] ||= [] ).pop
|
20
|
+
end
|
21
|
+
@handle = connect unless @handle
|
22
|
+
end
|
23
|
+
@handle
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :host, :port, :user, :password, :db, :pool
|
27
|
+
attr_reader :affectedRows
|
28
|
+
|
29
|
+
def connect
|
30
|
+
PG.connect host: @host, port: @post, user: @user, password: @password, dbname: @db
|
31
|
+
end
|
32
|
+
|
33
|
+
def release
|
34
|
+
return true unless @handle
|
35
|
+
if @pool && handle.transaction_status == 0
|
36
|
+
@pool[:db, *@sig].push @handle
|
37
|
+
@handle = false
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def poolSize
|
43
|
+
@pool.keys(:db, *@sig).size
|
44
|
+
end
|
45
|
+
|
46
|
+
# PostgreSQL array OIDs and coresponding data type
|
47
|
+
TypArray = {
|
48
|
+
1000 => 16, # bool
|
49
|
+
1001 => 17, # bytea
|
50
|
+
1002 => 18, # char
|
51
|
+
1003 => 19, # name
|
52
|
+
1016 => 20, # int8
|
53
|
+
1005 => 21, # int2
|
54
|
+
1006 => 22, # int2vector
|
55
|
+
1007 => 23, # int4
|
56
|
+
1008 => 24, # regproc
|
57
|
+
1009 => 25, # text
|
58
|
+
1028 => 26, # oid
|
59
|
+
1010 => 27, # tid
|
60
|
+
1011 => 28, # xid
|
61
|
+
1012 => 29, # cid
|
62
|
+
1013 => 30, # oidvector
|
63
|
+
199 => 114, # json
|
64
|
+
143 => 142, # xml
|
65
|
+
1017 => 600, # point
|
66
|
+
1018 => 601, # lseg
|
67
|
+
1019 => 602, # path
|
68
|
+
1020 => 603, # box
|
69
|
+
1027 => 604, # polygon
|
70
|
+
629 => 628, # line
|
71
|
+
651 => 650, # cidr
|
72
|
+
1021 => 700, # float4
|
73
|
+
1022 => 701, # float8
|
74
|
+
1023 => 702, # abstime
|
75
|
+
1024 => 703, # reltime
|
76
|
+
1025 => 704, # tinterval
|
77
|
+
719 => 718, # circle
|
78
|
+
791 => 790, # money
|
79
|
+
1040 => 829, # macaddr
|
80
|
+
1041 => 869, # inet
|
81
|
+
1034 => 1033, # aclitem
|
82
|
+
1014 => 1042, # bpchar
|
83
|
+
1015 => 1043, # varchar
|
84
|
+
1182 => 1082, # date
|
85
|
+
1183 => 1083, # time
|
86
|
+
1115 => 1114, # timestamp
|
87
|
+
1185 => 1184, # timestamptz
|
88
|
+
1187 => 1186, # interval
|
89
|
+
1270 => 1266, # timetz
|
90
|
+
1561 => 1560, # bit
|
91
|
+
1563 => 1562, # varbit
|
92
|
+
1231 => 1700, # numeric
|
93
|
+
2201 => 1790, # refcursor
|
94
|
+
2207 => 2202, # regprocedure
|
95
|
+
2208 => 2203, # regoper
|
96
|
+
2209 => 2204, # regoperator
|
97
|
+
2210 => 2205, # regclass
|
98
|
+
2211 => 2206, # regtype
|
99
|
+
2287 => 2249, # record
|
100
|
+
1263 => 2275, # cstring
|
101
|
+
2951 => 2950, # uuid
|
102
|
+
2949 => 2970, # txid_snapshot
|
103
|
+
3643 => 3614, # tsvector
|
104
|
+
3645 => 3615, # tsquery
|
105
|
+
3644 => 3642, # gtsvector
|
106
|
+
3735 => 3734, # regconfig
|
107
|
+
3770 => 3769, # regdictionary
|
108
|
+
3905 => 3904, # int4range
|
109
|
+
3907 => 3906, # numrange
|
110
|
+
3909 => 3908, # tsrange
|
111
|
+
3911 => 3910, # tstzrange
|
112
|
+
3913 => 3912, # daterange
|
113
|
+
3927 => 3926, # int8range
|
114
|
+
}
|
115
|
+
|
116
|
+
def castArray(value, type)
|
117
|
+
size, _, type = *value[0, 3*4].unpack('l>*')
|
118
|
+
return [] if size == 0
|
119
|
+
|
120
|
+
dims = value[3*4, 2*4*size].unpack('l>*').each_slice(2).map {
|
121
|
+
|x| x[0]
|
122
|
+
}
|
123
|
+
|
124
|
+
i = 3*4 + 2*4*size
|
125
|
+
data = dims.reduce(:*).times.map {
|
126
|
+
size = value[i, 4].unpack('l>')[0]
|
127
|
+
i += 4 + size
|
128
|
+
cast value[ i-size, size ], type
|
129
|
+
}
|
130
|
+
|
131
|
+
dims.reverse.reduce(data) { |orig, dim|
|
132
|
+
orig.each_slice dim
|
133
|
+
}.to_a[0]
|
134
|
+
end
|
135
|
+
|
136
|
+
Unpack = {
|
137
|
+
16 => 'C', # bool
|
138
|
+
21 => 's>', # int2
|
139
|
+
23 => 'l>', # int4
|
140
|
+
20 => 'q>', # int8
|
141
|
+
26 => 'L>', # oid
|
142
|
+
700 => 'g', # float4
|
143
|
+
701 => 'G', # float8
|
144
|
+
829 => 'C*', # macaddr
|
145
|
+
1082 => 'l>', # date
|
146
|
+
1114 => 'q>', # timestamp
|
147
|
+
1184 => 'q>', # timestamptz
|
148
|
+
1186 => 'q>l>l>', # interval
|
149
|
+
1700 => 's>*', # numeric
|
150
|
+
|
151
|
+
1007 => 'l>*'
|
152
|
+
}
|
153
|
+
|
154
|
+
def castNumeric(value)
|
155
|
+
_, fac, sign, scale, *digits = value
|
156
|
+
return Float::NAN if sign == -16384
|
157
|
+
|
158
|
+
sign = (sign == 0 ? '': '-')
|
159
|
+
value = digits.map { |digit|
|
160
|
+
'%04d' % digit
|
161
|
+
}
|
162
|
+
|
163
|
+
if scale != 0
|
164
|
+
require 'bigdecimal'
|
165
|
+
BigDecimal.new sign + value.insert(fac+1, '.').join
|
166
|
+
else
|
167
|
+
( sign + value.join + '0000'*fac ).to_i
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def cast(value, type)
|
172
|
+
return if value.nil?
|
173
|
+
|
174
|
+
if TypArray.key? type
|
175
|
+
return castArray value, TypArray[type]
|
176
|
+
end
|
177
|
+
|
178
|
+
if Unpack.key? type
|
179
|
+
value = value.unpack Unpack[type]
|
180
|
+
end
|
181
|
+
|
182
|
+
case type
|
183
|
+
when 16 # bool
|
184
|
+
value[0] == 1
|
185
|
+
when 17 # bytea
|
186
|
+
value
|
187
|
+
when 25, 1042, 1043, 18, 19 #text, bpchar, varchar, char, name
|
188
|
+
value.force_encoding 'UTF-8'
|
189
|
+
when 1700 # numeric
|
190
|
+
castNumeric value
|
191
|
+
when 114 # json
|
192
|
+
JSON.parse value, symbolize_names: true
|
193
|
+
when 1082 # date
|
194
|
+
Date.new(2000) + value[0]
|
195
|
+
when 1114, 1184 # timestamp, timestamptz
|
196
|
+
Time.gm(2000) + value[0]/1_000_000.0
|
197
|
+
when 1186 # interval
|
198
|
+
TimeInterval.new( *value.reverse )
|
199
|
+
when 829 # macaddr
|
200
|
+
value.map { |x| '%02x' % x } * ':'
|
201
|
+
when 869 # inet
|
202
|
+
require 'ipaddr'
|
203
|
+
ip = IPAddr.ntop value[4..-1]
|
204
|
+
IPAddr.new "#{ip}/#{value[1].ord}"
|
205
|
+
when 2278, 705 # void, unknown
|
206
|
+
nil
|
207
|
+
else
|
208
|
+
return value[0] if Unpack.key? type
|
209
|
+
throw "Invalid OID type of #{type} (#{value})" if type < 16384
|
210
|
+
value
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def castHash(hash, result)
|
215
|
+
Hash[ hash.map.with_index { |(key, value), i|
|
216
|
+
[ key.to_sym, cast(value, result.ftype(i)) ]
|
217
|
+
} ]
|
218
|
+
end
|
219
|
+
|
220
|
+
private :cast, :castHash, :castArray
|
221
|
+
|
222
|
+
def escape(param, type = nil)
|
223
|
+
case param
|
224
|
+
when Array
|
225
|
+
'(' + param.map { |element|
|
226
|
+
escape element
|
227
|
+
}.join(',') + ')'
|
228
|
+
when String
|
229
|
+
if param.encoding.name == 'ASCII-8BIT'
|
230
|
+
"'" + handle.escape_bytea(param) + "'"
|
231
|
+
else
|
232
|
+
"'" + handle.escape_string(param) + "'"
|
233
|
+
end
|
234
|
+
when Symbol
|
235
|
+
handle.quote_ident param.to_s
|
236
|
+
when Integer, Float, TimeInterval, FalseClass, TrueClass
|
237
|
+
param.to_s
|
238
|
+
else
|
239
|
+
'NULL'
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def prepare(query, *args)
|
244
|
+
hash = args[-1].kind_of?(Hash) && args.pop || {}
|
245
|
+
hash.merge! Hash[(0...args.size).zip args]
|
246
|
+
|
247
|
+
i = -1
|
248
|
+
query.gsub(/[[:alpha:]]+/i) { |match|
|
249
|
+
( match =~ /[[:upper:]]/ && match =~ /[[:lower:]]/ ) ? escape(match.to_sym) : match
|
250
|
+
}.gsub(/\?(\w+)?\??([b])?/) { |match|
|
251
|
+
key = $1 || i += 1
|
252
|
+
key = key.to_sym if key.respond_to? :to_sym
|
253
|
+
key = key.to_s unless hash.has_key? key
|
254
|
+
|
255
|
+
hash.has_key?(key) && escape(hash[key], $2) || match
|
256
|
+
}.tap { |out| log.sql out.gsub(/\s+/, ' ') }
|
257
|
+
end
|
258
|
+
|
259
|
+
def raw(query, *args)
|
260
|
+
handle.exec prepare(query, *args), [], 1
|
261
|
+
end
|
262
|
+
|
263
|
+
def value(query, *args)
|
264
|
+
result = raw query, *args
|
265
|
+
cast(result.getvalue(0, 0), result.ftype(0)) if result.ntuples > 0
|
266
|
+
end
|
267
|
+
|
268
|
+
def row(query, *args)
|
269
|
+
result = raw query, *args
|
270
|
+
castHash result[0], result if result.ntuples > 0
|
271
|
+
end
|
272
|
+
|
273
|
+
def column(query, *args)
|
274
|
+
result = raw query, *args
|
275
|
+
return [] if result.nfields == 0
|
276
|
+
result.column_values(0).map { |value|
|
277
|
+
cast value, result.ftype(0)
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
def result(query, *args, &block)
|
282
|
+
result = raw(query, *args)
|
283
|
+
if block
|
284
|
+
result.each { |row|
|
285
|
+
yield castHash row, result
|
286
|
+
}
|
287
|
+
else
|
288
|
+
result.map { |row|
|
289
|
+
castHash row, result
|
290
|
+
}
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def resultHash(key, query, *args)
|
295
|
+
ret = {}
|
296
|
+
result = raw query, *args
|
297
|
+
result.each { |row|
|
298
|
+
row = castHash row, result
|
299
|
+
|
300
|
+
[*key].reduce(ret) { |v, k|
|
301
|
+
bucket = row.delete k.to_sym
|
302
|
+
v[bucket] ||= {}
|
303
|
+
}.replace row
|
304
|
+
}
|
305
|
+
ret
|
306
|
+
end
|
307
|
+
|
308
|
+
def resultMultiHash(key, query, *args)
|
309
|
+
ret = {}
|
310
|
+
result = raw query, *args
|
311
|
+
result.each { |row|
|
312
|
+
row = castHash row, result
|
313
|
+
|
314
|
+
last = [*key].last
|
315
|
+
[*key].reduce(ret) { |v, k|
|
316
|
+
bucket = row.delete k.to_sym
|
317
|
+
if k.equal? last # is the last element
|
318
|
+
( v[bucket] ||= [] ) << row
|
319
|
+
else
|
320
|
+
v[bucket] ||= {}
|
321
|
+
end
|
322
|
+
}
|
323
|
+
}
|
324
|
+
ret
|
325
|
+
end
|
326
|
+
|
327
|
+
def transaction(mode = nil)
|
328
|
+
begin
|
329
|
+
raw 'BEGIN' + (' ISOLATION LEVEL '+mode if mode).to_s
|
330
|
+
yield
|
331
|
+
raw 'COMMIT' if inTransaction
|
332
|
+
rescue => e
|
333
|
+
puts e
|
334
|
+
puts e.backtrace
|
335
|
+
rollback
|
336
|
+
puts 'Transaction rolled back.'
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def rollback
|
341
|
+
raw 'ROLLBACK'
|
342
|
+
end
|
343
|
+
|
344
|
+
def inTransaction
|
345
|
+
handle.transaction_status == 2
|
346
|
+
end
|
347
|
+
|
348
|
+
def finalize
|
349
|
+
unless release
|
350
|
+
handle.finish unless handle.finished?
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
class TimeInterval
|
356
|
+
# this class directly adheres to Postgres's internal INTERVAL representation
|
357
|
+
|
358
|
+
def initialize(mon, day, usec)
|
359
|
+
@mon = mon
|
360
|
+
@day = day
|
361
|
+
@usec = usec
|
362
|
+
end
|
363
|
+
|
364
|
+
attr_accessor :mon, :day, :usec
|
365
|
+
|
366
|
+
def to_s
|
367
|
+
"#{mon}mon #{day}day #{usec}usec"
|
368
|
+
end
|
369
|
+
|
370
|
+
def +(time)
|
371
|
+
( (time >> mon) + day ).to_time + usec/1_000_000.0
|
372
|
+
end
|
373
|
+
|
374
|
+
def to_time
|
375
|
+
self + DateTime.new(1970)
|
376
|
+
end
|
377
|
+
|
378
|
+
def to_f
|
379
|
+
to_time.to_f
|
380
|
+
end
|
381
|
+
|
382
|
+
def to_i
|
383
|
+
to_time.to_i
|
384
|
+
end
|
385
|
+
|
386
|
+
def to_json(*)
|
387
|
+
to_f.to_s
|
388
|
+
end
|
389
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class FacebookPlugin < Rubko::Plugin
|
4
|
+
def init
|
5
|
+
@home = "http://#{url.host}/"
|
6
|
+
config
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :id, :secret, :home
|
10
|
+
attr_reader :token
|
11
|
+
|
12
|
+
# PART 1: Initiate login, ask user for permissions
|
13
|
+
def start
|
14
|
+
SecureRandom.hex(16).tap { |state|
|
15
|
+
url.redirect build 'https://www.facebook.com/dialog/oauth', query.merge({
|
16
|
+
scope: 'email,publish_stream', state: state
|
17
|
+
})
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
# PART 2: Return user data, populate @token
|
22
|
+
def login(state, str)
|
23
|
+
raise 'Please supply the return value of #start to #login' unless state
|
24
|
+
|
25
|
+
params = parse str
|
26
|
+
return false if state != params['state']
|
27
|
+
|
28
|
+
code = params['code']
|
29
|
+
url = build 'https://graph.facebook.com/oauth/access_token', query.merge({
|
30
|
+
client_secret: secret, code: code
|
31
|
+
})
|
32
|
+
@token = parse( httpGet url )['access_token']
|
33
|
+
|
34
|
+
jsonParse httpGet build 'https://graph.facebook.com/me', access_token: @token
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse(str)
|
40
|
+
Rack::Utils.parse_query str
|
41
|
+
end
|
42
|
+
|
43
|
+
def build(url, hash)
|
44
|
+
url + '?' + Rack::Utils.build_query(hash)
|
45
|
+
end
|
46
|
+
|
47
|
+
def query
|
48
|
+
{client_id: id, redirect_uri: home}
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class LogPlugin < Rubko::Plugin
|
2
|
+
|
3
|
+
def init
|
4
|
+
@mapper = {
|
5
|
+
sql: [:term, :file],
|
6
|
+
time: [:term],
|
7
|
+
debug: [:term],
|
8
|
+
}
|
9
|
+
config
|
10
|
+
end
|
11
|
+
|
12
|
+
Colors = {
|
13
|
+
black: 0,
|
14
|
+
red: 1,
|
15
|
+
green: 2,
|
16
|
+
yellow: 3,
|
17
|
+
blue: 4,
|
18
|
+
magenta: 5,
|
19
|
+
cyan: 6,
|
20
|
+
white: 7,
|
21
|
+
default: 9,
|
22
|
+
}
|
23
|
+
|
24
|
+
def mark(*what)
|
25
|
+
insert = what.map(&:to_s).join ';'
|
26
|
+
"\033[#{insert}m"
|
27
|
+
end
|
28
|
+
|
29
|
+
def color(color = 0, bold = 0)
|
30
|
+
mark bold, color+30
|
31
|
+
end
|
32
|
+
|
33
|
+
def bg(color = 0)
|
34
|
+
mark color+40
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset
|
38
|
+
mark 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(name, *params)
|
42
|
+
unless @mapper && @mapper.key?(name)
|
43
|
+
return super
|
44
|
+
end
|
45
|
+
|
46
|
+
clr = name.hash/2%7 + 1
|
47
|
+
puts "#{color clr, 1}#{name.to_s.upcase}:#{reset} #{params * ', '}"
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class SessionPlugin < Rubko::Plugin
|
4
|
+
def init
|
5
|
+
@name = :session # cookie name
|
6
|
+
@timeout = 2*60*60 # session timeout in seconds of inactivity
|
7
|
+
@purgeRate = 10_000 # interval (requests) to clear expired sessions
|
8
|
+
@hashSize = 16 # entropy of hash in byes
|
9
|
+
|
10
|
+
@storage = memory
|
11
|
+
@cookie = loadPlugin :cookie
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
if @id.nil?
|
16
|
+
config
|
17
|
+
purge if 0 == rand(purgeRate)
|
18
|
+
|
19
|
+
@id = if ip?
|
20
|
+
cookie.ip
|
21
|
+
else
|
22
|
+
cookie[name] || false
|
23
|
+
end
|
24
|
+
|
25
|
+
if @id
|
26
|
+
if time = storage[name, @id]
|
27
|
+
destroy if Time.now - time > @timeout
|
28
|
+
end
|
29
|
+
storage[name, @id] = Time.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@id
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :name, :path, :timeout, :purgeRate, :hashSize, :storage, :cookie
|
36
|
+
|
37
|
+
def ip?
|
38
|
+
name == :ip
|
39
|
+
end
|
40
|
+
|
41
|
+
def purge
|
42
|
+
storage.values(name).each { |id, time|
|
43
|
+
destroy if Time.now - time > timeout
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def create
|
48
|
+
return if id
|
49
|
+
cookie[name] = @id = hash unless ip?
|
50
|
+
storage[name, @id] = Time.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def destroy
|
54
|
+
return unless id
|
55
|
+
storage.prune name, id
|
56
|
+
cookie[name] = nil
|
57
|
+
@id = false
|
58
|
+
end
|
59
|
+
|
60
|
+
def hash
|
61
|
+
SecureRandom.urlsafe_base64 hashSize
|
62
|
+
end
|
63
|
+
|
64
|
+
def resid
|
65
|
+
return if ip? || !id
|
66
|
+
storage.rename name, id, (cookie[name] = hash)
|
67
|
+
end
|
68
|
+
|
69
|
+
private :hash
|
70
|
+
|
71
|
+
def []=(*args, val)
|
72
|
+
create
|
73
|
+
return unless id
|
74
|
+
storage[name, id, *args] = val
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.delegate(*funcs)
|
78
|
+
funcs.each { |func|
|
79
|
+
define_method(func) { |*args|
|
80
|
+
sid = id
|
81
|
+
storage.send func, name, sid, *args
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
delegate :[], :keys, :prune, :rename
|
87
|
+
delegate :values, :inspect
|
88
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubko/storage'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class DiskStoragePlugin < Rubko::Storage
|
5
|
+
|
6
|
+
def init
|
7
|
+
@root = '.run/storage'
|
8
|
+
@name = 'data.txt'
|
9
|
+
end
|
10
|
+
|
11
|
+
def escape(path)
|
12
|
+
@root + '/' + path.map(&:to_s) * '/'
|
13
|
+
end
|
14
|
+
|
15
|
+
def unescape(key)
|
16
|
+
key.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
private :escape, :unescape
|
20
|
+
|
21
|
+
def [](*path)
|
22
|
+
data = File.read escape(path)+'/'+@name rescue return nil
|
23
|
+
Marshal.load data rescue nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def []=(*path, val)
|
27
|
+
path = escape(path)
|
28
|
+
FileUtils.mkpath path
|
29
|
+
File.write path+'/'+@name, Marshal.dump(val)
|
30
|
+
val
|
31
|
+
end
|
32
|
+
|
33
|
+
def keys(*path)
|
34
|
+
Dir[ escape(path)+'/*/' ].map { |key|
|
35
|
+
unescape File.basename(key)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def prune(*path)
|
40
|
+
FileUtils.rmtree escape(path), secure: true
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def rename(*path, name)
|
45
|
+
new = escape(path[0..-2] + [name])
|
46
|
+
return false if File.exist? new
|
47
|
+
FileUtils.mv escape(path), new
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubko/storage'
|
2
|
+
|
3
|
+
class MemoryStoragePlugin < Rubko::Storage
|
4
|
+
|
5
|
+
@@data = {}
|
6
|
+
|
7
|
+
def find(path)
|
8
|
+
path.map { |x|
|
9
|
+
escape x
|
10
|
+
}.reduce( @@data ) { |v, k|
|
11
|
+
v[k] || {}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def escape(key)
|
16
|
+
key.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def unescape(key)
|
20
|
+
key.to_sym
|
21
|
+
end
|
22
|
+
|
23
|
+
private :find, :escape, :unescape
|
24
|
+
|
25
|
+
def [](*path)
|
26
|
+
find(path)[nil]
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(*path, val)
|
30
|
+
path.map! { |x| escape x }
|
31
|
+
path.reduce( @@data ) { |v, k|
|
32
|
+
v[k] ||= {}
|
33
|
+
if k.equal? path.last # is the last element
|
34
|
+
v[k][nil] = val
|
35
|
+
end
|
36
|
+
v[k]
|
37
|
+
}
|
38
|
+
val
|
39
|
+
end
|
40
|
+
|
41
|
+
def keys(*path)
|
42
|
+
find(path).keys.compact.map { |x|
|
43
|
+
unescape x
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def prune(*path)
|
48
|
+
last = escape path.pop
|
49
|
+
find(path).delete last
|
50
|
+
end
|
51
|
+
|
52
|
+
def rename(*path, name)
|
53
|
+
name = escape name
|
54
|
+
last = escape path.pop
|
55
|
+
hash = find path
|
56
|
+
return false if hash[name]
|
57
|
+
hash[name] = hash.delete last
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|