Module: Hibiki

Defined in:
lib/hibiki/dsl.rb,
lib/hibiki/root.rb,
lib/hibiki/batch.rb,
lib/hibiki/state.rb,
lib/hibiki/effect.rb,
lib/hibiki/derived.rb,
lib/hibiki/version.rb,
lib/hibiki/reactive.rb,
lib/hibiki/tracking.rb

Defined Under Namespace

Modules: DSL, Observer, Owner, Reactive, Trackable Classes: Derived, Effect, Root, State

Constant Summary collapse

VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.batchObject



18
19
20
21
22
23
24
25
26
27
# File 'lib/hibiki/batch.rb', line 18

def batch
  Thread.current[:hibiki_batch_depth] = batch_depth + 1
  yield
ensure
  # Flush even when the block raises: writes before the raise have
  # already landed, so effects must catch up with them.
  depth = batch_depth - 1
  Thread.current[:hibiki_batch_depth] = depth
  flush_effects if depth.zero?
end

.batching?Boolean

Returns:

  • (Boolean)


29
# File 'lib/hibiki/batch.rb', line 29

def batching? = batch_depth.positive?

.current_observerObject



14
# File 'lib/hibiki/tracking.rb', line 14

def current_observer = Fiber[:hibiki_observer]

.current_ownerObject

Solid keeps Owner separate from Listener: only effects own, and a lazy derived computing mid-effect must not steal ownership of effects its block creates. Hence a second slot rather than reusing the observer's.



32
# File 'lib/hibiki/tracking.rb', line 32

def current_owner = Fiber[:hibiki_owner]

.error_handlerObject

Where effect errors raised during a flush go (Solid's handleError): a callable receiving (error, effect). With a handler set the flush never raises; a raising handler propagates — we don't swallow it. Ractor-local, not a module ivar (IsolationError off the main Ractor) and not Thread.current (configuration set once, e.g. in an initializer, must be visible to threads spawned later). Per-Ractor fits the one-independent-world-per-Ractor model.



40
# File 'lib/hibiki/batch.rb', line 40

def error_handler = Ractor.current[:hibiki_error_handler]

.error_handler=(handler) ⇒ Object



42
43
44
# File 'lib/hibiki/batch.rb', line 42

def error_handler=(handler)
  Ractor.current[:hibiki_error_handler] = handler
end

.on_cleanup(&block) ⇒ Object

Solid's onCleanup: registers on the current OWNER, not the listener — a lazy derived computing mid-effect registers cleanups on the effect, matching how effects created there are adopted. The block runs before the owner's next rerun and on dispose. Outside any owner it can never run; mirror Solid and warn rather than raise.



47
48
49
50
51
52
53
# File 'lib/hibiki/tracking.rb', line 47

def on_cleanup(&block)
  owner = current_owner
  return warn("Hibiki.on_cleanup: no current owner (effect or root); the cleanup can never run") unless owner

  owner.add_cleanup(block)
  block
end

.own(owner) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/hibiki/tracking.rb', line 34

def own(owner)
  prev = Fiber[:hibiki_owner]
  Fiber[:hibiki_owner] = owner
  yield
ensure
  Fiber[:hibiki_owner] = prev
end

.rootObject

Hibiki.root { |root| ... } — build a graph inside an ownership scope; tear the whole thing down later with root.dispose. Unlike Solid (which returns the block's result and yields a dispose function) the root itself is yielded AND returned: in Ruby the caller, not the block, typically owns teardown.



44
# File 'lib/hibiki/root.rb', line 44

def root(&) = Root.new(&)

.schedule(effect) ⇒ Object



31
# File 'lib/hibiki/batch.rb', line 31

def schedule(effect) = (Thread.current[:hibiki_pending_effects] ||= Set.new) << effect

.track(observer) ⇒ Object



16
17
18
19
20
21
22
# File 'lib/hibiki/tracking.rb', line 16

def track(observer)
  prev = Fiber[:hibiki_observer]
  Fiber[:hibiki_observer] = observer
  yield
ensure
  Fiber[:hibiki_observer] = prev
end

.untrackObject

Solid's untrack: reads inside the block register nothing. Only the listener is suppressed — the owner slot is left alone, so effects created under untrack are still adopted.



27
# File 'lib/hibiki/tracking.rb', line 27

def untrack(&) = track(nil, &)