hijacker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +48 -0
- data/Guardfile +8 -0
- data/Rakefile +10 -0
- data/Readme.md +114 -0
- data/bin/hijacker +50 -0
- data/hijacker.gemspec +30 -0
- data/lib/hijacker/config.rb +19 -0
- data/lib/hijacker/logger.rb +53 -0
- data/lib/hijacker/version.rb +3 -0
- data/lib/hijacker.rb +116 -0
- data/spec/hijacker/config_spec.rb +14 -0
- data/spec/hijacker/logger_spec.rb +81 -0
- data/spec/hijacker_spec.rb +152 -0
- data/spec/spec_helper.rb +17 -0
- metadata +166 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use ruby-1.9.2@hijacker
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
hijacker (0.0.1)
|
5
|
+
trollop
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
configuration (1.2.0)
|
11
|
+
diff-lcs (1.1.2)
|
12
|
+
guard (0.2.2)
|
13
|
+
open_gem (~> 1.4.2)
|
14
|
+
thor (~> 0.14.3)
|
15
|
+
guard-rspec (0.1.8)
|
16
|
+
guard (>= 0.2.0)
|
17
|
+
launchy (0.3.7)
|
18
|
+
configuration (>= 0.0.5)
|
19
|
+
rake (>= 0.8.1)
|
20
|
+
open_gem (1.4.2)
|
21
|
+
launchy (~> 0.3.5)
|
22
|
+
rake (0.8.7)
|
23
|
+
rspec (2.1.0)
|
24
|
+
rspec-core (~> 2.1.0)
|
25
|
+
rspec-expectations (~> 2.1.0)
|
26
|
+
rspec-mocks (~> 2.1.0)
|
27
|
+
rspec-core (2.1.0)
|
28
|
+
rspec-expectations (2.1.0)
|
29
|
+
diff-lcs (~> 1.1.2)
|
30
|
+
rspec-mocks (2.1.0)
|
31
|
+
simplecov (0.3.7)
|
32
|
+
simplecov-html (>= 0.3.7)
|
33
|
+
simplecov-html (0.3.9)
|
34
|
+
thor (0.14.4)
|
35
|
+
trollop (1.16.2)
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
java
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
bundler (~> 1.0.7)
|
43
|
+
guard
|
44
|
+
guard-rspec
|
45
|
+
hijacker!
|
46
|
+
rspec (~> 2.1.0)
|
47
|
+
simplecov
|
48
|
+
trollop
|
data/Guardfile
ADDED
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#hijacker
|
2
|
+
|
3
|
+
A little gem that hijacks any ruby object and broadcasts all its activity
|
4
|
+
to a particular hijacker server. Useful for logging and those awfully hardcore
|
5
|
+
debugging afternoons! There might be other uses to it, for sure. Just be
|
6
|
+
creative :)
|
7
|
+
|
8
|
+
Hijacker is tested with Ruby 1.8.7, 1.9.2, JRuby 1.5.3 and Rubinius 1.1.
|
9
|
+
|
10
|
+
##Install and configure
|
11
|
+
|
12
|
+
In your Gemfile:
|
13
|
+
|
14
|
+
gem 'hijacker'
|
15
|
+
|
16
|
+
If you are using Rails, you might want to put this configuration snippet in an
|
17
|
+
initializer or something (you can always put it in any other part of the code
|
18
|
+
otherwise, as long as it's before hijacking any object):
|
19
|
+
|
20
|
+
Hijacker.configure do
|
21
|
+
uri '<YOUR HIJACKER SERVER URI>'
|
22
|
+
end
|
23
|
+
|
24
|
+
And that's it! Oooor not. You have to spawn your server. In the command line:
|
25
|
+
|
26
|
+
hijacker
|
27
|
+
|
28
|
+
And it will output the URI for this server. *Note this* and pass it to your
|
29
|
+
configuration block!
|
30
|
+
|
31
|
+
Some options you can pass to the server command:
|
32
|
+
|
33
|
+
hijacker --without-timestamps (don't show the timestamps)
|
34
|
+
hijacker --without-classes (don't show the object classes)
|
35
|
+
hijacker --port 1234 (spawn the server in port 1234 rather than 8787)
|
36
|
+
|
37
|
+
##Ok, and now for some hijacking action!
|
38
|
+
|
39
|
+
require 'hijacker' # You don't have to when using Bundler :)
|
40
|
+
|
41
|
+
class MyClass
|
42
|
+
def foo(bar, baz)
|
43
|
+
bar + baz
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
some_object = Object.new
|
48
|
+
|
49
|
+
# These are the important lines:
|
50
|
+
|
51
|
+
Hijacker.spy(some_object)
|
52
|
+
Hijacker.spy(MyClass)
|
53
|
+
|
54
|
+
instance = MyClass.new
|
55
|
+
instance.foo(3, 4)
|
56
|
+
|
57
|
+
Run this code and, if you look at the server output, you'll see nothing less
|
58
|
+
than...
|
59
|
+
|
60
|
+
<a nice timestamp> MyClass (Class) received :new and returned #<MyClass:0x000000874> (MyClass)
|
61
|
+
<a nice timestamp> #<MyClass:0x000000874> (MyClass) received :foo with 3
|
62
|
+
(Fixnum), 4 (Fixnum) and returned 7
|
63
|
+
|
64
|
+
If you want to un-hijack any object, just call #restore:
|
65
|
+
|
66
|
+
Hijacker.restore(MyClass)
|
67
|
+
Hijacker.restore(some_object)
|
68
|
+
|
69
|
+
If you don't want to have to remember every hijacked object you have to call
|
70
|
+
restore on it, you can just spy a particular object within the duration of a block:
|
71
|
+
|
72
|
+
Hijacker.spying(MyClass) do
|
73
|
+
# inside this block, MyClass will be spied
|
74
|
+
end
|
75
|
+
# here not anymore
|
76
|
+
|
77
|
+
Awesome! You can fine-tune your spying, for example by only spying on instance
|
78
|
+
methods or singleton methods only:
|
79
|
+
|
80
|
+
Hijacker.spy(MyClass, :only => :instance_methods) # or :singleton_methods
|
81
|
+
|
82
|
+
And, last but not least... you can specify a *particular hijacker server* for
|
83
|
+
a *particular object* you are spying on!
|
84
|
+
|
85
|
+
# All activity on MyClass and its instances will
|
86
|
+
# be sent to druby://localhost:9999
|
87
|
+
Hijacker.spy(MyClass, :uri => 'druby://localhost:9999')
|
88
|
+
|
89
|
+
# But for example, the activity of some_object will
|
90
|
+
# be sent to the default uri specified in the configuration
|
91
|
+
# back earlier (remember?)
|
92
|
+
Hijacker.spy(some_object)
|
93
|
+
|
94
|
+
Of course, you can specify a particular server uri for a block, with #spying:
|
95
|
+
|
96
|
+
Hijacker.spying(foo_object, :uri => 'druby://localhost:1234') do
|
97
|
+
# all the activity of foo_object inside this block
|
98
|
+
# will be sent to the hijacker server on druby://localhost:1234
|
99
|
+
end
|
100
|
+
|
101
|
+
##Note on Patches/Pull Requests
|
102
|
+
|
103
|
+
* Fork the project.
|
104
|
+
* Make your feature addition or bug fix.
|
105
|
+
* Add specs for it. This is important so I don't break it in a
|
106
|
+
future version unintentionally.
|
107
|
+
* Commit, do not mess with rakefile, version, or history.
|
108
|
+
If you want to have your own version, that is fine but bump version
|
109
|
+
in a commit by itself I can ignore when I pull.
|
110
|
+
* Send me a pull request. Bonus points for topic branches.
|
111
|
+
|
112
|
+
## Copyright
|
113
|
+
|
114
|
+
Copyright (c) 2010 Josep M. Bach. See LICENSE for details.
|
data/bin/hijacker
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# hijacker server
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
|
5
|
+
require 'hijacker'
|
6
|
+
|
7
|
+
opts = Trollop::options do
|
8
|
+
version "hijacker 0.0.1 (c) 2010 Josep M. Bach"
|
9
|
+
banner <<-EOS
|
10
|
+
Hijacker server listens for reports by hijackers spying on ruby objects.
|
11
|
+
|
12
|
+
Usage:
|
13
|
+
hijacker [options]
|
14
|
+
where [options] are:
|
15
|
+
EOS
|
16
|
+
opt :without_classes, "Don't show classes of objects"
|
17
|
+
opt :without_timestamps, "Don't show timestamps"
|
18
|
+
opt :port, "DRb port to use (default is 8787)", :default => 8787
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
raise unless opts[:port].to_i > 0 && opts[:port].to_i < 9999
|
23
|
+
rescue
|
24
|
+
Trollop::die :port, "must be a valid number between 0 and 9999"
|
25
|
+
end
|
26
|
+
|
27
|
+
DRB_URI="druby://localhost:#{opts[:port]}"
|
28
|
+
|
29
|
+
# Start up the DRb service
|
30
|
+
DRb.start_service DRB_URI, Hijacker::Logger.new(opts)
|
31
|
+
|
32
|
+
ANSI = Hijacker::Logger::ANSI
|
33
|
+
# We need the uri of the service to connect a client
|
34
|
+
welcome = []
|
35
|
+
welcome << ANSI[:BOLD] + "hijacker server"
|
36
|
+
welcome << "listening on"
|
37
|
+
welcome << ANSI[:BOLD] + DRb.uri + ANSI[:RESET]
|
38
|
+
puts welcome.join("#{ANSI[:RESET]} ") + "\n"
|
39
|
+
|
40
|
+
# We need the uri of the service to connect a client
|
41
|
+
instructions = "Put this code in the configuration of your ruby program #{ANSI[:BOLD]}before any call to Hijacker#{ANSI[:RESET]}:\n\n"
|
42
|
+
instructions += "\t" + "Hijacker.config do\n"
|
43
|
+
instructions += "\t" + " uri '#{DRb.uri}'\n"
|
44
|
+
instructions += "\t" + "end\n\n"
|
45
|
+
puts instructions
|
46
|
+
instructions = "Or optionally attach a particular hijacked object to this server adding :uri => '#{DRb.uri}' when calling Hijacker's :spy or :spying method.\n\n"
|
47
|
+
puts instructions
|
48
|
+
|
49
|
+
# wait for the DRb service to finish before exiting
|
50
|
+
DRb.thread.join
|
data/hijacker.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "hijacker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hijacker"
|
7
|
+
s.version = Hijacker::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Josep M. Bach"]
|
10
|
+
s.email = ["josep.m.bach@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/txus/hijacker"
|
12
|
+
s.summary = %q{Spy on your ruby objects and send their activity to a hijacker server anywhere through DRb}
|
13
|
+
s.description = %q{Spy on your ruby objects and send their activity to a hijacker server anywhere through DRb}
|
14
|
+
|
15
|
+
s.rubyforge_project = "hijacker"
|
16
|
+
|
17
|
+
s.add_runtime_dependency 'trollop'
|
18
|
+
s.default_executable = "hijacker"
|
19
|
+
|
20
|
+
s.add_development_dependency 'bundler', '~> 1.0.7'
|
21
|
+
s.add_development_dependency 'rspec', '~> 2.1.0'
|
22
|
+
s.add_development_dependency 'guard'
|
23
|
+
s.add_development_dependency 'guard-rspec'
|
24
|
+
s.add_development_dependency "simplecov"
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hijacker
|
2
|
+
class << self
|
3
|
+
def configure(&block)
|
4
|
+
self.instance_eval(&block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def uri(drb)
|
8
|
+
@@drb_uri = drb
|
9
|
+
end
|
10
|
+
|
11
|
+
def drb_uri
|
12
|
+
begin
|
13
|
+
@@drb_uri
|
14
|
+
rescue
|
15
|
+
raise "Neither a global nor a local Hijacker server URI is configured. Please refer to the README to find out how to do this."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Hijacker
|
2
|
+
class Logger
|
3
|
+
|
4
|
+
ANSI = {:RESET=>"\e[0m", :BOLD=>"\e[1m", :UNDERLINE=>"\e[4m",
|
5
|
+
:LGRAY=>"\e[0;37m", :GRAY=>"\e[1;30m",
|
6
|
+
:RED=>"\e[31m",
|
7
|
+
:GREEN=>"\e[32m", :LGREEN=>"\e[1;32m",
|
8
|
+
:YELLOW=>"\e[33m",
|
9
|
+
:BLUE=>"\e[34m", :LBLUE=>"\e[1;34m",
|
10
|
+
:PURPLE=>"\e[35m", :LPURPLE=>"\e[1;35m",
|
11
|
+
:CYAN=>"\e[36m", :LCYAN=>"\e[1;36m",
|
12
|
+
:WHITE=>"\e[37m"}
|
13
|
+
|
14
|
+
# Make dRuby send Logger instances as dRuby references,
|
15
|
+
# not copies.
|
16
|
+
include DRb::DRbUndumped
|
17
|
+
|
18
|
+
attr_reader :opts
|
19
|
+
|
20
|
+
def initialize(opts)
|
21
|
+
@opts = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def log(method, args, retval, object)
|
25
|
+
out = []
|
26
|
+
out << ANSI[:BOLD] + ANSI[:UNDERLINE] + "#{Time.now}" unless opts[:without_timestamps]
|
27
|
+
out << ANSI[:CYAN] + object[:inspect]
|
28
|
+
out << ANSI[:LCYAN] + "(#{object[:class]})" unless opts[:without_classes]
|
29
|
+
out << "received"
|
30
|
+
out << ANSI[:RED] + ":#{method}"
|
31
|
+
unless args.empty?
|
32
|
+
out << "with"
|
33
|
+
out << args.map do |arg|
|
34
|
+
ANSI[:GREEN] + arg[:inspect] + ANSI[:LGREEN] + (opts[:without_classes] ? "" : " (#{arg[:class]})") +
|
35
|
+
ANSI[:RESET]
|
36
|
+
end.join(', ')
|
37
|
+
end
|
38
|
+
out << "and returned"
|
39
|
+
out << ANSI[:BLUE] + retval[:inspect]
|
40
|
+
out << ANSI[:LBLUE] + "(#{retval[:class]})" unless opts[:without_classes]
|
41
|
+
out << ANSI[:RESET] + "\n"
|
42
|
+
stdout.print out.join("#{ANSI[:RESET]} ")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def stdout
|
48
|
+
$stdout
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/hijacker.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'trollop'
|
3
|
+
require 'hijacker/config'
|
4
|
+
require 'hijacker/logger'
|
5
|
+
|
6
|
+
module Hijacker
|
7
|
+
|
8
|
+
# Methods that won't be hijacked in any case
|
9
|
+
REJECTED_METHODS = (Object.instance_methods | Module.methods | %w{< <= > >= __original_[\w\d]+ [^\w\d]+})
|
10
|
+
FORBIDDEN_CLASSES = [Array, Hash, String, Fixnum, Float, Numeric, Symbol, Proc, Class, Object, BasicObject, Module]
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def spying(*args, &block)
|
15
|
+
raise "No block given" unless block
|
16
|
+
Hijacker.spy(*args)
|
17
|
+
block.call
|
18
|
+
Hijacker.restore(args.first)
|
19
|
+
end
|
20
|
+
|
21
|
+
def spy(object, options = {})
|
22
|
+
raise "Cannot spy on the following forbidden classes: #{FORBIDDEN_CLASSES.map(&:to_s).join(', ')}" if FORBIDDEN_CLASSES.include?(object)
|
23
|
+
rejection = /^(#{REJECTED_METHODS.join('|')})/
|
24
|
+
only = options[:only]
|
25
|
+
uri = options[:uri]
|
26
|
+
custom_rejection = options[:reject] if options[:reject].is_a?(Regexp)
|
27
|
+
|
28
|
+
inst_methods = guess_instance_methods_from(object).reject{|m| (m =~ rejection)}.reject{|m| m =~ custom_rejection}
|
29
|
+
sing_methods = guess_class_methods_from(object).reject{|m| m =~ rejection}.reject{|m| m =~ custom_rejection}
|
30
|
+
|
31
|
+
receiver = if object.is_a?(Class)
|
32
|
+
object
|
33
|
+
else
|
34
|
+
(class << object; self; end)
|
35
|
+
end
|
36
|
+
|
37
|
+
inst_methods.each do |met|
|
38
|
+
receiver.send(:alias_method, :"__original_#{met}", :"#{met}")
|
39
|
+
receiver.send(:undef_method, :"#{met}")
|
40
|
+
receiver.class_eval <<EOS
|
41
|
+
def #{met}(*args, &blk)
|
42
|
+
__original_#{met}(*args,&blk).tap do |retval|
|
43
|
+
Hijacker.register :#{met}, args, retval, self.dup, #{uri.inspect}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
EOS
|
47
|
+
end unless options[:only] == :singleton_methods
|
48
|
+
|
49
|
+
receiver = (class << object; self; end)
|
50
|
+
sing_methods.each do |met|
|
51
|
+
puts "Defining #{met}"
|
52
|
+
receiver.send(:alias_method, :"__original_#{met}", :"#{met}")
|
53
|
+
receiver.send(:undef_method, :"#{met}")
|
54
|
+
receiver.class_eval <<EOS
|
55
|
+
def #{met}(*args, &blk)
|
56
|
+
__original_#{met}(*args,&blk).tap do |retval|
|
57
|
+
Hijacker.register :#{met}, args, retval, self.dup, #{uri.inspect}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
EOS
|
61
|
+
end unless options[:only] == :instance_methods
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def restore(object)
|
66
|
+
receiver = if object.is_a?(Class)
|
67
|
+
object
|
68
|
+
else
|
69
|
+
(class << object; self; end)
|
70
|
+
end
|
71
|
+
guess_instance_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
|
72
|
+
met = met.to_s.gsub!("__original_", "")
|
73
|
+
receiver.send(:undef_method, :"#{met}")
|
74
|
+
receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
|
75
|
+
end
|
76
|
+
|
77
|
+
receiver = (class << object; self; end)
|
78
|
+
guess_class_methods_from(object).select{|m| m =~ /__original_/}.each do |met|
|
79
|
+
met = met.to_s.gsub!("__original_", "")
|
80
|
+
receiver.send(:undef_method, :"#{met}")
|
81
|
+
receiver.send(:alias_method, :"#{met}", :"__original_#{met}")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def register(method, args, retval, object, uri = nil)
|
86
|
+
args.map! do |arg|
|
87
|
+
{:inspect => arg.inspect, :class => arg.class.name}
|
88
|
+
end
|
89
|
+
retval = {:inspect => retval.inspect, :class => retval.class.name}
|
90
|
+
object = {:inspect => object.inspect, :class => object.class.name}
|
91
|
+
|
92
|
+
server = DRbObject.new nil, (uri || self.drb_uri)
|
93
|
+
server.log method, args, retval, object
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def guess_instance_methods_from(object)
|
99
|
+
if object.is_a?(Class)
|
100
|
+
object.instance_methods
|
101
|
+
else
|
102
|
+
object.methods
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def guess_class_methods_from(object)
|
107
|
+
if object.is_a?(Class)
|
108
|
+
object.methods
|
109
|
+
else
|
110
|
+
[]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hijacker, "configuration" do
|
4
|
+
|
5
|
+
describe "#configure" do
|
6
|
+
it 'accepts a block with the \'uri\' configuration option' do
|
7
|
+
Hijacker.configure do
|
8
|
+
uri 'druby://localhost:8787'
|
9
|
+
end
|
10
|
+
Hijacker.drb_uri.should == 'druby://localhost:8787'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Hijacker
|
4
|
+
describe Logger do
|
5
|
+
|
6
|
+
subject { Logger.new({:my => :option}) }
|
7
|
+
|
8
|
+
it "initializes with options" do
|
9
|
+
subject.opts.should == {:my => :option}
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#log" do
|
13
|
+
|
14
|
+
let(:args) do
|
15
|
+
[:bar,
|
16
|
+
[
|
17
|
+
{:inspect => "2", :class => "Fixnum"},
|
18
|
+
{:inspect => "\"string\"", :class => "String"},
|
19
|
+
],
|
20
|
+
{:inspect => "\"retval\"", :class => "String"},
|
21
|
+
{:inspect => "MyClass", :class => "Class"}]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'prints the received args' do
|
25
|
+
out = StringIO.new
|
26
|
+
subject.stub(:stdout).and_return out
|
27
|
+
|
28
|
+
Time.stub(:now).and_return Time.parse('2010-11-20')
|
29
|
+
|
30
|
+
subject.log(*args)
|
31
|
+
|
32
|
+
["00:00:00 +0100",
|
33
|
+
"MyClass",
|
34
|
+
"(Class)",
|
35
|
+
"received",
|
36
|
+
":bar",
|
37
|
+
"with",
|
38
|
+
"2",
|
39
|
+
"(Fixnum)",
|
40
|
+
"\"string\"",
|
41
|
+
"(String)",
|
42
|
+
"and returned",
|
43
|
+
"\"retval\"",
|
44
|
+
"(String)"].each do |str|
|
45
|
+
out.string.should include(str)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context "when given :without_timestamps" do
|
49
|
+
it 'discards the timestamps' do
|
50
|
+
logger = Logger.new({:without_timestamps => true})
|
51
|
+
|
52
|
+
out = StringIO.new
|
53
|
+
logger.stub(:stdout).and_return out
|
54
|
+
|
55
|
+
Time.stub(:now).and_return Time.parse('2010-11-20')
|
56
|
+
|
57
|
+
logger.log(*args)
|
58
|
+
|
59
|
+
out.string.should_not include("2010-11-20")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
context "when given :without_classes" do
|
63
|
+
it 'discards the classes' do
|
64
|
+
logger = Logger.new({:without_classes => true})
|
65
|
+
|
66
|
+
out = StringIO.new
|
67
|
+
logger.stub(:stdout).and_return out
|
68
|
+
|
69
|
+
Time.stub(:now).and_return Time.parse('2010-11-20')
|
70
|
+
|
71
|
+
logger.log(*args)
|
72
|
+
|
73
|
+
["(Class)", "(Fixnum)", "(String)"].each do |str|
|
74
|
+
out.string.should_not include(str)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MyClass
|
4
|
+
def self.foo
|
5
|
+
3 + 4
|
6
|
+
end
|
7
|
+
def self.bar(a,b)
|
8
|
+
b
|
9
|
+
end
|
10
|
+
|
11
|
+
def foo
|
12
|
+
3 + 4
|
13
|
+
end
|
14
|
+
def bar(a,b)
|
15
|
+
b
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Hijacker do
|
20
|
+
|
21
|
+
describe "#spying" do
|
22
|
+
it 'runs a block spying on a particular object' do
|
23
|
+
blk = lambda {
|
24
|
+
MyClass.foo
|
25
|
+
}
|
26
|
+
Hijacker.should_receive(:spy).with(MyClass).once.ordered
|
27
|
+
MyClass.should_receive(:foo).once.ordered
|
28
|
+
Hijacker.should_receive(:restore).with(MyClass).once.ordered
|
29
|
+
|
30
|
+
Hijacker.spying(MyClass, &blk)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raises if no block given' do
|
34
|
+
expect {
|
35
|
+
Hijacker.spying(MyClass)
|
36
|
+
}.to raise_error("No block given")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#spy - #restore" do
|
41
|
+
|
42
|
+
describe "hijacking a Class" do
|
43
|
+
describe "instance methods" do
|
44
|
+
before(:each) do
|
45
|
+
Hijacker.spy(MyClass, :only => :instance_methods)
|
46
|
+
end
|
47
|
+
it "registers method calls without arguments" do
|
48
|
+
Hijacker.should_receive(:register).with(:foo, [], 7, kind_of(MyClass), nil).ordered
|
49
|
+
MyClass.new.foo.should == 7
|
50
|
+
end
|
51
|
+
it "registers method calls with arguments" do
|
52
|
+
Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", kind_of(MyClass), nil).ordered
|
53
|
+
MyClass.new.bar(2, "string").should == "string"
|
54
|
+
end
|
55
|
+
after(:each) do
|
56
|
+
Hijacker.restore(MyClass)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
describe "class methods" do
|
60
|
+
before(:each) do
|
61
|
+
Hijacker.spy(MyClass)
|
62
|
+
end
|
63
|
+
it "registers method calls without arguments" do
|
64
|
+
Hijacker.should_receive(:register).with(:foo, [], 7, kind_of(Class), nil).ordered
|
65
|
+
MyClass.foo.should == 7
|
66
|
+
end
|
67
|
+
it "registers method calls with arguments" do
|
68
|
+
Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", kind_of(Class), nil).ordered
|
69
|
+
MyClass.bar(2, "string").should == "string"
|
70
|
+
end
|
71
|
+
after(:each) do
|
72
|
+
Hijacker.restore(MyClass)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
describe "forbidden classes (are not hijacked)" do
|
76
|
+
[Array, Hash, String, Fixnum, Float, Numeric, Symbol].each do |forbidden|
|
77
|
+
it "protects #{forbidden}" do
|
78
|
+
expect {
|
79
|
+
Hijacker.spy(forbidden)
|
80
|
+
}.to raise_error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
describe "hijacking an object" do
|
86
|
+
describe "instance methods" do
|
87
|
+
let(:object) { MyClass.new }
|
88
|
+
|
89
|
+
before(:each) do
|
90
|
+
def object.my_method
|
91
|
+
8
|
92
|
+
end
|
93
|
+
def object.my_method_with_args(a,b)
|
94
|
+
b
|
95
|
+
end
|
96
|
+
Hijacker.spy(object)
|
97
|
+
end
|
98
|
+
it "registers method calls without arguments" do
|
99
|
+
Hijacker.should_receive(:register).with(:foo, [], 7, kind_of(MyClass), nil).ordered
|
100
|
+
Hijacker.should_receive(:register).with(:my_method, [], 8, kind_of(MyClass), nil).ordered
|
101
|
+
|
102
|
+
object.foo.should == 7
|
103
|
+
object.my_method.should == 8
|
104
|
+
end
|
105
|
+
it "registers method calls with arguments" do
|
106
|
+
Hijacker.should_receive(:register).with(:bar, [2, "string"], "string", kind_of(MyClass), nil).ordered
|
107
|
+
Hijacker.should_receive(:register).with(:my_method_with_args, [2, "string"], "string", kind_of(MyClass), nil).ordered
|
108
|
+
|
109
|
+
object.bar(2, "string").should == "string"
|
110
|
+
object.my_method_with_args(2, "string").should == "string"
|
111
|
+
end
|
112
|
+
it "does not affect other instances of the object's class" do
|
113
|
+
Hijacker.should_not_receive(:register)
|
114
|
+
MyClass.new.foo.should == 7
|
115
|
+
end
|
116
|
+
after(:each) do
|
117
|
+
Hijacker.restore(object)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#register" do
|
125
|
+
it 'sends the registered method call to the DRb server' do
|
126
|
+
server = mock('DRb server')
|
127
|
+
|
128
|
+
Hijacker.stub(:drb_uri).and_return "druby://localhost:9999"
|
129
|
+
|
130
|
+
expected_args = [:bar,
|
131
|
+
[
|
132
|
+
{:inspect => "2", :class => "Fixnum"},
|
133
|
+
{:inspect => "\"string\"", :class => "String"},
|
134
|
+
],
|
135
|
+
{:inspect => "\"retval\"", :class => "String"},
|
136
|
+
{:inspect => "MyClass", :class => "Class"}
|
137
|
+
]
|
138
|
+
|
139
|
+
DRbObject.should_receive(:new).with(nil, "druby://localhost:9999").and_return server
|
140
|
+
server.should_receive(:log).with *expected_args
|
141
|
+
|
142
|
+
Hijacker.register(:bar, [2, "string"], "retval", MyClass)
|
143
|
+
end
|
144
|
+
context "when given a particular DRb uri" do
|
145
|
+
it "sends the call to that uri" do
|
146
|
+
DRbObject.should_receive(:new).with(nil, "druby://localhost:1212").and_return mock('DRb server', :log => true)
|
147
|
+
Hijacker.register(:bar, [2, "string"], "retval", MyClass, "druby://localhost:1212")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
begin
|
3
|
+
Bundler.setup(:default, :development)
|
4
|
+
rescue Bundler::BundlerError => e
|
5
|
+
$stderr.puts e.message
|
6
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
7
|
+
exit e.status_code
|
8
|
+
end
|
9
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
10
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
+
|
12
|
+
require 'hijacker'
|
13
|
+
require 'rspec'
|
14
|
+
|
15
|
+
# Requires supporting files with custom matchers and macros, etc,
|
16
|
+
# in ./support/ and its subdirectories.
|
17
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hijacker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Josep M. Bach
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-20 00:00:00 +01:00
|
18
|
+
default_executable: hijacker
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: trollop
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 0
|
44
|
+
- 7
|
45
|
+
version: 1.0.7
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 2
|
58
|
+
- 1
|
59
|
+
- 0
|
60
|
+
version: 2.1.0
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: guard
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: guard-rspec
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
type: :development
|
88
|
+
version_requirements: *id005
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: simplecov
|
91
|
+
prerelease: false
|
92
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
type: :development
|
101
|
+
version_requirements: *id006
|
102
|
+
description: Spy on your ruby objects and send their activity to a hijacker server anywhere through DRb
|
103
|
+
email:
|
104
|
+
- josep.m.bach@gmail.com
|
105
|
+
executables:
|
106
|
+
- hijacker
|
107
|
+
extensions: []
|
108
|
+
|
109
|
+
extra_rdoc_files: []
|
110
|
+
|
111
|
+
files:
|
112
|
+
- .gitignore
|
113
|
+
- .rspec
|
114
|
+
- .rvmrc
|
115
|
+
- Gemfile
|
116
|
+
- Gemfile.lock
|
117
|
+
- Guardfile
|
118
|
+
- Rakefile
|
119
|
+
- Readme.md
|
120
|
+
- bin/hijacker
|
121
|
+
- hijacker.gemspec
|
122
|
+
- lib/hijacker.rb
|
123
|
+
- lib/hijacker/config.rb
|
124
|
+
- lib/hijacker/logger.rb
|
125
|
+
- lib/hijacker/version.rb
|
126
|
+
- spec/hijacker/config_spec.rb
|
127
|
+
- spec/hijacker/logger_spec.rb
|
128
|
+
- spec/hijacker_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
has_rdoc: true
|
131
|
+
homepage: http://github.com/txus/hijacker
|
132
|
+
licenses: []
|
133
|
+
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
segments:
|
153
|
+
- 0
|
154
|
+
version: "0"
|
155
|
+
requirements: []
|
156
|
+
|
157
|
+
rubyforge_project: hijacker
|
158
|
+
rubygems_version: 1.3.7
|
159
|
+
signing_key:
|
160
|
+
specification_version: 3
|
161
|
+
summary: Spy on your ruby objects and send their activity to a hijacker server anywhere through DRb
|
162
|
+
test_files:
|
163
|
+
- spec/hijacker/config_spec.rb
|
164
|
+
- spec/hijacker/logger_spec.rb
|
165
|
+
- spec/hijacker_spec.rb
|
166
|
+
- spec/spec_helper.rb
|