Rails number_to_percentage и суб странное поведение

Я пытаюсь заменить большие числа% на более короткие версии (10000% -> 10k%). Вообще код работает, но если number_to_percentage использовать его перестать работать (с полностью одинаковой строкой).

Loading development environment (Rails 5.1.2)
2.3.1 :001 > "10000.000%".bytes
=> [49, 48, 48, 48, 48, 46, 48, 48, 48, 37]    
2.3.1 :002 > helper.number_to_percentage(10000).bytes
=> [49, 48, 48, 48, 48, 46, 48, 48, 48, 37] # totally same
2.3.1 :003 > helper.number_to_percentage(10000).sub(/(\d\d)\d\d\d(?:[.,]\d+)?\%$/){ "#{$1}k%" }
=> "k%"  # doesn't work
2.3.1 :004 > "10000.000%".sub(/(\d\d)\d\d\d(?:[.,]\d+)?\%$/){ "#{$1}k%" }
=> "10k%" # works

Что может вызвать это? Есть идеи?

2 ответа

Решение

Ключевое отличие:

"10000.000%".class #=> String
number_to_percentage(10000).class # => ActiveSupport::SafeBuffer

ActiveSupport::SafeBuffer это подкласс String и содержит понятие UNSAFE_STRING_METHODS (в том числе sub а также gsub). Эта концепция полезна для просмотра рельсов (где number_to_percentage обычно используется!), в отношении безопасности; предотвращение уязвимостей XSS.

Обходной путь должен был бы явно преобразовать переменную в String:

number_to_percentage(10000).to_str.sub(/(\d\d)\d\d\d(?:[.,]\d+)?\%$/){ "#{$1}k%" }
=> "10k%"

(Обратите внимание, что это to_str не to_s! to_s просто возвращается self т.е. экземпляр ActiveSupport::SafeBuffer; в то время как to_str возвращает регулярный String.)

This article, а также this rails issue углубиться в подробности по этому вопросу.

В качестве альтернативы, вы можете написать свой код следующим образом, и он будет работать как положено:

number_to_percentage(10000).sub(/(\d\d)\d\d\d(?:[.,]\d+)?%$/, '\1k%')
#=> "10k%"

Я бы на самом деле предпочел этот подход, так как вы больше не полагаетесь на модификацию (не-поточной) глобальной переменной.

Потому что number_to_percentage возвращает ActiveSupport::SafeBuffer, а не String.

helper.number_to_percentage(10000).class # => ActiveSupport::SafeBuffer

ActiveSupport::SafeBuffer (который является подклассом String) решает проблему небезопасных методов, таких как sub. Вот почему у вас могут быть некоторые сюрпризы.

Другие вопросы по тегам