ESI meets ERB, with some odd results

Hi --

I've got some ESI variables in a template, and am trying to assign their values to Ruby variables. Strangely, it seems to work and not work at the same time.

Here's the relevant part of the template, with some comments and followed by explanation:

  <esi:vars>   <% state = "$(GEO{'region_code'})" %>   <% city = "$(GEO{'city'})" %>

  State: <%= state %><br/> # CT   City: <%= city %><br/> # NEWHAVEN

  Massaged city: <%= CITIES[state][city] %> # Uses the literal EIS strings

  </esi:vars>

The CITIES hash provides corrected city names when the ESI value lacks spaces -- so, for example, CITIES["CT"]["NEWHAVEN"] is "New Haven". The state and city variables display correctly in those middle lines -- but when addressing the CITIES hash with those same variables, they're evaluated as the literal ESI strings ("$(GEO..."). The evidence for this is that I've seeded the hash:

  CITIES["$(GEO{'region_code'})"]["$(GEO{'city'})"] = "KEYING LITERAL ESI STRINGS"

and that "KEYING..." string appears next to "Massaged city".

So, strange as it sounds, the variables seem to be differently bound depending on which line is being evaluated. So far I've not been able to puzzle through the sequence of events in such a way as to come up with a way to inject the state and city values into a call to the CITIES hash. I'd be interested in any ideas or solutions people might have.

David

hi david!

David A. Black [2011-07-20 15:24]:

  <esi:vars>   <% state = "$(GEO{'region_code'})" %>   <% city = "$(GEO{'city'})" %>

  State: <%= state %><br/> # CT   City: <%= city %><br/> # NEWHAVEN

  Massaged city: <%= CITIES[state][city] %> # Uses the literal EIS strings

  </esi:vars>

well, as far as ruby/erb is concerned there is no ESI, right? it simply produces:

  <esi:vars>

  State: $(GEO{'region_code'})   City: $(GEO{'city'})

  Massaged city: whatever-the-value-of-CITIES["$(GEO{'region_code'})"]["$(GEO{'city'})"]-is

  </esi:vars>

the hash lookup occurs before ESI gets any chance to replace the variables. so i don't think there's any way to achieve what you want (other than going back to the server by some means after ESI processing has happened). i'm totally unfamiliar with ESI, though, so i might be off.

cheers jens

Hi –

David Black [2011-07-20 21:08]:

What's odd is that both behaviors seem to happen: the Ruby variables being bound to the literal GEO strings, and the Ruby variables being bound to the ESI interpolations of those strings.

no, it's not. the ruby variables are replaced with the string values you initialized them with - in *both* places. the ESI processing kicks in *after* that (it operates on what the application sends as HTML response; just like SSI in principle). so there's nothing unusual going on at all. it's just that both processors (ruby/erb and ESI) operate in the opposite order than you seem to have assumed.

but now that you found a different solution this is all moot, of course :wink:

Hi –

David Black [2011-07-20 21:08]:

What’s odd is that both behaviors seem to happen: the Ruby

variables being bound to the literal GEO strings, and the Ruby

variables being bound to the ESI interpolations of those strings.

no, it’s not. the ruby variables are replaced with the string values

you initialized them with - in both places. the ESI processing

kicks in after that (it operates on what the application sends as

HTML response; just like SSI in principle). so there’s nothing

unusual going on at all. it’s just that both processors (ruby/erb

and ESI) operate in the opposite order than you seem to have assumed

What I’m not seeing is why this:

CITIES[state][city]

doesn’t undergo the same evaluation/interpolation as this:

<%= state %>

Whatever happens, I would expect “state” to end up representing the same thing every time, instead of representing the string “CT” at one point and the string “$(GEO{‘region_code’})” at another point. Why doesn’t the EIS processing ensure that we get CITIES[“CT”][“NEWHAVEN”]?

but now that you found a different solution this is all moot, of

course :wink:

True :slight_smile: except I hate letting things slip away without fully understanding them.

David

Hi David,

I'll describe two scenarios and I hope I can clarify the problem.

First of all the Mapping: BAZ = {   'GEO_foo' => { 'GEO_bar' => 'GEO_baz' },   "$(GEO{foo})" => { "$(GEO{bar})" => 'KEYING LITERAL ESI STRINGS' } }

Second the default order of processing...

1. source: ------------------------------------------------------------------- <% foo = "$(GEO{'foo'})" %> <% bar = "$(GEO{'bar'})" %>

Foo: <%= foo %> Bar: <%= bar %>

Baz: <%= BAZ[foo][bar] %>

# ... up to this point nothing was evaluated. 2. erb'd: --------------------------------------------------------------------

Foo: $(GEO{'foo'}) Bar: $(GEO{'bar'})

Baz: KEYING LITERAL ESI STRINGS

# ... the time erb evaluates the five expressions the first two assign their # values, which still contain the ESI instructions. In the following # expressions these instructions are written to the document. # As for the last expression Ruby tries to resolve the key `$(GEO{'foo'})` in # the BAZ hash. # At this time the BAZ hash has no member with this name resulting in nil # unless otherwise defined. # In the first line you see the assignment I made to produce this - kinda # unwanted - result.

3. ESI: ----------------------------------------------------------------------

Foo: GEO_foo Bar: GEO_bar

Baz: KEYING LITERAL ESI STRINGS

# Assuming that GEO['foo'] contains the string GEO_foo and GEO['bar'] contains # GEO_bar the values are correctly replaced. # But since 'KEYING LITERAL ESI STRINGS' is not in any way an esi instruction # nothing happens here.

*snip* ***********************************************************************

So, what happens when we switch 2 and 3? First of all I think this could either suck performance wise or improve it. I don't know 'cause I'm not so much into socket communication. That aside:

3. ESI: ---------------------------------------------------------------------- <% foo = "GEO_foo" %> <% bar = "GEO_bar" %>

Foo: <%= foo %> Bar: <%= bar %>

Baz: <%= BAZ[foo][bar] %>

# ... the ESI instructions were replaced correctly as intended.

2. erb'd: --------------------------------------------------------------------

Foo: GEO_foo Bar: GEO_foo

Baz: GEO_baz

# ... well, I think this is the result you expected. Mapping is done # correctly, and Foo: & Bar: show the correct values.

*snip* ***********************************************************************

Switching step 2 and 3 could be a problem depending on your setup, but not impossible. But still I'd not mix two templating engines as I still think that it is possible to expose, if not already exposed, the GEO variables to your Rack::Request object. They should be members of the #env property. If not you could, if you use VCL and the GeoIP library, modify the plugin in a way it exposes them.

best Florian

Hi –