whimsical-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Sam Ruby <ru...@apache.org>
Subject [whimsy.git] [1/1] Commit cf34643: realtime updates of email changes
Date Wed, 13 Jan 2016 14:15:49 GMT
Commit cf3464317ba769af1ee5c9d94c70f5ffc282c887:
    realtime updates of email changes


Branch: refs/heads/master
Author: Sam Ruby <rubys@intertwingly.net>
Committer: Sam Ruby <rubys@intertwingly.net>
Pusher: rubys <rubys@apache.org>

------------------------------------------------------------
www/secmail/Gemfile                                          | +++ ---
www/secmail/Rakefile                                         | + -
www/secmail/models/events.rb                                 | +++++++++ 
www/secmail/server.rb                                        | +++++++++++++ 
www/secmail/views/index.js.rb                                | +++++++++ --
------------------------------------------------------------
124 changes: 117 additions, 7 deletions.
------------------------------------------------------------


diff --git a/www/secmail/Gemfile b/www/secmail/Gemfile
index 25f19b5..a1812fd 100644
--- a/www/secmail/Gemfile
+++ b/www/secmail/Gemfile
@@ -3,14 +3,14 @@ source 'https://rubygems.org'
 gem 'mail'
 gem 'rake'
 gem 'zip'
-gem 'whimsy-asf'
+gem 'whimsy-asf', '~> 0.0.73'
 gem 'sinatra'
 gem 'sanitize'
-gem 'wunderbar', '~> 1.0.11'
+gem 'wunderbar', '~> 1.0.13'
 gem 'ruby2js', '~> 2.0.12'
 gem 'execjs'
+gem 'listen'
 
 group :demo do
-  gem 'listen'
   gem 'puma'
 end
diff --git a/www/secmail/Rakefile b/www/secmail/Rakefile
index fb0d600..a0137cb 100644
--- a/www/secmail/Rakefile
+++ b/www/secmail/Rakefile
@@ -32,7 +32,7 @@ end
 
 desc 'WebServer that provides an interface to explore emails'
 task :server => :bundle do
-  ENV['RACK_ENV']='production'
+  ENV['RACK_ENV']='development'
 
   require 'wunderbar'
   module Wunderbar::Listen
diff --git a/www/secmail/models/events.rb b/www/secmail/models/events.rb
new file mode 100644
index 0000000..42ad142
--- /dev/null
+++ b/www/secmail/models/events.rb
@@ -0,0 +1,71 @@
+require 'listen'
+require 'thread'
+
+class Events
+  @@list = []
+
+  def initialize
+    @@list.push self
+
+    @events = Queue.new
+
+    @listener = Listen.to ARCHIVE do |modified, added, removed|
+      (modified + added).each do |file|
+         next unless file.end_with? '.yml'
+         mbox = Mailbox.new(File.basename(file))
+         @events.push({messages: mbox.client_headers})
+      end
+    end
+
+    @listener.start
+
+    @closed = false
+
+    # As some TCP/IP implementations will close idle sockets after as little
+    # as 30 seconds, sent out a heartbeat every 25 seconds.  Due to limitations
+    # of some versions of Ruby (2.0, 2.1), this is lowered to every 5 seconds
+    # in development mode to allow for quicker restarting after a trap/signal.
+    Thread.new do
+      loop do
+        sleep(ENV['RACK_ENV'] == 'development' ? 5 : 25)
+        break if @closed
+        @events.push(:heartbeat)
+      end
+
+      @events.push(:exit)
+      @listener.stop
+    end
+  end
+
+  def pop
+    @events.pop
+  end
+
+  def close
+    @@list.delete self
+    @events.clear
+    @closed = true
+
+    begin
+      @events.push :exit
+    rescue ThreadError
+      # some versions of Ruby don't allow queue operations in traps
+    end
+  end
+
+  def self.shutdown
+    @@list.dup.each {|event| event.close}
+  end
+end
+
+# puma uses SIGUSR2
+restart_usr2 ||= trap 'SIGUSR2' do
+  restart_usr2.call if Proc === restart_usr2
+  Events.shutdown
+end
+
+# thin uses SIGHUP
+restart_hup ||= trap 'SIGHUP' do
+  restart_hup.call if Proc === restart_hup
+  Events.shutdown
+end
diff --git a/www/secmail/server.rb b/www/secmail/server.rb
index bb95918..198c5a4 100644
--- a/www/secmail/server.rb
+++ b/www/secmail/server.rb
@@ -12,6 +12,7 @@
 require_relative 'helpers'
 require_relative 'models/mailbox'
 require_relative 'models/safetemp'
+require_relative 'models/events'
 
 # list of messages
 get '/' do
@@ -124,3 +125,27 @@
 
   [200, {'Content-Type' => part.content_type}, part.body.to_s]
 end
+
+# event stream for server sent events (a.k.a EventSource)
+get '/events', provides: 'text/event-stream' do
+  events = Events.new
+
+  stream :keep_open do |out|
+    out.callback {events.close}
+
+    loop do
+      event = events.pop
+
+      if Hash === event or Array === event
+        out << "data: #{JSON.dump(event)}\n\n"
+      elsif event == :heartbeat
+        out << ":\n"
+      elsif event == :exit
+        out.close
+        break
+      else
+        out << "data: #{event.inspect}\n\n"
+      end
+    end
+  end
+end
diff --git a/www/secmail/views/index.js.rb b/www/secmail/views/index.js.rb
index d0e0dba..90d35f7 100644
--- a/www/secmail/views/index.js.rb
+++ b/www/secmail/views/index.js.rb
@@ -57,8 +57,8 @@ def componentWillMount()
     @nextmbox = @@mbox
   end
 
-  # on initial load, fetch latest mailbox and subscribe to keyboard events,
-  # initialize selected item.
+  # on initial load, fetch latest mailbox, subscribe to keyboard and
+  # server side events, and initialize selected item.
   def componentDidMount()
     self.fetch_month() do
       # for the first week of the month, fetch previous month too
@@ -66,7 +66,21 @@ def componentDidMount()
     end
 
     window.onkeydown = self.keydown
-     self.selectRow Status.selected if @messages.length > 0
+
+    # when events are received, update messages
+    events = EventSource.new('events')
+    events.addEventListener :message do |event|
+      messages = JSON.parse(event.data).messages
+      self.merge messages if messages
+    end
+
+    # close connection on exit
+    window.addEventListener :unload do |event|
+      events.close()
+    end
+
+    # select row
+    self.selectRow Status.selected if @messages.length > 0
   end
 
   # when content changes, ensure selected message is visible

Mime
View raw message