I am writing a helper DSL to make it easier to craft a nice menu ui within a view. The view's erb is producing an error undefined method 'safe_append=' for nil:NilClass
when I break the block across multiple erb tags but it works fine if I stick it in one tag. I want to understand why -- it should work across multiple tags and is a lot more natural.
This doesn't work:
<%= @menu.start do -%>
<%= menu_item some_path_in_routesrb,
title: "Dashboard",
details: "12 New Updates",
icon: "feather:home",
highlight: true
%>
<%= menu_item next_path,
title: "Magical stuff",
details: "unicorn registry",
icon: "fontawesome:rainbow",
highlight: true
%>
<% end -%>
But this works:
<%= @menu.start do
menu_item "#",
title: "Dashboard",
details: "12 New Updates",
icon: "fe:home",
first: true,
highlight: true
menu_item organizations_path,
title: "Organization",
details: "33k Updates",
icon: "fa:university"
end -%>
The aforementioned start
method for a menu looks like this
def start(&block)
if block_given?
self.instance_eval(&block)
else
raise "menu expected a block!"
end
rescue => e
@logger.ap e.message, :error
@logger.ap e.backtrace, :error
ensure
if @menu_items.size > 0
return content_tag(:div, content_tag(:ul, self.display, class: "menu-items"), class:"sidebar-menu")
else
return "There is nothing to render here. Place an item in the menu"
end
end
What am I missing?
I tried to find an example of what you are trying to do and found that the closest thing to it is the form_for
.
Then I tried to find why would your way not work.
After tracing the execution of code, it seems that the block is trying to render itself assuming it is inside the ActionView::Context
instance where it is going to find Context#output_buffer where it finds nil
and can't call safe_append
on it.
Now how to solve this problem.
You have to make sure that whatever you are trying to render in the view has all the context it needs to render itself which is what Rails does in the form_for
<%= @menu.start do |m| -%>
<% m.menu_item some_path_in_routesrb,
title: "Dashboard",
details: "12 New Updates",
icon: "feather:home",
highlight: true
%>
<% m.menu_item next_path,
title: "Magical stuff",
details: "unicorn registry",
icon: "fontawesome:rainbow",
highlight: true
%>
<% end -%>
And have this in the menu class
def start(&block)
if block_given?
yield self
else
raise "menu expected a block!"
end
rescue => e
@logger.ap e.message, :error
@logger.ap e.backtrace, :error
ensure
if @menu_items.size > 0
return content_tag(:div, content_tag(:ul, self.display, class: "menu-items"), class:"sidebar-menu")
else
return "There is nothing to render here. Place an item in the menu"
end
end
Now the idea of having eval_instance
can be done but won't be actually that clean IMHO as it means that you will try to mimic the same behaviour of ERB parsing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With