actionview

package module
v0.0.0-...-607871a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 15 Imported by: 0

README

go-ruby-actionview/actionview

actionview — go-ruby-actionview

License Go Coverage

A pure-Go (no cgo) reimplementation of the core of Rails' ActionView — the html-safe output buffer, the tag / url / form / text / number view helpers, and a rendering pipeline — faithful to MRI 4.0.5 / actionview 8.1 output, without any Ruby runtime.

It reproduces ActionView's observable markup byte-for-byte where it matters: tag_options attribute rendering, the form field name/id conventions (user[name] / user_name), currency and human number formatting, link_to and content_tag markup. It is intended as the ActionView backend for a future go-embedded-ruby binding, but is a standalone, reusable module.

It reuses go-ruby-activesupport (the inflector and core-ext string helpers) and go-ruby-erb (HTML / URL escaping), and is a sibling of go-ruby-set and the rest of the go-ruby-* stdlib ecosystem.

MRI-faithful. This mirrors ActionView's observable output, not its Ruby object model. SafeBuffer is the html-safe string; the helpers are Go functions (and Context methods for the request/CSRF-aware ones). Template evaluation (ERB / Ruby) is intentionally not here — it is an injectable seam, so this package stays free of any template engine.

The html-safe buffer

SafeBuffer is the foundation (ActiveSupport::SafeBuffer / ActionView::OutputBuffer): a string known to be HTML-safe, which auto-escapes non-safe fragments on concatenation.

var b actionview.SafeBuffer
b.Concat("<script>")           // untrusted -> escaped:   &lt;script&gt;
b.SafeConcat("<br>")           // trusted markup -> verbatim: <br>
b.AppendSafe(actionview.Raw("<b>")) // another safe buffer -> verbatim
// b.String() == "&lt;script&gt;<br><b>"

Every helper returns a SafeBuffer (or, for the formatters, a plain string), and escapes its inputs unless you pass a SafeBuffer / Raw, exactly like Rails' raw / html_safe.

Helpers

// TagHelper
actionview.ContentTag("div", "hi", actionview.Attrs{{"class", "a"}}) // <div class="a">hi</div>
actionview.Tag("br", nil, nil)                                       // <br>
actionview.ContentTag("div", "c", actionview.Attrs{{"data", map[string]any{"user_id": 5}}})
// <div data-user-id="5">c</div>
actionview.TokenList("a", "a", map[string]any{"b": true})            // a b
actionview.CDATASection("x]]>y")

// UrlHelper
actionview.LinkTo("Home", "/home", nil)                             // <a href="/home">Home</a>
actionview.MailTo("a@b.com", "Email", actionview.Attrs{{"subject", "Hi"}})
ctx := &actionview.Context{}
ctx.ButtonTo("Delete", "/posts/1", "delete", nil)

// FormTagHelper / FormHelper — exact name/id conventions
b := actionview.FormBuilderFor("user", map[string]any{"name": "Dave"})
b.TextField("name", nil)   // <input type="text" value="Dave" name="user[name]" id="user_name" />
b.Label("name", "", nil)   // <label for="user_name">Name</label>
b.CheckBox("admin", "", "", nil)

// TextHelper
actionview.Truncate("Once upon a time", 10, "...", " ", true)       // Once...
actionview.SimpleFormat("Hello\n\nWorld", nil, "", nil)
actionview.Pluralize(2, "person", "")                               // 2 people

// NumberHelper
actionview.NumberToCurrency(1234.5)                                 // $1,234.50
actionview.NumberToHuman(1234567)                                   // 1.23 Million
actionview.NumberToHumanSize(1234567)                               // 1.18 MB
actionview.NumberToPercentage(99.5)                                 // 99.500%

Rendering pipeline & the RenderTemplate seam

Context.Render owns the lookup, partial-name derivation and collection iteration (_counter / _iteration locals); the actual template evaluation is an injectable seam (Context.RenderTemplate), so you can plug in ERB, Ruby, or anything else:

ctx := &actionview.Context{
    RenderTemplate: func(id string, locals map[string]any) (string, error) {
        return myEngine.Render(id, locals) // ERB / go-ruby-erb / rbgo / ...
    },
}
out, _ := ctx.Render(actionview.RenderOptions{
    Partial:    "users/_user",
    Collection: []any{userA, userB},   // each element -> user, user_counter, user_iteration
    Spacer:     "<hr>",
})

Fidelity

Helper output is validated byte-for-byte against the real actionview gem (8.1 on MRI 4.0.5) in oracle_test.gocontent_tag, link_to, mail_to, the number formatters, truncate / simple_format / highlight / word_wrap, and the FormBuilder field name/id conventions all match exactly. CI installs the gem and runs the diff on the ubuntu/macOS lanes.

Two documented, deterministic divergences: attributes supplied via a Go map are emitted in sorted-key order (Go maps have no insertion order; MRI preserves it — use the ordered Attrs slice for exact control), and, in the sanitizer, golang.org/x/net/html normalises attribute order to sorted on HTML5 active-formatting elements (a, b, i, em, strong, code, …) while preserving source order everywhere else (div, p, span, img, …) — so a formatting element carrying two or more surviving attributes may reorder them. This never affects which attributes survive, only their order.

HTML sanitization

