To clarify a bit more… this is how ECR works.
ECR is a macro that reads the ecr
files and converts it into Crystal code.
For example this ecr file:
hello world
it just this crystal code:
io << "hello world"
where the name io
is chosen by the macro (it’s actually __io__
)
If you have this:
hello <%= world %>
that’s converted to:
io << "hello " << world
If you have this:
hello
<% foo %>
world
it’s converted to:
io << "hello\n"
foo
io << "world"
And if you have this:
<% 3.times do %>
hello
<% end %>
it’s converted into this:
3.times do
io << "hello\n"
end
So my question is… if you have this:
<%= something do %>
hello world
<% end %>
what Crystal code it translates too?
I can’t understand because I don’t know what Rails does in this case.
I’m guessing something
calls the block and expects a string as its value? Then we could translate it to this code:
io << something {
String.build do |io|
io << "hello world"
end
}
And something
needs to be defined like this:
def something
"<tag>#{yield}</tag>"
end
However, that approach is building some intermediate strings. So maybe in Crystal the contract could be: an io
is passed as the first argument to the method (in addition to whatever arguments are passed to the method), and it’s the method’s responsibility to write to the io
. The method must also yield io
. Then this:
<%= something do %>
hello world
<% end %>
is translated into this:
something(io) do |io|
io << "hello world"
end
and something
is defined like this:
def something(io)
io << "<tag>"
yield io # here it's fine to yield the same io
io << "</tag>"
end
You could also have something like this:
<%= upcase do %>
hello world
<% end %>
where upcase
needs to be defined like this:
def upcase(io)
String.build do |sub_io|
yield sub_io
end.downcase(io)
end
Here a separate io
was yielded because we need an intermediate string so we can later downcase it (though we could have a streaming IO that downcases on the fly, if you really wanted that).
My problem with all of this is that it’s not very intuitive… as I said, I have no idea how it works in Ruby, what happens with the block given to that method and what it translates too. But this is something that we could definitely do, we just need to design it and implement it.