Class: Nobject::Local

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

Overview

this class is used by the client application, wraps a local object, pushes it to a Nobject::Serve4r, which will then send method calls to a matching Nobject::Remote object

Defined Under Namespace

Classes: InvalidMethod, MethodInvocationRetriesExceeded, MethodRequestFailure, MethodResponseFailure, UnknownReturnDataType

Constant Summary collapse

MAX_RETRIES =
3

Instance Method Summary collapse

Constructor Details

#initialize(host, port, obj) ⇒ Local

host: the hostname of the server to push obj to port: the port number of the server to push obj to obj: the obj to store over the network

ex:

# this will create a new Nobject::Local, then push it to the specified
server Nobject::Local.new('localhost', 1234, <object>)


17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/nobject/local.rb', line 17

def initialize(host, port, obj)
  @msg_counter = 0
  @socket = TCPSocket.new(host, port)
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

  obj_bytes = Marshal.dump(obj)

  File.open('/tmp/nobject.log', 'a') {|f| f.puts "L:##{@msg_counter += 1} sz#{obj_bytes.length}"; f.flush }
  @socket.send([obj_bytes.length].pack('Q>'), 0)
  @socket.send(obj_bytes, 0)
  @socket.flush
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, **kwargs, &block) ⇒ Object



30
31
32
33
34
35
36
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
# File 'lib/nobject/local.rb', line 30

def method_missing(method, *args, **kwargs, &block)
  msg = { method: method, args: args }
  msg_bytes = Marshal.dump(msg)

  retries = 0
  loop do
    raise MethodInvocationRetriesExceeded.new("Exceeded retry limit (#{MAX_RETRIES})") if retries == MAX_RETRIES

    begin
      @socket.send([msg_bytes.length].pack('Q>'), 0)
      File.open('/tmp/nobject.log', 'a') {|f| f.puts "  LMS:##{@msg_counter += 1} sz#{msg_bytes.length} m:#{method}"; f.flush }
      @socket.send(msg_bytes, 0)
      @socket.flush
    rescue Exception
      raise Local::MethodRequestFailure.new("did not receive response from call to `#{method}' over the network")
    end

    return_data = begin
                    msg_size = @socket.recv(8).unpack('Q>').first
                    raw_bytes = @socket.recv(msg_size)
                    File.open('/tmp/nobject.log', 'a') {|f| f.puts "    LMGotit :##{@msg_counter += 1} sz#{msg_size} bytes:#{raw_bytes.length} m:#{method}"; f.flush }
                    if msg_size != raw_bytes.length
                      retries += 1
                      print "\a"  # TODO: consider removing this after the
                                  #   retry logic seems solid, it's extra,
                                  #   literal noise :)
                      redo
                    end

                    Marshal.load(raw_bytes)
                  rescue Exception => e
                    # TODO: consider removing this rescue block:
                    #   this rescue may be unreachable now, since the only
                    #   time it used to happen was when:
                    #     msg_size != raw_bytes.length
                    #
                    #   ... which is now handled by the retry logic above
                    error_msg = "                      did not receive response from call to `\#{method}' over the network\n                      would have been msg_id \#{@msg_counter} OR \#{@msg_counter + 1} when trying to receive \#{msg_size} bytes\n                      caused by \#{e.class.name}\n                        exception backtrace:\n                        \#{e.backtrace.join(\"\\n    \")}\n                    MSG\n                    raise Local::MethodResponseFailure.new(error_msg)\n                  end\n\n    break (\n      case return_data.first\n      when :ok then return_data.last\n      when :raise then raise return_data.last\n      else\n        raise Local::UnknownReturnDataType.new(\"unknown data type '\#{return_data.first}' within Nobject::Local (Nobject::Local::UnknownReturnDataType)\")\n      end\n    )\n  end\nend\n"

Instance Method Details

#!~(other) ⇒ Object

method overridden from Object class



91
# File 'lib/nobject/local.rb', line 91

def !~(other);      method_missing(:is_a?, other);  end

#<=>(other) ⇒ Object



92
# File 'lib/nobject/local.rb', line 92

def <=>(other);     method_missing(:<=>, other);    end

#===(other) ⇒ Object



93
# File 'lib/nobject/local.rb', line 93

def ===(other);     method_missing(:<=>, other);    end

#inspectObject



95
# File 'lib/nobject/local.rb', line 95

def inspect;        method_missing(:inspect);       end

#is_a?(klass) ⇒ Boolean

Returns:

  • (Boolean)


94
# File 'lib/nobject/local.rb', line 94

def is_a?(klass);   method_missing(:is_a?, klass);  end

#to_sObject



96
# File 'lib/nobject/local.rb', line 96

def to_s;           method_missing(:to_s);          end