SanitizeHelper is implemented on top of the stdlib golang.org/x/net/html (pure Go, no cgo), reproducing the observable behaviour of Rails' rails-html-sanitizer / Loofah HTML5 vendor (Rails::HTML5::Sanitizer) — the natural match for x/net/html's HTML5 parser. The security-critical outcomes (XSS vectors neutralised, disallowed tags/attributes stripped, allowed markup preserved) are identical to ActionView's default sanitize helper.

// sanitize: SafeList/PermitScrubber allow-list scrubbing.
actionview.Sanitize(`<script>alert(1)</script>hi`, nil, nil)      // hi
actionview.Sanitize(`<a href="javascript:x">y</a>`, nil, nil)     // <a>y</a>
actionview.Sanitize(`<a href="/x" onclick="e">y</a>`, nil, nil)   // <a href="/x">y</a>
actionview.Sanitize(`<b>b</b> <i>i</i>`, []string{"b"}, nil)      // <b>b</b> i

// strip_tags / strip_links / sanitize_css.
actionview.StripTags(`<b>hi</b> <a href="/x">y</a>`)              // hi y
actionview.StripLinks(`<a href="/x">click</a> here`)              // click here
actionview.SanitizeCSS(`color: red; background: url(javascript:1)`) // color:red;

// The scrubber classes and configurable defaults.
s := actionview.NewSafeListSanitizer()   // + NewFullSanitizer / NewLinkSanitizer
s.Sanitize(html, tags, attributes)       // WhiteListSanitizer is the alias
actionview.SanitizedAllowedTags          // = DefaultAllowedTags (mutable policy)
actionview.SanitizedAllowedAttributes    // = DefaultAllowedAttributes

The sanitizer neutralises javascript: URIs (including entity-obfuscated colons), drops event handlers, demotes <script>/<style> bodies to inert text, removes comments, prunes foreign (svg/math) subtrees, and enforces the Rails default tag/attribute/protocol/CSS allow-lists. SanitizeSanitizer adapts it to the Sanitizer seam so SimpleFormat / Highlight can run with sanitize: true. Behaviour is validated byte-for-byte against both rails-html-sanitizer's HTML5 sanitizer and ActionView's SanitizeHelper (sanitize / strip_tags / strip_links / sanitize_css) in sanitizer_oracle_test.go.

Roadmap (deferred from v0.1)

This is the foundation. Deliberately deferred, in rough priority order:

  • Template resolver / LookupContext — formats, variants, locales, digests, the real partial/template file lookup (only the RenderTemplate seam exists now).
  • Layouts and content_for / yield / provide.
  • Fragment / Russian-doll caching.
  • AssetTagHelper (image/stylesheet/javascript tags, the asset pipeline).
  • FormOptionsHelper beyond the basics (grouped/collection selects, time zones).
  • DateHelper (distance_of_time_in_words, date/time selects).
  • TranslationHelper / i18n (t / l, locale-driven number & date formats).
  • Streaming (render stream:) and CSP nonce helpers.
  • Branding (family banner / logo / favicon) push.

Tests & coverage

$ GOWORK=off CGO_ENABLED=0 go test -race \
    -coverpkg=$(go list ./... | paste -sd, -) -coverprofile=cover.out ./...
$ go tool cover -func=cover.out | tail -1
total:   (statements)   100.0%

100% line coverage is enforced in CI, across the six supported 64-bit targets (amd64, arm64, riscv64, loong64, ppc64le, s390x — including big-endian). The MRI oracle tests skip themselves where ruby or the gem is unavailable, so the deterministic suite alone holds the gate.

License

BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-actionview/actionview authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package actionview is a pure-Go (no cgo), MRI-faithful reimplementation of the core of Rails' ActionView view layer: the html-safe output buffer, the tag / url / form / text / number view helpers, and a rendering pipeline whose template evaluation is an injectable seam.

It reproduces ActionView's observable output byte-for-byte where it matters — tag_options attribute rendering, form field name/id conventions (user[name] / user_name), currency and human number formatting, link_to and content_tag markup — without any Ruby runtime. It reuses go-ruby-activesupport (inflector, core-ext string helpers) and go-ruby-erb (HTML/URL escaping), and is intended as the ActionView backend for a future go-embedded-ruby binding.

The pure helpers (tag, text, number, link_to, mail_to) are package functions. Helpers that need request, routing or CSRF state — button_to, form_tag, form_with, current_page? and Render — are methods on Context, whose zero value behaves like a bare helper include with forgery protection disabled. Template evaluation (ERB / Ruby) is delegated to Context.RenderTemplate, keeping this package free of any template engine.

SanitizeHelper is implemented (Sanitize, SanitizeCSS, StripTags, StripLinks and the FullSanitizer / LinkSanitizer / SafeListSanitizer classes), mirroring rails-html-sanitizer / Loofah's HTML5 vendor on top of the stdlib golang.org/x/net/html.

See the README for the v0.1 surface and the roadmap of deferred pieces (the full template resolver with formats/variants/digests, layouts, fragment caching, AssetTagHelper, DateHelper, i18n TranslationHelper and streaming).

Index

Constants

View Source
const NoUncheckedValue = "\x00__actionview_no_unchecked__"

