Fetching gem metadata from https://rubygems.org/ Fetching version metadata from https://rubygems.org/ Resolving dependencies... Using concurrent-ruby 1.0.2 Using i18n 0.7.0 Using minitest 5.9.0 Using thread_safe 0.3.5 Using benchmark-ips 2.6.1 Using bundler 1.12.3 Using tzinfo 1.2.2 Using activesupport 5.0.0.rc1 Warming up -------------------------------------- as::delegate 1.855k i/100ms op::delegate 2.080k i/100ms Calculating ------------------------------------- as::delegate 20.128k (± 5.6%) i/s - 102.025k in 5.082693s op::delegate 20.725k (± 9.6%) i/s - 104.000k in 5.073423s
-
-
Save casperisfine/a40137b7b3ce0413d23f16cd7d1d8a05 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
begin | |
require 'bundler/inline' | |
rescue LoadError => e | |
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' | |
raise e | |
end | |
gemfile(true) do | |
source 'https://rubygems.org' | |
gem 'activesupport', '5.0.0rc1' | |
gem 'benchmark-ips' | |
end | |
require 'benchmark/ips' | |
require 'active_support/all' | |
class Module | |
def optimized_delegate(*methods, to: nil, prefix: nil, allow_nil: nil) | |
unless to | |
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).' | |
end | |
if prefix == true && to =~ /^[^a-z_]/ | |
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.' | |
end | |
method_prefix = \ | |
if prefix | |
"#{prefix == true ? to : prefix}_" | |
else | |
'' | |
end | |
location = caller_locations(1, 1).first | |
file, line = location.path, location.lineno | |
to = to.to_s | |
to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to) | |
methods.each do |method| | |
# Attribute writer methods only accept one argument. Makes sure []= | |
# methods still accept two arguments. | |
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block' | |
# The following generated method calls the target exactly once, storing | |
# the returned value in a dummy variable. | |
# | |
# Reason is twofold: On one hand doing less calls is in general better. | |
# On the other hand it could be that the target has side-effects, | |
# whereas conceptually, from the user point of view, the delegator should | |
# be doing one call. | |
if allow_nil | |
method_def = [ | |
"def #{method_prefix}#{method}(#{definition})", | |
"_ = #{to}", | |
"if !_.nil? || nil.respond_to?(:#{method})", | |
" _.#{method}(#{definition})", | |
"end", | |
"end" | |
].join ';' | |
else | |
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") | |
method_def = [ | |
"def #{method_prefix}#{method}(#{definition})", | |
" _ = #{to}", | |
" _.#{method}(#{definition})", | |
"rescue NoMethodError => e", | |
" if _.nil? && e.name == :#{method}", | |
" #{exception}", | |
" else", | |
" raise", | |
" end", | |
"end" | |
].join ';' | |
end | |
module_eval(method_def, file, line) | |
end | |
end | |
end | |
def bench_delegate | |
Class.new do | |
delegate :foo, to: :@bar | |
end | |
end | |
def bench_optimized_delegate | |
Class.new do | |
optimized_delegate :foo, to: :@bar | |
end | |
end | |
Benchmark.ips do |x| | |
x.report('as::delegate') do | |
bench_delegate | |
end | |
x.report('op::delegate') do | |
bench_optimized_delegate | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment