Class: Tupelo::AppBuilder

Inherits:
Object show all
Defined in:
lib/tupelo/app/trace.rb,
lib/tupelo/app/remote.rb,
lib/tupelo/app/builder.rb

Overview

Not an essential part of the library, but used to build up groups of processes for use in examples, tests, benchmarks, etc.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ez, argv: argv, owns_services: nil, tunnel_default: false) ⇒ AppBuilder

Returns a new instance of AppBuilder.



31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/tupelo/app/builder.rb', line 31

def initialize ez, argv: argv, owns_services: nil, tunnel_default: false
  @ez = ez
  @owns_services = owns_services
  @tunnel_default = tunnel_default
  @argv = argv

  # When connecting to remote, non-sibling (not started by the same ancestor
  # process) services, use a tunnel if requested to (see note under
  # #tunnel_default).
  if not owns_services and tunnel_default and not ez.sibling
    ez.tunnel_to_remote_services
  end
end

Instance Attribute Details

#argvObject (readonly)

Arguments available to application after tupelo has parsed out switches and args that it recognizes.



29
30
31
# File 'lib/tupelo/app/builder.rb', line 29

def argv
  @argv
end

#ezObject (readonly)

Returns the value of attribute ez.



5
6
7
# File 'lib/tupelo/app/builder.rb', line 5

def ez
  @ez
end

#owns_servicesObject (readonly)

Does this app own (as child processes) the seq, cseq, and arc services?



8
9
10
# File 'lib/tupelo/app/builder.rb', line 8

def owns_services
  @owns_services
end

#tunnel_defaultObject (readonly)

Do remote clients default to using ssh tunnels for data? This has slightly different meanings in two cases:

  1. When the client is started by the #remote method, as in many simpler examples, the #tunnel_default is the default for the tunnel keyword argument of the #remote method. (Uses ssh -R.)

  2. When the client is started as an unrelated process (for example, connecting to a pre-existing tupelo cluster running on a different host), there is no #remote call, and tunneling is automatically set up. (Uses ssh -L.)

In both cases, the –tunnel command line switch sets tunnel_default to true.



25
26
27
# File 'lib/tupelo/app/builder.rb', line 25

def tunnel_default
  @tunnel_default
end

Instance Method Details

#child(client_class = Client, passive: false, **opts, &block) ⇒ Object

Yields a client that runs in a subprocess.

A passive client will be forced to stop after all active clients exit. Use the passive flag for processes that wait for tuples and respond in some way. Then you do not have to manually interrupt the whole application when the active processes are done. See examples.

Returns pid of child process.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/tupelo/app/builder.rb', line 73

def child client_class = Client, passive: false, **opts, &block
  ez.child :seqd, :cseqd, :arcd, passive: passive do |seqd, cseqd, arcd|
    opts = {seq: seqd, cseq: cseqd, arc: arcd, log: log}.merge(opts)
    run_client client_class, **opts do |client|
      if block
        if block.arity == 0
          client.instance_eval &block
        else
          yield client
        end
      end
    end
  end
end

#local(client_class = Client, **opts, &block) ⇒ Object

Yields a client that runs in this process.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/tupelo/app/builder.rb', line 50

def local client_class = Client, **opts, &block
  ez.local :seqd, :cseqd, :arcd do |seqd, cseqd, arcd|
    opts = {seq: seqd, cseq: cseqd, arc: arcd, log: log}.merge(opts)
    run_client client_class, **opts do |client|
      if block
        if block.arity == 0
          client.instance_eval &block
        else
          yield client
        end
      end
    end
  end
end

#logObject



45
46
47
# File 'lib/tupelo/app/builder.rb', line 45

def log
  ez.log
end

#remote(client_class = Client, client_lib: 'tupelo/client', host: nil, **opts) ⇒ Object

Perform client operations on another host.

There are two modes: drb and eval. In the drb mode, a block executes locally, performing tuplespace operations by proxy to the remote tupelo client instance. Results of the ops are available in the local process. This is very useful for testing and examples.

In the eval mode, a string is evaluated on the remote host in the context of a client instance.

There are some minor limitations compared to #child:

In eval mode, the code string is always treated as in the arity 0 case of #child, in other words “DSL mode”, in other words the self is the client.

Unlike #child, there is no mode that returns a Client instance.

Note that EasyServe options apply, including the ‘tunnel: true` option. The default for this option is true if the `–tunnel` switch is present on the command line.



27
28
29
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
# File 'lib/tupelo/app/remote.rb', line 27

def remote client_class = Client,
    client_lib: 'tupelo/client', host: nil, **opts
  require 'easy-serve/remote'
  ## detach option so that remote process doesn't keep ssh connection
  snames = :seqd, :cseqd, :arcd

  if tunnel_default and not opts.key?(:tunnel)
    opts[:tunnel] = true
  end

  if opts[:eval]
    ez.remote *snames, host: host, **opts, eval: %{
      require #{client_lib.inspect}

      seqd, cseqd, arcd = *conns
      client_class = Object.const_get(#{client_class.name.inspect})

      begin
        log.progname = "client <starting in \#{log.progname}>"
        client = client_class.new(
          seq: seqd, cseq: cseqd, arc: arcd, log: log)
        client.start do
          log.progname = "client \#{client.client_id}"
        end
        client.instance_eval #{opts[:eval].inspect}
      ensure
        client.stop if client
      end
    }

  elsif block_given?
    block = Proc.new
    ez.remote *snames, host: host, **opts do |seqd, cseqd, arcd|
      run_client client_class,
          seq: seqd, cseq: cseqd, arc: arcd, log: log do |client|
        if block.arity == 0
          client.instance_eval &block
        else
          yield client
        end
      end
    end

  else
    raise ArgumentError, "cannot select remote mode based on arguments"
  end
end

#run_client(client_class, opts) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/tupelo/app/builder.rb', line 88

def run_client client_class, opts
  log = opts[:log]
  log.progname = "client <starting in #{log.progname}>"
  client = client_class.new opts
  client.start do
    log.progname = "client #{client.client_id}"
  end
  yield client
ensure
  client.stop if client # gracefully exit the tuplespace management thread
end

#start_traceObject



49
50
51
52
53
54
55
56
57
# File 'lib/tupelo/app/trace.rb', line 49

def start_trace
  child passive: true do
    trap :INT do
      exit!
    end

    trace_loop
  end
end