rubko 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|