-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Don't re-initialize safe string #17250
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
Conversation
There are cases where either a customer calls html_safe or it is called internally on an already safe string. When that happens we should return self instead of a new SafeBuffer ``` require 'benchmark/ips' require 'erb' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/string/output_safety' string = "foo" safe = ActiveSupport::SafeBuffer.new(string ) safe_too = safe.dup safe.define_singleton_method(:html_safe) do ActiveSupport::SafeBuffer.new(self) end safe_too.define_singleton_method(:html_safe) do @html_safe = true self end Benchmark.ips do |x| x.report("safe") { safe.html_safe } x.report("safe_too") { safe_too.html_safe } end ``` Results ``` Calculating ------------------------------------- safe 45950 i/100ms safe_too 121646 i/100ms ------------------------------------------------- safe 664721.6 (±7.1%) i/s - 3308400 in 5.005413s safe_too 4751968.1 (±7.0%) i/s - 23720970 in 5.021281s ```
We had a similar pull request last week. I don't remember what we did with
|
It was this one https://github.com/rails/rails/pull/17199/files They tried moving up the definition of |
I wonder if this method should mutate the object it is called. When we call
|
That equality seems perfectly reasonable to me. |
Minimum viable patch defense: All the tests pass 😁 I'm with @sgrif, i took the method to be mutating an internal flag. My original thinking was that calling
However, I also agree that someone may have written code somewhere that depends on this behavior. These subtle changes can be the worst 😦 On the other hand |
@rafaelfranca was talking about #17206 -- which sounds like a much safer option than this. Having The actually-subtle issue -- sometimes returning the same object -- seems more open to interpretation. Like @sgrif, I don't find it objectionable per se... but the compatibility issue certainly looms. |
@matthewd there is precedence in conditional mutation, |
@egilburg |
@matthewd sorry good point, I mixed mutation with conditional dup |
@matthewd Yes. That one.
|
#17206 alternative is as fast for the case where your string is already safe, but still requires an initialization when not safe, in that case this method is much faster (even when not considering the additional require 'benchmark/ips'
require 'erb'
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/string/output_safety'
STRING = "foo"
SAFE = STRING.html_safe
SAFE_TOO = STRING.html_safe
SAFE_TOO.define_singleton_method(:html_safe) do
@html_safe = true
self
end
Benchmark.ips do |x|
x.report("super") { SAFE.html_safe }
x.report("return-self") { SAFE_TOO.html_safe }
end result
|
It is faster, but is it safer to include in a minor release? People may expect that calling |
I don't see how the sometimes new object behavior would be less confusing. I also don't see either PR as being a very backwards compatible change. Even if it's not a public method, it's used internally and contributors should have some reference instead of guessing behavior. Let's document it and call it out as explicitly not for public consumption. Maybe we can even document the correct way people should be using the API. |
|
(okay, not literally that, because |
A lot of guides on the web (e.g. one from 2010 by Yehuda Katz) recommend calling If we shouldn't call |
@egilburg However, if you want to call |
@schneems prior to yehuda's refactoring to use a string subclass, we used essentially this approach. There were two methods. Changing |
@NZKoz thanks for explanation. BTW The I18n gem supports magic keys with name |
@egilburg yep, but check out how The issue is not the function html_safe, but rather marking something as safe, rather than relying on the escaping code to do it for you, often leads to mistakes. |
@NZKoz thanks. If the goal is to support mutative html_safe while not breaking compatibility for those not explicitly using it, perhaps revert to having |
Not sure that buys you very much though as you can't implement |
So...my benchmarks are all over the place here (on codetriage.com). It seems like this patch helps, but it's not enough to be reproducible at a level i'm comfortable with. Based on this info, i'm going to close this issue. |
There are cases where either a customer calls html_safe or it is called internally on an already safe string. When that happens we should return self instead of a new SafeBuffer
Results