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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport, init_proc = nil) ⇒ Endpoint

Create endpoint connected to some transport

Parameters:


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

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.


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

def provider
  @provider
end

Class Method Details

.open(transport, &block) ⇒ Object


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

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

Hashie::Mash.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:


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

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


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

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)


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

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


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

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


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

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.


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

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:


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

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:


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

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