[ANN] Detective - a new ruby gem

A

Ahmed Eldawy

Detective is a new gem build by BadrIT (www.badrit.com) to investigate
the ruby source code.

Example
View the source of a class method ...

require 'detective'

Detective.view_source('ActiveRecord::Base.find_by_sql')

Result

def find_by_sql(sql)
connection.select_all(sanitize_sql(sql), "#{name} Load").collect!
{ |record| instantiate(record) }
end

Detective is hosted on gemcutter
[sudo] gem install detective

More examples and details on Detective homepage
http://github.com/aseldawy/detective
 
J

Jan

[Note: parts of this message were removed to make it a legal post.]

Detective is a new gem build by BadrIT (www.badrit.com) to investigate
the ruby source code.

Example
View the source of a class method ...

require 'detective'

Detective.view_source('ActiveRecord::Base.find_by_sql')

Result

def find_by_sql(sql)
connection.select_all(sanitize_sql(sql), "#{name} Load")ollect!
{ |record| instantiate(record) }
end
Interesting - just got a problem when have fun with it in irb:

I have a simple hello.rb:

class Foo
def bar
puts "hey"
end
end

in irb:
changed

A bug or I did something wrong?

Thanks,
Jan

Detective is hosted on gemcutter
[sudo] gem install detective

More examples and details on Detective homepage
http://github.com/aseldawy/detective
 
A

Ahmed Eldawy

Emmm ...
Actually that's how it should work. It's not a bug.
What the gem actually does is opening the files where you have defined a
method and extract the lines of method definition.
When you define a method in irb it's not saved in any files. This means
we have now way to extract its code.
The same problem occurs when you define a method using eval method. The
method is defined in a string variable (memory) not in a file.
Provided that the above cases rarely happen in a real application, we
can say that this is not a problem with Detective.
 
M

Marnen Laibow-Koser

Ahmed said:
Emmm ...
Actually that's how it should work. It's not a bug.
What the gem actually does is opening the files where you have defined a
method and extract the lines of method definition.
When you define a method in irb it's not saved in any files. This means
we have now way to extract its code.
The same problem occurs when you define a method using eval method. The
method is defined in a string variable (memory) not in a file.
Provided that the above cases rarely happen in a real application, we
can say that this is not a problem with Detective.

I like the idea behind Detective, but if it can't cope with dynamically
defined methods, then it won't be terribly useful in practice.
method_missing trickery and dynamic metaprogramming are fairly common in
Ruby.

Best,
 
A

Ahmed Eldawy

Actually it works correctly with dynamically defined methods; I mean
methods defined using 'define_method'.
For example, here's a method defined dynamically in another gem called
QuickMagick.
/usr/local/lib/ruby/gems/1.8/gems/quick_magick-0.7.1/lib/quick_magick/image.rb,
line 184
184: define_method((method).to_sym) do |*args|
185: append_to_operators(method, QuickMagick::geometry(*args) )
186: end

I tried Detective with it and here's the result

print Detective.view_source('QuickMagick::Image#resize', :rdoc)
/usr/local/lib/ruby/gems/1.8/gems/quick_magick-0.7.1/lib/quick_magick/image.rb,
line 185
185: append_to_operators(method, QuickMagick::geometry(*args) )

It showed me just the body of the method not the header. But I think
this is enough. Actually this is what ruby executes, so there's nothing
more to tell you.

For methods working using method_missing, you are correct. Detective
will not show it to you. I'll consider this in the next gem release.
 
A

Ahmed Eldawy

Marnen said:
I like the idea behind Detective, but if it can't cope with dynamically
defined methods, then it won't be terribly useful in practice.
method_missing trickery and dynamic metaprogramming are fairly common in
Ruby.

Best,

I've released a new version which supports method_missing like methods.
Here's an example
Detective is now also working with method_missing …

Detective.view_source('ActiveRecord::Base#find_by_id')

Result

def method_missing(method_id, *args, &block)
method_name = method_id.to_s

if self.class.private_method_defined?(method_name)
raise NoMethodError.new("Attempt to call private method",
method_name, args)
end

# If we haven't generated any methods yet, generate them, then
# see if we've created the method we're looking for.
if !self.class.generated_methods?
self.class.define_attribute_methods
if self.class.generated_methods.include?(method_name)
return self.send(method_id, *args, &block)
end
end

if self.class.primary_key.to_s == method_name
id
elsif md = self.class.match_attribute_method?(method_name)
attribute_name, method_type = md.pre_match, md.to_s
if @attributes.include?(attribute_name)
__send__("attribute#{method_type}", attribute_name, *args,
&block)
else
super
end
elsif @attributes.include?(method_name)
read_attribute(method_name)
else
super
end
end
 
Y

Yossef Mendelssohn

I've released a new version which supports method_missing like methods.
Here's an example
Detective is now also working with method_missing =85

=A0 Detective.view_source('ActiveRecord::Base#find_by_id')

The trouble is that find_by_id is a class method of AR::Base, not an
instance method (you should be looking for
ActiveRecord::Base.find_by_id). It seems you have correctly returned
the instance method_missing (ActiveRecord::Base#method_missing), but
it's highly unlikely that this will fulfill a request for find_by_id.

I haven't bothered to look at the Detective source. Since you
mentioned something about finding the lines in the file and then
opening the file itself to get the source code, I suggest you may want
to take a look at ruby2ruby.
 
M

Marcin Raczkowski

It's not using introspecition (like ruby2ruby) but simply finds a
declaration of function in file!

So you get content of the function just like you written it, but
unfrotunatelly, all metaprogramming is lost
 
A

Ahmed Eldawy

Marcin said:
It's not using introspecition (like ruby2ruby) but simply finds a
declaration of function in file!

So you get content of the function just like you written it,

That's correct. It is not meant to be a kind of reverse-engineering. It
relies on the fact that the source code is *somewhere* on your machine.
It just finds it quickly and displays it to you. It needs to show the
original code with its comments and indentation. This allows the
developer to easily override a method or find bugs.
but unfrotunatelly, all metaprogramming is lost
Not exactly. Methods overrided with a plugin or gem can still be shown
correctly. There's an example of a method overrided and it shows the new
code not the old one. Also there's another example with a method defined
using define_method and it still gets the correct source code and its
location.
However, it cannot find methods defined in a string using eval() method.
It will tell you "cannot find source code". It will not show you an
incorrect or false code. Though, I think methods defined using eval
scripts are very rare in the real world. Do you know famous examples for
it?

I'm thinking now of using introspecition with methods that I cannot find
its source code. This will be some kind of plan B. I'll consider this in
a future release.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,219
Latest member
KristieKoh

Latest Threads

Top