NoUncheckedValue is the sentinel uncheckedValue that tells CheckBox to omit the companion hidden field (ActionView's check_box(..., include_hidden: false)). It is a value no real form would use.

Variables

View Source
var (
	SanitizedAllowedTags       = append([]string(nil), DefaultAllowedTags...)
	SanitizedAllowedAttributes = append([]string(nil), DefaultAllowedAttributes...)
)

SanitizedAllowedTags and SanitizedAllowedAttributes are the configurable allow-lists the package-level Sanitize helper consults when its tags / attributes arguments are nil — ActionView's sanitized_allowed_tags / sanitized_allowed_attributes class attributes. Assign to them to change the default policy process-wide.

View Source
var DefaultAllowedAttributes = []string{
	"abbr", "alt", "cite", "class", "datetime", "height", "href", "lang", "name",
	"src", "title", "width", "xml:lang",
}

DefaultAllowedAttributes is Rails::HTML's SafeList DEFAULT_ALLOWED_ATTRIBUTES: the attributes the SafeListSanitizer keeps when no explicit attribute list is supplied. It is also the initial value of SanitizedAllowedAttributes.

View Source
var DefaultAllowedTags = []string{
	"a", "abbr", "acronym", "address", "b", "big", "blockquote", "br", "cite",
	"code", "dd", "del", "dfn", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4",
	"h5", "h6", "hr", "i", "img", "ins", "kbd", "li", "mark", "ol", "p", "pre",
	"samp", "small", "span", "strong", "sub", "sup", "time", "tt", "ul", "var",
}

DefaultAllowedTags is Rails::HTML's SafeList DEFAULT_ALLOWED_TAGS: the tags the SafeListSanitizer keeps when no explicit tag list is supplied. It is also the initial value of SanitizedAllowedTags.

View Source
var ErrNoRenderTemplate = errors.New("actionview: Context.RenderTemplate is nil")

ErrNoRenderTemplate is returned by Render when the context has no RenderTemplate seam wired up, so there is nothing to evaluate a template with.

Functions

func Excerpt

func Excerpt(text, phrase string, radius int, omission, separator string) (string, bool)

Excerpt returns the first occurrence of phrase in text with up to radius characters (or separator-delimited tokens) of surrounding context, prefixing and suffixing omission when the excerpt does not reach the text boundaries — ActionView's excerpt. It returns ("", false) when phrase is not found.

func HTMLEscape

func HTMLEscape(s string) string

HTMLEscape is ActionView's html_escape (ERB::Util.html_escape): it replaces the five HTML-significant characters with their entity references, escaping the apostrophe as "&#39;". It is re-exported from go-ruby-erb so callers can escape a raw string exactly as MRI does.

func IdentitySanitizer

func IdentitySanitizer(s string) string

IdentitySanitizer returns text unchanged. It is the default sanitizer, so the text helpers behave like their MRI counterparts invoked with sanitize: false.

func NumberToCurrency

func NumberToCurrency(v any, opts ...Option) string

NumberToCurrency formats a number as currency, mirroring ActionView's number_to_currency: precision 2, unit "$", format "%u%n", negative_format "-%u%n". A custom Format also updates the negative template unless NegativeFormat is given.

func NumberToDelimited

func NumberToDelimited(v any, opts ...Option) string

NumberToDelimited is the modern name for NumberWithDelimiter.

func NumberToHuman

func NumberToHuman(v any, opts ...Option) string

NumberToHuman formats a number with a decimal-magnitude word suffix (Thousand/Million/...), mirroring ActionView's number_to_human: the value is first rounded to 3 significant figures, then scaled to its unit and rounded again, with trailing zeros stripped.

func NumberToHumanSize

func NumberToHumanSize(v any, opts ...Option) string

NumberToHumanSize formats a byte count with a binary (1024) unit suffix, mirroring ActionView's number_to_human_size: 3 significant figures, trailing zeros stripped. Values below 1024 render as an integer count of Bytes/Byte.

func NumberToPercentage

func NumberToPercentage(v any, opts ...Option) string

NumberToPercentage formats a number as a percentage, mirroring ActionView's number_to_percentage: precision 3, no delimiter, format "%n%".

func NumberToRounded

func NumberToRounded(v any, opts ...Option) string

NumberToRounded is the modern name for NumberWithPrecision.

func NumberWithDelimiter

func NumberWithDelimiter(v any, opts ...Option) string

NumberWithDelimiter formats a number with grouped thousands and the given decimal separator, without rounding — ActionView's number_with_delimiter (a.k.a. number_to_delimited). Defaults: delimiter ",", separator ".".

func NumberWithPrecision

func NumberWithPrecision(v any, opts ...Option) string

NumberWithPrecision rounds to a fixed number of decimal places (or significant figures) and delimits the result — ActionView's number_with_precision / number_to_rounded. Defaults: precision 3, delimiter "", separator ".".

func Pluralize

func Pluralize(count any, singular, plural string) string

Pluralize renders "count word", using the singular form when count is 1 (or "1"/"1.0") and otherwise the supplied plural or the inflector's plural of singular — ActionView's pluralize. Pass an empty plural to defer to the inflector.

func SanitizeSanitizer

func SanitizeSanitizer(s string) string

SanitizeSanitizer adapts the SafeList sanitizer to the Sanitizer seam so it can be handed to SimpleFormat / Highlight as their sanitize argument, giving those helpers the sanitize: true behaviour instead of the identity default.

func TagOptions

func TagOptions(opts Attrs) string

TagOptions renders just the attribute string for opts (with a leading space per attribute), exposing ActionView's tag_options for callers assembling tags by hand.

func WordWrap

func WordWrap(text string, lineWidth int, breakSequence string) string

WordWrap wraps text to lines no longer than lineWidth characters, breaking on whitespace, joined by breakSequence — a faithful port of ActionView's word_wrap regexp. lineWidth defaults to 80 when non-positive; breakSequence defaults to "\n" when empty.

Types

type Attr

type Attr struct {
	Key string
	Val any
}

Attr is a single HTML attribute: a name and a value. Attributes are carried in an ordered Attrs slice (not a map) because ActionView emits attributes in a deterministic order and that order is part of the byte-for-byte output the helpers reproduce.

The value's dynamic type selects the rendering rule, matching MRI's tag_options: a bool toggles a boolean attribute, nil drops the attribute, a map[string]any under the "data" or "aria" key expands to prefixed sub-attrs, and a slice/map under "class" is token-joined.

type Attrs

type Attrs []Attr

Attrs is an ordered list of HTML attributes. Construct it with a slice literal, e.g. Attrs{{"class", "btn"}, {"id", "go"}}; the emission order is the slice order, mirroring Ruby hash insertion order.

func Opts

func Opts(m map[string]any) Attrs

Opts converts a Go map to an Attrs slice with keys in ascending order. It is a convenience for callers who have a map and do not care about attribute order; the sort makes the output deterministic. See the README fidelity note: MRI preserves hash insertion order, this package sorts map input.

type Choice

type Choice struct {
	Text  string
	Value string
}

Choice is a single <option>: its visible Text and submitted Value. A slice of Choice is what OptionsForSelect and the form builder's Select consume.

func ChoicesFromStrings

func ChoicesFromStrings(ss []string) []Choice

ChoicesFromStrings builds a Choice slice where each string is used as both the option text and its value, the shape ActionView's options_for_select produces from a flat array of strings.

type Context

type Context struct {
	// URLFor resolves a routing argument to a URL string (the routes seam). When
	// nil, a string argument passes through unchanged and any other value is
	// stringified, so simple string URLs work with no wiring.
	URLFor func(any) string

	// AuthenticityToken is the CSRF token value emitted in the hidden
	// authenticity_token field when ProtectAgainstForgery is true.
	AuthenticityToken string

	// ProtectAgainstForgery toggles emission of the CSRF hidden field in
	// form_tag / form_with / button_to. False (the default) matches a context
	// whose protect_against_forgery? returns false.
	ProtectAgainstForgery bool

	// SuppressUTF8Enforcer drops the hidden utf8 "✓" field that form_tag /
	// form_with emit by default. Leave false to match ActionView's default
	// enforce_utf8 behaviour.
	SuppressUTF8Enforcer bool

	// RequestMethod is the current request's HTTP method symbol (lower-case,
	// e.g. "get"), used by current_page? to decide whether the method matches.
	// Empty is treated as "get".
	RequestMethod string

	// RequestPath and RequestFullpath are the current request's path (without
	// query) and full path (with query), consulted by current_page?.
	RequestPath     string
	RequestFullpath string

	// RenderTemplate is the template-evaluation seam used by Render for
	// :template / :partial / :inline sources and by partial iteration. It
	// receives a resolved identifier and the locals to expose and returns the
	// rendered markup. When nil, Render returns an ErrNoRenderTemplate error.
	RenderTemplate func(identifier string, locals map[string]any) (string, error)
}

Context is a view context: the small bundle of request/routing/CSRF state and injectable seams that the stateful helpers (button_to, form_tag, form_with, current_page?, render) need. The pure helpers — tag, url, text and number formatting — are package functions and do not require a Context.

The zero value is usable and mirrors a bare ActionView helper include with forgery protection disabled: no CSRF hidden field is emitted and url_for is the identity on strings. Populate the fields to opt into CSRF tokens, routing, current-page detection and template rendering.

func (*Context) ButtonTo

func (c *Context) ButtonTo(name any, url string, method string, opts Attrs) SafeBuffer

ButtonTo renders a single-button form that submits to url, mirroring ActionView's button_to. method selects the HTTP verb: "get" produces a GET form, anything else a POST form with a hidden _method override (for verbs other than post). When the context enables forgery protection a hidden authenticity_token field is added. name is the button label (defaults to url).

func (*Context) CurrentPage

func (c *Context) CurrentPage(target any) bool

CurrentPage reports whether target refers to the request's current page, mirroring ActionView's current_page?. It resolves target through url_for and compares it to the request path (or full path when target carries a query), ignoring a single trailing slash. It requires RequestPath to be set; when the request method does not match RequestMethod it returns false.

func (*Context) FormTag

func (c *Context) FormTag(url, method string, opts Attrs, content SafeBuffer) SafeBuffer

FormTag renders a complete form around content, ActionView's form_tag with a block: the opening tag, hidden fields, the body, and the closing </form>.

func (*Context) FormTagOpen

func (c *Context) FormTagOpen(url, method string, opts Attrs) SafeBuffer

FormTagOpen renders a form's opening tag plus its hidden enforcer/method/CSRF fields, without a closing tag — ActionView's form_tag called without a block. url becomes the action; method selects the HTTP verb.

func (*Context) FormWith

func (c *Context) FormWith(objectName string, object map[string]any, url string, opts Attrs, persisted bool, fn func(*FormBuilder) SafeBuffer) SafeBuffer

FormWith renders a complete model-bound form, a pragmatic subset of ActionView's form_with / form_for: it opens a form at url, exposes a FormBuilder to fn to render the fields, and closes the form. An empty method defaults to "post" for a new record and "patch" for a persisted one. url is resolved through the context's url_for seam.

func (*Context) Render

func (c *Context) Render(opts RenderOptions) (SafeBuffer, error)

Render evaluates a template, partial, inline source, or partial collection and returns the html-safe result. It resolves identifiers and, for collections, iterates the elements while injecting the element, its zero-based _counter and its _iteration locals — then defers the actual template evaluation to the context's RenderTemplate seam. It returns ErrNoRenderTemplate when no seam is configured, and propagates any error the seam returns.

type FormBuilder

type FormBuilder struct {
	// ObjectName is the parameter prefix, e.g. "user" -> user[field].
	ObjectName string
	// Object holds the model's attribute values, keyed by field name.
	Object map[string]any
	// Persisted marks an existing record, switching the default submit label
	// from "Create <Model>" to "Update <Model>".
	Persisted bool
	// ModelName is the humanized model label used in the default submit value;
	// it defaults to the humanized ObjectName.
	ModelName string
}

FormBuilder binds form field helpers to a model: an object name (the parameter prefix, e.g. "user") and a map of attribute values. It generates the exact user[field] name and user_field id conventions ActionView's FormBuilder does.

func FormBuilderFor

func FormBuilderFor(objectName string, object map[string]any) *FormBuilder

FormBuilderFor returns a FormBuilder for the given object name and attribute map, seeding ModelName from the object name.

func (*FormBuilder) CheckBox

func (b *FormBuilder) CheckBox(method, checkedValue, uncheckedValue string, opts Attrs) SafeBuffer

CheckBox renders a model-bound checkbox pair, ActionView's FormBuilder#check_box: a hidden field carrying uncheckedValue followed by the checkbox carrying checkedValue, with checked set from the stored value. An checkedValue defaults to "1" and uncheckedValue to "0" when empty, matching ActionView's defaults; pass NoUncheckedValue as uncheckedValue to omit the hidden field entirely.

func (*FormBuilder) FieldID

func (b *FormBuilder) FieldID(method string) string

FieldID returns the HTML id for a field, e.g. user_email, derived from the field name via sanitize_to_id.

func (*FormBuilder) FieldName

func (b *FormBuilder) FieldName(method string) string

FieldName returns the HTML name for a field, e.g. user[email] — the convention Rails params parsing expects.

func (*FormBuilder) HiddenField

func (b *FormBuilder) HiddenField(method string, opts Attrs) SafeBuffer

HiddenField renders a model-bound <input type="hidden">, with the leading autocomplete="off" ActionView prepends to hidden fields.

func (*FormBuilder) Label

func (b *FormBuilder) Label(method, text string, opts Attrs) SafeBuffer

Label renders a model-bound <label>, defaulting the text to the humanized field name and pointing for at the field's id.

func (*FormBuilder) PasswordField

func (b *FormBuilder) PasswordField(method string, opts Attrs) SafeBuffer

PasswordField renders a model-bound <input type="password">. The stored value is not echoed back.

func (*FormBuilder) RadioButton

func (b *FormBuilder) RadioButton(method, tagValue string, opts Attrs) SafeBuffer

RadioButton renders a model-bound radio input, ActionView's FormBuilder#radio_button: checked when the stored value equals tagValue, with an id of field_id + "_" + value.

func (*FormBuilder) Select

func (b *FormBuilder) Select(method string, choices []Choice, opts Attrs) SafeBuffer

Select renders a model-bound <select>, ActionView's FormBuilder#select, marking the option matching the stored value as selected.

func (*FormBuilder) Submit

func (b *FormBuilder) Submit(value string, opts Attrs) SafeBuffer

Submit renders the form's submit button, ActionView's FormBuilder#submit. An empty value defaults to "Create <Model>" for a new record or "Update <Model>" for a persisted one.

func (*FormBuilder) TextArea

func (b *FormBuilder) TextArea(method string, opts Attrs) SafeBuffer

TextArea renders a model-bound <textarea>, ActionView's FormBuilder#text_area, with the stored value as its (escaped) content.

func (*FormBuilder) TextField

func (b *FormBuilder) TextField(method string, opts Attrs) SafeBuffer

TextField renders a model-bound <input type="text">, ActionView's FormBuilder#text_field: caller options first, then type and value, then the derived name and id (name/id always come last).

type FullSanitizer

type FullSanitizer struct{}

FullSanitizer is the pure-Go Rails::HTML5::FullSanitizer: it removes every tag and comment, keeping only the text — ActionView's strip_tags.

func NewFullSanitizer

func NewFullSanitizer() *FullSanitizer

NewFullSanitizer returns a FullSanitizer.

func (*FullSanitizer) Sanitize

func (s *FullSanitizer) Sanitize(input string) SafeBuffer

Sanitize strips all markup from html, leaving the text content. The result is html-safe.

type LinkSanitizer

type LinkSanitizer struct{}

LinkSanitizer is the pure-Go Rails::HTML5::LinkSanitizer: it removes <a> tags (and href attributes) while keeping their inner content — ActionView's strip_links.

func NewLinkSanitizer

func NewLinkSanitizer() *LinkSanitizer

NewLinkSanitizer returns a LinkSanitizer.

func (*LinkSanitizer) Sanitize

func (s *LinkSanitizer) Sanitize(input string) SafeBuffer

Sanitize removes anchor tags from html, keeping the link text. The result is html-safe.

type Option

type Option func(*numberOpts)

Option overrides a single number-formatting field, letting callers write e.g. NumberToCurrency(x, Precision(0), Unit("€")) without a positional option soup.

func Delimiter

func Delimiter(s string) Option

Delimiter sets the thousands delimiter (default "," for most helpers).

func Format

func Format(s string) Option

Format sets the positive template (with %n for number, %u for unit).

func NegativeFormat

func NegativeFormat(s string) Option

NegativeFormat sets the template used for negative currency values.

func Precision

func Precision(p int) Option

Precision sets the number of digits kept (significant or decimal, per helper).

func Separator

func Separator(s string) Option

Separator sets the decimal separator (default ".").

func Significant

func Significant(b bool) Option

Significant switches precision between decimal places (false) and significant figures (true).

func StripInsignificantZeros

func StripInsignificantZeros(b bool) Option

StripInsignificantZeros toggles trailing-zero removal after the separator.

func Unit

func Unit(s string) Option

Unit sets the currency/measurement unit string.

type PartialIteration

type PartialIteration struct {
	Index int
	Size  int
}

PartialIteration is the per-element iteration state ActionView exposes to a collection partial as the <name>_iteration local. Index is zero-based and Size is the collection length.

func (PartialIteration) First

func (p PartialIteration) First() bool

First reports whether this is the first element (index 0), like ActionView::PartialIteration#first?.

func (PartialIteration) Last

func (p PartialIteration) Last() bool

Last reports whether this is the last element, like ActionView::PartialIteration#last?.

type RenderOptions

type RenderOptions struct {
	// Inline is a template source rendered directly (render(inline: "...")).
	Inline string
	// Template is a full-template identifier (render(template: "...")).
	Template string
	// Partial is a partial identifier (render(partial: "...")).
	Partial string
	// Collection renders Partial once per element (render(collection: [...],
	// partial: "...")), exposing each element plus its _counter and _iteration.
	Collection []any
	// Locals are exposed to every rendered template.
	Locals map[string]any
	// As overrides the local name a collection element is bound to; it defaults
	// to the partial's base name.
	As string
	// Spacer is html-safe markup inserted between collection elements
	// (render(..., spacer_template:)).
	Spacer string
}

RenderOptions selects what to render, mirroring the keyword forms of ActionView's render. Exactly one of Inline, Template, Partial (optionally with Collection) should be set; Locals are passed to the evaluated template. This package owns the lookup, partial-name and collection-iteration logic; the actual template evaluation is delegated to Context.RenderTemplate.

type SafeBuffer

type SafeBuffer struct {
	// contains filtered or unexported fields
}

SafeBuffer is ActionView's html-safe string, the backing type of ActiveSupport::SafeBuffer / ActionView::OutputBuffer. A SafeBuffer carries a string that is known to be HTML-safe (already escaped or trusted markup).

The whole point of SafeBuffer is automatic escaping on concatenation: when a raw (non-safe) fragment is appended with Concat, it is html-escaped first, while an already-safe fragment is appended verbatim. This is what lets view helpers compose trusted markup with untrusted user data without double-escaping and without opening XSS holes.

The zero value is a usable, empty, safe buffer.

func ButtonTag

func ButtonTag(content any, opts Attrs) SafeBuffer

ButtonTag renders a <button type="submit">, ActionView's button_tag. content defaults to "Save changes" when nil.

func CDATASection

func CDATASection(content string) SafeBuffer

CDATASection wraps content in a CDATA section, escaping any embedded "]]>" by splitting it across two sections, exactly like ActionView's cdata_section.

func CheckBoxTag

func CheckBoxTag(name, value string, checked bool, opts Attrs) SafeBuffer

CheckBoxTag renders <input type="checkbox">, ActionView's check_box_tag. value defaults to "1" when empty; checked adds checked="checked".

func ClassNames

func ClassNames(args ...any) SafeBuffer

ClassNames is ActionView's alias for TokenList.

func ContentTag

func ContentTag(name string, content any, opts Attrs) SafeBuffer

ContentTag renders an HTML block tag surrounding content, mirroring ActionView's content_tag. It always emits a closing tag (even for names like br), html-escapes content unless it is a SafeBuffer, and renders opts via the tag_options rules. Pass a nil opts for no attributes.

func ContentTagRaw

func ContentTagRaw(name string, content any, opts Attrs) SafeBuffer

ContentTagRaw is ContentTag with escaping disabled (content_tag(..., escape: false)): content and attribute values are emitted verbatim. Use only with trusted input.

func HiddenFieldTag

func HiddenFieldTag(name string, value any, opts Attrs) SafeBuffer

HiddenFieldTag renders <input type="hidden">, ActionView's hidden_field_tag, including the trailing autocomplete="off" ActionView adds to hidden fields.

func Highlight

func Highlight(text string, phrases []string, highlighter string, sanitize Sanitizer) SafeBuffer

Highlight wraps every occurrence of any phrase in text with highlighter (default "<mark>\1</mark>", where \1 is the match), skipping text inside HTML tags — ActionView's highlight. Matching is case-insensitive. sanitize runs first (default IdentitySanitizer). The result is html-safe.

func LabelTag

func LabelTag(name, text string, opts Attrs) SafeBuffer

LabelTag renders a <label>, ActionView's label_tag. The for attribute is the sanitized name; the text defaults to the humanized name when empty.

func LinkTo

func LinkTo(content any, url string, opts Attrs) SafeBuffer

LinkTo renders an anchor tag, mirroring ActionView's link_to for a resolved string URL. The url becomes the href (appended after the given attributes). A "method" attribute (other than get) is converted to rel="nofollow" plus data-method, and a truthy "remote" attribute becomes data-remote="true", matching convert_options_to_data_attributes. content is html-escaped unless it is a SafeBuffer; pass nil content to use the url as the link text.

func MailTo

func MailTo(email string, content any, opts Attrs) SafeBuffer

MailTo renders a mailto: anchor, mirroring ActionView's mail_to. Recognised option keys (cc, bcc, body, subject, reply_to) become url-encoded query parameters in a fixed order; any other attributes are rendered on the anchor. content defaults to the email address when nil.

func NewSafeBuffer

func NewSafeBuffer() *SafeBuffer

NewSafeBuffer returns an empty SafeBuffer, the equivalent of ActionView::OutputBuffer.new. The zero value works too; this exists for readability at call sites that build up a buffer.

func OptionsForSelect

func OptionsForSelect(choices []Choice, selected string) SafeBuffer

OptionsForSelect renders a run of <option> tags, mirroring ActionView's options_for_select. The option whose value equals selected gets selected="selected" (emitted before the value attribute, as MRI does). Options are joined by newlines; text and value are html-escaped.

func PasswordFieldTag

func PasswordFieldTag(name string, value any, opts Attrs) SafeBuffer

PasswordFieldTag renders <input type="password">, ActionView's password_field_tag. A nil value is omitted (the password is not echoed back).

func RadioButtonTag

func RadioButtonTag(name, value string, checked bool, opts Attrs) SafeBuffer

RadioButtonTag renders <input type="radio">, ActionView's radio_button_tag. The id combines the sanitized name and value (name_value) so a group of radios gets distinct ids.

func Raw

func Raw(s string) SafeBuffer

Raw wraps an already-trusted markup string in a SafeBuffer without escaping it. It is the Go spelling of String#html_safe / ActionView's raw helper: use it only for markup you have produced or vetted yourself.

func Sanitize

func Sanitize(input string, tags, attributes []string) SafeBuffer

Sanitize scrubs html with the SafeList policy, keeping only allowed tags and attributes and neutralising unsafe URIs — ActionView's sanitize. Pass nil tags / attributes to use the configurable SanitizedAllowedTags / SanitizedAllowedAttributes defaults, or explicit lists to override them for this call. The result is html-safe.

func SanitizeCSS

func SanitizeCSS(style string) SafeBuffer

SanitizeCSS scrubs a CSS declaration list, dropping properties, keywords and functions that are not on the safe list — ActionView's sanitize_css. The result is html-safe.

func SelectTag

func SelectTag(name string, optionTags SafeBuffer, includeBlank bool, opts Attrs) SafeBuffer

SelectTag renders a <select> around the given option markup, ActionView's select_tag. optionTags must already be html-safe option elements. When includeBlank is true a leading blank option is prepended.

func SimpleFormat

func SimpleFormat(text string, htmlOpts Attrs, wrapperTag string, sanitize Sanitizer) SafeBuffer

SimpleFormat wraps text in paragraph tags, converting blank lines to paragraph breaks and single newlines to <br /> — ActionView's simple_format. It applies sanitize first (default IdentitySanitizer, i.e. no sanitization); pass a real Sanitizer to strip markup. htmlOpts are applied to each wrapper tag; wrapperTag defaults to "p" when empty.

func StripLinks(input string) SafeBuffer

StripLinks removes anchor tags from html, keeping the link text — ActionView's strip_links. The result is html-safe.

func StripTags

func StripTags(input string) SafeBuffer

StripTags removes every tag and comment from html, leaving only the text — ActionView's strip_tags. The result is html-safe.

func SubmitTag

func SubmitTag(value string, opts Attrs) SafeBuffer

SubmitTag renders <input type="submit">, ActionView's submit_tag. value defaults to "Save changes"; a data-disable-with attribute defaulting to the value is added to prevent double submission.

func Tag

func Tag(name string, content any, opts Attrs) SafeBuffer

Tag renders a tag through the ActionView tag builder (tag.NAME). A void element (br, input, ...) becomes <name attrs>; an SVG self-closing element becomes <name attrs />; anything else becomes <name attrs>content</name>. Pass content=nil for an empty non-void element.

func TagOpen

func TagOpen(name string, opts Attrs, open bool) SafeBuffer

TagOpen renders the legacy standalone tag helper (tag(name, options, open)): <name attrs /> by default, or <name attrs> when open is true. This XHTML-style " />" suffix is what the form-tag helpers build on, distinct from the HTML5 tag builder above.

func TextAreaTag

func TextAreaTag(name string, content any, opts Attrs) SafeBuffer

TextAreaTag renders a <textarea>, ActionView's text_area_tag. Content is html-escaped unless it is a SafeBuffer, and the tag carries the leading newline ActionView inserts before textarea content.

func TextFieldTag

func TextFieldTag(name string, value any, opts Attrs) SafeBuffer

TextFieldTag renders <input type="text">, mirroring ActionView's text_field_tag: name, id (sanitized from name) and value, then any extra attributes. A nil value is omitted.

func TokenList

func TokenList(args ...any) SafeBuffer

TokenList builds a deduplicated, space-joined token string from its arguments, implementing ActionView's token_list / class_names: hash keys with truthy values, nested arrays, and present scalars, with HTML entities decoded and whitespace-split before de-duplication.

func Truncate

func Truncate(text string, length int, omission, separator string, escape bool) SafeBuffer

Truncate shortens text to at most length characters, appending omission if it had to cut, and (when separator is non-empty) backing up to the last separator so words are not split — ActionView's truncate. The result is html-safe: it is html-escaped unless escape is false.

func (*SafeBuffer) AppendSafe

func (b *SafeBuffer) AppendSafe(other SafeBuffer) *SafeBuffer

AppendSafe appends another SafeBuffer's already-safe contents verbatim, the safe-vs-safe case of ActiveSupport::SafeBuffer#concat. It returns the receiver so calls chain.

func (*SafeBuffer) Concat

func (b *SafeBuffer) Concat(value string) *SafeBuffer

Concat appends value, html-escaping it first (ActiveSupport::SafeBuffer#<< / #concat with an unsafe argument). Use it for untrusted content: the raw characters are escaped exactly once. It returns the receiver so calls chain.

func (SafeBuffer) HTMLSafe

func (b SafeBuffer) HTMLSafe() bool

HTMLSafe reports that the value is html-safe. It always returns true for a SafeBuffer, mirroring ActiveSupport::SafeBuffer#html_safe? which is always true once a string has been marked safe.

func (SafeBuffer) Len

func (b SafeBuffer) Len() int

Len reports the byte length of the buffer, like String#length would on the underlying bytes. It is provided so callers can cheaply test for emptiness.

func (*SafeBuffer) SafeConcat

func (b *SafeBuffer) SafeConcat(value string) *SafeBuffer

SafeConcat appends value verbatim, without escaping (ActiveSupport::SafeBuffer#safe_concat). value MUST already be html-safe markup; passing untrusted data through SafeConcat is an XSS bug. It returns the receiver so calls chain.

func (SafeBuffer) String

func (b SafeBuffer) String() string

String returns the buffer's contents (ActiveSupport::SafeBuffer#to_s). The result is the raw markup with no further escaping.

type SafeListSanitizer

type SafeListSanitizer struct {
	AllowedTags       []string
	AllowedAttributes []string
}

SafeListSanitizer is the pure-Go Rails::HTML5::SafeListSanitizer: it keeps only the tags and attributes on its allow-lists, neutralises unsafe URIs and scrubs style declarations. The zero value is not usable; construct one with NewSafeListSanitizer.

func NewSafeListSanitizer

func NewSafeListSanitizer() *SafeListSanitizer

NewSafeListSanitizer returns a SafeListSanitizer seeded with the Rails default allow-lists. Set AllowedTags / AllowedAttributes to override them.

func (*SafeListSanitizer) Sanitize

func (s *SafeListSanitizer) Sanitize(input string, tags, attributes []string) SafeBuffer

Sanitize scrubs html against the sanitizer's allow-lists. tags and attributes override the instance allow-lists for this call when non-nil (ActionView's sanitize(html, tags:, attributes:)). The result is html-safe.

func (*SafeListSanitizer) SanitizeCSS

func (s *SafeListSanitizer) SanitizeCSS(style string) SafeBuffer

SanitizeCSS scrubs a CSS declaration list against the safe CSS property / keyword / function allow-lists — ActionView's sanitize_css. The result is html-safe.

type Sanitizer

type Sanitizer func(string) string

Sanitizer is the seam ActionView's simple_format / highlight use to strip unsafe markup before wrapping it. The default is IdentitySanitizer, which passes text through unchanged, equivalent to calling the helpers with sanitize: false; pass SanitizeSanitizer (the full Rails::HTML / Loofah-style SafeList sanitizer implemented in sanitizer.go) for sanitize: true behaviour.

type Stringer

type Stringer interface{ String() string }

Stringer mirrors fmt.Stringer; a value that implements it renders through its String method, letting callers pass custom types (e.g. model ids) that know their own Ruby-facing representation.

type WhiteListSanitizer

type WhiteListSanitizer = SafeListSanitizer

WhiteListSanitizer is the deprecated Rails alias for SafeListSanitizer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL