rubko 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/bin/rubko-deploy +40 -0
  4. data/bin/rubko-init +6 -0
  5. data/lib/rubko/app.rb +79 -0
  6. data/lib/rubko/asset/fileStream.rb +18 -0
  7. data/lib/rubko/asset/gzipStream.rb +26 -0
  8. data/lib/rubko/asset.rb +71 -0
  9. data/lib/rubko/base.rb +123 -0
  10. data/lib/rubko/controller.rb +17 -0
  11. data/lib/rubko/model.rb +5 -0
  12. data/lib/rubko/plugin.rb +7 -0
  13. data/lib/rubko/plugins/cookie.rb +45 -0
  14. data/lib/rubko/plugins/db.rb +389 -0
  15. data/lib/rubko/plugins/facebook.rb +50 -0
  16. data/lib/rubko/plugins/log.rb +49 -0
  17. data/lib/rubko/plugins/session.rb +88 -0
  18. data/lib/rubko/plugins/storage/db.rb +6 -0
  19. data/lib/rubko/plugins/storage/disk.rb +50 -0
  20. data/lib/rubko/plugins/storage/memory.rb +60 -0
  21. data/lib/rubko/plugins/url.rb +91 -0
  22. data/lib/rubko/socket.rb +23 -0
  23. data/lib/rubko/storage.rb +27 -0
  24. data/lib/rubko.rb +32 -0
  25. data/rubko.gemspec +22 -0
  26. data/stamp/.gitignore +3 -0
  27. data/stamp/config/db.rb +8 -0
  28. data/stamp/config/facebook.rb +8 -0
  29. data/stamp/config/session.rb +7 -0
  30. data/stamp/config/url.rb +7 -0
  31. data/stamp/config.ru +38 -0
  32. data/stamp/controllers/error404.rb +19 -0
  33. data/stamp/controllers/files.rb +20 -0
  34. data/stamp/controllers/welcome.rb +6 -0
  35. data/stamp/models/user.rb +14 -0
  36. data/stamp/public/css/.private/default.sass +31 -0
  37. data/stamp/public/img/favicon.png +0 -0
  38. data/stamp/public/js/jquery/iframize.js +25 -0
  39. data/stamp/public/js/jquery/translate.js +112 -0
  40. data/stamp/public/js/merge.sh +10 -0
  41. data/stamp/tarok.yml +17 -0
  42. data/stamp/views/welcome.haml +11 -0
  43. 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,6 @@
1
+ require 'rubko/storage'
2
+
3
+ class DbStoragePlugin < Rubko::Storage
4
+
5
+ # stub
6
+ 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