Class: Softwear::Library::LightServer

Inherits:
Object
  • Object
show all
Defined in:
lib/softwear/library/light_server.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.test!(file_path) ⇒ Object



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

def self.test!(file_path)
  Object.class_eval do
    def start_server!(*args)
      Thread.new do
        Softwear::Library::LightServer.new.start_server!(*args)
      end
    end
  end
  load file_path
end

Instance Method Details

#dev_log(*a) ⇒ Object



32
33
34
# File 'lib/softwear/library/light_server.rb', line 32

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

#log(*a) ⇒ Object



36
37
38
# File 'lib/softwear/library/light_server.rb', line 36

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

#report_error(rep, whole_command, error) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/softwear/library/light_server.rb', line 21

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



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

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



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
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/softwear/library/light_server.rb', line 51

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

  rep = zmq.bind(:REP, socket_address)

  log "Ready! Using \"#{ActiveRecord::Base.connection.try(:current_database) || 'in-memory'}\" 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
              instance_exec(reply, rest_of_command, &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 = zmq.bind(:REP, socket_address)
    end
  end
end

#zmqObject



136
137
138
# File 'lib/softwear/library/light_server.rb', line 136

def zmq
  $zmq_context ||= ZMQ::Context.new
end