DEV Community

RobL
RobL

Posted on

How do you test frozen_string_literal in Ruby?

Just posed the question on LinkedIn'

Potentially silly question, I am not ashamed of not knowing this... How do you mark a Haml file with the magic comment # frozen_string_literal: true. Is it even possible?

https://www.linkedin.com/feed/update/urn:li:activity:7140300572573724673/

Alek K came back with a bit of research. I'm a fan of poking something with a stick.

That's a quite interesting question, one I've never thought of myself before. As a response, I wrote a short article on my blog, describing a little experiment. I hope this can shed some light on the issue.

https://torrocus.com/blog/haml-with-frozen-string-literal-wtf/

His theory was Rubocop must know how to solve this. Sadly.

# frozen_string_literal: true
SOMETHING
Enter fullscreen mode Exit fullscreen mode

Is not valid Haml. Since it's trying to render a # which would be a div with no id. Simply won't work. I thought I'd take a quick stab at this.

Suppose we have. Silly example. But all I want to prove is that with frozen_string_literal that the object_id is the same for all instances of "A" since it's the same String.

# frozen_string_literal: true
puts "--#{__FILE__}"
puts "A".object_id
puts "A".object_id
puts "A".object_id
puts "A".object_id
Enter fullscreen mode Exit fullscreen mode

Here goes

% ruby frozen.rb
--/Users/roblacey/repos/personal/robl.me/frozen.rb
60
60
60
60
Enter fullscreen mode Exit fullscreen mode

Yep it's the same object_id everytime. So how would we even test this in Erb?

# frozen_string_literal: true

<% puts "--#{__FILE__}" %>
<% puts "A".object_id %>
<% puts "A".object_id %>
<% puts "A".object_id %>
<% puts "A".object_id %>
Enter fullscreen mode Exit fullscreen mode

I guess we do...

% irb
irb(main):001> require 'erb'
irb(main):002> ERB.new(File.read('frozen.erb')).result(binding)
--(erb)
7980
8000
8020
8040
=> "# frozen_string_literal: true\n\n\n\n\n\n"
Enter fullscreen mode Exit fullscreen mode

Nope none of these are the same object_id. There is some more magic going on. Indeed our Haml file is going to fail with what we saw before.

# frozen_string_literal: true

- puts "--#{__FILE__}"
- puts "A".object_id
- puts "A".object_id
- puts "A".object_id
- puts "A".object_id
Enter fullscreen mode Exit fullscreen mode

Yep fails.

require 'haml'
=> true
irb(main):005> (Haml::Template.new() { File.read('frozen.haml') }).render
(__TEMPLATE__):2:in `__tilt_18120': Illegal element: classes and ids must have values. (Haml::SyntaxError)
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/tilt-2.3.0/lib/tilt/template.rb:207:in `bind_call'
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/tilt-2.3.0/lib/tilt/template.rb:207:in `evaluate'
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/tilt-2.3.0/lib/tilt/template.rb:102:in `render'
    from (irb):5:in `<main>'
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.9.1/exe/irb:9:in `<top (required)>'
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/bin/irb:25:in `load'
    from /Users/roblacey/.asdf/installs/ruby/3.2.2/bin/irb:25:in `<main>'
Enter fullscreen mode Exit fullscreen mode

So honestly, I am not sure how this even works with ERB templates in Rails. I need to take a further dig into the Rails source to find out more.

Anyone, Bueller?

UPDATE: Cheers @castwide seems so obvious now. erb templates aren't Ruby they are just converted/eval'ed into Ruby. So this makes sense if the first line is the comment has the comment delimited.

<%# frozen_string_literal: true %>

<% puts "--#{__FILE__}" %>
<% puts "A".object_id %>
<% puts "A".object_id %>
<% puts "A".object_id %>
<% puts "A".object_id %>
Enter fullscreen mode Exit fullscreen mode
require 'erb'
=> true
irb(main):003> ERB.new(File.read('frozen.erb')).result(binding)
--(erb)
27600
27600
27600
27600
Enter fullscreen mode Exit fullscreen mode

Haml clearly doesn't work in the same way. As we can't just replicate it.

-# frozen_string_literal: true

- puts "--#{__FILE__}"
- puts "A".object_id
- puts "A".object_id
- puts "A".object_id
- puts "A".object_id
Enter fullscreen mode Exit fullscreen mode
require 'haml'
=> true
irb(main):003> (Haml::Template.new() { File.read('frozen.haml') }).render
--(__TEMPLATE__)
48000
48020
48040
48060
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
castwide profile image
Fred Snyder • Edited

In ERB, the comment needs the code tags.

<%# frozen_string_literal: true %>
<%= 'hi'.upcase! %>
Enter fullscreen mode Exit fullscreen mode

When rendered, it raises FrozenError:

(erb):3:in `upcase!': can't modify frozen String: "hi" (FrozenError)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
braindeaf profile image
RobL

Seems so obvious now. Cheers.