Class: Ernie

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

Defined Under Namespace

Classes: Mod, ServerError

Constant Summary collapse

VERSION =
self.version

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.auto_startObject

Returns the value of attribute auto_start



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

def auto_start
  @auto_start
end

.countObject

Returns the value of attribute count



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

def count
  @count
end

.current_modObject

Returns the value of attribute current_mod



7
8
9
# File 'lib/ernie.rb', line 7

def current_mod
  @current_mod
end

.logObject

Returns the value of attribute log



7
8
9
# File 'lib/ernie.rb', line 7

def log
  @log
end

.modsObject

Returns the value of attribute mods



7
8
9
# File 'lib/ernie.rb', line 7

def mods
  @mods
end

.virgin_proclineObject

Returns the value of attribute virgin_procline



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

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



79
80
81
82
83
# File 'lib/ernie.rb', line 79

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



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

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



37
38
39
# File 'lib/ernie.rb', line 37

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



61
62
63
# File 'lib/ernie.rb', line 61

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



69
70
71
# File 'lib/ernie.rb', line 69

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



25
26
27
28
29
30
# File 'lib/ernie.rb', line 25

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

.procline(msg) ⇒ Object



183
184
185
# File 'lib/ernie.rb', line 183

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



90
91
92
93
94
# File 'lib/ernie.rb', line 90

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



101
102
103
104
105
106
# File 'lib/ernie.rb', line 101

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



122
123
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
# File 'lib/ernie.rb', line 122

def self.start
  self.procline('starting')
  self.log.info("(#{Process.pid}) Starting")
  self.log.debug(self.mods.inspect)

  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."
      self.log.info("(#{Process.pid}) Could not read BERP length header. Ernie server may have gone away. Exiting now.")
      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)
      begin
        res = self.dispatch(mod, fun, args)
        oruby = t[:reply, res]
        self.log.debug("<- " + oruby.inspect)
        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)
        self.log.error(e.backtrace.join("\n"))
        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)
        self.log.error(e.backtrace.join("\n"))
        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)
      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)
      oruby = t[:error, t[:server, 0, "Invalid request: #{iruby.inspect}"]]
      self.log.error("<- " + oruby.inspect)
      write_berp(output, oruby)
    end
  end
end

.versionObject



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

def self.version
  yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
  "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
rescue
  'unknown'
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



113
114
115
116
117
# File 'lib/ernie.rb', line 113

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