Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Conflicts

  • Loading branch information...
commit 3d04e2fa53e3fe9808751880386e7e2c05eb5bdf 2 parents b3bfaa0 + 78335c7
@txus authored
View
11 Changelog
@@ -0,0 +1,11 @@
+=== Version 0.2.0 / 2010-12-05
+
+* enhancements
+ * report blocks passed to hijacked methods as regular arguments
+ * report raised exceptions in hijacked methods
+ * automatically load custom handler classes in these paths:
+ * ./.hijacker/**/*.rb
+ * ~/.hijacker/**/*.rb
+
+* bug fixes
+ * fixed many issues with 1.8.7
View
2  Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- hijacker (0.1.1)
+ hijacker (0.2.0)
trollop
GEM
View
31 Readme.md
@@ -16,7 +16,9 @@ creative! :)
(See the "Extending hijacker blabla" part below to know how to write your own
handlers)
-Hijacker is tested with Ruby 1.8.7, 1.9.2, JRuby 1.5.3 and Rubinius 1.1.
+Hijacker is tested with Ruby 1.8.7, 1.9.2, and JRuby 1.5.3. Unfortunately there
+are some issues with Rubinius, mostly due to the metaprogramming stuff, which I
+will definitely look into.
##Install and configure
@@ -44,6 +46,15 @@ So you type:
And it will output the URI for this super fancy hijacker logging server.
*Remember this URI* and pass it to your configuration block!
+If you have some custom handler, you should send me a pull request! In case you
+don't want to, Hijacker automatically requires all ruby files inside these
+paths:
+
+ ./.hijacker/**/**.rb
+ ~/.hijacker/**/**.rb
+
+So you put your handlers in there and have fun! :)
+
Some options you can pass to the server command:
hijacker <handler> --port 1234 (spawn the server in port 1234 rather than 8787)
@@ -122,13 +133,13 @@ Of course, you can specify a particular server uri for a block, with #spying:
It is really easy to write your own handlers. Why don't you write one and send
me a pull request? I mean now. What are you waiting for, why are you still reading?
-Ok, maybe a bit of explanation on that. Handlers live here:
-
- lib/hijacker/handlers/your_handler.rb
+Ok, maybe a bit of explanation on that. Handlers are automatically loaded from
+here:
-They are autoloaded and automatically registered, so all you have to do is
-write them like this:
+ ./.hijacker/**/*.rb
+ ~/.hijacker/**/*.rb
+They are automatically registered, so all you have to do is write them like this:
module Hijacker
class MyHandler < Handler # Yes, you have to subclass Hijacker::Handler!
@@ -154,10 +165,14 @@ write them like this:
#
# args [{:inspect => '3', :class => 'Fixnum'},
# {:inspect => '"string"', :class => 'String'}]
+ #
+ # retval {:inspect => ':bar', :class => 'Symbol'}
+ # (note: retval will be nil if the method raised)
#
- # retval [{:inspect => ':bar', :class => 'Symbol'}]
+ # raised {:inspect => 'oops', :class => 'StandardError'}
+ # (note: raised will be nil unless the method raised, obviously)
#
- # object [{:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}]
+ # object {:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}
#
def handle(method, args, retval, object)
# Do what you want with these!
View
2  bin/hijacker
@@ -58,7 +58,7 @@ puts welcome.join("#{ANSI[:RESET]} ") + "\n"
# We need the uri of the service to connect a client
instructions = "Put this code in the configuration of your ruby program #{ANSI[:BOLD]}before any call to Hijacker#{ANSI[:RESET]}:\n\n"
-instructions += "\t" + "Hijacker.config do\n"
+instructions += "\t" + "Hijacker.configure do\n"
instructions += "\t" + " uri '#{DRb.uri}'\n"
instructions += "\t" + "end\n\n"
puts instructions
View
107 lib/hijacker.rb
@@ -1,113 +1,34 @@
require 'drb'
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]+ [^\w\d]+})
- FORBIDDEN_CLASSES = [Array, Hash, String, Fixnum, Float, Numeric, Symbol, Proc, Class, Object, Module]
-
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
-
- inst_methods.each do |met|
- receiver.send(:alias_method, :"__original_#{met}", :"#{met}")
- receiver.send(:undef_method, :"#{met}")
- receiver.class_eval <<EOS
- def #{met}(*args, &blk)
- __original_#{met}(*args,&blk).tap do |retval|
- Hijacker.register :#{met}, args, retval, self, #{uri.inspect}
- end
- end
-EOS
- end unless options[:only] == :singleton_methods
+ include MethodDefiner
+ private :define_hijacked
- receiver = (class << object; self; end)
- sing_methods.each do |met|
- receiver.send(:alias_method, :"__original_#{met}", :"#{met}")
- receiver.send(:undef_method, :"#{met}")
- receiver.class_eval <<EOS
- def #{met}(*args, &blk)
- __original_#{met}(*args,&blk).tap do |retval|
- Hijacker.register :#{met}, args, retval, self, #{uri.inspect}
- end
- end
-EOS
- end unless options[:only] == :instance_methods
+ include Spy
+ public :spy, :spying, :restore
- 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
-
- def register(method, args, retval, object, uri = nil)
+ def register(method, args, retval, raised, object, uri = nil)
args.map! do |arg|
{:inspect => arg.inspect, :class => arg.class.name}
end
- retval = {:inspect => retval.inspect, :class => retval.class.name}
- object = {:inspect => object.inspect, :class => object.class.name}
-
- server = DRbObject.new nil, (uri || self.drb_uri)
- server.handle method, args, retval, object
- end
-
- private
-
- def guess_instance_methods_from(object)
- if object.is_a?(Class)
- object.instance_methods
+ if raised
+ raised = {:inspect => raised.message, :class => raised.class.name}
else
- object.methods
+ retval = {:inspect => retval.inspect, :class => retval.class.name}
end
- end
+ object = {:inspect => object.inspect, :class => object.class.name}
- def guess_class_methods_from(object)
- if object.is_a?(Class)
- object.methods
- else
- []
- end
+ server = DRbObject.new nil, (uri || self.drb_uri)
+ server.handle method, args, retval, raised, object
end
end
View
4 lib/hijacker/config.rb
@@ -11,8 +11,8 @@ def uri(drb)
def drb_uri
begin
@@drb_uri
- rescue
- raise "Neither a global nor a local Hijacker server URI is configured. Please refer to the README to find out how to do this."
+ rescue NameError
+ raise UndefinedUriError, "Neither a global nor a local Hijacker server URI is configured. Please refer to the README to find out how to do this."
end
end
end
View
3  lib/hijacker/exceptions.rb
@@ -0,0 +1,3 @@
+module Hijacker
+ class UndefinedUriError < StandardError; end;
+end
View
22 lib/hijacker/handler.rb
@@ -21,7 +21,7 @@ def initialize(opts)
@opts = opts
end
- def handle(method, args, retval, object)
+ def handle(method, args, retval, raised, object)
# Parameters received
#
# method :foo
@@ -29,9 +29,14 @@ def handle(method, args, retval, object)
# args [{:inspect => '3', :class => 'Fixnum'},
# {:inspect => '"string"', :class => 'String'}]
#
- # retval [{:inspect => ':bar', :class => 'Symbol'}]
+ # retval {:inspect => ':bar', :class => 'Symbol'}
+ #
+ # - In case the method raised something, retval will be nil,
+ # and the exception info will be available in raised:
#
- # object [{:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}]
+ # raised {:inspect => 'wrong number of arguments (0 for 2)', :class => 'ArgumentError'}
+ #
+ # object {:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}
#
raise NotImplementedError.new("You are supposed to subclass Handler")
end
@@ -52,7 +57,14 @@ def handlers
end
end
-# Automatically load all handlers
-Dir[File.dirname(File.join(File.dirname(__FILE__), 'handlers', '**', '*.rb'))].entries.each do |handler|
+# Automatically load all handlers in the following paths:
+#
+# ./.hijacker/**/*.rb
+# ~/.hijacker/**/*.rb
+# lib/handlers/**/*.rb
+#
+(Dir[File.dirname(File.join(Dir.pwd, '.hijacker', '**', '*.rb'))] + \
+Dir[File.dirname(File.expand_path(File.join('~', '.hijacker', '**', '*.rb')))] + \
+Dir[File.dirname(File.join(File.dirname(__FILE__), 'handlers', '**', '*.rb'))]).entries.each do |handler|
require(handler) && Hijacker::Handler.register_handler(handler)
end
View
17 lib/hijacker/handlers/logger.rb
@@ -18,7 +18,7 @@ def self.cli_options
:CYAN=>"\e[36m", :LCYAN=>"\e[1;36m",
:WHITE=>"\e[37m"}
- def handle(method, args, retval, object)
+ def handle(method, args, retval, raised, object)
out = []
out << ANSI[:BOLD] + ANSI[:UNDERLINE] + "#{Time.now}" unless opts[:without_timestamps]
out << ANSI[:CYAN] + object[:inspect]
@@ -32,10 +32,17 @@ def handle(method, args, retval, object)
ANSI[:RESET]
end.join(', ')
end
- out << "and returned"
- out << ANSI[:BLUE] + retval[:inspect]
- out << ANSI[:LBLUE] + "(#{retval[:class]})" unless opts[:without_classes]
- out << ANSI[:RESET] + "\n"
+ if raised
+ out << "and raised"
+ out << ANSI[:BLUE] + raised[:inspect]
+ out << ANSI[:LBLUE] + "(#{raised[:class]})" unless opts[:without_classes]
+ out << ANSI[:RESET] + "\n"
+ else
+ out << "and returned"
+ out << ANSI[:BLUE] + retval[:inspect]
+ out << ANSI[:LBLUE] + "(#{retval[:class]})" unless opts[:without_classes]
+ out << ANSI[:RESET] + "\n"
+ end
stdout.print out.join("#{ANSI[:RESET]} ")
end
View
27 lib/hijacker/method_definer.rb
@@ -0,0 +1,27 @@
+module Hijacker
+ module MethodDefiner
+
+ def define_hijacked(methods, receiver, uri)
+ methods.each do |met|
+ receiver.send(:alias_method, :"__original_#{met}", :"#{met}")
+ receiver.send(:undef_method, :"#{met}")
+ writer = (met =~ /=$/)
+ receiver.class_eval <<EOS
+ def #{met}(#{writer ? 'arg' : '*args, &blk'})
+ _args = #{writer ? '[arg]' : 'args'}
+ _args += [blk] if block_given?
+ begin
+ __original_#{met}(#{writer ? 'arg' : '*args, &blk'}).tap do |retval|
+ Hijacker.register :#{met}, _args, retval, nil, self, #{uri.inspect}
+ end
+ rescue=>error
+ Hijacker.register :#{met}, _args, nil, error, self, #{uri.inspect}
+ raise error
+ end
+ end
+EOS
+ end
+ end
+
+ end
+end
View
75 lib/hijacker/spy.rb
@@ -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.to_s =~ rejection}.reject{|m| m.to_s =~ custom_rejection}
+ sing_methods = guess_singleton_methods_from(object).reject{|m| m.to_s =~ rejection}.reject{|m| m.to_s =~ 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.to_s =~ /__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_singleton_methods_from(object).select{|m| m.to_s =~ /__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_singleton_methods_from(object)
+ if object.is_a?(Class)
+ object.methods
+ else
+ []
+ end
+ end
+
+ end
+end
View
2  lib/hijacker/version.rb
@@ -1,3 +1,3 @@
module Hijacker
- VERSION = "0.1.1"
+ VERSION = "0.2.0"
end
View
98 spec/acceptance/acceptance_spec.rb
@@ -0,0 +1,98 @@
+require 'spec_helper'
+
+# TODO: Turn into green cukes!
+describe Hijacker, "acceptance specs" do
+ 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
View
31 spec/hijacker/config_spec.rb
@@ -3,11 +3,34 @@
describe Hijacker, "configuration" do
describe "#configure" do
- it 'accepts a block with the \'uri\' configuration option' do
- Hijacker.configure do
- uri 'druby://localhost:8787'
+ it 'evaluates the passed block' do
+ block = Proc.new {}
+ Hijacker.should_receive(:instance_eval).with(&block).once
+ Hijacker.configure(&block)
+ end
+ end
+
+ describe "#uri" do
+ it 'assigns the DRb uri as a class variable' do
+ Hijacker.uri 'druby://localhost:8787'
+ Hijacker.send(:class_variable_get, :@@drb_uri).should == 'druby://localhost:8787'
+ end
+ end
+
+ describe "#drb_uri" do
+ context "when the class variable is set" do
+ it 'is an accessor to it' do
+ Hijacker.send(:class_variable_set, :@@drb_uri, 'druby://localhost:8787')
+ Hijacker.drb_uri.should == 'druby://localhost:8787'
+ end
+ end
+ context "otherwise" do
+ it 'raises an error' do
+ Hijacker.send(:remove_class_variable, :@@drb_uri)
+ expect {
+ Hijacker.drb_uri
+ }.to raise_error(Hijacker::UndefinedUriError)
end
- Hijacker.drb_uri.should == 'druby://localhost:8787'
end
end
View
33 spec/hijacker/handler_spec.rb
@@ -9,8 +9,13 @@ module Hijacker
subject.opts.should == {:my => :option}
end
- describe "#handle" do
+ it { should respond_to(:opts)}
+
+ it "includes DRb::DRbUndumpled" do
+ Handler.included_modules.should include(DRb::DRbUndumped)
+ end
+ describe "#handle" do
let(:args) do
[:bar,
[
@@ -18,6 +23,7 @@ module Hijacker
{:inspect => "\"string\"", :class => "String"},
],
{:inspect => "\"retval\"", :class => "String"},
+ nil,
{:inspect => "MyClass", :class => "Class"}]
end
@@ -31,12 +37,29 @@ module Hijacker
describe "class methods" do
describe "#register_handler" do
- it 'registers a loaded handler' do
- Hijacker::Handler.register_handler "/path/to/my/handlers/benchmark.rb"
- Hijacker::Handler.handlers.should include('benchmark')
+ context "when given a relative path" do
+ it 'registers that file as a handler' do
+ Hijacker::Handler.register_handler "/path/to/my/handlers/benchmark.rb"
+ Hijacker::Handler.handlers.should include('benchmark')
+ end
+ end
+ context "when given an absolute path" do
+ it 'registers that file as well' do
+ Hijacker::Handler.register_handler "~/.handlers/custom_handler.rb"
+ Hijacker::Handler.handlers.should include('custom_handler')
+ end
end
end
-
+
+ describe "#handlers" do
+ it 'is an accessor to the class variable' do
+ handlers = [double('handler'),
+ double('another handler')]
+ Handler.send(:class_variable_set, :@@handlers, handlers)
+ Handler.handlers.should == handlers
+ end
+ end
+
end
end
View
41 spec/hijacker/handlers/logger_spec.rb
@@ -27,6 +27,7 @@ module Hijacker
{:inspect => "\"string\"", :class => "String"},
],
{:inspect => "\"retval\"", :class => "String"},
+ nil,
{:inspect => "MyClass", :class => "Class"}]
end
@@ -84,6 +85,46 @@ module Hijacker
end
end
end
+ context "when given a raised exception" do
+
+ let(:args) do
+ [:bar,
+ [
+ {:inspect => "2", :class => "Fixnum"},
+ {:inspect => "\"string\"", :class => "String"},
+ ],
+ nil,
+ {:inspect => "wrong number of arguments (0 for 2)", :class => "ArgumentError"},
+ {:inspect => "MyClass", :class => "Class"}]
+ end
+
+ it "prints it instead of the return value" do
+ out = StringIO.new
+ subject.stub(:stdout).and_return out
+
+ Time.stub(:now).and_return Time.parse('2010-11-20')
+
+ subject.handle(*args)
+
+ ["00:00:00 +0100",
+ "MyClass",
+ "(Class)",
+ "received",
+ ":bar",
+ "with",
+ "2",
+ "(Fixnum)",
+ "\"string\"",
+ "(String)",
+ "and raised",
+ "wrong number of arguments (0 for 2)",
+ "(ArgumentError)"].each do |str|
+ out.string.should include(str)
+ end
+ end
+
+ end
+
end
end
View
113 spec/hijacker/method_definer_spec.rb
@@ -0,0 +1,113 @@
+require 'spec_helper'
+
+module Hijacker
+ describe MethodDefiner do
+
+ describe "#define_hijacked" do
+ let(:receiver) do
+ class SomeClass
+ def foo
+ 7
+ end
+ def bar(a, b)
+ a + b
+ end
+ def baz=(value)
+ @baz = value
+ end
+ def method_with_block(argument)
+ yield if block_given?
+ end
+ def method_that_raises
+ raise StandardError, "Something went wrong"
+ end
+ end
+ SomeClass
+ end
+
+ before(:each) do
+ Hijacker.stub(:register)
+ end
+
+ after(:each) do
+ Hijacker.restore(receiver)
+ end
+
+ it 'saves original methods on the receiver' do
+ Hijacker.send(:define_hijacked, [:foo, :bar], receiver, nil)
+ instance = receiver.new
+ instance.should respond_to(:__original_foo, :__original_bar)
+ instance.should_not respond_to(:__original_baz)
+ end
+
+ it 'creates aliased methods' do
+ Hijacker.send(:define_hijacked, [:foo], receiver, nil)
+ instance = receiver.new
+
+ instance.should_receive(:__original_foo).once
+ instance.foo
+ instance.should_not_receive(:__original_bar)
+ instance.bar(9,10)
+ end
+
+ describe "registering method calls" do
+ context "with no arguments" do
+ it "registers the method call" do
+ Hijacker.send(:define_hijacked, [:foo], receiver, nil)
+ instance = receiver.new
+ Hijacker.should_receive(:register).with :foo,
+ [],
+ 7,
+ nil,
+ instance,
+ nil
+ instance.foo.should == 7
+ end
+ end
+ context "with arguments" do
+ it "registers the method call" do
+ Hijacker.send(:define_hijacked, [:bar], receiver, nil)
+ instance = receiver.new
+ Hijacker.should_receive(:register).with :bar,
+ [1,2],
+ 3,
+ nil,
+ instance,
+ nil
+ instance.bar(1,2).should == 3
+ end
+ end
+ context "with arguments and a block" do
+ it "registers the method call" do
+ Hijacker.send(:define_hijacked, [:method_with_block], receiver, nil)
+ instance = receiver.new
+ Hijacker.should_receive(:register).with :method_with_block,
+ [1,kind_of(Proc)],
+ 3,
+ nil,
+ instance,
+ nil
+ instance.method_with_block(1) do
+ 3
+ end.should == 3
+ end
+ end
+ context "raising an exception" do
+ it "registers the method call" do
+ Hijacker.send(:define_hijacked, [:method_that_raises], receiver, nil)
+ instance = receiver.new
+ Hijacker.should_receive(:register).with :method_that_raises,
+ [],
+ nil,
+ kind_of(StandardError),
+ instance,
+ nil
+ expect {
+ instance.method_that_raises
+ }.to raise_error(StandardError)
+ end
+ end
+ end
+ end
+ end
+end
View
156 spec/hijacker/spy_spec.rb
@@ -0,0 +1,156 @@
+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
+ let(:object) { MyClass.new }
+ let(:klass) { MyClass }
+ let(:inst_methods) { [:foo, :bar] }
+ let(:sing_methods) { [:foo, :bar] }
+
+ 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" do
+
+ context "when given a class" do
+ before(:each) do
+ Hijacker.should_receive(:guess_instance_methods_from).with(klass).and_return(inst_methods)
+ Hijacker.should_receive(:guess_singleton_methods_from).with(klass).and_return(sing_methods)
+ end
+
+ let(:metaclass) { (class << klass; self; end) }
+
+ it 'calls define_hijacked on all methods' do
+ Hijacker.should_receive(:define_hijacked).with(inst_methods, klass, nil).once
+ Hijacker.should_receive(:define_hijacked).with(sing_methods, metaclass, nil).once
+
+ Hijacker.spy(klass)
+ end
+ context "with :only => :singleton_methods" do
+ it 'calls define_hijacked only on singleton methods' do
+ Hijacker.should_not_receive(:define_hijacked).with(inst_methods, klass, nil)
+ Hijacker.should_receive(:define_hijacked).with(sing_methods, metaclass, nil).once
+
+ Hijacker.spy(klass, :only => :singleton_methods)
+ end
+ end
+ context "with :only => :instance_methods" do
+ it 'calls define_hijacked only on instance methods' do
+ Hijacker.should_receive(:define_hijacked).with(inst_methods, klass, nil)
+ Hijacker.should_not_receive(:define_hijacked).with(sing_methods, metaclass, nil)
+
+ Hijacker.spy(klass, :only => :instance_methods)
+ end
+ end
+ end
+ context "when given an object" do
+ before(:each) do
+ Hijacker.stub(:guess_instance_methods_from).with(object).and_return(inst_methods)
+ Hijacker.stub(:guess_singleton_methods_from).with(object).and_return(sing_methods)
+ end
+
+ let(:metaclass) { (class << object; self; end) }
+ it 'calls define_hijacked on all methods' do
+ Hijacker.should_receive(:define_hijacked).with(inst_methods, metaclass, nil).once
+ Hijacker.should_receive(:define_hijacked).with(sing_methods, metaclass, nil).once
+
+ Hijacker.spy(object)
+ end
+ context "with :only => :singleton_methods" do
+ it 'calls define_hijacked only on singleton methods' do
+ Hijacker.should_receive(:define_hijacked).with(sing_methods, metaclass, nil).once
+
+ Hijacker.spy(object, :only => :singleton_methods)
+ end
+ end
+ context "with :only => :instance_methods" do
+ it 'calls define_hijacked only on instance methods' do
+ Hijacker.should_receive(:define_hijacked).with(inst_methods, metaclass, nil).once
+
+ Hijacker.spy(object, :only => :instance_methods)
+ end
+ end
+ end
+ context "when given a forbidden class" do
+ it "raises" do
+ expect {
+ Hijacker.spy(Array)
+ }.to raise_error(StandardError)
+ end
+ end
+ it "rejects methods from REJECTED_METHODS constant" do
+ inst_methods_with_some_rejected = inst_methods | [:instance_eval, :__original_something]
+ sing_methods_with_some_rejected = sing_methods | [:instance_eval, :__original_something]
+
+ Hijacker.should_receive(:guess_instance_methods_from).with(object).and_return(inst_methods_with_some_rejected)
+ Hijacker.should_receive(:guess_singleton_methods_from).with(object).and_return(sing_methods_with_some_rejected)
+
+ Hijacker.should_receive(:define_hijacked).with(inst_methods, kind_of(Class), nil).once
+ Hijacker.should_receive(:define_hijacked).with(sing_methods, kind_of(Class), nil).once
+
+ Hijacker.spy(object)
+ end
+ end
+
+ describe "#restore" do
+ it "restores all methods from the object" do
+ inst_methods_with_some_hijacked = inst_methods | [:__original_foo, :__original_bar]
+ sing_methods_with_some_hijacked = sing_methods | [:__original_foo, :__original_bar]
+ receiver = (class << object; self; end)
+
+ Hijacker.should_receive(:guess_instance_methods_from).with(object).and_return(inst_methods_with_some_hijacked)
+ Hijacker.should_receive(:guess_singleton_methods_from).with(object).and_return(sing_methods_with_some_hijacked)
+
+ receiver.should_receive(:undef_method).with(:foo).twice # instance and singleton methods
+ receiver.should_receive(:alias_method).with(:foo, :__original_foo).twice
+ receiver.should_receive(:undef_method).with(:bar).twice
+ receiver.should_receive(:alias_method).with(:bar, :__original_bar).twice
+
+ Hijacker.restore(object)
+ end
+ end
+
+
+ end
+end
View
113 spec/hijacker_spec.rb
@@ -14,112 +14,18 @@ def foo
def bar(a,b)
b
end
-end
-
-describe Hijacker 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
+ def baz=(value)
+ @value = value
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, 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", 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, 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", 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, kind_of(MyClass), nil).ordered
- Hijacker.should_receive(:register).with(:my_method, [], 8, kind_of(MyClass), nil).ordered
+ def method_that_raises
+ raise StandardError, "Something went wrong"
+ end
- 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", kind_of(MyClass), nil).ordered
- Hijacker.should_receive(:register).with(:my_method_with_args, [2, "string"], "string", kind_of(MyClass), nil).ordered
+end unless defined?(MyClass)
- object.bar(2, "string").should == "string"
- object.my_method_with_args(2, "string").should == "string"
- 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
+describe Hijacker do
describe "#register" do
it 'sends the registered method call to the DRb server' do
@@ -133,18 +39,19 @@ def object.my_method_with_args(a,b)
{:inspect => "\"string\"", :class => "String"},
],
{:inspect => "\"retval\"", :class => "String"},
+ nil,
{:inspect => "MyClass", :class => "Class"}
]
DRbObject.should_receive(:new).with(nil, "druby://localhost:9999").and_return server
server.should_receive(:handle).with *expected_args
- Hijacker.register(:bar, [2, "string"], "retval", MyClass)
+ Hijacker.register(:bar, [2, "string"], "retval", nil, MyClass)
end
context "when given a particular DRb uri" do
it "sends the call to that uri" do
DRbObject.should_receive(:new).with(nil, "druby://localhost:1212").and_return mock('DRb server', :handle => true)
- Hijacker.register(:bar, [2, "string"], "retval", MyClass, "druby://localhost:1212")
+ Hijacker.register(:bar, [2, "string"], "retval", nil, MyClass, "druby://localhost:1212")
end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.