Refactoring #method_missing to define a previously missing method in the Decorator when calling it for the first time.
This commit is contained in:
parent
78b200a84f
commit
ebe30511b7
|
@ -157,7 +157,10 @@ module Draper
|
||||||
def method_missing(method, *args, &block)
|
def method_missing(method, *args, &block)
|
||||||
if allow?(method)
|
if allow?(method)
|
||||||
begin
|
begin
|
||||||
|
self.class.send :define_method, method do |*args, &block|
|
||||||
model.send(method, *args, &block)
|
model.send(method, *args, &block)
|
||||||
|
end
|
||||||
|
self.send(method, *args, &block)
|
||||||
rescue NoMethodError
|
rescue NoMethodError
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
module ActiveRecord
|
||||||
|
class Base
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,55 @@
|
||||||
|
require 'rubygems'
|
||||||
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
||||||
|
Bundler.require(:default) if defined?(Bundler)
|
||||||
|
|
||||||
|
require "benchmark"
|
||||||
|
require "draper"
|
||||||
|
require "./performance/models"
|
||||||
|
require "./performance/decorators"
|
||||||
|
|
||||||
|
Benchmark.bm do |bm|
|
||||||
|
puts "\n[ Exclusivelly using #method_missing for model delegation ]"
|
||||||
|
[ 1_000, 10_000, 100_000 ].each do |i|
|
||||||
|
puts "\n[ #{i} ]"
|
||||||
|
bm.report("#new ") do
|
||||||
|
i.times do |n|
|
||||||
|
ProductDecorator.decorate(Product.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bm.report("#hello_world ") do
|
||||||
|
i.times do |n|
|
||||||
|
ProductDecorator.decorate(Product.new).hello_world
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bm.report("#sample_class_method ") do
|
||||||
|
i.times do |n|
|
||||||
|
ProductDecorator.decorate(Product.new).class.sample_class_method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "\n[ Defining methods on method_missing first hit ]"
|
||||||
|
[ 1_000, 10_000, 100_000 ].each do |i|
|
||||||
|
puts "\n[ #{i} ]"
|
||||||
|
bm.report("#new ") do
|
||||||
|
i.times do |n|
|
||||||
|
FastProductDecorator.decorate(FastProduct.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bm.report("#hello_world ") do
|
||||||
|
i.times do |n|
|
||||||
|
FastProductDecorator.decorate(FastProduct.new).hello_world
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bm.report("#sample_class_method ") do
|
||||||
|
i.times do |n|
|
||||||
|
FastProductDecorator.decorate(FastProduct.new).class.sample_class_method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,47 @@
|
||||||
|
require "./performance/models"
|
||||||
|
class ProductDecorator < Draper::Base
|
||||||
|
decorates :product
|
||||||
|
|
||||||
|
def awesome_title
|
||||||
|
"Awesome Title"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Original #method_missing
|
||||||
|
def method_missing(method, *args, &block)
|
||||||
|
if allow?(method)
|
||||||
|
begin
|
||||||
|
model.send(method, *args, &block)
|
||||||
|
rescue NoMethodError
|
||||||
|
super
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class FastProductDecorator < Draper::Base
|
||||||
|
decorates :product
|
||||||
|
|
||||||
|
def awesome_title
|
||||||
|
"Awesome Title"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Modified #method_missing
|
||||||
|
def method_missing(method, *args, &block)
|
||||||
|
if allow?(method)
|
||||||
|
begin
|
||||||
|
self.class.send :define_method, method do |*args, &block|
|
||||||
|
model.send(method, *args, &block)
|
||||||
|
end
|
||||||
|
self.send(method, *args, &block)
|
||||||
|
rescue NoMethodError
|
||||||
|
super
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
require "./performance/active_record"
|
||||||
|
class Product < ActiveRecord::Base
|
||||||
|
def self.sample_class_method
|
||||||
|
"sample class method"
|
||||||
|
end
|
||||||
|
|
||||||
|
def hello_world
|
||||||
|
"Hello, World"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class FastProduct < ActiveRecord::Base
|
||||||
|
def self.sample_class_method
|
||||||
|
"sample class method"
|
||||||
|
end
|
||||||
|
|
||||||
|
def hello_world
|
||||||
|
"Hello, World"
|
||||||
|
end
|
||||||
|
end
|
|
@ -382,4 +382,21 @@ describe Draper::Base do
|
||||||
subject.kind_of?(source.class).should == true
|
subject.kind_of?(source.class).should == true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#method_missing" do
|
||||||
|
context "when #hello_world is called for the first time" do
|
||||||
|
it "hits method missing" do
|
||||||
|
subject.should_receive(:method_missing)
|
||||||
|
subject.hello_world
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when #hello_world is called again" do
|
||||||
|
before { subject.hello_world }
|
||||||
|
it "proxies method directly after first hit" do
|
||||||
|
subject.should_not_receive(:method_missing)
|
||||||
|
subject.hello_world
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue