mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Improve human_attribute_name performance
This reduces allocations and improves performance by ~35% when a translation is defined and ~50% when a translation is not defined. Benchmark script: ```ruby require "benchmark/memory" require "benchmark/ips" class BaseModel extend ActiveModel::Translation end Person = Class.new(BaseModel) module A Person = Class.new(BaseModel) module B Person = Class.new(BaseModel) end end I18n.backend.store_translations "en", activemodel: { attributes: { person: { has_translation: "translated" } } } Benchmark.memory do |x| x.report("warmup") do Person.human_attribute_name("first_name") A::Person.human_attribute_name("first_name") A::B::Person.human_attribute_name("first_name") Person.human_attribute_name("has_translation") end x.report("no namespace") { Person.human_attribute_name("first_name") } x.report("1 namespace") { A::Person.human_attribute_name("first_name") } x.report("2 namespaces") { A::B::Person.human_attribute_name("first_name") } x.report("has translation") { Person.human_attribute_name("has_translation") } end Benchmark.ips do |x| x.report("no namespace") { Person.human_attribute_name("first_name") } x.report("1 namespace") { A::Person.human_attribute_name("first_name") } x.report("2 namespaces") { A::B::Person.human_attribute_name("first_name") } x.report("has translation") { Person.human_attribute_name("has_translation") } end ``` Before: ``` Calculating ------------------------------------- warmup 988.923k memsize ( 24.587k retained) 2.441k objects ( 339.000 retained) 50.000 strings ( 50.000 retained) no namespace 6.416k memsize ( 0.000 retained) 58.000 objects ( 0.000 retained) 18.000 strings ( 0.000 retained) 1 namespace 6.416k memsize ( 0.000 retained) 58.000 objects ( 0.000 retained) 18.000 strings ( 0.000 retained) 2 namespaces 6.416k memsize ( 0.000 retained) 58.000 objects ( 0.000 retained) 18.000 strings ( 0.000 retained) has translation 4.501k memsize ( 0.000 retained) 46.000 objects ( 0.000 retained) 18.000 strings ( 0.000 retained) Warming up -------------------------------------- no namespace 567.000 i/100ms 1 namespace 563.000 i/100ms 2 namespaces 565.000 i/100ms has translation 839.000 i/100ms Calculating ------------------------------------- no namespace 5.642k (± 0.9%) i/s - 28.350k in 5.025255s 1 namespace 5.652k (± 0.9%) i/s - 28.713k in 5.080325s 2 namespaces 5.662k (± 1.1%) i/s - 28.815k in 5.090226s has translation 8.391k (± 1.6%) i/s - 42.789k in 5.100484s ``` After: ``` Calculating ------------------------------------- warmup 982.803k memsize ( 24.587k retained) 2.385k objects ( 339.000 retained) 50.000 strings ( 50.000 retained) no namespace 4.712k memsize ( 0.000 retained) 44.000 objects ( 0.000 retained) 13.000 strings ( 0.000 retained) 1 namespace 4.712k memsize ( 0.000 retained) 44.000 objects ( 0.000 retained) 12.000 strings ( 0.000 retained) 2 namespaces 4.712k memsize ( 0.000 retained) 44.000 objects ( 0.000 retained) 12.000 strings ( 0.000 retained) has translation 3.493k memsize ( 0.000 retained) 32.000 objects ( 0.000 retained) 11.000 strings ( 0.000 retained) Warming up -------------------------------------- no namespace 850.000 i/100ms 1 namespace 846.000 i/100ms 2 namespaces 842.000 i/100ms has translation 1.127k i/100ms Calculating ------------------------------------- no namespace 8.389k (± 0.9%) i/s - 42.500k in 5.066296s 1 namespace 8.412k (± 0.6%) i/s - 42.300k in 5.028401s 2 namespaces 8.423k (± 0.6%) i/s - 42.942k in 5.098322s has translation 11.303k (± 1.1%) i/s - 57.477k in 5.085568s ```
This commit is contained in:
parent
abde74c118
commit
acbc39b663
1 changed files with 15 additions and 13 deletions
|
@ -35,6 +35,8 @@ module ActiveModel
|
|||
ancestors.select { |x| x.respond_to?(:model_name) }
|
||||
end
|
||||
|
||||
MISSING_TRANSLATION = Object.new # :nodoc:
|
||||
|
||||
# Transforms attribute names into a more human format, such as "First name"
|
||||
# instead of "first_name".
|
||||
#
|
||||
|
@ -42,29 +44,29 @@ module ActiveModel
|
|||
#
|
||||
# Specify +options+ with additional translating options.
|
||||
def human_attribute_name(attribute, options = {})
|
||||
options = { count: 1 }.merge!(options)
|
||||
parts = attribute.to_s.split(".")
|
||||
attribute = parts.pop
|
||||
namespace = parts.join("/") unless parts.empty?
|
||||
attributes_scope = "#{i18n_scope}.attributes"
|
||||
attribute = attribute.to_s
|
||||
|
||||
if attribute.include?(".")
|
||||
namespace, _, attribute = attribute.rpartition(".")
|
||||
namespace.tr!(".", "/")
|
||||
|
||||
if namespace
|
||||
defaults = lookup_ancestors.map do |klass|
|
||||
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
|
||||
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
|
||||
end
|
||||
defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
|
||||
defaults << :"#{i18n_scope}.attributes.#{namespace}.#{attribute}"
|
||||
else
|
||||
defaults = lookup_ancestors.map do |klass|
|
||||
:"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
|
||||
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
|
||||
end
|
||||
end
|
||||
|
||||
defaults << :"attributes.#{attribute}"
|
||||
defaults << options.delete(:default) if options[:default]
|
||||
defaults << attribute.humanize
|
||||
defaults << options[:default] if options[:default]
|
||||
defaults << MISSING_TRANSLATION
|
||||
|
||||
options[:default] = defaults
|
||||
I18n.translate(defaults.shift, **options)
|
||||
translation = I18n.translate(defaults.shift, count: 1, **options, default: defaults)
|
||||
translation = attribute.humanize if translation == MISSING_TRANSLATION
|
||||
translation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue