Skip to content

Commit af84b2e

Browse files
committed
Implement a method cache for faster dispatch
1 parent d8b2b94 commit af84b2e

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

lib/active_model/serializer/attributes.rb

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,46 @@
11
module ActiveModel
22
class Serializer
33
module Attributes
4+
# @api private
5+
module AttrNames
6+
def self.set_name_cache(name, value)
7+
const_name = "ATTR_#{name}"
8+
unless const_defined? const_name
9+
const_set const_name, value.duplicable? ? value.dup.freeze : value
10+
end
11+
end
12+
end
13+
14+
# @api private
15+
class Cache
16+
def initialize
17+
@module = Module.new
18+
@method_cache = ThreadSafe::Cache.new
19+
end
20+
21+
def [](name)
22+
@method_cache.compute_if_absent(name) do
23+
safe_name = name.to_s.unpack('h*').first
24+
temp_method = "__temp__#{safe_name}"
25+
ActiveModel::Serializer::Attributes::AttrNames.set_name_cache safe_name, name
26+
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
27+
@module.instance_method temp_method
28+
end
29+
end
30+
31+
private
32+
33+
def method_body(method_name, const_name)
34+
<<-EOMETHOD
35+
def #{method_name}(instance)
36+
name = ::ActiveModel::Serializer::Attributes::AttrNames::ATTR_#{const_name}
37+
instance.read_attribute_for_serialization(name)
38+
end
39+
EOMETHOD
40+
end
41+
end
42+
# @api private
43+
MethodCache = Cache.new
444
# @api private
545
class Attribute
646
delegate :call, to: :reader
@@ -24,7 +64,7 @@ def self.build(name, block)
2464
class AttributeReader < Attribute
2565
def initialize(name)
2666
super(name)
27-
@reader = ->(instance) { instance.read_attribute_for_serialization(name) }
67+
define_method(:call, MethodCache[name])
2868
end
2969
end
3070
# @api private

0 commit comments

Comments
 (0)