Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[ANN] Detective - a new ruby gem

1 view
Skip to first unread message

Ahmed Eldawy

unread,
Nov 21, 2009, 8:42:50 AM11/21/09
to
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
--
Posted via http://www.ruby-forum.com/.

Jan

unread,
Nov 21, 2009, 9:27:40 AM11/21/09
to
[Note: parts of this message were removed to make it a legal post.]

On Sat, Nov 21, 2009 at 9:42 PM, Ahmed Eldawy <ahmed....@badrit.com>wrote:

> 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:

>> require 'hello'
=> true
>> require 'detective'
=> true
>> Detective.view_source "Foo#bar"
=> " def bar\n puts "hey"\n end\n"
>> class Foo
>> def bar
>> puts "changed"
>> end
>> end
=> nil
>> Detective.view_source "Foo#bar"
=> "Cannot find source code"
>> Foo.new.bar
changed

A bug or I did something wrong?

Thanks,
Jan

Ahmed Eldawy

unread,
Nov 21, 2009, 10:29:02 AM11/21/09
to
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.

Flower Born wrote:
> On Sat, Nov 21, 2009 at 9:42 PM, Ahmed Eldawy
> <ahmed....@badrit.com>wrote:
>

--
Posted via http://www.ruby-forum.com/.

Marnen Laibow-Koser

unread,
Nov 21, 2009, 1:08:00 PM11/21/09
to
Ahmed Eldawy wrote:
> 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,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org

Ahmed Eldawy

unread,
Nov 22, 2009, 4:45:28 AM11/22/09
to
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.

Ahmed Eldawy

unread,
Nov 22, 2009, 1:39:18 PM11/22/09
to
Marnen Laibow-Koser wrote:
> 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,
> --
> Marnen Laibow-Koser
> http://www.marnen.org
> mar...@marnen.org

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

Yossef Mendelssohn

unread,
Nov 22, 2009, 6:24:12 PM11/22/09
to
On Nov 22, 12:39 pm, Ahmed Eldawy <ahmed.eld...@badrit.com> wrote:
> 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')

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.

--
-yossef

Marcin Raczkowski

unread,
Nov 23, 2009, 2:23:09 AM11/23/09
to
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

Ahmed Eldawy

unread,
Nov 25, 2009, 4:56:55 AM11/25/09
to

Marcin Raczkowski wrote:
> 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.

0 new messages