-
-
Save kurko/1805561 to your computer and use it in GitHub Desktop.
Crazy hack to intercept all changes to instance variables
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
module IVarInterceptor | |
def self.included(klass) | |
def klass.new(*args, &block) | |
super.extend IVarInterceptor | |
end | |
end | |
def self.extended(object) | |
object.singleton_class.class_eval do | |
original_methods = instance_methods.each_with_object({}) do |method_name, hash| | |
next if [ :instance_variable_get, :instance_variables, :object_id ].include?(method_name) | |
next if BasicObject.instance_methods.include?(method_name) | |
next if IVarInterceptor.instance_methods.include?(method_name) | |
hash[method_name] = instance_method(method_name) | |
undef_method method_name | |
end | |
define_method :method_missing do |method_name, *args, &block| | |
return_value, changed_ivars = capture_ivar_changes do | |
original_methods[method_name].bind(self).call(*args, &block) | |
end | |
if changed_ivars.any? | |
puts "The following instance variables changed as a result of calling ##{method_name}: " | |
changed_ivars.each do |ivar, change| | |
puts " - #{ivar} changed from #{change.fetch(:from, "(unset)").inspect} to #{change.fetch(:to, "(unset)").inspect}" | |
end | |
else | |
puts "No instance variables changed as a result of calling ##{method_name}." | |
end | |
return_value | |
end | |
end | |
end | |
def capture_ivar_changes | |
ivar_values_1 = current_ivar_values | |
return_value = yield | |
ivar_values_2 = current_ivar_values | |
changed_ivars = {} | |
(ivar_values_1.keys | ivar_values_2.keys).each do |ivar| | |
case | |
when ivar_values_1.has_key?(ivar) && !ivar_values_2.has_key?(ivar) | |
changed_ivars[ivar] = { :from => ivar_values_1[ivar] } | |
when ivar_values_2.has_key?(ivar) && !ivar_values_1.has_key?(ivar) | |
changed_ivars[ivar] = { :to => ivar_values_2[ivar] } | |
when ivar_values_1[ivar] != ivar_values_2[ivar] | |
changed_ivars[ivar] = { :from => ivar_values_1[ivar], :to => ivar_values_2[ivar] } | |
end | |
end | |
return return_value, changed_ivars | |
end | |
def current_ivar_values | |
instance_variables.each_with_object({}) do |ivar, values| | |
values[ivar] = instance_variable_get(ivar) | |
end | |
end | |
end | |
class Point | |
include IVarInterceptor | |
attr_accessor :x, :y | |
def product | |
@product ||= x * y | |
end | |
end | |
p = Point.new | |
p.x = 7 | |
p.y = 3 | |
puts "The product is: #{p.product}" | |
p.x = 2 | |
puts "The product is: #{p.product}" |
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
$ ruby intercept_ivars.rb | |
The following instance variables changed as a result of calling #x=: | |
- @x changed from "(unset)" to 7 | |
The following instance variables changed as a result of calling #y=: | |
- @y changed from "(unset)" to 3 | |
No instance variables changed as a result of calling #x. | |
No instance variables changed as a result of calling #y. | |
The following instance variables changed as a result of calling #product: | |
- @product changed from "(unset)" to 21 | |
The product is: 21 | |
The following instance variables changed as a result of calling #x=: | |
- @x changed from 7 to 2 | |
No instance variables changed as a result of calling #product. | |
The product is: 21 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment