Top Level Namespace

Defined Under Namespace

Modules: Softwear Classes: ErrorReportMailer

Instance Method Summary collapse

Instance Method Details

#dev_log(*a) ⇒ Object



18
19
20
# File 'lib/softwear/library/light_server.rb', line 18

def dev_log(*a)
  $stdout.puts(*a) #if Rails.env.development?
end

#log(*a) ⇒ Object



22
23
24
# File 'lib/softwear/library/light_server.rb', line 22

def log(*a)
  $stdout.puts(*a) #unless Rails.env.test?
end

#report_error(rep, whole_command, error) ⇒ Object



7
8
9
10
11
12
13
14
15
16
# File 'lib/softwear/library/light_server.rb', line 7

def report_error(rep, whole_command, error)
  $stderr.puts "=== ERROR WHILE PROCESSING THE COMMAND \"#{whole_command}\" ===\n"\
    "#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")}"

  begin
    rep.send "sorry" if rep
  rescue StandardError => e
    $stderr.puts "(could not send 'sorry' message: \"#{e.class} #{e.message}\")"
  end
end

#split(string, limit = nil) ⇒ Object



3
4
5
# File 'lib/softwear/library/light_server.rb', line 3

def split(string, limit = nil)
  string.split(/\s+/, limit)
end

#start_server!(*args) ⇒ Object

Send Format: =======

One line strings: “#command #arg1 #arg2 #etc” The last argument, depending on the command, may contain spaces (but usually does not need to)

Receive Format: =====

Usually one string, like “yes”, or “no”. Returns “denied” if an unauthorized command was attempted. Returns “invalid” if an invalid command was attempted. Returns “sorry” if an error was raised while processing the command. Can be a json argument, often following “yes ”.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/softwear/library/light_server.rb', line 37

def start_server!(*args)
  log "Connecting...!"
  
  if args.size > 1
    port = args.first
  else
    port = ENV['port'] || ENV['PORT'] || 2900
  end
  address = ENV['SOCKET_ADDR'] || "tcp://*"

  if address =~ /:$/
    socket_address = address
  else
    socket_address = "#{address}:#{port}"
  end

  ctx = ZMQ::Context.new
  rep = ctx.bind(:REP, socket_address)

  log "Ready! Using \"#{ActiveRecord::Base.connection.current_database}\" database"

  commands = args.last

  loop do
    begin
      loop do
        line_in = rep.recv
        raise "Got nil response (ZMQ REP/REQ out of sync?)" if line_in.nil?
        command, rest_of_command = split(line_in, 2)

        before = Time.now
        begin
          command = commands[command.downcase.to_sym]

          if command.nil?
            log "Received invalid command: \"#{line_in}\""
          else
            dev_log "<== #{line_in}"
            ActiveRecord::Base.connection_pool.with_connection do

              # The ZMQ socket requires that a reply be send after every
              # message -- so we pass a lambda for the client code to
              # call to send a message and make sure it only happens
              # once.
              replied = false
              reply = lambda do |msg|
                if replied
                  raise "Reply sent twice"
                else
                  rep.send(msg)
                  replied = true
                end
              end
              command.call(reply, rest_of_command)

              if !replied
                rep.send "noreply"
              end

            end
          end

        rescue StandardError => e
          report_error(rep, line_in, e)
        rescue Exception => e
          report_error(rep, line_in, e)
          break
        end
        after = Time.now

        ms = (after - before) * 1000
        dev_log %[(#{'%.2f' % ms}ms)]
        dev_log ""
      end

    rescue StandardError => error
      $stderr.puts "=== ERROR -- RESTARTING SERVER ===\n"\
        "#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")}"

      rep.destroy
      log "Reconnecting...!"
      rep = ctx.bind(:REP, socket_address)
    end
  end
end