Permalink
Browse files

Extract spy methods from hijacker

  • Loading branch information...
1 parent 714a483 commit 477349cb51014be025eb65c773a6fa2e9f4b9c5b @txus committed Nov 29, 2010
Showing with 229 additions and 188 deletions.
  1. +5 −72 lib/hijacker.rb
  2. +75 −0 lib/hijacker/spy.rb
  3. +145 −0 spec/hijacker/spy_spec.rb
  4. +4 −116 spec/hijacker_spec.rb
View
@@ -2,68 +2,19 @@
require 'trollop'
require 'hijacker/exceptions'
require 'hijacker/method_definer'
+require 'hijacker/spy'
require 'hijacker/config'
require 'hijacker/handler'
module Hijacker
- # Methods that won't be hijacked in any case
- REJECTED_METHODS = (Object.instance_methods | Module.methods | %w{__original_[\w\d]+})
- FORBIDDEN_CLASSES = [Array, Hash, String, Fixnum, Float, Numeric, Symbol, Proc, Class, Object, Module]
-
- extend MethodDefiner # declares `define_hijacked`
- private :define_hijacked
-
class << self
- def spying(*args, &block)
- raise "No block given" unless block
- Hijacker.spy(*args)
- block.call
- Hijacker.restore(args.first)
- end
-
- def spy(object, options = {})
- raise "Cannot spy on the following forbidden classes: #{FORBIDDEN_CLASSES.map(&:to_s).join(', ')}" if FORBIDDEN_CLASSES.include?(object)
- rejection = /^(#{REJECTED_METHODS.join('|')})/
- only = options[:only]
- uri = options[:uri]
- custom_rejection = options[:reject] if options[:reject].is_a?(Regexp)
-
- inst_methods = guess_instance_methods_from(object).reject{|m| (m =~ rejection)}.reject{|m| m =~ custom_rejection}
- sing_methods = guess_class_methods_from(object).reject{|m| m =~ rejection}.reject{|m| m =~ custom_rejection}
-
- receiver = if object.is_a?(Class)
- object
- else
- (class << object; self; end)
- end
+ include MethodDefiner
+ private :define_hijacked
- define_hijacked(inst_methods, receiver, uri) unless options[:only] == :singleton_methods
- receiver = (class << object; self; end)
- define_hijacked(sing_methods, receiver, uri) unless options[:only] == :instance_methods
-
- end
-
- def restore(object)
- receiver = if object.is_a?(Class)
- object
- else
- (class << object; self; end)
- end
- guess_instance_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
- met = met.to_s.gsub!("__original_", "")
- receiver.send(:undef_method, :"#{met}")
- receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
- end
-
- receiver = (class << object; self; end)
- guess_class_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
- met = met.to_s.gsub!("__original_", "")
- receiver.send(:undef_method, :"#{met}")
- receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
- end
- end
+ include Spy
+ public :spy, :spying, :restore
def register(method, args, retval, raised, object, uri = nil)
args.map! do |arg|
@@ -80,24 +31,6 @@ def register(method, args, retval, raised, object, uri = nil)
server.handle method, args, retval, raised, object
end
- private
-
- def guess_instance_methods_from(object)
- if object.is_a?(Class)
- object.instance_methods
- else
- object.methods
- end
- end
-
- def guess_class_methods_from(object)
- if object.is_a?(Class)
- object.methods
- else
- []
- end
- end
-
end
end
View
@@ -0,0 +1,75 @@
+module Hijacker
+ module Spy
+
+ REJECTED_METHODS = (Object.instance_methods | Module.methods | %w{__original_[\w\d]+})
+ FORBIDDEN_CLASSES = [Array, Hash, String, Fixnum, Float, Numeric, Symbol, Proc, Class, Object, Module]
+
+ def spying(*args, &block)
+ raise "No block given" unless block
+ Hijacker.spy(*args)
+ block.call
+ Hijacker.restore(args.first)
+ end
+
+ def spy(object, options = {})
+ raise "Cannot spy on the following forbidden classes: #{FORBIDDEN_CLASSES.map(&:to_s).join(', ')}" if FORBIDDEN_CLASSES.include?(object)
+ rejection = /^(#{REJECTED_METHODS.join('|')})/
+ only = options[:only]
+ uri = options[:uri]
+ custom_rejection = options[:reject] if options[:reject].is_a?(Regexp)
+
+ inst_methods = guess_instance_methods_from(object).reject{|m| (m =~ rejection)}.reject{|m| m =~ custom_rejection}
+ sing_methods = guess_class_methods_from(object).reject{|m| m =~ rejection}.reject{|m| m =~ custom_rejection}
+
+ receiver = if object.is_a?(Class)
+ object
+ else
+ (class << object; self; end)
+ end
+
+ define_hijacked(inst_methods, receiver, uri) unless options[:only] == :singleton_methods
+ receiver = (class << object; self; end)
+ define_hijacked(sing_methods, receiver, uri) unless options[:only] == :instance_methods
+
+ end
+
+ def restore(object)
+ receiver = if object.is_a?(Class)
+ object
+ else
+ (class << object; self; end)
+ end
+ guess_instance_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
+ met = met.to_s.gsub!("__original_", "")
+ receiver.send(:undef_method, :"#{met}")
+ receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
+ end
+
+ receiver = (class << object; self; end)
+ guess_class_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
+ met = met.to_s.gsub!("__original_", "")
+ receiver.send(:undef_method, :"#{met}")
+ receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
+ end
+ end
+
+ private
+
+ def guess_instance_methods_from(object)
+ if object.is_a?(Class)
+ object.instance_methods
+ else
+ object.methods
+ end
+ end
+
+ def guess_class_methods_from(object)
+ if object.is_a?(Class)
+ object.methods
+ else
+ []
+ end
+ end
+
+ end
+end
@@ -0,0 +1,145 @@
+require 'spec_helper'
+
+class MyClass
+ def self.foo
+ 3 + 4
+ end
+ def self.bar(a,b)
+ b
+ end
+
+ def foo
+ 3 + 4
+ end
+ def bar(a,b)
+ b
+ end
+
+ def baz=(value)
+ @value = value
+ end
+
+ def nasty_method
+ raise StandardError, "Something went wrong"
+ end
+
+end unless defined?(MyClass)
+
+module Hijacker
+ describe Spy do
+
+ describe "#spying" do
+ it 'runs a block spying on a particular object' do
+ blk = lambda {
+ MyClass.foo
+ }
+ Hijacker.should_receive(:spy).with(MyClass).once.ordered
+ MyClass.should_receive(:foo).once.ordered
+ Hijacker.should_receive(:restore).with(MyClass).once.ordered
+
+ Hijacker.spying(MyClass, &blk)
+ end
+
+ it 'raises if no block given' do
+ expect {
+ Hijacker.spying(MyClass)
+ }.to raise_error("No block given")
+ end
+ end
+
+ describe "#spy - #restore" do
+
+ describe "hijacking a Class" do
+ describe "instance methods" do
+ before(:each) do
+ Hijacker.spy(MyClass, :only => :instance_methods)
+ end
+ it "registers method calls without arguments" do
+ Hijacker.should_receive(:register).with(:foo, [], 7, nil, kind_of(MyClass), nil).ordered
+ MyClass.new.foo.should == 7
+ end
+ it "registers method calls with arguments" do
+ Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", nil, kind_of(MyClass), nil).ordered
+ MyClass.new.bar(2, "string").should == "string"
+ end
+ after(:each) do
+ Hijacker.restore(MyClass)
+ end
+ end
+ describe "class methods" do
+ before(:each) do
+ Hijacker.spy(MyClass)
+ end
+ it "registers method calls without arguments" do
+ Hijacker.should_receive(:register).with(:foo, [], 7, nil, kind_of(Class), nil).ordered
+ MyClass.foo.should == 7
+ end
+ it "registers method calls with arguments" do
+ Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", nil, kind_of(Class), nil).ordered
+ MyClass.bar(2, "string").should == "string"
+ end
+ after(:each) do
+ Hijacker.restore(MyClass)
+ end
+ end
+ describe "forbidden classes (are not hijacked)" do
+ [Array, Hash, String, Fixnum, Float, Numeric, Symbol].each do |forbidden|
+ it "protects #{forbidden}" do
+ expect {
+ Hijacker.spy(forbidden)
+ }.to raise_error
+ end
+ end
+ end
+ end
+ describe "hijacking an object" do
+ describe "instance methods" do
+ let(:object) { MyClass.new }
+
+ before(:each) do
+ def object.my_method
+ 8
+ end
+ def object.my_method_with_args(a,b)
+ b
+ end
+ Hijacker.spy(object)
+ end
+ it "registers method calls without arguments" do
+ Hijacker.should_receive(:register).with(:foo, [], 7, nil, kind_of(MyClass), nil).ordered
+ Hijacker.should_receive(:register).with(:my_method, [], 8, nil, kind_of(MyClass), nil).ordered
+
+ object.foo.should == 7
+ object.my_method.should == 8
+ end
+ it "registers method calls with arguments" do
+ Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", nil, kind_of(MyClass), nil).ordered
+ Hijacker.should_receive(:register).with(:my_method_with_args, [2, "string"], "string", nil, kind_of(MyClass), nil).ordered
+
+ object.bar(2, "string").should == "string"
+ object.my_method_with_args(2, "string").should == "string"
+ end
+ it "works well with writers" do
+ Hijacker.should_receive(:register).with(:baz=, [2], 2, nil, kind_of(MyClass), nil).ordered
+ object.baz = 2
+ end
+ it "records exceptions" do
+ Hijacker.should_receive(:register).with(:nasty_method, [], nil, kind_of(StandardError), kind_of(MyClass), nil).ordered
+ expect {
+ object.nasty_method
+ }.to raise_error(StandardError)
+ end
+ it "does not affect other instances of the object's class" do
+ Hijacker.should_not_receive(:register)
+ MyClass.new.foo.should == 7
+ end
+ after(:each) do
+ Hijacker.restore(object)
+ end
+ end
+ end
+
+ end
+
+ end
+end
Oops, something went wrong.

0 comments on commit 477349c

Please sign in to comment.