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.
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