Reputation: 15985
I have a loop that renders a partial
1000.times do |i|
render partial: 'test', locals: {i: i}
end
this is really slow, up to 0.1 ms for foreach render call, even if the partial only prints out i
my_partial = render_to_method(partial: 'test')
1000.times do |i|
my_partial(locals: {i: i})
end
Now this should be doing the same thing way faster right? But I don't know how to do this.
update:
I've tried the to do it this way:
Haml::Engine.new(File.read(File.expand_path File.dirname(FILE)+"/../table/#{column.macro}/_#{column.display_type}.haml"))
.render(OpenStruct.new({column_value: column_value, object: object}))
two major drawbacks:
update 2:
Many of the answers try to solve the problem by using other techniques. I my real application this techniques can't be applied. https://github.com/antpaw/bhf/blob/master/app/views/bhf/pages/_platform.haml#L54 mainly because this line is a dynamic string that sometimes links to partials that don't even exsist in the gem and are defined in the main_app. I wonder why it's so hard to do something that is so basic: 1. grab the view. 2. render it. (3. render it again.)
update 3:
@Flash Gordon suggested this https://gist.github.com/antpaw/d6670c23d6f08e35812e#file-gistfile1-haml-L54
template = lookup_context.find_template "#{column.macro}/#{column.display_type}", ['bhf/table'], true
template.render(self, {column_value: column_value, object: object})
it almost works just having some troubles with locals
. But it already feels like the part where you grab the template and the render part is well separated.
Upvotes: 5
Views: 1441
Reputation: 1204
Here is an example from one of my previous answers. It's extracted from PartialRenderer
sources.
- local_names = [:i]
- partials = {}
- 1000.times do |i|
- name = 'name_%s' % (i % 10)
- partials[name] ||= lookup_context.find_template(name, lookup_context.prefixes, true, local_names)
= partials[name].render(self, i: i)
I'd recommend you to wrap it with a helper method. Keep in mind that locals' names appear here twice: first in local_names
as an array and second in hash's keys passed as the second argument of #render
method.
Upvotes: 2
Reputation: 742
If you need render many times and it is slow, I suggest you do not call render
many times. I know, I am captain obvious.
If locals
contains simple strings or numbers, try to render your view once, and then just replace parts with actual variables.
text = render(partial: 'test', locals: {i: '$'})
1000.times do |i|
output[i] = text.gsub('$', i)
end
Upvotes: 0
Reputation: 7301
I love this question, but it may be a "wrong tool for the job" thing
Basically:
Plan Bs:
gsub
PS have you tried out cells? https://github.com/apotonick/cells
Upvotes: 5
Reputation: 1204
Use :collection
option:
= render partial: 'test', collection: 10.times.to_a, as: :i
Docs: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
Upvotes: 0
Reputation: 246
You could add a helper method and use content_tag
. This accomplishes your goal of having a method work as a partial.
def my_partial(i)
content_tag(:div, i, class: 'iterator')
end
Using helper methods with content_tag
probably isn't a good solution if you have a lot of content in your partial, for readability's sake.
An alternative recommendation: Instead of rendering the same partial multiple times within a loop, you could render a single partial that includes a loop. If you needed to reuse the partial for only one object, you could just pass the object as an array.
Here's a more real-world example:
# index.html.erb
<%= render partial: 'dogs', locals: {dogs: @dogs} %>
# show.html.erb
<%= render partial: 'dogs', locals: {dogs: [@dog]} %>
# _dogs.html.erb
<% dogs.each do |dog| %>
<%= dog.name %>
<% end %>
Upvotes: 0
Reputation: 4551
This is an interesting proposition and it seems harder than I thought to find information about it. However from what I can tell from the implementation of the response
object I would think it should be possible to append (<<
) your output to the stream
object of the response
object. This will be rendered as a string into the response#body
as necessary.
The tricky part is to find a good place to define your my_partial
method. While it is not commonplace to do this in a View
it should still be possible and I think you should have access to the response
object directly. Otherwise you might define it in a helper, though you probably do not want to use this code in a controller.
Sorry if this is rather an idea than an answer, but I lack the time to test it properly.
Upvotes: 0