提交 9fa329b7 编写于 作者: J José Valim

Speed up attribute invocation by checking if both name and calls are compilable.

上级 2088f1b3
......@@ -57,7 +57,8 @@ class MissingAttributeError < NoMethodError
module AttributeMethods
extend ActiveSupport::Concern
COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
included do
class_attribute :attribute_method_matchers, :instance_writer => false
......@@ -112,7 +113,7 @@ def define_attr_method(name, value=nil, &block)
# If we can compile the method name, do it. Otherwise use define_method.
# This is an important *optimization*, please don't change it. define_method
# has slower dispatch and consumes more memory.
if name =~ COMPILABLE_REGEXP
if name =~ NAME_COMPILABLE_REGEXP
sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
RUBY
......@@ -240,18 +241,7 @@ def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
matcher_new = matcher.method_name(new_name).to_s
matcher_old = matcher.method_name(old_name).to_s
if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{matcher_new}(*args)
send(:#{matcher_old}, *args)
end
RUBY
else
define_method(matcher_new) do |*args|
send(matcher_old, *args)
end
end
define_optimized_call self, matcher_new, matcher_old
end
end
......@@ -293,17 +283,7 @@ def define_attribute_method(attr_name)
if respond_to?(generate_method)
send(generate_method, attr_name)
else
if method_name =~ COMPILABLE_REGEXP
defn = "def #{method_name}(*args)"
else
defn = "define_method(:'#{method_name}') do |*args|"
end
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
#{defn}
send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
end
RUBY
define_optimized_call generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
end
end
end
......@@ -342,11 +322,11 @@ def instance_method_already_implemented?(method_name)
# 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
def attribute_method_matchers_cache #:nodoc:
@attribute_method_matchers_cache ||= {}
end
def attribute_method_matcher(method_name)
def attribute_method_matcher(method_name) #:nodoc:
if attribute_method_matchers_cache.key?(method_name)
attribute_method_matchers_cache[method_name]
else
......@@ -359,6 +339,31 @@ def attribute_method_matcher(method_name)
end
end
# Define a method `name` in `mod` that dispatches to `send`
# using the given `extra` args. This fallbacks `define_method`
# and `send` if the given names cannot be compiled.
def define_optimized_call(mod, name, send, *extra) #:nodoc:
if name =~ NAME_COMPILABLE_REGEXP
defn = "def #{name}(*args)"
else
defn = "define_method(:'#{name}') do |*args|"
end
extra = (extra.map(&:inspect) << "*args").join(", ")
if send =~ CALL_COMPILABLE_REGEXP
target = "#{send}(#{extra})"
else
target = "send(:'#{send}', #{extra})"
end
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
#{defn}
#{target}
end
RUBY
end
class AttributeMethodMatcher
attr_reader :prefix, :suffix, :method_missing_target
......
......@@ -77,7 +77,7 @@ def define_read_method(method_name, attr_name, column)
#
# The second, slower, branch is necessary to support instances where the database
# returns columns with extra stuff in (like 'my_column(omg)').
if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
if method_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
def _#{method_name}
#{access_code}
......
......@@ -10,7 +10,7 @@ module Write
module ClassMethods
protected
def define_method_attribute=(attr_name)
if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
else
generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册