-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inheritance #1
Inheritance #1
Conversation
The PdfHelper module is prepended to ActionController::Base, but the proper ruby prepend patter is not used. Instead of defining the method to be overridden in the module and prepending that module in the target class, the module has uses its self.prepended method to open up the target class (with class_eval), and defines the method right there on the target class (as well as aliasing the old render method). This monkeypatching is exactly the sort of thing prepend is supposed to help you get away from. The monkeypatching can cause serious issue if any other gems are prepending to ActionController::Base, since those gems will (hopefully) use the new prepend pattern and call super to delegate to the original implementation, but super now calls the version of render defined in wicked's class_eval. That render then calls render_without_wicked_pdf, which is now aliased to any render override that has been prepended before wicked, and you get an infinite loop.
un-assigning you adam because i want to draft the actual PR text and have you look at it. feel free to look at code now though |
just realized my change to .prepended likely broke the alias method chain in .included |
so I found a way simpler way to fix their monkeypatch for both prepend and include. Also added a description for the PR, and added an explanatory comment to the test. If the maintainers don't want to merge this in, i suppose we can try do the prepend the wrong way in our fortitude fork as well to avoid having to maintain 2 forks |
end | ||
|
||
def render_with_wicked_pdf(options = nil, *args, &block) | ||
if options.is_a?(Hash) && options.key?(:pdf) | ||
log_pdf_creation | ||
options[:basic_auth] = set_basic_auth(options) | ||
make_and_send_pdf(options.delete(:pdf), (WickedPdf.config || {}).merge(options)) | ||
else | ||
elsif respond_to?(:render_without_wicked_pdf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand. What determines whether this returns true
or false
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If PdfHelper is included, this method gets created by alias_method_chain. If its prepended, this method does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah jeez, I hadn't seen their railtie.rb. Makes sense now.
Let me test my Rails 5 branch with this version of wicked_pdf and our Fortitude... but this looks 👍 to me. I'm sure the wicked_pdf maintainers will be better suited to giving feedback. |
This works as expected in our Rails 5 branch. Gonna merge this, and 👍 on you submitting upstream. |
After upgrading to rails 5 we found that the way wicked_pdf prepends
ActionController::Base#render
causes an infinite loop when combined with another gem prepending render.I believe our issue is generally related to mileszs#111, though this PR focuses on prepend behavior.
The loop happens because wicked_pdf is not using the traditional prepend inheritance pattern, which looks like this
Instead, PdfHelper aliases the current render method away, then defines its own render method on the base class, which then calls the new render_with_wicked_pdf method.
If any other gem prepends its own render method using the inheritance pattern, before wicked_pdf prepends PdfHelper, this happens:
I've included a test that demonstrates this behavior. Its quite ugly as it has to replicate what railtie.rb does after another gem is prepended, which i couldn't figure out how to do since all of wicked is loaded before the tests run, so my solution was to just demonstrate it with a mock ActionController::Base class.
I'm new to test-unit so please let me know if these tests can be cleaned up.