Module: Hibiki::Rails::Helpers

Defined in:
lib/hibiki/rails/helpers.rb

Overview

Opt-in helpers that stamp the packaged client's attribute protocol (interpreted by the vendored hibiki.js Stimulus controller). Strictly opt-in, mirroring Hibiki::DSL: include it where you want the bare names — ApplicationHelper for ERB views, individual Phlex components — the gem never includes it for you.

div(**hibiki_island(CounterChannel, cid:)) do
button(**on(:increment)) { "+" }
button(**on(:toggle, with: { index: 3 })) { "toggle" }
input(name: "step", **on(:set_step, event: :change))
form(**on(:add, event: :submit)) { ... }
end

Both helpers return a { data: { ... } } hash: splat it into Phlex element methods or Rails tag helpers (tag.div(**hibiki_island(...))). When the element needs other attributes on the same data: key, merge the hashes yourself (Phlex's mix does this).

The emitted attribute names are a private contract between these helpers and the gem's JS — they version together; don't hand-write them in app code.

#reactive is the exception to the splat shape: it returns a complete placeholder element (<%= reactive :doubled, 0 %> in ERB). Phlex components splat instead: span(**reactive_attrs(:doubled)).

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.value_name(name) ⇒ Object

The shared name validator for both halves of a reactive value (the view-side data-hibiki-value placeholder and the channel's #transmit_value message).



44
45
46
47
48
49
50
51
# File 'lib/hibiki/rails/helpers.rb', line 44

def self.value_name(name)
  name = name.to_s
  unless VALUE_NAME.match?(name)
    raise ArgumentError,
          "reactive value name #{name.inspect} must match #{VALUE_NAME.inspect}"
  end
  name
end

Instance Method Details

#hibiki_island(channel, cid:) ⇒ Object

The island root: one channel subscription per island, identified by a per-page-load cid (each tab is its own graph). channel is the channel class or its name as a string.



56
57
58
59
60
# File 'lib/hibiki/rails/helpers.rb', line 56

def hibiki_island(channel, cid:)
  channel_name = channel.is_a?(Class) ? channel.name : channel.to_s
  { data: { controller: "hibiki", hibiki_channel_value: channel_name,
            hibiki_cid_value: cid } }
end

#on(action, event: :click, with: nil) ⇒ Object

Forward a DOM event on this element as a channel action. event: picks the DOM event (:click, :change, :submit); with: is a hash sent as the action's payload. The client adds event-derived data on top: a changed control contributes { name => value }, a submitted form contributes its FormData (and is reset after the perform).



67
68
69
70
71
# File 'lib/hibiki/rails/helpers.rb', line 67

def on(action, event: :click, with: nil)
  data = { hibiki_on: "#{event}->#{action}" }
  data[:hibiki_with] = JSON.generate(with) unless with.nil?
  { data: }
end

#reactive(name, placeholder = "", tag_name: :span) ⇒ Object

Placeholder for a single reactive value: <%= reactive :doubled, 0 %> paints <span data-hibiki-value="doubled">0</span>; the channel's transmit_value(:doubled) { ... } keeps it fresh. The same value may be placed any number of times, anywhere on the page — every placeholder updates (the client matches document-wide, so a value can render outside its island too). Names must be page-unique across channels. Only the placeholder text is server-rendered: each site keeps its own tag, classes, and attributes across updates.



81
82
83
84
85
86
87
88
89
90
# File 'lib/hibiki/rails/helpers.rb', line 81

def reactive(name, placeholder = "", tag_name: :span)
  tag_name = tag_name.to_s
  unless VALUE_TAG.match?(tag_name)
    raise ArgumentError,
          "reactive value tag #{tag_name.inspect} must match #{VALUE_TAG.inspect}"
  end
  html = %(<#{tag_name} data-hibiki-value="#{Helpers.value_name(name)}">) +
         %(#{CGI.escapeHTML(placeholder.to_s)}</#{tag_name}>)
  html.respond_to?(:html_safe) ? html.html_safe : html
end

#reactive_attrs(name) ⇒ Object

The value's attributes, for stamping the placeholder yourself — the Phlex form of #reactive: span(**reactive_attrs(:doubled)) { "0" }.



94
# File 'lib/hibiki/rails/helpers.rb', line 94

def reactive_attrs(name) = { data: { hibiki_value: Helpers.value_name(name) } }