Kogui: generate HTML using Crystal

Hi! I just published a new shard called “Kogui” (https://es.wikipedia.org/wiki/Kogui). It is inspired in Clearwater.rb. It generates the HTML. I’m planning to implement the router system.

Example of use:

require "kogui"

class App
  include Kogui::Component

  def render
    html lang: "en" do
      [
        head do
          title { "Title" }
        end,

        body style: "width: 100vw" do
          [
            h1 id: "__title", class: "bold__text" do
              h1 style: {"font-size": "2rem"} do
                Array.new(5) do
                  h1 { Time.utc.to_s }
                end
              end
            end,
          ]
        end,
      ]
    end
  end
end

app = App.new
html = app.to_s # <html><head><titl...

Result (prettified):

<html lang="en">

<head>
    <title>Title</title>
</head>

<body style="width: 100vw">
    <h1 id="__title" class="bold__text">
        <h1 style="font-size:2rem">
            <h1>2020-04-10 18:18:21 UTC</h1>
            <h1>2020-04-10 18:18:21 UTC</h1>
            <h1>2020-04-10 18:18:21 UTC</h1>
            <h1>2020-04-10 18:18:21 UTC</h1>
            <h1>2020-04-10 18:18:21 UTC</h1>
        </h1>
    </h1>
</body>

</html>
3 Likes

May i suggest using blocks/named_arguments instead of arrays/methods? I think that would make the API a bit cleaner.

E.x.

class App
  include Kogui::Component

  def render
    html do
      head do
        title "Title"
      end

      body styles: {width: 100vw"} do
        h1 "Hello world!", id: "__title", classes: "bold-text", styles: {"font-size": "23px"}
      end
    end
  end
end

EDIT: To be clear html, head etc would still be methods, they would just use a block to know what goes inside of it, versus an array of items.

EDIT2: Some resources

1 Like

@krthr nice! We do something similar in Lucky https://luckyframework.org/guides/frontend/rendering-html

We’re planning to release a shard with just the HTML part taken out so other libs can use it. Maybe you’d be interested in using that or working together on making sure it fits your needs?

4 Likes

:o I didn’t know about this. Looks amazing! @paulcsmith

Thank you for the idea. I just updated the code ;)

@krthr watch out on the intermedita strings. to_s should be defined with to_s(io : IO) and the same should apply to the to_html.

io << obj will call obj.to_s(io)

Why, why, and why?

Looks like Crystal’s syntax devolved into Rust 2.0

@IEatReturnValues First off welcome to Crystal! Seems like you are new here so thanks for giving it a try!

I can understand what you mean, but I think the comment could be given more constructively. Not everyone prefers the same syntax, and it looks like he updated it to look a bit more Crystaly so maybe you like it now!

If you don’t like this you can always use https://github.com/jeromegn/kilt and one of the many templating languages available!

1 Like
  def render
    html lang: "en" do
      [
        head do
          title { "Title" }
        end,

        body style: "width: 100vw" do
          [
            h1 id: "__title", class: "bold__text" do
              h1 style: {"font-size": "2rem"} do
                Array.new(5) do
                  h1 { Time.utc.to_s }
                end
              end
            end,
          ]
        end,
      ]
    end

I’d rather write normal HTML at this point… Templating degrades front-end development and makes web development far more convoluted than it needs to be.

It literally transformed a simple body tag, with h1 children into something that gives flashbacks of callback hell, but with ends

This is the total opposite of “elegant” syntax. Which is one of the beauties of Crystal.

1 Like

Yeah, I get your point. I also prefer write HTML, VueJS specificaly.

But… I made this small project for practice and learn new stuff. It doesn’t mean it has to be 100% usefull for everybody.

I learnt more about blocks, splats, double splats and macros.

2 Likes

I think it is a cool lib @krthr!

@IEatReturnValues You are stating your opinion as fact. Some people like different things. It is fun to experiment too! See if you can comment a bit more positively and constructively. If someone sent your comment to your code how would you feel?

Second, there are advantages to approaches like this. You always have a correct end tag, you get auto formatting for free with crystal tool format. You can extract private methods to help make your templates more readable. You have access to all of Crystal so you can do some powerful stuff.

So before you judge something purely at the surface level, consider that there are downsides and upsides to every approach. This may not work for you, but it may work for others

1 Like

Eh, I’d say objectively, a regular body and H1 children in plain HTML are easier to write, and much cleaner than template code. Far less confusing as well.

But I mean, look where web development is nowadays. Just layer after layer of stuff being added on top of it.

1 Like

I agree to that. The concept of building HTML with a different language doesn’t appeal to me at all. I understand it can have some benefits, but essentially it’s just that you need the combined knowledge of two languages (HTML and Crystal) plus the builder API in order to work with that.
When you just write HTML with a simple template syntax, you’ll still need to learn HTML and the template syntax. But you don’t need to know Crystal to edit the HTML code.

Advertise: In case someone’s looking for a runtime template engine (works with HTML and whetever markup you like), take a look at crinja =)

1 Like

I’ve been a front-end dev for almost a decade, someone needs to press the restart button.

We’ve gone too far.

@IEatReturnValues there is no way that is objectively true. I’d say Lucky HTML is much easier to write in almost every way. Fewer characters in many cases, auto formatting with Crystal, automatic end tags in the right places, etc. But that is also “subjective” not everyone is the same. Some people just don’t like it, that’s fine. But it is NOT objective :joy:

For example:

body do
  h1 "This is my page"
  div do
    ul do
      li "my item", class: "list-item"
    end
  end

  h2 "some other headeing"
end

And in HTML

<body>
  <h1>This is my page"</h1>
  <div>
    <ul>
      <li class="list-item">my item</li>
    </ul>
  </div>
  <h2>some other heading</h2>
</body>

As you can see the structure is identical that was a main goal of Lucky. If you know HTML, you pretty much know Lucky HTML. Plus we have a converter so you can copy HTML from the web and get Crystal out of it.

There are also lots of other advantages to this approach, like creating classes where you can have helper methods to extract HTML, makes it super easy to test because you can pass in methods just like any other class, and type-safety. In Lucky we disallow accidentally printing nil or some other object to your code. As I said, there are definite advantages, so…

…let’s stop with the “objectively better/worse/whatever”. It is simply false in both directions. Let’s be pleasant to each other and not arrogant. We have more in common than we think

2 Likes

Woow 10 years! Impressive! I have 12 years so does that mean my opinion matters more?? :joy:

I agree front end dev has gotten complex, but the community does not need this kind of negativity. Please communicate more kindly and realize that not everyone has the same opinions or problems to solve as you. There is room for multiple solutions

2 Likes

@straight-shoota that seems like a totally fair opinion. Crinja looks dope! Does it work with Kilt?

1 Like

What if you want to add event listeners, attributes, etc? Let’s see the code now lol

There’s no integration currently. It could probably be made to work. But the interface Kilt offers is for compile time templates. So I’m not exactly sure how useful it would be for Crinja. It would probably lose a lot of versatility.

I wish you were actually asking and not trying to “catch me”, but it is incredibly simple.

You add event listeners in JS so that has nothing to do with this. If you are talking about Vue style attributes, then you do that like this: div "@click": "myMethod"

You are not contributing anything of value. Please stop commenting until you are ready to treat people like human beings that just might be as smart as you think you are

1 Like