Class: Ernie

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

Defined Under Namespace

Classes: Mod, ServerError

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

.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

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



76
77
78
79
80
# File 'lib/ernie.rb', line 76

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



43
44
45
46
47
48
49
50
51
52
# File 'lib/ernie.rb', line 43

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



34
35
36
# File 'lib/ernie.rb', line 34

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



58
59
60
# File 'lib/ernie.rb', line 58

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



66
67
68
# File 'lib/ernie.rb', line 66

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



22
23
24
25
26
27
# File 'lib/ernie.rb', line 22

def self.mod(name, block)
  m = Mod.new(name)
  self.current_mod = m
  self.mods[name] = m
  block.call
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



87
88
89
90
91
# File 'lib/ernie.rb', line 87

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



98
99
100
101
102
103
# File 'lib/ernie.rb', line 98

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



119
120
121
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
# File 'lib/ernie.rb', line 119

def self.start
  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
    iruby = self.read_berp(input)
    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.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.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.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

.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



110
111
112
113
114
# File 'lib/ernie.rb', line 110

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