session.rb 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. require 'json'
  2. module Session
  3. @path = '/tmp/websocket'
  4. @@user_threads = {}
  5. @@client_threads = {}
  6. def self.create( client_id, session, meta )
  7. path = @path + '/' + client_id.to_s
  8. FileUtils.mkpath path
  9. meta[:last_ping] = Time.new.to_i.to_s
  10. File.open( path + '/session', 'w' ) { |file|
  11. data = {
  12. :user => {
  13. :id => session['id'],
  14. },
  15. :meta => meta,
  16. }
  17. # puts 'CREATE' + Marshal.dump(data)
  18. file.puts Marshal.dump(data)
  19. }
  20. # send update to browser
  21. if session['id']
  22. self.send( client_id, {
  23. :event => 'ws:login',
  24. :data => { :success => true },
  25. })
  26. end
  27. end
  28. def self.list
  29. client_ids = self.sessions
  30. session_list = {}
  31. client_ids.each { |client_id|
  32. data = self.get(client_id)
  33. next if !data
  34. session_list[client_id] = data
  35. }
  36. return session_list
  37. end
  38. def self.touch( client_id )
  39. data = self.get(client_id)
  40. path = @path + '/' + client_id.to_s
  41. data[:meta][:last_ping] = Time.new.to_i.to_s
  42. File.open( path + '/session', 'w' ) { |file|
  43. file.puts Marshal.dump(data)
  44. }
  45. return true
  46. end
  47. def self.get( client_id )
  48. session_file = @path + '/' + client_id.to_s + '/session'
  49. data = nil
  50. return if !File.exist? session_file
  51. File.open( session_file, 'r' ) { |file|
  52. all = ''
  53. while line = file.gets
  54. all = all + line
  55. end
  56. begin
  57. data = Marshal.load( all )
  58. rescue
  59. return
  60. end
  61. }
  62. return data
  63. end
  64. def self.send( client_id, data )
  65. path = @path + '/' + client_id.to_s + '/'
  66. filename = 'send-' + Time.new().to_i.to_s + '-' + rand(999999).to_s
  67. if File::exists?( path + filename )
  68. filename = filename + '-1'
  69. if File::exists?( path + filename )
  70. filename = filename + '-1'
  71. if File::exists?( path + filename )
  72. filename = filename + '-1'
  73. if File::exists?( path + filename )
  74. filename = filename + '-' + rand(999999).to_s
  75. end
  76. end
  77. end
  78. end
  79. return false if !File.directory? path
  80. File.open( path + 'a-' + filename, 'w' ) { |file|
  81. file.puts data.to_json
  82. }
  83. FileUtils.mv( path + 'a-' + filename, path + filename)
  84. return true
  85. end
  86. def self.jobs
  87. Thread.abort_on_exception = true
  88. while true
  89. client_ids = self.sessions
  90. client_ids.each { |client_id|
  91. # connection already open
  92. next if @@client_threads[client_id]
  93. # get current user
  94. session_data = Session.get( client_id )
  95. next if !session_data
  96. next if !session_data[:user]
  97. next if !session_data[:user][:id]
  98. user = User.find( session_data[:user][:id] )
  99. next if !user
  100. # start user thread
  101. start_user_thread = false
  102. if !@@user_threads[user.id]
  103. start_user_thread = true
  104. @@user_threads[user.id] = Thread.new {
  105. UserState.new(user.id)
  106. @@user_threads[user.id] = nil
  107. puts "close user(#{user.id}) thread"
  108. # raise "Exception from thread"
  109. }
  110. end
  111. # wait with client thread unil user thread has done some little work
  112. if start_user_thread
  113. sleep 0.5
  114. end
  115. # start client thread
  116. if !@@client_threads[client_id]
  117. @@client_threads[client_id] = Thread.new {
  118. ClientState.new(client_id)
  119. @@client_threads[client_id] = nil
  120. puts "close client(#{client_id}) thread"
  121. # raise "Exception from thread"
  122. }
  123. end
  124. }
  125. # system settings
  126. sleep 0.5
  127. end
  128. end
  129. def self.sessions
  130. path = @path + '/'
  131. data = []
  132. Dir.foreach( path ) do |entry|
  133. if entry != '.' && entry != '..'
  134. data.push entry
  135. end
  136. end
  137. return data
  138. end
  139. def self.queue( client_id )
  140. path = @path + '/' + client_id.to_s + '/'
  141. data = []
  142. Dir.foreach( path ) do |entry|
  143. if /^send/.match( entry )
  144. data.push Session.queue_file( path, entry )
  145. end
  146. end
  147. return data
  148. end
  149. def self.queue_file( path, filename )
  150. file_old = path + filename
  151. file_new = path + 'a-' + filename
  152. FileUtils.mv( file_old, file_new )
  153. data = nil
  154. all = ''
  155. File.open( file_new, 'r' ) { |file|
  156. while line = file.gets
  157. all = all + line
  158. end
  159. }
  160. File.delete( file_new )
  161. data = JSON.parse( all )
  162. return data
  163. end
  164. def self.destory( client_id )
  165. path = @path + '/' + client_id.to_s
  166. FileUtils.rm_rf path
  167. end
  168. end
  169. module CacheIn
  170. @@data = {}
  171. @@data_time = {}
  172. @@expires_in = {}
  173. @@expires_in_ttl = {}
  174. def self.set( key, value, params = {} )
  175. # puts 'CacheIn.set:' + key + '-' + value.inspect
  176. if params[:expires_in]
  177. @@expires_in[key] = Time.now + params[:expires_in]
  178. @@expires_in_ttl[key] = params[:expires_in]
  179. end
  180. @@data[ key ] = value
  181. @@data_time[ key ] = Time.now
  182. end
  183. def self.expired( key, params = {} )
  184. # expire if value never was set
  185. return true if !@@data.include? key
  186. # ignore_expire
  187. return false if params[:ignore_expire]
  188. # set re_expire
  189. if params[:re_expire]
  190. if @@expires_in[key]
  191. @@expires_in[key] = Time.now + @@expires_in_ttl[key]
  192. end
  193. return false
  194. end
  195. # check if expired
  196. if @@expires_in[key]
  197. return true if @@expires_in[key] < Time.now
  198. return false
  199. end
  200. # return false if key was set without expires_in
  201. return false
  202. end
  203. def self.get_time( key, params = {} )
  204. data = self.get( key, params )
  205. if data
  206. return @@data_time[key]
  207. end
  208. return nil
  209. end
  210. def self.get( key, params = {} )
  211. # puts 'CacheIn.get:' + key + '-' + @@data[ key ].inspect
  212. return if self.expired( key, params )
  213. @@data[ key ]
  214. end
  215. end
  216. class UserState
  217. def initialize( user_id )
  218. @user_id = user_id
  219. @data = {}
  220. @cache_key = 'user_' + user_id.to_s
  221. self.log 'notify', "---user started user state"
  222. CacheIn.set( 'last_run_' + user_id.to_s , true, { :expires_in => 20.seconds } )
  223. self.fetch
  224. end
  225. def fetch
  226. user = User.find( @user_id )
  227. return if !user
  228. while true
  229. # check if user is still with min one open connection
  230. if !CacheIn.get( 'last_run_' + user.id.to_s )
  231. self.log 'notify', "---user - closeing thread - no open user connection"
  232. return
  233. end
  234. self.log 'notice', "---user - fetch user data"
  235. # overview
  236. cache_key = @cache_key + '_overview'
  237. if CacheIn.expired(cache_key)
  238. overview = Ticket.overview(
  239. :current_user => user,
  240. )
  241. overview_cache = CacheIn.get( cache_key, { :re_expire => true } )
  242. self.log 'notice', 'fetch overview - ' + cache_key
  243. if overview != overview_cache
  244. self.log 'notify', 'fetch overview changed - ' + cache_key
  245. # puts overview.inspect
  246. # puts '------'
  247. # puts overview_cache.inspect
  248. CacheIn.set( cache_key, overview, { :expires_in => 3.seconds } )
  249. end
  250. end
  251. # overview lists
  252. overviews = Ticket.overview_list(
  253. :current_user => user,
  254. )
  255. overviews.each { |overview|
  256. cache_key = @cache_key + '_overview_data_' + overview.meta[:url]
  257. if CacheIn.expired(cache_key)
  258. overview_data = Ticket.overview(
  259. :view => overview.meta[:url],
  260. # :view_mode => params[:view_mode],
  261. :current_user => user,
  262. :array => true,
  263. )
  264. overview_data_cache = CacheIn.get( cache_key, { :re_expire => true } )
  265. self.log 'notice', 'fetch overview_data - ' + cache_key
  266. if overview_data != overview_data_cache
  267. self.log 'notify', 'fetch overview_data changed - ' + cache_key
  268. CacheIn.set( cache_key, overview_data, { :expires_in => 5.seconds } )
  269. end
  270. end
  271. }
  272. # create_attributes
  273. cache_key = @cache_key + '_ticket_create_attributes'
  274. if CacheIn.expired(cache_key)
  275. ticket_create_attributes = Ticket.attributes_to_change(
  276. :current_user_id => user.id,
  277. )
  278. ticket_create_attributes_cache = CacheIn.get( cache_key, { :re_expire => true } )
  279. self.log 'notice', 'fetch ticket_create_attributes - ' + cache_key
  280. if ticket_create_attributes != ticket_create_attributes_cache
  281. self.log 'notify', 'fetch ticket_create_attributes changed - ' + cache_key
  282. CacheIn.set( cache_key, ticket_create_attributes, { :expires_in => 2.minutes } )
  283. end
  284. end
  285. # recent viewed
  286. cache_key = @cache_key + '_recent_viewed'
  287. if CacheIn.expired(cache_key)
  288. recent_viewed = History.recent_viewed(user)
  289. recent_viewed_cache = CacheIn.get( cache_key, { :re_expire => true } )
  290. self.log 'notice', 'fetch recent_viewed - ' + cache_key
  291. if recent_viewed != recent_viewed_cache
  292. self.log 'notify', 'fetch recent_viewed changed - ' + cache_key
  293. recent_viewed_full = History.recent_viewed_fulldata(user)
  294. CacheIn.set( cache_key, recent_viewed, { :expires_in => 5.seconds } )
  295. CacheIn.set( cache_key + '_push', recent_viewed_full )
  296. end
  297. end
  298. # activity steam
  299. cache_key = @cache_key + '_activity_stream'
  300. if CacheIn.expired(cache_key)
  301. activity_stream = History.activity_stream( user )
  302. activity_stream_cache = CacheIn.get( cache_key, { :re_expire => true } )
  303. self.log 'notice', 'fetch activity_stream - ' + cache_key
  304. if activity_stream != activity_stream_cache
  305. self.log 'notify', 'fetch activity_stream changed - ' + cache_key
  306. activity_stream_full = History.activity_stream_fulldata( user )
  307. CacheIn.set( cache_key, activity_stream, { :expires_in => 0.75.minutes } )
  308. CacheIn.set( cache_key + '_push', activity_stream_full )
  309. end
  310. end
  311. # rss
  312. cache_key = @cache_key + '_rss'
  313. if CacheIn.expired(cache_key)
  314. url = 'http://www.heise.de/newsticker/heise-atom.xml'
  315. rss_items = RSS.fetch( url, 8 )
  316. rss_items_cache = CacheIn.get( cache_key, { :re_expire => true } )
  317. self.log 'notice', 'fetch rss - ' + cache_key
  318. if rss_items != rss_items_cache
  319. self.log 'notify', 'fetch rss changed - ' + cache_key
  320. CacheIn.set( cache_key, rss_items, { :expires_in => 2.minutes } )
  321. CacheIn.set( cache_key + '_push', {
  322. head: 'Heise ATOM',
  323. items: rss_items,
  324. })
  325. end
  326. end
  327. # auto population of default collections
  328. self.log 'notice', "---user - fetch push_collection data"
  329. # get available collections
  330. cache_key = @cache_key + '_push_collections'
  331. collections = CacheIn.get( cache_key )
  332. if !collections
  333. collections = {}
  334. push_collection = SessionHelper::push_collections(user)
  335. push_collection.each { | key, value |
  336. collections[ key ] = true
  337. }
  338. CacheIn.set( cache_key, collections, { :expires_in => 2.minutes } )
  339. end
  340. # check all collections to push
  341. push_collection = {}
  342. collections.each { | key, v |
  343. cache_key = @cache_key + '_push_collections_' + key
  344. if CacheIn.expired(cache_key)
  345. if push_collection.empty?
  346. push_collection = SessionHelper::push_collections(user)
  347. end
  348. push_collection_cache = CacheIn.get( cache_key, { :re_expire => true } )
  349. self.log 'notice', "---user - fetch push_collection data " + cache_key
  350. if !push_collection[key] || !push_collection_cache || push_collection[key] != push_collection_cache || !push_collection[ key ].zip( push_collection_cache ).all? { |x, y| x.attributes == y.attributes }
  351. self.log 'notify', 'fetch push_collection changed - ' + cache_key
  352. CacheIn.set( cache_key, push_collection[key], { :expires_in => 1.minutes } )
  353. end
  354. end
  355. }
  356. self.log 'notice', "---/user-"
  357. sleep 1
  358. end
  359. end
  360. def log( level, data )
  361. return if level == 'notice'
  362. puts "#{Time.now}:user_id(#{ @user_id }) #{ data }"
  363. end
  364. end
  365. class ClientState
  366. def initialize( client_id )
  367. @client_id = client_id
  368. @cache_key = ''
  369. @data = {}
  370. @pushed = {}
  371. self.log 'notify', "---client start ws connection---"
  372. self.fetch
  373. self.log 'notify', "---client exiting ws connection---"
  374. end
  375. def fetch
  376. loop_count = 0
  377. while true
  378. # get connection user
  379. session_data = Session.get( @client_id )
  380. return if !session_data
  381. return if !session_data[:user]
  382. return if !session_data[:user][:id]
  383. user = User.where( :id => session_data[:user][:id] ).first
  384. return if !user
  385. # set cache key
  386. @cache_key = 'user_' + user.id.to_s
  387. loop_count += 1
  388. self.log 'notice', "---client - looking for data of user #{user.id}"
  389. # remember last run
  390. CacheIn.set( 'last_run_' + user.id.to_s , true, { :expires_in => 20.seconds } )
  391. # verify already pushed data
  392. if !CacheIn.get( 'pushed_users' + @client_id.to_s )
  393. CacheIn.set( 'pushed_users' + @client_id.to_s , true, { :expires_in => 20.seconds } )
  394. if @pushed[:users]
  395. users = {}
  396. @pushed[:users].each {|user_id, user_o|
  397. self.user( user_id, users )
  398. }
  399. if !users.empty?
  400. users.each {|user_id, user_data|
  401. self.log 'notify', "push update of already pushed user id #{user_id}"
  402. }
  403. # send update to browser
  404. self.send({
  405. :data => {
  406. :collections => {
  407. :User => users,
  408. },
  409. },
  410. :event => [ 'loadCollection', 'ticket_overview_rebuild' ],
  411. });
  412. end
  413. end
  414. end
  415. # verify already pushed data
  416. if !CacheIn.get( 'pushed_tickets' + @client_id.to_s )
  417. CacheIn.set( 'pushed_tickets' + @client_id.to_s , true, { :expires_in => 20.seconds } )
  418. if @pushed[:tickets]
  419. tickets = []
  420. users = {}
  421. @pushed[:tickets].each {|ticket_id, ticket_data|
  422. self.ticket( ticket_id, tickets, users )
  423. }
  424. if !tickets.empty?
  425. tickets.each {|ticket|
  426. self.log 'notify', "push update of already pushed ticket id #{ticket['id']}"
  427. }
  428. # send update to browser
  429. self.send({
  430. :data => {
  431. :collections => {
  432. :Ticket => tickets,
  433. :User => users,
  434. },
  435. },
  436. :event => [ 'loadCollection', 'ticket_overview_rebuild' ],
  437. });
  438. end
  439. end
  440. end
  441. # overview
  442. cache_key = @cache_key + '_overview'
  443. overview_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  444. if overview_time && @data[:overview_time] != overview_time
  445. @data[:overview_time] = overview_time
  446. overview = CacheIn.get( cache_key, { :ignore_expire => true } )
  447. self.log 'notify', "push overview for user #{user.id}"
  448. # send update to browser
  449. self.send({
  450. :event => 'navupdate_ticket_overview',
  451. :data => overview,
  452. })
  453. end
  454. # overview_data
  455. overviews = Ticket.overview_list(
  456. :current_user => user,
  457. )
  458. overviews.each { |overview|
  459. cache_key = @cache_key + '_overview_data_' + overview.meta[:url]
  460. overview_data_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  461. if overview_data_time && @data[cache_key] != overview_data_time
  462. @data[cache_key] = overview_data_time
  463. overview_data = CacheIn.get( cache_key, { :ignore_expire => true } )
  464. self.log 'notify', "push overview_data #{overview.meta[:url]} for user #{user.id}"
  465. users = {}
  466. tickets = []
  467. overview_data[:ticket_list].each {|ticket_id|
  468. self.ticket( ticket_id, tickets, users )
  469. }
  470. # get groups
  471. group_ids = []
  472. Group.where( :active => true ).each { |group|
  473. group_ids.push group.id
  474. }
  475. agents = {}
  476. Ticket.agents.each { |user|
  477. agents[ user.id ] = 1
  478. }
  479. groups_users = {}
  480. groups_users[''] = []
  481. group_ids.each {|group_id|
  482. groups_users[ group_id ] = []
  483. Group.find(group_id).users.each {|user|
  484. next if !agents[ user.id ]
  485. groups_users[ group_id ].push user.id
  486. if !users[user.id]
  487. users[user.id] = User.user_data_full(user.id)
  488. end
  489. }
  490. }
  491. # send update to browser
  492. self.send({
  493. :data => {
  494. :overview => overview_data[:overview],
  495. :ticket_list => overview_data[:ticket_list],
  496. :tickets_count => overview_data[:tickets_count],
  497. :collections => {
  498. :User => users,
  499. :Ticket => tickets,
  500. },
  501. :bulk => {
  502. :group_id__owner_id => groups_users,
  503. :owner_id => [],
  504. },
  505. },
  506. :event => [ 'loadCollection', 'ticket_overview_rebuild' ],
  507. :collection => 'ticket_overview_' + overview.meta[:url].to_s,
  508. })
  509. end
  510. }
  511. # ticket_create_attributes
  512. cache_key = @cache_key + '_ticket_create_attributes'
  513. ticket_create_attributes_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  514. if ticket_create_attributes_time && @data[:ticket_create_attributes_time] != ticket_create_attributes_time
  515. @data[:ticket_create_attributes_time] = ticket_create_attributes_time
  516. create_attributes = CacheIn.get( cache_key, { :ignore_expire => true } )
  517. users = {}
  518. create_attributes[:owner_id].each {|user_id|
  519. if !users[user_id]
  520. users[user_id] = User.user_data_full(user_id)
  521. end
  522. }
  523. data = {
  524. :users => users,
  525. :edit_form => create_attributes,
  526. }
  527. self.log 'notify', "push ticket_create_attributes for user #{user.id}"
  528. # send update to browser
  529. self.send({
  530. :collection => 'ticket_create_attributes',
  531. :data => data,
  532. })
  533. end
  534. # recent viewed
  535. cache_key = @cache_key + '_recent_viewed'
  536. recent_viewed_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  537. if recent_viewed_time && @data[:recent_viewed_time] != recent_viewed_time
  538. @data[:recent_viewed_time] = recent_viewed_time
  539. recent_viewed = CacheIn.get( cache_key, { :ignore_expire => true } )
  540. self.log 'notify', "push recent_viewed for user #{user.id}"
  541. # send update to browser
  542. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  543. self.send({
  544. :event => 'update_recent_viewed',
  545. :data => r,
  546. })
  547. end
  548. # activity stream
  549. cache_key = @cache_key + '_activity_stream'
  550. activity_stream_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  551. if activity_stream_time && @data[:activity_stream_time] != activity_stream_time
  552. @data[:activity_stream_time] = activity_stream_time
  553. activity_stream = CacheIn.get( cache_key, { :ignore_expire => true } )
  554. self.log 'notify', "push activity_stream for user #{user.id}"
  555. # send update to browser
  556. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  557. self.send({
  558. :event => 'activity_stream_rebuild',
  559. :collection => 'activity_stream',
  560. :data => r,
  561. })
  562. end
  563. # rss
  564. cache_key = @cache_key + '_rss'
  565. rss_items_time = CacheIn.get_time( cache_key, { :ignore_expire => true } )
  566. if rss_items_time && @data[:rss_time] != rss_items_time
  567. @data[:rss_time] = rss_items_time
  568. rss_items = CacheIn.get( cache_key, { :ignore_expire => true } )
  569. self.log 'notify', "push rss for user #{user.id}"
  570. # send update to browser
  571. r = CacheIn.get( cache_key + '_push', { :ignore_expire => true } )
  572. self.send({
  573. :event => 'rss_rebuild',
  574. :collection => 'dashboard_rss',
  575. :data => r,
  576. })
  577. end
  578. # push_collections
  579. cache_key = @cache_key + '_push_collections'
  580. collections = CacheIn.get( cache_key ) || {}
  581. collections.each { | key, v |
  582. collection_cache_key = @cache_key + '_push_collections_' + key
  583. collection_time = CacheIn.get_time( collection_cache_key, { :ignore_expire => true } )
  584. if collection_time && @data[ collection_cache_key + '_time' ] != collection_time
  585. @data[ collection_cache_key + '_time' ] = collection_time
  586. push_collections = CacheIn.get( collection_cache_key, { :ignore_expire => true } )
  587. self.log 'notify', "push push_collections #{key} for user #{user.id}"
  588. # send update to browser
  589. data = {}
  590. data['collections'] = {}
  591. data['collections'][key] = push_collections
  592. self.send({
  593. :event => 'resetCollection',
  594. :data => data,
  595. })
  596. end
  597. }
  598. self.log 'notice', "---/client-"
  599. # start faster in the beginnig
  600. if loop_count < 20
  601. sleep 0.6
  602. else
  603. sleep 1
  604. end
  605. end
  606. end
  607. # add ticket if needed
  608. def ticket( ticket_id, tickets, users )
  609. if !@pushed[:tickets]
  610. @pushed[:tickets] = {}
  611. end
  612. ticket = Ticket.lookup( :id => ticket_id )
  613. if @pushed[:tickets][ticket_id] != ticket['updated_at']
  614. @pushed[:tickets][ticket_id] = ticket['updated_at']
  615. tickets.push ticket
  616. end
  617. # add users if needed
  618. self.user( ticket['owner_id'], users )
  619. self.user( ticket['customer_id'], users )
  620. self.user( ticket['created_by_id'], users )
  621. end
  622. # add user if needed
  623. def user( user_id, users )
  624. if !@pushed[:users]
  625. @pushed[:users] = {}
  626. end
  627. # get user
  628. user = User.user_data_full( user_id )
  629. # user is already on client and not changed
  630. return if @pushed[:users][ user_id ] == user['updated_at']
  631. @pushed[:users][user_id] = user['updated_at']
  632. # user not on client or different
  633. self.log 'notice', 'push user ... ' + user['login']
  634. users[ user_id ] = user
  635. end
  636. # send update to browser
  637. def send( data )
  638. Session.send( @client_id, data )
  639. end
  640. def log( level, data )
  641. return if level == 'notice'
  642. puts "#{Time.now}:client(#{ @client_id }) #{ data }"
  643. end
  644. end