Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sbsoftware/to_html.cr
The fastest HTML builder engine for Crystal
https://github.com/sbsoftware/to_html.cr
builder crystal dsl html speed
Last synced: 6 days ago
JSON representation
The fastest HTML builder engine for Crystal
- Host: GitHub
- URL: https://github.com/sbsoftware/to_html.cr
- Owner: sbsoftware
- License: mit
- Created: 2023-08-24T18:31:20.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-07-25T19:12:28.000Z (4 months ago)
- Last Synced: 2024-10-25T01:21:17.440Z (14 days ago)
- Topics: builder, crystal, dsl, html, speed
- Language: Crystal
- Homepage:
- Size: 59.6 KB
- Stars: 10
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-crystal - to_html - The fastest HTML builder engine for Crystal (HTML Builders)
README
# `to_html`
`to_html` is the fastest HTML builder library for Crystal you can get (see [Benchmark](#benchmark)).
![CI badge](https://github.com/sbsoftware/to_html.cr/actions/workflows/crystal.yml/badge.svg?event=push)
![Weekly CI badge](https://github.com/sbsoftware/to_html.cr/actions/workflows/weekly_crystal.yml/badge.svg)The main idea behind this project is to be able to output HTML without leaving the Crystal syntax, as most templates need to contain logic and thus most other templating engines implement their own way of expressing such logic. Why not just use the tools we already have?
Another important core concept is that what can be known at compile-time should not be calculated at runtime. That's why all of this functionality is provided via macros that do their best to prepare everything while compiling to be ready to *just render* when your program runs. Just like [ECR](https://crystal-lang.org/api/latest/ECR.html) (and almost as fast as it), only with a better interface ;-)
## Contents
- [Usage](#usage)
- [Examples](#examples)
- [Benchmark](#benchmark)
- [Contributing](#contributing)## Usage
*shard.yml*
```crystal
dependencies:
to_html:
github: sbsoftware/to_html.cr
```*somewhere in your code*
```crystal
require "to_html"
```*in any class/module*
```crystal
class MyView
# defines #to_html(io : IO) and #to_html
ToHtml.instance_template do
# use any HTML5 tag name as a method call with a block
html lang: "en" do
head do
title { "My View" }
end
body do
main do
p { "Tada!" }
# special case: `select` is a reserved keyword in crystal; need to use `select_tag` instead
select_tag do
option(value: "one") { "One" }
option(value: "two", selected: true) { "Two" }
end
end
end
end
end
end
```### Element Concatenation
Just write your tag name calls and as many string literals as you want below each other - they will all be concatenated. You can also use control logic like `if`s and `#each`.
```crystal
require "to_html"class MyLongView
getter show_author : Bool
getter random_lines : Array(String)def initialize(@show_author, @random_lines); end
ToHtml.instance_template do
div do
p do
"In the realm of code, so vast and wide,"
br
"An HTML div began its thrilling ride."
br
"With attributes unique, it came alive,"
br
"To journey through the web, it would strive."
endif show_author
div class: "author" do
i { "ChatGPT" }
end
endp do
random_lines.each do |random_line|
strong { random_line }
br
end
end
end
end
endputs MyLongView.new(true, ["Test", "Blah", "Foo"]).to_html
```### Global Macros
If you're sure you won't get any name clashes (or you don't care), you can `require "to_html/globals"` to get rid of the module name before calling the template macros.
```crystal
require "to_html/globals"class MyView
instance_template do
div do
strong { "More concise!" }
end
end
end
```## Examples
### Static Instance Template
```crystal
require "to_html"class StaticInstanceView
ToHtml.instance_template do
h1 { "Hello World!" }
p do
"This is a template."
br
"Treat it well."
end
end
endputs StaticInstanceView.new.to_html
```### Dynamic Instance Template
```crystal
require "to_html"class DynamicInstanceView
getter heading : Stringdef initialize(@heading); end
ToHtml.instance_template do
h1 { heading }
p do
"This is "
i { "another" }
" template."
end
end
endputs DynamicInstanceView.new("Template!").to_html
```### Class template
```crystal
require "to_html"class ClassView
ToHtml.class_template do
div do
p { "This needs no instance" }
end
end
endputs ClassView.to_html
```### Attributes
#### Named Arguments
You can add attributes to your tags via named arguments to the calls.
```crystal
require "to_html"class NamedArgumentsView
ToHtml.instance_template do
div class: "my-div" do
form action: "https://www.example.com", method: "POST" do
input type: "submit", name: "submit", value: "Submit!"
a href: "/index" do
"Cancel"
end
end
end
end
endputs NamedArgumentsView.new.to_html
```#### Object Interface
Another way to add attributes is via objects that implement `#to_html_attrs`. The easiest way to do so is via the `ToHtml.instance_tag_attrs`/`ToHtml.class_tag_attrs` macros.
This is still a bit experimental but the following example shows a few possible use cases, as well as the full potential of these macros.```crystal
require "to_html"class ObjectInterfaceView
ToHtml.instance_template do
div MyObject do
form MyResource do
input type: "submit", name: "submit", value: "Submit"
end
a MyResource do
MyResource.name
end
end
end
endclass MyObject
ToHtml.class_tag_attrs do
data_controller = "my-object"
end
endclass MyResource
def self.path
"/my_resource"
endToHtml.class_tag_attrs do
form do
action = path
end
a do
href = path
end
end
end#
puts ObjectInterfaceView.new.to_html
```### More
For more examples, the [specs](https://github.com/sbsoftware/to_html.cr/tree/main/spec) are quite expressive.
## Benchmark
Have a look into the `benchmark/` folder to find out how these numbers were determined. As this has only been done on one local machine, the absolute numbers are not meaningful. The ratios are the interesting part and the reason for this listing.
Execute `crystal run --release benchmark/benchmark.cr` to reproduce.
```
ecr 1.55M (645.91ns) (± 5.16%) 4.27kB/op fastest
to_html 902.82k ( 1.11µs) (± 8.18%) 5.52kB/op 1.71× slower
blueprint 170.78k ( 5.86µs) (± 8.85%) 9.8kB/op 9.07× slower
html_builder 62.44k ( 16.01µs) (± 3.41%) 10.4kB/op 24.79× slower
water 61.90k ( 16.16µs) (± 1.18%) 11.2kB/op 25.01× slower
markout 44.77k ( 22.34µs) (± 1.72%) 15.6kB/op 34.58× slower
```Compared shards taken from [awesome-crystal](https://github.com/veelenga/awesome-crystal#html-builders)
## Contributing
1. Fork this repository
2. Read the [Contribution Guidelines](CONTRIBUTING.md)
3. Create a branch with your desired changes
4. Create a pull request