hijacker 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hijacker (0.0.1)
4
+ hijacker (0.1.0)
5
5
  trollop
6
6
 
7
7
  GEM
data/Readme.md CHANGED
@@ -1,9 +1,20 @@
1
1
  #hijacker
2
2
 
3
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 :)
4
+ to a particular hijacker server. The server exposes a special handler object
5
+ which then does fun things with the received data!
6
+
7
+ For example, by now there is only one handler: Logger. This handler shows the
8
+ received activity in the server output with fancy logging-style colors. Could
9
+ be quite useful for those awfully long debugging afternoons!
10
+
11
+ Of course, you can write your own handlers to do whatever you want with the
12
+ data: maybe record how many arguments do your methods accept on average, log
13
+ into disk any method calls containing a certain type of argument... just be
14
+ creative! :)
15
+
16
+ (See the "Extending hijacker blabla" part below to know how to write your own
17
+ handlers)
7
18
 
8
19
  Hijacker is tested with Ruby 1.8.7, 1.9.2, JRuby 1.5.3 and Rubinius 1.1.
9
20
 
@@ -23,16 +34,24 @@ otherwise, as long as it's before hijacking any object):
23
34
 
24
35
  And that's it! Oooor not. You have to spawn your server. In the command line:
25
36
 
26
- hijacker
37
+ hijacker <handler>
38
+
39
+ Where <handler> must be a registered handler (for now there is only 'logger').
40
+ So you type:
27
41
 
28
- And it will output the URI for this server. *Note this* and pass it to your
29
- configuration block!
42
+ hijacker logger
43
+
44
+ And it will output the URI for this super fancy hijacker logging server.
45
+ *Remember this URI* and pass it to your configuration block!
30
46
 
31
47
  Some options you can pass to the server command:
