Skip to content
Browse files

Finished custom Handlers support :)

  • Loading branch information...
1 parent 08ad4ae commit 1f063880dc99f0e2290b8207c24f98c70e82e06d @txus committed Nov 21, 2010
View
2 Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- hijacker (0.0.2)
+ hijacker (0.1.0)
trollop
GEM
View
95 Readme.md
@@ -1,9 +1,20 @@
#hijacker
A little gem that hijacks any ruby object and broadcasts all its activity
-to a particular hijacker server. Useful for logging and those awfully hardcore
-debugging afternoons! There might be other uses to it, for sure. Just be
-creative :)
+to a particular hijacker server. The server exposes a special handler object
+which then does fun things with the received data!
+
+For example, by now there is only one handler: Logger. This handler shows the
+received activity in the server output with fancy logging-style colors. Could
+be quite useful for those awfully long debugging afternoons!
+
+Of course, you can write your own handlers to do whatever you want with the
+data: maybe record how many arguments do your methods accept on average, log
+into disk any method calls containing a certain type of argument... just be
+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.
@@ -23,16 +34,24 @@ otherwise, as long as it's before hijacking any object):
And that's it! Oooor not. You have to spawn your server. In the command line:
- hijacker
+ hijacker <handler>
+
+Where <handler> must be a registered handler (for now there is only 'logger').
+So you type:
-And it will output the URI for this server. *Note this* and pass it to your
-configuration block!
+ hijacker logger
+
+And it will output the URI for this super fancy hijacker logging server.
+*Remember this URI* and pass it to your configuration block!
Some options you can pass to the server command:
- hijacker --without-timestamps (don't show the timestamps)
- hijacker --without-classes (don't show the object classes)
- hijacker --port 1234 (spawn the server in port 1234 rather than 8787)
+ hijacker <handler> --port 1234 (spawn the server in port 1234 rather than 8787)
+
+Specific handlers can accept specific options. Logger accepts these:
+
+ hijacker logger --without-timestamps (don't show the timestamps)
+ hijacker logger --without-classes (don't show the object classes)
##Ok, and now for some hijacking action!
@@ -54,11 +73,13 @@ Some options you can pass to the server command:
instance = MyClass.new
instance.foo(3, 4)
-Run this code and, if you look at the server output, you'll see nothing less than...
+Run this code and, given we are using the Logger handler, if you look at the server output, you'll see nothing less than...
<a nice timestamp> MyClass (Class) received :new and returned #<MyClass:0x000000874> (MyClass)
<a nice timestamp> #<MyClass:0x000000874> (MyClass) received :foo with 3 (Fixnum), 4 (Fixnum) and returned 7 (Fixnum)
+But in a nice set of colors.
+
If you want to un-hijack any object, just call #restore:
Hijacker.restore(MyClass)
@@ -95,7 +116,59 @@ Of course, you can specify a particular server uri for a block, with #spying:
# all the activity of foo_object inside this block
# will be sent to the hijacker server on druby://localhost:1234
end
-
+
+##Extending hijacker with moar handlers
+
+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
+
+They are autoloaded and 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!
+
+ # You must implement a class method named cli_options which must
+ # return a Trollop-friendly Proc, for command-line options parsing.
+ #
+ # These options can be accessed within the #handle method by calling
+ # the opts method.
+ #
+ def self.cli_options
+ Proc.new {
+ opt :without_foo, "Don't use foo to handle the method name"
+ opt :using_bar, "Use bar as much as you can"
+ }
+ end
+
+ # This is the most important method. This is what is called every time
+ # a method call is performed on a hijacked object. The received params
+ # look like this:
+ #
+ # method :foo
+ #
+ # args [{:inspect => '3', :class => 'Fixnum'},
+ # {:inspect => '"string"', :class => 'String'}]
+ #
+ # retval [{:inspect => ':bar', :class => 'Symbol'}]
+ #
+ # object [{:inspect => '#<MyClass:0x003457>', :class => 'MyClass'}]
+ #
+ def handle(method, args, retval, object)
+ # Do what you want with these!
+ end
+
+ end
+ end
+
+Try to think of creative uses of hijacker, write your own handlers and send
+them to me ZOMG I CAN HAZ MOAR HENDLARZ
+
##Note on Patches/Pull Requests
* Fork the project.
View
33 bin/hijacker
@@ -3,21 +3,32 @@
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
require 'hijacker'
+require 'hijacker/version'
+SUB_COMMANDS = Hijacker::Handler.handlers
opts = Trollop::options do
- version "hijacker 0.0.1 (c) 2010 Josep M. Bach"
+ version "hijacker #{Hijacker::VERSION} (c) 2010 Josep M. Bach"
banner <<-EOS
- Hijacker server listens for reports by hijackers spying on ruby objects.
+ Hijacker server listens for reports by hijackers spying on ruby objects,
+ and passes them to a given handler to process those reports.
+
+ Specific handlers may accept specific options. To learn about which options
+ does a particular handler accept, try:
+
+ hijacker <handler> --help
+
+ The available handlers are:
+ #{SUB_COMMANDS.join(', ')}
Usage:
- hijacker [options]
+ hijacker <handler> [options]
where [options] are:
EOS
- opt :without_classes, "Don't show classes of objects"
- opt :without_timestamps, "Don't show timestamps"
opt :port, "DRb port to use (default is 8787)", :default => 8787
+ stop_on SUB_COMMANDS
end
+# Port resolution
begin
raise unless opts[:port].to_i > 0 && opts[:port].to_i < 9999
rescue
@@ -26,15 +37,23 @@ end
DRB_URI="druby://localhost:#{opts[:port]}"
+# Handler resolution
+handler = ARGV.shift # get the handler
+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)
+
+handler_class = eval("Hijacker::#{handler.capitalize}")
+Trollop::options(&handler_class.cli_options)
+
# Start up the DRb service
-DRb.start_service DRB_URI, Hijacker::Logger.new(opts)
+DRb.start_service DRB_URI, handler_class.new(opts)
ANSI = Hijacker::Logger::ANSI
# We need the uri of the service to connect a client
welcome = []
welcome << ANSI[:BOLD] + "hijacker server"
welcome << "listening on"
-welcome << ANSI[:BOLD] + DRb.uri + ANSI[:RESET]
+welcome << ANSI[:BOLD] + DRb.uri
+welcome << "\nUsing " + ANSI[:GREEN] + handler.capitalize + ANSI[:RESET] + " handler" + ANSI[:RESET]
puts welcome.join("#{ANSI[:RESET]} ") + "\n"
# We need the uri of the service to connect a client
View
28 lib/hijacker/handler.rb
@@ -1,11 +1,22 @@
module Hijacker
class Handler
+ # Your custom handlers need to respond to this class method, which will be
+ # expected to return a proc meant to be sent to Trollop during the command
+ # line parsing. For example, the Logger handler implements cli_options like
+ # this:
+ #
+ # def self.cli_options
+ # Proc.new {
+ # opt :without_classes, "Don't show classes of objects"
+ # opt :without_timestamps, "Don't show timestamps"
+ # }
+ # end
+
# Make dRuby send Handler instances as dRuby references,
# not copies.
include DRb::DRbUndumped
attr_reader :opts
-
def initialize(opts)
@opts = opts
end
@@ -25,10 +36,23 @@ def handle(method, args, retval, object)
raise NotImplementedError.new("You are supposed to subclass Handler")
end
+ class << self
+
+ @@handlers = []
+
+ def register_handler(handler)
+ handler.match(/handlers\/(\w+)/)
+ handler = $1.strip if $1
+ @@handlers << handler
+ end
+ def handlers
+ @@handlers
+ end
+ end
end
end
# Automatically load all handlers
Dir[File.dirname(File.join(File.dirname(__FILE__), 'handlers', '**', '*.rb'))].entries.each do |handler|
- require handler
+ require(handler) && Hijacker::Handler.register_handler(handler)
end
View
7 lib/hijacker/handlers/logger.rb
@@ -1,6 +1,13 @@
module Hijacker
class Logger < Handler
+ def self.cli_options
+ Proc.new {
+ opt :without_classes, "Don't show classes of objects"
+ opt :without_timestamps, "Don't show timestamps"
+ }
+ end
+
ANSI = {:RESET=>"\e[0m", :BOLD=>"\e[1m", :UNDERLINE=>"\e[4m",
:LGRAY=>"\e[0;37m", :GRAY=>"\e[1;30m",
:RED=>"\e[31m",
View
2 lib/hijacker/version.rb
@@ -1,3 +1,3 @@
module Hijacker
- VERSION = "0.0.2"
+ VERSION = "0.1.0"
end
View
11 spec/hijacker/handler_spec.rb
@@ -27,6 +27,17 @@ module Hijacker
}.to raise_error NotImplementedError
end
end
+
+ 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')
+ end
+ end
+
+ end
end
end
View
5 spec/hijacker/handlers/logger_spec.rb
@@ -13,6 +13,11 @@ module Hijacker
subject.should be_kind_of(Handler)
end
+ it "implements the cli_options class method" do
+ Hijacker::Logger.should respond_to(:cli_options)
+ Hijacker::Logger.cli_options.should be_kind_of(Proc)
+ end
+
describe "#handle" do
let(:args) do

0 comments on commit 1f06388

Please sign in to comment.
Something went wrong with that request. Please try again.