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 SmartHash 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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport, init_proc = nil) ⇒ Endpoint

Create endpoint connected to some transport

Parameters:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/farcall/endpoint.rb', line 23

def initialize(transport, init_proc=nil)
  @transport                  = transport
  @in_serial                  = @out_serial = 0
  @send_lock    = Mutex.new
  @receive_lock = Mutex.new
  @handlers     = {}
  @waiting      = {}

  init_proc.call(self) if init_proc

  # @!visibility private
  def push_input data
    @in_buffer << data
    drain
  end

  @transport.on_data_received = -> (data) {
    begin
      _received(data)
    rescue
      abort :format_error, $!
    end
  }
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

Class Method Details

.open(transport, &block) ⇒ Object



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

def self.open(transport, &block)
  Endpoint.new(transport, block)
end

Instance Method Details

#call(name, *args, **kwargs, &block) ⇒ Farcall::Promise

Call the remote party, non blocking, returns Promise instance to handle the remote return valy asynchronously (recommended way).

Optionally the block could be provided that takes |error, result| parameters. Error must be nil or

SmartHash.new({'class' =>, 'text' => text [, data: {some_data}] })

if error is presented, the result is always the nil.

Usually, using #remote which returns Interface is more effective rather than this low-level method.

The returned Promise instance let add any number of callbacks on commend execution, success or failure.

Parameters:

  • name (String)

    of the remote command

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/farcall/endpoint.rb', line 98

def call(name, *args, **kwargs, &block)
  promise = Farcall::Promise.new
  @send_lock.synchronize {
      @waiting[@out_serial] = -> (error, result) {
        block.call(error, result) if block
        if error
          promise.set_fail error
        else
          promise.set_success result
        end
      }
      _send(cmd: name.to_s, args: args, kwargs: kwargs)
  }
  promise
end

#closeObject

Close endpoint and connected transport



74
75
76
77
78
# File 'lib/farcall/endpoint.rb', line 74

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)



173
174
175
# File 'lib/farcall/endpoint.rb', line 173

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



54
55
56
# File 'lib/farcall/endpoint.rb', line 54

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



59
60
61
# File 'lib/farcall/endpoint.rb', line 59

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.



159
160
161
# File 'lib/farcall/endpoint.rb', line 159

def on_remote_call &block
  @on_remote_call = block
end

#remoteFarcall::Interface

Get the Interface connnected to this endpoint. Any subsequent calls with return the same instance.

Returns:



181
182
183
# File 'lib/farcall/endpoint.rb', line 181

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 SmartHash instance.

Raises:



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

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'], error['data'])
  end
  result
end