# \[ANN\] Detective - a new ruby gem

**URL:** <https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299>\
**Category:** Ruby\
**Created:** [November 21, 2009, 2:42pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299 "2009-11-21T14:42:36Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 21, 2009, 2:42pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/1 "2009-11-21T14:42:36Z")

</div>

Detective is a new gem build by BadrIT ([www.badrit.com](http://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

> **[GitHub - aseldawy/detective: A ruby gem built by BadrIT...](https://github.com/aseldawy/detective)**
>
> A ruby gem built by BadrIT (http://www.badrit.com) that allows you to check source code of methods - GitHub - aseldawy/detective: A ruby gem built by BadrIT (http://www.badrit.com) that allows you ...

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 21, 2009, 3:28pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/2 "2009-11-21T15:28:34Z")

</div>

On Sat, Nov 21, 2009 at 9:42 PM, Ahmed E.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)wrote:

> 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

Detective is hosted on gemcutter

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 21, 2009, 4:29pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/3 "2009-11-21T16:29:00Z")

</div>

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 E.  
> [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)wrote:
> 
> > 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
> 
> Detective is hosted on gemcutter

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 22, 2009, 10:45am UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/4 "2009-11-22T10:45:15Z")

</div>

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.

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](http://www.marnen.org)  
> removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 21, 2009, 7:07pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/5 "2009-11-21T19:07:57Z")

</div>

Ahmed E. 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](http://www.marnen.org)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 22, 2009, 7:38pm UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/6 "2009-11-22T19:38:26Z")

</div>

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](http://www.marnen.org)  
> removed\_email\_address@domain.invalid

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

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 23, 2009, 12:24am UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/7 "2009-11-23T00:24:52Z")

</div>

On Nov 22, 12:39 pm, Ahmed E. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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.

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 25, 2009, 10:55am UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/8 "2009-11-25T10:55:16Z")

</div>

Marcin R. 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.

---

<div class="post-metadata">

**Author:** ![aelshafee](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/46a35a/32.png) [@aelshafee](https://www.ruby-forum.com/u/aelshafee)\
**Post date:** [November 23, 2009, 8:26am UTC](https://www.ruby-forum.com/t/ann-detective-a-new-ruby-gem/179299/9 "2009-11-23T08:26:32Z")

</div>

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