32
48
 
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)
49
+ hijacker <handler> --port 1234 (spawn the server in port 1234 rather than 8787)
50
+
51
+ Specific handlers can accept specific options. Logger accepts these:
52
+
53
+ hijacker logger --without-timestamps (don't show the timestamps)
54
+ hijacker logger --without-classes (don't show the object classes)
36
55
 
37
56
  ##Ok, and now for some hijacking action!
38
57
 
@@ -54,11 +73,12 @@ Some options you can pass to the server command:
54
73
  instance = MyClass.new
55
74
  instance.foo(3, 4)
56
75
 
57
- Run this code and, if you look at the server output, you'll see nothing less
58
- than...
76
+ Run this code and, given we are using the Logger handler, if you look at the server output, you'll see nothing less than...
59
77
 
60
78
  <a nice timestamp> MyClass (Class) received :new and returned #<MyClass:0x000000874> (MyClass)
61
- <a nice timestamp> #<MyClass:0x000000874> (MyClass) received :foo with 3 (Fixnum), 4 (Fixnum) and returned 7
79
+ <a nice timestamp> #<MyClass:0x000000874> (MyClass) received :foo with 3 (Fixnum), 4 (Fixnum) and returned 7 (Fixnum)
80
+
81
+ But in a nice set of colors.
62
82
 
63
83
  If you want to un-hijack any object, just call #restore:
64
84
 
@@ -96,7 +116,59 @@ Of course, you can specify a particular server uri for a block, with #spying:
96
116
  # all the activity of foo_object inside this block
97
117
  # will be sent to the hijacker server on druby://localhost:1234
98
118
  end
99
-
119
+
120
+ ##Extending hijacker with moar handlers
121
+
122
+ It is really easy to write your own handlers. Why don't you write one and send
123
+ me a pull request? I mean now. What are you waiting for, why are you still reading?
124
+
125
+ Ok, maybe a bit of explanation on that. Handlers live here:
126
+
127
+ lib/hijacker/handlers/your_handler.rb
128
+
129
+ They are autoloaded and automatically registered, so all you have to do is
130
+ write them like this:
131
+
132
+
133
+ module Hijacker
134
+ class MyHandler < Handler # Yes, you have to subclass Hijacker::Handler!
135
+
136
+ # You must implement a class method named cli_options which must
137
+ # return a Trollop-friendly Proc, for command-line options parsing.
138
+ #
139
+ # These options can be accessed within the #handle method by calling
140
+ # the opts method.
141
+ #
142
+ def self.cli_options
143
+ Proc.new {
144
+ opt :without_foo, "Don't use foo to handle the method name"
145
+ opt :using_bar, "Use bar as much as you can"
146
+ }
147
+ end
148
+
149
+ # This is the most important method. This is what is called every time
150
+ # a method call is performed on a hijacked object. The received params
151
+ # look like this:
152
+ #
153
+ # method :foo
154
+ #
155
+ # args [{:inspect => '3', :class => 'Fixnum'},
156
+ # {:inspect => '"string"', :class => 'String'}]
157
+ #
158
+ # retval [{:inspect => ':bar', :class => 'Symbol'}]
159
+ #
160
+ # object [{:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}]
161
+ #
162
+ def handle(method, args, retval, object)
163
+ # Do what you want with these!
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ Try to think of creative uses of hijacker, write your own handlers and send
170
+ them to me ZOMG I CAN HAZ MOAR HENDLARZ
171
+
100
172
  ##Note on Patches/Pull Requests
101
173
 
102
174
  * Fork the project.
data/bin/hijacker CHANGED
@@ -3,21 +3,32 @@
3
3
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
4
 
5
5
  require 'hijacker'
6
+ require 'hijacker/version'
6
7
 
8
+ SUB_COMMANDS = Hijacker::Handler.handlers
7
9
  opts = Trollop::options do
8
- version "hijacker 0.0.1 (c) 2010 Josep M. Bach"
10
+ version "hijacker #{Hijacker::VERSION} (c) 2010 Josep M. Bach"
9
11
  banner <<-EOS
10
- Hijacker server listens for reports by hijackers spying on ruby objects.
12
+ Hijacker server listens for reports by hijackers spying on ruby objects,
13
+ and passes them to a given handler to process those reports.
14
+
15
+ Specific handlers may accept specific options. To learn about which options
16
+ does a particular handler accept, try:
17
+
18
+ hijacker <handler> --help
19
+
20
+ The available handlers are:
21
+ #{SUB_COMMANDS.join(', ')}
11
22
 
12
23
  Usage:
13
- hijacker [options]
24
+ hijacker <handler> [options]
14
25
  where [options] are:
15
26
  EOS
16
- opt :without_classes, "Don't show classes of objects"
17
- opt :without_timestamps, "Don't show timestamps"
18
27
  opt :port, "DRb port to use (default is 8787)", :default => 8787
28
+ stop_on SUB_COMMANDS
19
29
  end
20
30
 
31
+ # Port resolution
21
32
  begin
22
33
  raise unless opts[:port].to_i > 0 && opts[:port].to_i < 9999
23
34
  rescue
@@ -26,15 +37,23 @@ end
26
37
 
27
38
  DRB_URI="druby://localhost:#{opts[:port]}"
28
39
 
40
+ # Handler resolution
41
+ handler = ARGV.shift # get the handler
42
+ Trollop::die "You need to specify a handler, which must be one of the following: #{SUB_COMMANDS.join(', ')}\n\nMaybe you just feel a bit lost.." unless SUB_COMMANDS.include?(handler)
43
+
44
+ handler_class = eval("Hijacker::#{handler.capitalize}")
45
+ Trollop::options(&handler_class.cli_options)
46
+
29
47
  # Start up the DRb service
30
- DRb.start_service DRB_URI, Hijacker::Logger.new(opts)
48
+ DRb.start_service DRB_URI, handler_class.new(opts)
31
49
 
32
50
  ANSI = Hijacker::Logger::ANSI
33
51
  # We need the uri of the service to connect a client
34
52
  welcome = []
35
53
  welcome << ANSI[:BOLD] + "hijacker server"
36
54
  welcome << "listening on"
37
- welcome << ANSI[:BOLD] + DRb.uri + ANSI[:RESET]
55
+ welcome << ANSI[:BOLD] + DRb.uri
56
+ welcome << "\nUsing " + ANSI[:GREEN] + handler.capitalize + ANSI[:RESET] + " handler" + ANSI[:RESET]
38
57
  puts welcome.join("#{ANSI[:RESET]} ") + "\n"
39
58
 
40
59
  # We need the uri of the service to connect a client
data/lib/hijacker.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'drb'
2
2
  require 'trollop'
3
3
  require 'hijacker/config'
4
- require 'hijacker/logger'
4
+ require 'hijacker/handler'
5
5
 
6
6
  module Hijacker
7
7
 
@@ -89,7 +89,7 @@ EOS
89
89
  object = {:inspect => object.inspect, :class => object.class.name}
90
90
 
91
91
  server = DRbObject.new nil, (uri || self.drb_uri)
92
- server.log method, args, retval, object
92
+ server.handle method, args, retval, object
93
93
  end
94
94
 
95
95
  private
@@ -0,0 +1,58 @@
1
+ module Hijacker
2
+ class Handler
3
+ # Your custom handlers need to respond to this class method, which will be
4
+ # expected to return a proc meant to be sent to Trollop during the command
5
+ # line parsing. For example, the Logger handler implements cli_options like
6
+ # this:
7
+ #
8
+ # def self.cli_options
9
+ # Proc.new {
10
+ # opt :without_classes, "Don't show classes of objects"
11
+ # opt :without_timestamps, "Don't show timestamps"
12
+ # }
13
+ # end
14
+
15
+ # Make dRuby send Handler instances as dRuby references,
16
+ # not copies.
17
+ include DRb::DRbUndumped
18
+
19
+ attr_reader :opts
20
+ def initialize(opts)
21
+ @opts = opts
22
+ end
23
+
24
+ def handle(method, args, retval, object)
25
+ # Parameters received
26
+ #
27
+ # method :foo
28
+ #
29
+ # args [{:inspect => '3', :class => 'Fixnum'},
30
+ # {:inspect => '"string"', :class => 'String'}]
31
+ #
32
+ # retval [{:inspect => ':bar', :class => 'Symbol'}]
33
+ #
34
+ # object [{:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}]
35
+ #
36
+ raise NotImplementedError.new("You are supposed to subclass Handler")
37
+ end
38
+
39
+ class << self
40
+
41
+ @@handlers = []
42
+
43
+ def register_handler(handler)
44
+ handler.match(/handlers\/(\w+)/)
45
+ handler = $1.strip if $1
46
+ @@handlers << handler
47
+ end
48
+ def handlers
49
+ @@handlers
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Automatically load all handlers
56
+ Dir[File.dirname(File.join(File.dirname(__FILE__), 'handlers', '**', '*.rb'))].entries.each do |handler|
57
+ require(handler) && Hijacker::Handler.register_handler(handler)
58
+ end
@@ -1,5 +1,12 @@
1
1
  module Hijacker
2
- class Logger
2
+ class Logger < Handler
3
+
4
+ def self.cli_options
5
+ Proc.new {
6
+ opt :without_classes, "Don't show classes of objects"
7
+ opt :without_timestamps, "Don't show timestamps"
8
+ }
9
+ end
3
10
 
4
11
  ANSI = {:RESET=>"\e[0m", :BOLD=>"\e[1m", :UNDERLINE=>"\e[4m",
5
12
  :LGRAY=>"\e[0;37m", :GRAY=>"\e[1;30m",
@@ -10,18 +17,8 @@ module Hijacker
10
17
  :PURPLE=>"\e[35m", :LPURPLE=>"\e[1;35m",
11
18
  :CYAN=>"\e[36m", :LCYAN=>"\e[1;36m",
12
19
  :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
20
 
24
- def log(method, args, retval, object)
21
+ def handle(method, args, retval, object)
25
22
  out = []
26
23
  out << ANSI[:BOLD] + ANSI[:UNDERLINE] + "#{Time.now}" unless opts[:without_timestamps]
27
24
  out << ANSI[:CYAN] + object[:inspect]
@@ -1,3 +1,3 @@
1
1
  module Hijacker
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Hijacker
4
+ describe Handler do
5
+
6
+ subject { Handler.new({:my => :option}) }
7
+
8
+ it "initializes with options" do
9
+ subject.opts.should == {:my => :option}
10
+ end
11
+
12
+ describe "#handle" 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 "is meant to be overriden by subclasses" do
25
+ expect {
26
+ subject.handle(*args)
27
+ }.to raise_error NotImplementedError
28
+ end
29
+ end
30
+
31
+ describe "class methods" do
32
+
33
+ describe "#register_handler" do
34
+ it 'registers a loaded handler' do
35
+ Hijacker::Handler.register_handler "/path/to/my/handlers/benchmark.rb"
36
+ Hijacker::Handler.handlers.should include('benchmark')
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -9,7 +9,16 @@ module Hijacker
9
9
  subject.opts.should == {:my => :option}
10
10
  end
11
11
 
12
- describe "#log" do
12
+ it "inherits from Handler" do
13
+ subject.should be_kind_of(Handler)
14
+ end
15
+
16
+ it "implements the cli_options class method" do
17
+ Hijacker::Logger.should respond_to(:cli_options)
18
+ Hijacker::Logger.cli_options.should be_kind_of(Proc)
19
+ end
20
+
21
+ describe "#handle" do
13
22
 
14
23
  let(:args) do
15
24
  [:bar,
@@ -27,7 +36,7 @@ module Hijacker
27
36
 
28
37
  Time.stub(:now).and_return Time.parse('2010-11-20')
29
38
 
30
- subject.log(*args)
39
+ subject.handle(*args)
31
40
 
32
41
  ["00:00:00 +0100",
33
42
  "MyClass",
@@ -54,7 +63,7 @@ module Hijacker
54
63
 
55
64
  Time.stub(:now).and_return Time.parse('2010-11-20')
56
65
 
57
- logger.log(*args)
66
+ logger.handle(*args)
58
67
 
59
68
  out.string.should_not include("2010-11-20")
60
69
  end
@@ -68,7 +77,7 @@ module Hijacker
68
77
 
69
78
  Time.stub(:now).and_return Time.parse('2010-11-20')
70
79
 
71
- logger.log(*args)
80
+ logger.handle(*args)
72
81
 
73
82
  ["(Class)", "(Fixnum)", "(String)"].each do |str|
74
83
  out.string.should_not include(str)
@@ -137,13 +137,13 @@ describe Hijacker do
137
137
  ]
138
138
 
139
139
  DRbObject.should_receive(:new).with(nil, "druby://localhost:9999").and_return server
140
- server.should_receive(:log).with *expected_args
140
+ server.should_receive(:handle).with *expected_args
141
141
 
142
142
  Hijacker.register(:bar, [2, "string"], "retval", MyClass)
143
143
  end
144
144
  context "when given a particular DRb uri" do
145
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)
146
+ DRbObject.should_receive(:new).with(nil, "druby://localhost:1212").and_return mock('DRb server', :handle => true)
147
147
  Hijacker.register(:bar, [2, "string"], "retval", MyClass, "druby://localhost:1212")
148
148
  end
149
149
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 2
9
- version: 0.0.2
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Josep M. Bach
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-20 00:00:00 +01:00
17
+ date: 2010-11-21 00:00:00 +01:00
18
18
  default_executable: hijacker
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -121,10 +121,12 @@ files:
121
121
  - hijacker.gemspec
122
122
  - lib/hijacker.rb
123
123
  - lib/hijacker/config.rb
124
- - lib/hijacker/logger.rb
124
+ - lib/hijacker/handler.rb
125
+ - lib/hijacker/handlers/logger.rb
125
126
  - lib/hijacker/version.rb
126
127
  - spec/hijacker/config_spec.rb
127
- - spec/hijacker/logger_spec.rb
128
+ - spec/hijacker/handler_spec.rb
129
+ - spec/hijacker/handlers/logger_spec.rb
128
130
  - spec/hijacker_spec.rb
129
131
  - spec/spec_helper.rb
130
132
  has_rdoc: true
@@ -161,6 +163,7 @@ specification_version: 3
161
163
  summary: Spy on your ruby objects and send their activity to a hijacker server anywhere through DRb
162
164
  test_files:
163
165
  - spec/hijacker/config_spec.rb
164
- - spec/hijacker/logger_spec.rb
166
+ - spec/hijacker/handler_spec.rb
167
+ - spec/hijacker/handlers/logger_spec.rb
165
168
  - spec/hijacker_spec.rb
166
169
  - spec/spec_helper.rb