Module: DuoRuby

Defined in:
lib/duoruby/cli.rb,
lib/duoruby/boot.rb,
lib/duoruby/group.rb,
lib/duoruby/client.rb,
lib/duoruby/config.rb,
lib/duoruby/server.rb,
lib/duoruby/socket.rb,
lib/duoruby/channel.rb,
lib/duoruby/message.rb,
lib/duoruby/testing.rb,
lib/duoruby/version.rb,
lib/duoruby/launcher.rb,
lib/duoruby/reply_error.rb,
lib/duoruby/reply_promise.rb,
lib/duoruby/setup/backend.rb,
lib/duoruby/setup/frontend.rb,
lib/duoruby/socket/transport.rb,
lib/duoruby/channel/namespace.rb,
lib/duoruby/socket/test_promise.rb,
lib/duoruby/channel/handler_methods.rb,
lib/duoruby/server/frontend_compiler.rb

Defined Under Namespace

Modules: Testing Classes: CLI, Channel, Client, Config, Group, Launcher, Message, ReplyError, ReplyPromise, Server, Socket

Constant Summary collapse

VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.app=(instance) ⇒ Object

Registers the application instance produced by a setup file. Called from within app/setup/<side>.rb; the registered value is read back by load_app and then cleared.

Parameters:

  • instance (Object)

    the backend or frontend instance



68
69
70
# File 'lib/duoruby/boot.rb', line 68

def self.app=(instance)
  @app = instance
end

.app_paths(side, root: Dir.pwd) ⇒ Array<String, String>

Returns the canonical pair of application file paths for side.

Parameters:

  • side (:backend, :frontend)
  • root (String) (defaults to: Dir.pwd)

    the application root directory

Returns:

  • (Array<String, String>)

    [config_path, setup_path]



91
92
93
94
# File 'lib/duoruby/boot.rb', line 91

def self.app_paths(side, root: Dir.pwd)
  root = File.expand_path(root)
  [File.join(root, "duoruby.rb"), File.join(root, "app", "setup", "#{side}.rb")]
end

.boot(side = :backend, root: Dir.pwd) ⇒ Array<String>

Loads the application files for side from root, adding the root and app/ directories to $LOAD_PATH first.

Uses Kernel#load for both files so they are always re-evaluated (rather than skipped if previously required). Both files are optional — only those that exist are loaded.

Parameters:

  • side (:backend, :frontend) (defaults to: :backend)

    which side to boot

  • root (String) (defaults to: Dir.pwd)

    the application root directory (defaults to Dir.pwd)

Returns:

  • (Array<String>)

    absolute paths of the files that were loaded



28
29
30
31
32
33
34
35
# File 'lib/duoruby/boot.rb', line 28

def self.boot(side = :backend, root: Dir.pwd)
  root = prepare_root(root)
  paths = app_paths(side, root: root)

  paths = paths.select { |path| File.file?(path) }
  paths.each { |path| load path }
  paths
end

.configConfig

Returns the current framework configuration object.

Returns:



35
36
37
# File 'lib/duoruby/config.rb', line 35

def self.config
  @config ||= Config.new
end

.configure {|config| ... } ⇒ Object

Yields the configuration object for mutation.

Yield Parameters:



42
43
44
# File 'lib/duoruby/config.rb', line 42

def self.configure
  yield config
end

.load_app(side = :backend, root: Dir.pwd) ⇒ Object?

Loads the application files for side and returns the registered app object.

This is a CRuby/server-side method — it is never run inside an Opal compiled bundle. Both files are loaded with Kernel#load (a runtime file loader that accepts a computed path) rather than require.

The setup file is expected to call DuoRuby.app = <instance> to register the backend it creates. load_app returns that registered value and clears the slot so successive calls in the same process behave independently.

Parameters:

  • side (:backend, :frontend) (defaults to: :backend)

    which side to load

  • root (String) (defaults to: Dir.pwd)

    the application root directory

Returns:

  • (Object, nil)

    the value passed to DuoRuby.app=, or nil if the setup file does not exist or no app was registered



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/duoruby/boot.rb', line 51

def self.load_app(side = :backend, root: Dir.pwd)
  root = prepare_root(root)
  config_path = File.join(root, "duoruby.rb")
  load config_path if File.file?(config_path)

  setup_path = File.join(root, "app", "setup", "#{side}.rb")
  return unless File.file?(setup_path)

  load setup_path
  @app.tap { @app = nil }
end

.prepare_root(root) ⇒ String

Expands root to an absolute path and prepends both <root> and <root>/app to $LOAD_PATH (unless already present).

Parameters:

  • root (String)

    the raw root path

Returns:

  • (String)

    the expanded absolute root path



77
78
79
80
81
82
83
84
# File 'lib/duoruby/boot.rb', line 77

def self.prepare_root(root)
  root = File.expand_path(root)
  [File.join(root, "app"), root].each do |path|
    $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
  end

  root
end

.server {|—| ... } ⇒ Server

Creates a new application Server instance, optionally configuring it via a block.

The block is evaluated in the context of the server instance, so handler registration methods (on, one, off) are available directly.

Examples:

server = DuoRuby.server do
  on(:join) { |client, room:| group(room) << client }
end

Yield Parameters:

  • (block is instance_eval'd on the server; no explicit param)

Returns:



19
20
21
22
23
# File 'lib/duoruby/setup/backend.rb', line 19

def self.server(&block)
  Server.new.tap do |server|
    server.instance_eval(&block) if block
  end
end

.socket(transport: nil) {|—| ... } ⇒ Socket

Creates a new browser Socket instance, optionally configuring it via a block.

The block is evaluated in the context of the socket instance, so handler registration methods (on, one, off) are available directly.

Examples:

socket = DuoRuby.socket do
  on(:snapshot) { |rooms:, **| puts rooms.inspect }
end

Parameters:

Yield Parameters:

  • (block is instance_eval'd on the socket; no explicit param)

Returns:



28
29
30
31
32
# File 'lib/duoruby/setup/frontend.rb', line 28

def self.socket(transport: nil, &block)
  Socket.new(transport: transport).tap do |socket|
    socket.instance_eval(&block) if block
  end
end