bull 0.0.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.
@@ -0,0 +1,94 @@
1
+ require 'set'
2
+
3
+ class RVar
4
+
5
+ attr_reader :value
6
+ @@ticket = 0
7
+ @@group = nil
8
+ @@backup = []
9
+
10
+ def initialize value
11
+ @value = value
12
+ @blocks = {}
13
+ @forms = Set.new
14
+ end
15
+
16
+ def self.raise_if_dirty
17
+ @@group = Set.new
18
+ @@backup = []
19
+ raised = false
20
+ begin
21
+ yield
22
+ rescue
23
+ @@backup.each do |v|
24
+ v.call
25
+ end
26
+ raised = true
27
+ raise
28
+ #else
29
+ # @@group.each do |blk|
30
+ # blk.call
31
+ # end
32
+ ensure
33
+ if !raised
34
+ @@group.each do |blk|
35
+ blk.call
36
+ end
37
+ end
38
+ @@group = nil
39
+ @@backup = []
40
+ end
41
+ end
42
+
43
+ def self.rgrouping
44
+ @@group = Set.new
45
+ yield
46
+ @@group.each {|blk| blk.call}
47
+ @@group = nil
48
+ @@backup = []
49
+ end
50
+
51
+ def value= value
52
+ if value != @value
53
+ if @@group.nil?
54
+ @value = value
55
+ @blocks.each_value {|b| b.call}
56
+ else
57
+ @forms.each { |form| raise Exception.new('dirty form') if form.dirty?}
58
+ old_value = @value
59
+ @value = value
60
+ @blocks.each_value {|b| @@group.add b; @@backup << lambda{@value = old_value}}
61
+ end
62
+ end
63
+ end
64
+
65
+ def add block
66
+ id = @@ticket
67
+ @@ticket += 1
68
+ @blocks[id] = block
69
+ id
70
+ end
71
+
72
+ def remove id
73
+ @blocks.delete id
74
+ end
75
+
76
+ def add_form form
77
+ @forms.add form
78
+ end
79
+
80
+ def remove_form form
81
+ @forms.delete form
82
+ end
83
+ end
84
+
85
+ def reactive(*args, &block)
86
+ ret = {}
87
+ args.each do |v|
88
+ id = v.add(block)
89
+ ret[id] = v
90
+ end
91
+ block.call
92
+ ret
93
+ end
94
+
@@ -0,0 +1,468 @@
1
+ #$LOAD_PATH.unshift '..'
2
+ require 'eventmachine'
3
+ require 'json'
4
+ require 'time'
5
+ require 'bcrypt'
6
+ require_relative 'encode_times' #..
7
+ require_relative 'symbolize' #..
8
+ require 'em-http-request'
9
+ require 'logger'
10
+ require 'fiber'
11
+ #require 'liquid'
12
+ require_relative 'mreport'
13
+
14
+ class EMLogger < Logger
15
+ def initialize(file, count: 1, size: 1024000, level: Logger::DEBUG)
16
+ super file, count, size
17
+ @level = level
18
+ end
19
+
20
+ def error msg
21
+ #puts 'error:', msg
22
+ EventMachine.defer(proc {super msg})
23
+ end
24
+
25
+ def info msg
26
+ #puts 'info:', msg
27
+ EventMachine.defer(proc {super msg})
28
+ end
29
+
30
+ def debug msg
31
+ #puts 'debug:', msg
32
+ EventMachine.defer(proc {super msg})
33
+ end
34
+
35
+ def warn msg
36
+ #puts 'warn:', msg
37
+ EventMachine.defer(proc {super msg})
38
+ end
39
+ end
40
+
41
+ module Logging
42
+ def logger
43
+ Logging.logger
44
+ end
45
+
46
+ def self.logger
47
+ @logger ||= EMLogger.new(File.join(File.expand_path(File.dirname(__FILE__)), 'log', 'log.txt'), count: 10, size: 1024000, level: Logger::DEBUG)
48
+ end
49
+
50
+ def stdout_logger
51
+ Logging.stdout_logger
52
+ end
53
+
54
+ def self.stdout_logger
55
+ @stdout_logger ||= EMLogger.new(STDOUT, level: Logger::DEBUG)
56
+ end
57
+ end
58
+
59
+ #module Bull
60
+ class BullServerController
61
+
62
+ include Logging
63
+ include MReport
64
+
65
+ def initialize(ws, conn)
66
+ @ws = ws
67
+ @conn = conn
68
+ @watch = {}
69
+ @user_id = nil
70
+ @user_doc = nil
71
+ @root = Fiber.current
72
+ end
73
+
74
+ def notify(msg)
75
+ msg = JSON.parse msg
76
+ #logger.info msg
77
+ stdout_logger.info msg
78
+ command = msg['command']
79
+ kwargs = symbolize_keys(msg['kwargs'])
80
+ resolve_times kwargs, msg['times']
81
+
82
+ if command.start_with? 'rpc_'
83
+ handle_rpc command, msg['id'], *msg['args'], **kwargs
84
+ elsif command.start_with? 'task_'
85
+ handle_task command, *msg['args'], **kwargs
86
+ elsif command.start_with? 'file_'
87
+ handle_file command, *msg['args'], **kwargs
88
+ elsif command.start_with? 'watch_'
89
+ handle_watch command, msg['id'], *msg['args'], **kwargs
90
+ elsif command == 'stop_watch'
91
+ handle_stop_watch msg['id']
92
+ end
93
+ end
94
+
95
+ def close
96
+ @watch.each_value {|w| w.close}
97
+ end
98
+
99
+ private
100
+
101
+ def check arg, type
102
+ raise Exception.new("#{arg} is not a #{type}") if !arg.nil? && !arg.is_a?(type)
103
+ end
104
+
105
+ def get_unique pred #table, filter
106
+ count = rsync pred.count # $r.table(table).filter(filter).count
107
+ if count == 0
108
+ return nil #Hash.new
109
+ else
110
+ docs = rmsync pred #$r.table(table).filter(filter)
111
+ doc = docs[0]
112
+ doc['owner'] = owner? doc
113
+ return symbolize_keys doc
114
+ end
115
+ end
116
+
117
+ def get_array predicate
118
+ ret = []
119
+ docs_with_count(predicate) do |count, row|
120
+ if count == 0
121
+ yield []
122
+ else
123
+ ret << symbolize_keys(row)
124
+ yield ret if ret.length == count
125
+ end
126
+ end
127
+ end
128
+
129
+ def file_send id, predicate, keys
130
+ ret = ""
131
+ size = 0
132
+ total = 0
133
+ @ws.send({response: 'file', id: id, data: keys.join(';'), end: false, times: []}.to_json)
134
+ docs_with_count(predicate) do |count, row|
135
+ if count == 0
136
+ @ws.send({response: 'file', id: id, data: '', end: true, times: []}.to_json)
137
+ else
138
+ total += 1
139
+ size += 1
140
+ aux = keys.inject([]){|r, k| r << row[k]}
141
+ ret << aux.join(';') << "\n"
142
+ if total == count || size == 10
143
+ @ws.send({response: 'file', id: id, data: ret, end: total==count, times: []}.to_json)
144
+ size = 0
145
+ ret = ""
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def docs_with_count predicate
152
+ predicate.count.em_run(@conn) do |count|
153
+ if count == 0
154
+ yield 0, {}
155
+ else
156
+ predicate.em_run(@conn) do |doc|
157
+ doc['owner'] = owner? doc
158
+ yield count, doc
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ def rpc_user_exist? user
165
+ check user, String
166
+ if user == ''
167
+ return true # false
168
+ else
169
+ count = rsync $r.table('user').filter(user: user).count
170
+ if count == 0
171
+ return false
172
+ else
173
+ return true
174
+ end
175
+ end
176
+ end
177
+
178
+ def rpc_login user, password
179
+ check user, String
180
+ check password, String
181
+ count = rsync $r.table('user').filter(user: user).count
182
+ if count == 0
183
+ return false
184
+ else
185
+ response = rmsync $r.table('user').filter(user: user)
186
+ response = response[0]
187
+ pass = response['password']
188
+ pass = BCrypt::Password.new(pass)
189
+ if response['secondary_password']
190
+ secondary_password = response['secondary_password']
191
+ secondary_password = BCrypt::Password.new(secondary_password)
192
+ end
193
+ if pass == password || (response['secondary_password'] && pass == secondary_password)
194
+ @user_id = user
195
+ @user_doc = response
196
+ @roles = response['roles']
197
+ return response['roles']
198
+ else
199
+ return false
200
+ end
201
+ end
202
+ end
203
+
204
+ def rpc_change_password new_password
205
+ check new_password, String
206
+ pass = BCrypt::Password.new(new_password)
207
+ ret = rsync $r.table('user').filter(user: @user_id).update(password: pass, secondary_password: nil)
208
+ ret['replaced']
209
+ #$r.table('user').filter(user: @user_id).update(password: pass, secondary_password: nil).em_run(@conn){|ret| yield ret['replaced']}
210
+ end
211
+
212
+ def task_send_code_to_email user, answer
213
+ check user, String
214
+ check answer, String
215
+ if test_answer(answer) && !@email_code
216
+ code = ('a'..'z').to_a.sample(8).join
217
+ @email_code = code
218
+ puts code
219
+ t = reports['mail_code_new_user']
220
+ html = t.render('code' => code)
221
+ body = {to: user, subject: 'code', html: html, from: $from}
222
+ EventMachine::HttpRequest.new($mail_key).post :body => body
223
+ end
224
+ end
225
+
226
+ def task_forgotten_password user
227
+ check user, String
228
+ secondary_password = ('a'..'z').to_a.sample(8).join
229
+ puts secondary_password
230
+ t = reports['mail_forgotten_password']
231
+ html = t.render('password' => secondary_password)
232
+ body = {to: user, subject: 'new password', html: html, from: $from}
233
+ EventMachine::HttpRequest.new($mail_key).post :body => body
234
+ pass = BCrypt::Password.new(secondary_password)
235
+ #$r.table('user').filter(user: user).update(secondary_password: pass).em_run(@conn){}
236
+ rsync $r.table('user').filter(user: user).update(secondary_password: pass)
237
+ end
238
+
239
+ def rpc_logout
240
+ close
241
+ @user_id = nil
242
+ @roles = nil
243
+ true
244
+ end
245
+
246
+ #def user_is_owner? doc
247
+ # doc[:owner] == @user_id
248
+ #end
249
+
250
+ def owner? doc
251
+ doc[:owner] == @user_id
252
+ end
253
+
254
+ def before_update_user old, new, merged
255
+ @roles.include? 'admin'
256
+ end
257
+
258
+ def i_timestamp! doc
259
+ doc[:i_timestamp] = Time.now
260
+ end
261
+
262
+ def u_timestamp! doc
263
+ doc[:u_timestamp] = Time.now
264
+ end
265
+
266
+ def owner! doc
267
+ doc[:owner] = @user_id
268
+ end
269
+
270
+ def rpc_insert(table, value:)
271
+ check table, String
272
+ new_val = value
273
+ new_val.delete :u_timestamp
274
+ new_val.delete :i_timestamp
275
+ new_val.delete :owner
276
+ new_val.delete :id
277
+
278
+ if !self.send('before_insert_'+table, new_val)
279
+ ret = nil
280
+ else
281
+ ret = rsync $r.table(table).insert(new_val)
282
+ ret = ret['generated_keys'][0]
283
+ self.send('after_insert_'+table, new_val) if respond_to?('after_insert_'+table) && !ret.nil?
284
+ end
285
+ ret
286
+ end
287
+
288
+ def rpc_delete(table, id)
289
+ check table, String
290
+ check id, String
291
+ doc = rsync $r.table(table).get(id)
292
+ doc = symbolize_keys doc
293
+ if doc.nil? || !respond_to?('before_delete_'+table) || !self.send('before_delete_'+table, doc)
294
+ ret = 0
295
+ else
296
+ ret = rsync $r.table(table).get(id).delete
297
+ ret = ret['deleted']
298
+ self.send('after_delete_'+table, doc) if respond_to?('after_delete_'+table) && ret == 1
299
+ end
300
+ ret
301
+ end
302
+
303
+ def rsync pred
304
+ fb = Fiber.current
305
+ pred.em_run(@conn) do |doc|
306
+ fb.transfer doc
307
+ end
308
+ @root.transfer
309
+ end
310
+
311
+ def rmsync pred
312
+ fb = Fiber.current
313
+ get_array(pred){|docs| fb.transfer docs}
314
+ @root.transfer
315
+ end
316
+
317
+ def rsync_ pred
318
+ helper = Fiber.new do |parent|
319
+ pred.em_run(@conn) do |doc|
320
+ parent.transfer doc
321
+ end
322
+ end
323
+ helper.transfer Fiber.current
324
+ end
325
+
326
+ def rmsync_ pred
327
+ helper = Fiber.new do |parent|
328
+ get_array(pred){|docs| parent.transfer docs}
329
+ end
330
+ helper.transfer Fiber.current
331
+ end
332
+
333
+ def rpc_update(table, id, value:)
334
+ check table, String
335
+ check id, String
336
+ value.delete :u_timestamp
337
+ value.delete :i_timestamp
338
+ value.delete :owner
339
+ value.delete :id
340
+
341
+ old_doc = rsync $r.table(table).get(id)
342
+ if old_doc.nil? || !respond_to?('before_update_'+table)
343
+ return 0
344
+ end
345
+ old_doc = symbolize_keys old_doc
346
+ merged = old_doc.merge(value)
347
+ if !self.send('before_update_'+table, old_doc, value, merged)
348
+ ret = 0
349
+ else
350
+ response = rsync $r.table(table).get(id).update(merged)
351
+ ret = response['replaced']
352
+ self.send('after_update_'+table, merged) if respond_to?('after_update_'+table) && ret == 1
353
+ end
354
+ ret
355
+ end
356
+
357
+ def handle_watch command, id, *args, **kwargs
358
+ if kwargs.empty?
359
+ w = self.send command, *args
360
+ else
361
+ w = self.send command, *args, **kwargs
362
+ end
363
+ return if !w
364
+ w = w.changes({include_initial: true})
365
+ #EventMachine.run do
366
+ @watch[id] = w.em_run(@conn) do |doc|
367
+ doc['owner'] = owner? doc
368
+ ret = {}
369
+ ret[:response] = 'watch'
370
+ ret[:id] = id
371
+ ret[:data] = doc
372
+ ret[:times] = times doc
373
+ #begin
374
+ @ws.send ret.to_json
375
+ #rescue
376
+ #end
377
+ end
378
+ #end
379
+ end
380
+
381
+ def handle_stop_watch id
382
+ check id, Integer
383
+ w = @watch[id]
384
+ if w
385
+ w.close
386
+ @watch.delete id
387
+ end
388
+ #@watch[id].close
389
+ #@watch.delete id
390
+ end
391
+
392
+ def handle_task command, *args, **kwargs
393
+ helper = Fiber.new do
394
+ begin
395
+ if kwargs.empty?
396
+ self.send(command, *args)
397
+ else
398
+ self.send(command, *args, **kwargs)
399
+ end
400
+ rescue Exception => e
401
+ #logger.debug e
402
+ stdout_logger.debug e
403
+ end
404
+ end
405
+ helper.transfer
406
+ end
407
+
408
+ def handle_rpc command, id, *args, **kwargs
409
+ helper = Fiber.new do
410
+ begin
411
+ if kwargs.empty?
412
+ v = self.send(command, *args)
413
+ else
414
+ v = self.send(command, *args, **kwargs)
415
+ end
416
+ @ws.send({response: 'rpc', id: id, result: v, times: times(v)}.to_json)
417
+ rescue Exception => e
418
+ #logger.debug e
419
+ stdout_logger.debug e
420
+ end
421
+ end
422
+ helper.transfer
423
+ end
424
+
425
+ def handle_file command, id, *args, **kwargs
426
+ helper = Fiber.new do
427
+ begin
428
+ if kwargs.empty?
429
+ predicate, keys = self.send(command, *args)
430
+ else
431
+ predicate, keys = self.send(command, *args, **kwargs)
432
+ end
433
+ file_send id, predicate, keys
434
+ rescue Exception => e
435
+ #logger.debug e
436
+ stdout_logger.debug e
437
+ end
438
+ end
439
+ helper.transfer
440
+ end
441
+
442
+ def get table, id, symbolize=true
443
+ if id.nil?
444
+ return nil # Hash.new # nil
445
+ else
446
+ doc = rsync $r.table(table).get(id)
447
+ doc['owner'] = owner? doc
448
+ if symbolize
449
+ return symbolize_keys doc
450
+ else
451
+ return doc
452
+ end
453
+ end
454
+ end
455
+
456
+ def times ret
457
+ if !ret.respond_to? :each_pair
458
+ if ret.instance_of? Time
459
+ ['result']
460
+ else
461
+ []
462
+ end
463
+ else
464
+ encode_times ret, ''
465
+ end
466
+ end
467
+ end
468
+ #end