Class: Farcall::Endpoint

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

Overview

The protocol endpoint. Takes some transport and implements Farcall protocol over it. You can use it direcly or with Farcall::RemoteInterface and Farcall::LocalProvider helper classes.

Note that the returned data is converted to Hashie::Mash primarily for the sake of :key vs. ‘key’ ambigity that otherwise might appear depending on the transport encoding protocol. Anyway it is better than ruby hash ;)

Endpoint class is thread-safe.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport) ⇒ Endpoint

Create endpoint connected to some transport

Parameters:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/farcall/endpoint.rb', line 23

def initialize(transport)
  @transport                  = transport
  @in_serial                  = @out_serial = 0
  @transport.on_data_received = -> (data) {
    begin
      _received(data)
    rescue
      abort :format_error, $!
    end
  }

  @send_lock    = Mutex.new
  @receive_lock = Mutex.new
  @handlers     = {}
  @waiting      = {}
end

Instance Attribute Details

#providerObject

Set or get provider instance. When provider is set, its public methods are called by the remote and any possible exception are passed back to caller party. You can use any ruby class instance everything will work, operators, indexes[] and like.



19
20
21
# File 'lib/farcall/endpoint.rb', line 19

def provider
  @provider
end

Instance Method Details

#abort(reason, exception = nil) ⇒ Object

:nodoc:



52
53
54
55
56
57
58
59
# File 'lib/farcall/endpoint.rb', line 52

def abort reason, exception = nil
  puts "*** Abort: reason #{reason || exception.to_s}"
  @abort_hadnler and @abort_hadnler.call reason, exception
  if exception
    raise exception
  end
  close
end

#call(name, *args, **kwargs, &block) ⇒ Object

Call remote party. Retruns immediately. When remote party answers, calls the specified block if present. The block should take |error, result| parameters. If result’s content hashes or result itself are instances of th Hashie::Mash. Error could be nil or {‘class’ =>, ‘text’ => } Hashie::Mash hash. result is always nil if error is presented.

It is desirable to use Farcall::Endpoint#interface or Farcall::RemoteInterface rather than this low-level method.

Parameters:

  • name (String)

    of the remote command



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/farcall/endpoint.rb', line 77

def call(name, *args, **kwargs, &block)
  @send_lock.synchronize {
    if block != nil
      @waiting[@out_serial] = {
          time: Time.new,
          proc: block
      }
      _send(cmd: name.to_s, args: args, kwargs: kwargs)
    end
  }
end

#closeObject

Close endpoint and connected transport



62
63
64
65
66
# File 'lib/farcall/endpoint.rb', line 62

def close
  @transport.close
  @transport = nil
  @close_handler and @close_handler.call
end

#on(name, &block) ⇒ Object

Set handler to perform the named command. Block will be called when the remote party calls with parameters passed from the remote. The block returned value will be passed back to the caller.

The provider if set is calling instead.

If the block raises the exception it will be reported to the caller as an error (depending on it’s platofrm, will raise exception on its end or report error)



148
149
150
# File 'lib/farcall/endpoint.rb', line 148

def on(name, &block)
  @handlers[name.to_s] = block
end

#on_abort(&proc) ⇒ Object

The provided block will be called if endpoint functioning will be aborted. The block should take |reason, exception| parameters - latter could be nil



42
43
44
# File 'lib/farcall/endpoint.rb', line 42

def on_abort &proc
  @abort_hadnler = proc
end

#on_close(&block) ⇒ Object

Add the close handler. Specified block will be called when the endpoint is been closed



47
48
49
# File 'lib/farcall/endpoint.rb', line 47

def on_close &block
  @close_handler = block
end

#on_remote_call(&block) ⇒ Object Also known as: on_command

Process remote commands. Provided block will be executed on every remote command taking parameters |name, args, kwargs|. Whatever block returns will be passed to a calling party. The same any exception that the block might raise would be send back to caller.

this block will be called onlly of there wes no ‘provider` specified and no #on handler set for the command being executed.



134
135
136
# File 'lib/farcall/endpoint.rb', line 134

def on_remote_call &block
  @on_remote_call = block
end

#remoteObject

Get the Farcall::RemoteInterface connnected to this endpoint. Any subsequent calls with return the same instance.



155
156
157
# File 'lib/farcall/endpoint.rb', line 155

def remote
  @remote ||= Farcall::Interface.new endpoint: self
end

#sync_call(name, *args, **kwargs) ⇒ Object

Call the remote party and wait for the return.

It is desirable to use Farcall::Endpoint#interface or Farcall::RemoteInterface rather than this low-level method.

Parameters:

  • name (String)

    of the remote command

Returns:

  • (Object)

    any data that remote party retruns. If it is a hash, it is a Hashie::Mash instance.

Raises:



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
# File 'lib/farcall/endpoint.rb', line 99

def sync_call(name, *args, **kwargs)
  mutex          = Mutex.new
  resource       = ConditionVariable.new
  error          = nil
  result         = nil
  calling_thread = Thread.current

  mutex.synchronize {
    same_thread = false
    call(name, *args, **kwargs) { |e, r|
      error, result = e, r
      # Absolutly stupid wait for self situation
      # When single thread is used to send and receive
      # - often happens in test environments
      if calling_thread == Thread.current
        same_thread = true
      else
        resource.signal
      end
    }
    same_thread or resource.wait(mutex)
  }
  if error
    raise Farcall::RemoteError.new(error['class'], error['text'])
  end
  result
end