Skip to content

Commit

Permalink
Decrease string allocations in apply_inflections
Browse files Browse the repository at this point in the history
In `apply_inflections` a string is down cased and some whitespace stripped in the front (which allocate strings). This would normally be fine, however `uncountables` is a fairly small array (10 elements out of the box) and this method gets called a TON. Instead we can keep an array of valid regexes for each uncountable so we don't have to allocate new strings.

This change buys us 325,106 bytes of memory and 3,251 fewer objects per request.
  • Loading branch information
schneems committed Jul 30, 2015
1 parent f80aa59 commit 1bf50ba
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
37 changes: 34 additions & 3 deletions activesupport/lib/active_support/inflector/inflections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,45 @@ module Inflector
class Inflections
@__instance__ = ThreadSafe::Cache.new

class Uncountables < Array
def initialize
@regex_array = []
super
end

def delete(entry)
super entry
@regex_array.delete(to_regex(entry))
end

def <<(*word)
add(word)
end

def add(words)
self.concat(words.flatten.map(&:downcase))
@regex_array += self.map {|word| to_regex(word) }
self
end

def uncountable?(str)
@regex_array.detect {|regex| regex.match(str) }
end

private
def to_regex(string)
/\b#{::Regexp.escape(string)}\Z/i
end
end

def self.instance(locale = :en)
@__instance__[locale] ||= new
end

attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex

def initialize
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/
end

# Private, for the test suite.
Expand Down Expand Up @@ -160,7 +191,7 @@ def irregular(singular, plural)
# uncountable 'money', 'information'
# uncountable %w( money information rice )
def uncountable(*words)
@uncountables += words.flatten.map(&:downcase)
@uncountables.add(words)
end

# Specifies a humanized form of a string by a regular expression rule or
Expand All @@ -185,7 +216,7 @@ def human(rule, replacement)
def clear(scope = :all)
case scope
when :all
@plurals, @singulars, @uncountables, @humans = [], [], [], []
@plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
else
instance_variable_set "@#{scope}", []
end
Expand Down
4 changes: 2 additions & 2 deletions activesupport/lib/active_support/inflector/methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ def ordinalize(number)
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
def const_regexp(camel_cased_word) #:nodoc:
parts = camel_cased_word.split("::")
parts = camel_cased_word.split("::".freeze)

return Regexp.escape(camel_cased_word) if parts.blank?

Expand All @@ -372,7 +372,7 @@ def const_regexp(camel_cased_word) #:nodoc:
def apply_inflections(word, rules)
result = word.to_s.dup

if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
if word.empty? || inflections.uncountables.uncountable?(result)
result
else
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
Expand Down

0 comments on commit 1bf50ba

Please sign in to comment.