camertron/rux

Looping and outputting HTML?

Closed this issue ยท 11 comments

I'm trying to do something like this, but running into syntax errors:

<div>
  {3.times.map do
    <p>Welcome to {"Rux" + " and"} Bridgetown!</p>
  end}
</div>

Ah shoot yeah, HTML tags in loops aren't currently supported... or rather, you have to specify the constantized name of a component in between the brackets. A problem I've run into when developing the library is determining when to interpret a particular point in the code as an HTML tag instead of "less than constant/identifier" (or even inheritance). For example,

class Foo < Bar
end

How should Rux know that < Bar is inheritance instead of the beginning of a tag? It's probably worth rethinking the detection logic here, because it's bitten me a number of times, and now is biting you.

For what it's worth, this is supported:

def call
  <FooComponent>
    <p>Foo</p>
    <p>Bar</p>
  </FooComponent>
end

In other words, you can put tags inside other tags.

Oh yeah, I can't even imagine the headaches involved in the parsing logic required for this. Just figured I'd mention something I suspect will be a pretty common example.

Yeah, I agree. Let me see what I can do.

mbj commented

@camertron Feel free to ping me when your PR is up. I'm happy to look at it and given the context of my Ruby tooling I'll likely be able to help a bit.

@mbj awesome thanks, will do!

@jaredcwhite hmm I just took a look at and discovered this actually should work. I don't get any syntax errors with this, for example:

require 'rux'

Rux.to_ruby(<<~RUBY)
  <div>
    {3.times.map do
      <p>Welcome to {"Rux" + " and"} Bridgetown!</p>
    end}
  </div>
RUBY

Is there any other context I'm missing, eg. any surrounding Ruby code?

This is the code I'm getting from the Rux parser:

Rux.tag("div") {
  Rux.create_buffer.tap { |_rux_buf_,|
    _rux_buf_ << " "
    _rux_buf_ << 3.times.map {
      Rux.tag("p") {
        Rux.create_buffer.tap { |_rux_buf_,|
          _rux_buf_ << "Welcome to "
          _rux_buf_ << "Rux".+(" and")
          _rux_buf_ << " Bridgetown!"
        }.to_s
      }
    }
    _rux_buf_ << " "
  }.to_s
}

but then when I try to eval it I get this error:

TypeError: no implicit conversion of Array into String
  /Users/jared/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/rux-1.0.0/lib/rux/buffer.rb:8:in `<<'
  index.rux:4:in `block (2 levels) in convert'
  index.rux:2:in `tap'
  index.rux:2:in `block in convert'
  /Users/jared/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/rux-1.0.0/lib/rux/default_tag_builder.rb:5:in `call'
  /Users/jared/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/rux-1.0.0/lib/rux.rb:59:in `tag'

Well, this is a little embarrassing. All rux's tests are focused on the transpiler producing the correct output, but none of them actually run the output ๐Ÿคฆ

The problem lies with the default buffer's << implementation, which isn't expecting to receive an array (i.e. the result of the map). Because rux-rails uses ActiveSupport::SafeBuffer, I never noticed. Anyone using rux outside a rails context is going to run into this though.

Alright, here we go. Should be fixed as of v1.0.1 ๐Ÿ˜„

mbj commented

but none of them actually run the output

Its likely mutant would have forced you to specify this semantics. still owe you a PR.

Works great, thanks!

I'll probably end up using AS's SafeBuffer anyway (since we adopted it for Bridgetown views), but nice to have the fix for the OOTB setup.