Class: ActionView::Helpers::TagHelper::VueBuilder
- Inherits:
-
Object
- Object
- ActionView::Helpers::TagHelper::VueBuilder
- Defined in:
- lib/actionview/vue_tag_helper/vue_builder.rb
Overview
VueBuilder generates Vue component markup from a proxy-style builder
interface identical to the built-in tag helper:
It is intentionally not a subclass of TagBuilder. The two helpers have different semantics and will diverge further as Vue-specific features are added.
Tag names
Method names are dasherized before use as the HTML tag name
(+my_feed_item+ → my-feed-item). The resulting name must be a valid
kebab-cased custom-element name: all-lowercase, letters/digits only,
and at least one hyphen separating two non-empty segments. Anything
else — PascalCase, a plain lowercase word, underscores — raises
ArgumentError immediately, before attributes or content are evaluated.
This mirrors the HTML Living Standard requirement that custom element names contain at least one hyphen, which also guarantees they can never collide with current or future built-in HTML elements.
Closing tags
Every tag is emitted with an explicit closing tag. Vue component tags are never self-closing.
Attribute quoting
Standard TagBuilder always uses double-quote delimiters and escapes any
literal " inside a value as ". That is safe but verbose — a
JSON blob like {"key":"value"} becomes
data="{"key":"value"}".
VueBuilder switches to single-quote delimiters whenever the stringified
value contains a double-quote, so the same blob is emitted as
data='{"key":"value"}'. The delimiter decision is independent
of the escape: flag.
Escaping rules when single-quote delimiters are chosen:
& → &
< → <
> → >
' → ' (protects the delimiter)
" → (unchanged — the whole point)
Values already marked html_safe? are passed through as-is except that
a literal \' is escaped when single-quote delimiters are in use.
Typed attributes / v-bind shorthand
Vue components use typed props (+defineProps<{ count: number }>()+).
Passing a plain HTML string like count="42" triggers a Vue runtime
warning because the prop expects a Number, not a String.
VueBuilder avoids this by inspecting the Ruby value type:
String/Symbol— emitted as a plain attribute (no colon).true— emitted as a valueless attribute (+disabled+). Vue interprets attribute presence as a truthy boolean.- everything else (+Integer+,
Float,BigDecimal,false,Array,Hash, …) — the attribute name is prefixed with:(thev-bindshorthand) and the value is serialised with#to_json. The resulting JSON string is then subject to the normal single/double-quote quoting rules.
The class: key is exempt from the v-bind rule: an Array or Hash
value is always flattened into a space-separated CSS token list.
data: and aria: hashes
A Hash passed under the data: or aria: key is expanded into
individual prefixed attributes. Values are serialised as follows:
String,Symbol— passed through unchanged.BigDecimal— converted withto_s("F")(fixed-point notation, independent of any ActiveSupport monkey-patches).- everything else — serialised with
#to_json.
Defined Under Namespace
Classes: InvalidTagNameError
Instance Method Summary collapse
-
#initialize(view_context) ⇒ VueBuilder
constructor
A new instance of VueBuilder.
Constructor Details
#initialize(view_context) ⇒ VueBuilder
Returns a new instance of VueBuilder.
153 154 155 |
# File 'lib/actionview/vue_tag_helper/vue_builder.rb', line 153 def initialize(view_context) @view_context = view_context end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(called, *args, escape: true, **options) ⇒ Object (private)
163 164 165 166 167 168 169 170 |
# File 'lib/actionview/vue_tag_helper/vue_builder.rb', line 163 def method_missing(called, *args, escape: true, **, &) original = called.name name = original.underscore.dasherize raise InvalidTagNameError.new(original, name) unless KEBAB_TAG_RE.match?(name) content = build_inline_content(args, escape, &) "<#{name}#{(, escape)}>#{content}</#{name}>".html_safe # rubocop:disable Rails/OutputSafety end |