Class: Ernie

Inherits:
Object
  • Object
show all
Defined in:
lib/ernie.rb

Defined Under Namespace

Classes: Mod, ServerError

Constant Summary collapse

VERSION =
'2.5.2'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.auto_startObject

Returns the value of attribute auto_start


10
11
12
# File 'lib/ernie.rb', line 10

def auto_start
  @auto_start
end

.countObject

Returns the value of attribute count


11
12
13
# File 'lib/ernie.rb', line 11

def count
  @count
end

.current_modObject

Returns the value of attribute current_mod


9
10
11
# File 'lib/ernie.rb', line 9

def current_mod
  @current_mod
end

.logObject

Returns the value of attribute log


9
10
11
# File 'lib/ernie.rb', line 9

def log
  @log
end

.modsObject

Returns the value of attribute mods


9
10
11
# File 'lib/ernie.rb', line 9

def mods
  @mods
end

.virgin_proclineObject

Returns the value of attribute virgin_procline


11
12
13
# File 'lib/ernie.rb', line 11

def virgin_procline
  @virgin_procline
end

Class Method Details

.dispatch(mod, fun, args) ⇒ Object

Dispatch the request to the proper mod:fun.

+mod+ is the module Symbol
+fun+ is the function Symbol
+args+ is the Array of arguments

Returns the Ruby object response


81
82
83
84
85
# File 'lib/ernie.rb', line 81

def self.dispatch(mod, fun, args)
  self.mods[mod] || raise(ServerError.new("No such module '#{mod}'"))
  self.mods[mod].funs[fun] || raise(ServerError.new("No such function '#{mod}:#{fun}'"))
  self.mods[mod].funs[fun].call(*args)
end

.expose(name, mixin) ⇒ Object

Expose all public methods in a Ruby module:

+name+ is the ernie module Symbol
+mixin+ is the ruby module whose public methods are exposed

Returns nothing


48
49
50
51
52
53
54
55
56
57
# File 'lib/ernie.rb', line 48

def self.expose(name, mixin)
  context = Object.new
  context.extend mixin
  mod(name, lambda {
    mixin.public_instance_methods.each do |meth|
      fun(meth.to_sym, context.method(meth))
    end
  })
  context
end

.fun(name, block) ⇒ Object

Record a function.

+name+ is the function Symbol
+block+ is the Block to associate

Returns nothing


39
40
41
# File 'lib/ernie.rb', line 39

def self.fun(name, block)
  self.current_mod.fun(name, block)
end

.logfile(file) ⇒ Object

Set the logfile to given path.

+file+ is the String path to the logfile

Returns nothing


63
64
65
# File 'lib/ernie.rb', line 63

def self.logfile(file)
  self.log = Logger.new(file)
end

.loglevel(level) ⇒ Object

Set the log level.

+level+ is the Logger level (Logger::WARN, etc)

Returns nothing


71
72
73
# File 'lib/ernie.rb', line 71

def self.loglevel(level)
  self.log.level = level
end

.mod(name, block) ⇒ Object

Record a module.

+name+ is the module Symbol
+block+ is the Block containing function definitions

Returns nothing


27
28
29
30
31
32
# File 'lib/ernie.rb', line 27

def self.mod(name, block)
  m = Mod.new(name)
  self.current_mod = m
  self.mods[name] = m
  block.call
end

.procline(msg) ⇒ Object


187
188
189
# File 'lib/ernie.rb', line 187

def self.procline(msg)
  $0 = "ernie handler #{VERSION} (ruby) - #{self.virgin_procline} - [#{self.count}] #{msg}"[0..159]
end

.read_4(input) ⇒ Object

Read the length header from the wire.

+input+ is the IO from which to read

Returns the size Integer if one was read Returns nil otherwise


92
93
94
95
96
# File 'lib/ernie.rb', line 92

def self.read_4(input)
  raw = input.read(4)
  return nil unless raw
  raw.unpack('N').first
end

.read_berp(input) ⇒ Object

Read a BERP from the wire and decode it to a Ruby object.

+input+ is the IO from which to read

Returns a Ruby object if one could be read Returns nil otherwise


103
104
105
106
107
108
# File 'lib/ernie.rb', line 103

def self.read_berp(input)
  packet_size = self.read_4(input)
  return nil unless packet_size
  bert = input.read(packet_size)
  BERT.decode(bert)
end

.startObject

Start the processing loop.

Loops forever


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/ernie.rb', line 124

def self.start
  self.procline('starting')
  self.log.info("(#{Process.pid}) Starting") if self.log.level <= Logger::INFO
  self.log.debug(self.mods.inspect) if self.log.level <= Logger::DEBUG

  input = IO.new(3)
  output = IO.new(4)
  input.sync = true
  output.sync = true

  loop do
    self.procline('waiting')
    iruby = self.read_berp(input)
    self.count += 1

    unless iruby
      puts "Could not read BERP length header. Ernie server may have gone away. Exiting now."
      if self.log.level <= Logger::INFO
        self.log.info("(#{Process.pid}) Could not read BERP length header. Ernie server may have gone away. Exiting now.")
      end
      exit!
    end

    if iruby.size == 4 && iruby[0] == :call
      mod, fun, args = iruby[1..3]
      self.procline("#{mod}:#{fun}(#{args})")
      self.log.info("-> " + iruby.inspect) if self.log.level <= Logger::INFO
      begin
        res = self.dispatch(mod, fun, args)
        oruby = t[:reply, res]
        self.log.debug("<- " + oruby.inspect) if self.log.level <= Logger::DEBUG
        write_berp(output, oruby)
      rescue ServerError => e
        oruby = t[:error, t[:server, 0, e.class.to_s, e.message, e.backtrace]]
        self.log.error("<- " + oruby.inspect) if self.log.level <= Logger::ERROR
        self.log.error(e.backtrace.join("\n")) if self.log.level <= Logger::ERROR
        write_berp(output, oruby)
      rescue Object => e
        oruby = t[:error, t[:user, 0, e.class.to_s, e.message, e.backtrace]]
        self.log.error("<- " + oruby.inspect) if self.log.level <= Logger::ERROR
        self.log.error(e.backtrace.join("\n")) if self.log.level <= Logger::ERROR
        write_berp(output, oruby)
      end
    elsif iruby.size == 4 && iruby[0] == :cast
      mod, fun, args = iruby[1..3]
      self.procline("#{mod}:#{fun}(#{args})")
      self.log.info("-> " + [:cast, mod, fun, args].inspect) if self.log.level <= Logger::INFO
      begin
        self.dispatch(mod, fun, args)
      rescue Object => e
        # ignore
      end
      write_berp(output, t[:noreply])
    else
      self.procline("invalid request")
      self.log.error("-> " + iruby.inspect) if self.log.level <= Logger::ERROR
      oruby = t[:error, t[:server, 0, "Invalid request: #{iruby.inspect}"]]
      self.log.error("<- " + oruby.inspect) if self.log.level <= Logger::ERROR
      write_berp(output, oruby)
    end
  end
end

.versionObject


191
192
193
# File 'lib/ernie.rb', line 191

def self.version
  VERSION
end

.write_berp(output, ruby) ⇒ Object

Write the given Ruby object to the wire as a BERP.

+output+ is the IO on which to write
+ruby+ is the Ruby object to encode

Returns nothing


115
116
117
118
119
# File 'lib/ernie.rb', line 115

def self.write_berp(output, ruby)
  data = BERT.encode(ruby)
  output.write([data.length].pack("N"))
  output.write(data)
end