Skip to content
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

Optimization of ActiveModel's match_attribute_method? #2075

Merged
merged 3 commits into from
Jul 18, 2011
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions activemodel/lib/active_model/attribute_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -314,13 +314,15 @@ def #{method_name}(*args)
end
end
end
attribute_method_matchers_cache.clear
end

# Removes all the previously dynamically defined methods from the class
def undefine_attribute_methods
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
end
attribute_method_matchers_cache.clear
end

# Returns true if the attribute methods defined have been generated.
Expand All @@ -338,6 +340,29 @@ def instance_method_already_implemented?(method_name)
end

private
# The methods +method_missing+ and +respond_to?+ of this module are
# invoked often in a typical rails, both of which invoke the method
# +match_attribute_method?+. The latter method iterates through an
# array doing regular expression matches, which results in a lot of
# object creations. Most of the times it returns a +nil+ match. As the
# match result is always the same given a +method_name+, this cache is
# used to alleviate the GC, which ultimately also speeds up the app
# significantly (in our case our test suite finishes 10% faster with
# this cache).
def attribute_method_matchers_cache
@attribute_method_matchers_cache ||= {}
end

def attribute_method_matcher(method_name)
if attribute_method_matchers_cache.key?(method_name)
attribute_method_matchers_cache[method_name]
else
match = nil
attribute_method_matchers.detect { |method| match = method.match(method_name) }
attribute_method_matchers_cache[method_name] = match
end
end

class AttributeMethodMatcher
attr_reader :prefix, :suffix, :method_missing_target

Expand Down Expand Up @@ -411,12 +436,8 @@ def attribute_method?(attr_name)
# Returns a struct representing the matching attribute method.
# The struct's attributes are prefix, base and suffix.
def match_attribute_method?(method_name)
self.class.attribute_method_matchers.each do |method|
if (match = method.match(method_name)) && attribute_method?(match.attr_name)
return match
end
end
nil
match = self.class.send(:attribute_method_matcher, method_name)
match && attribute_method?(match.attr_name) ? match : nil
end

# prevent method_missing from calling private methods with #send
Expand Down