Parse Ruby file for comments - ruby

I want something that can parse a ruby file to give me the file positions of comments. Ranked by desirability:
Ideally, there would be some command-line arg I could pass to "ruby" since of course "ruby" would know. But there doesn't seem to be one for this?
Does anyone know if/where in "ruby" I could hook in and use its methods to know where the comments are?
Some well-known regular expression?
Thanks!

Found: https://github.com/ruby/ruby/tree/trunk/ext/ripper
Example:
require 'ripper'
require 'pp'
class CommentRipper < Ripper::SexpBuilder
def on_comment(token)
super.tap { |result| pp result }
end
end
contents = File.read("file.rb")
pp CommentRipper.new(contents).parse
Helped me understand Ripper better: http://svenfuchs.com/2009/7/5/using-ruby-1-9-ripper

Related

Get list of classes and methods that call a specific global method

I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:
class A
def foo
$muffinize(1)
end
def bar
...
end
end
class B
def shoop
$muffinize(2)
end
def woop
...
end
end
class C
def nope
...
end
end
I would like to the result to be (written to a file):
A:foo
B:shoop
I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?
Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.
If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.
Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either
#!/usr/local/env ruby
require 'ripper'
#require 'pry'
contents = File.open('example.rb').read
code = Ripper.lex(contents)
code.each do |line|
if(line[1] == :on_ident and line[2] == "muffinize")
puts "muffinize found at line #{line.first.first}"
end
end
Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.
Here's a simple example:
class A
def foo
bar
muffinize(1)
end
end
A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.
By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.

How can you find the name of the file that called a method in Ruby?

Is there a nice, recommended way to get the name of the file that called a method? I don't want to pass __FILE__ every time. The closest thing I've found is taking the first element of Kernel.caller, which is okay but has the calling line number appended like "test.rb:7". It's easy enough to strip off, but using it seems like something that might be dependent on the interpreter or might change in the future.
In Ruby 2.0+ you can do this using Kernel#caller_locations. It's very similar to caller, with the added benefit that you don't need to parse out the file name manually, since it returns Thread::Backtrace::Location objects instead of Strings:
file1.rb:
def some_method
puts caller_locations.first.path
end
file2.rb:
require_relative './file1'
some_method
Shell:
$ ruby file2.rb
file2.rb
Perhaps it's safer than you think? I found this other post http://snippets.dzone.com/posts/show/2787 where someone did something similar to what you're suggesting...

Is there something similar to Nokogiri for parsing Ruby code?

Nokogiri is awesome. I can do things like #css('.bla') which will return the first matching element.
Right now we need to do some parsing of Ruby source code - finding all methods within a class etc. We're using the ruby_parser gem, but all it does is comb your source code and spit out S-expressions. Is there anything like Nokogiri for these S-expressions which can do things like "return S-expression for first method found named 'foo'"?
The only thing I can think of, is Adam Sanderson's SExpPath library.
Although I am accepting Jörg's answer because it is more complete, I ended up discovering something else which I ended up using. ruby_parser installs a dependent gem named sexp_processor (it is in this gem where the Sexp class is actually defined). If you view the class docs there are a few methods that will help with basic Ruby finders. Here's an example:
class Sexp
def name # convenience method
self.sexp_body.first
end
end
# Print out all instance methods within classes. Beware - if "code" sexp itself
# is a class, it will NOT be included!
code = RubyParser.new.parse(IO.read('/src/file'))
code.each_of_type(:class){ |klass|
klass.each_of_type(:defn){ |meth|
puts meth.name
}
}
I don't know anything about gem that you are looking for, but you can find all methods within a class using instance_methods:
class Foo
def bar
end
end
irb(main):005:0> Foo.instance_methods - Object.instance_methods
=> [:bar]
You could check the rubinius parser, it may help you to do what you want.

Truncate #inspect output in irb (ruby)

I want to truncate #inspect output in irb (a large output must be cropped to MAX_LEN).
Currently, I override :inspect, :to_s methods for all specific objects.
Is there are other solution?
change $stdout ?
other?
For a clean solution, gem install hirb. hirb pages irb's returned values if they get too long.
If you want to monkeypatch irb:
module IRB
class Irb
def output_value
#context.last_value.to_s.slice(0, MAX_LEN)
end
end
end
I don't recommend this because it's a hack and breaks any time gems like ap and hirb are required.
Instead of monkeypatching irb, I'd recommend trying ripl, an irb alternative that is meant to extended.
The above as a ripl plugin would be:
require 'ripl'
module Ripl::SlicedInspect
def format_result(result)
result_prompt + result.inspect.slice(MAX_LEN)
end
end
Ripl::Shell.send :include, Ripl::SlicedInspect
With this plugin, you could require it as needed or add to your ~/.riplrc if you want to always use it.
Your solution is good.
It involves no dark magic, which might make the code less understandable and error-prone.
If you're just in IRB - you could define a monkeypatch in irb itself and or load a file that monkeypatches inspect via 'load'. This way you keep it out of your core codebase but you still get the functionality you need w/o having to override inspect in every class you wish to inspect....
If it's because you have a nested hash or something that's hard to decipher, try awesome_print. You can make it the default output formatter in irb by placing the following in your .irbrc:
require 'ap'
module IRB
class Irb
def output_value
ap #context.last_value
end
end
end
This makes objects with lots of data easy to decipher in IRB.
Even if you don't use awesome_print, you can truncate output using this same technique so you don't have to override to_s in your code.
For rails 3.1.1+, place the code below in helpers/irb_helper.rb
module IRB
class Irb
MAX_LEN = 10000
def output_value
if (#context.inspect_last_value.length > MAX_LEN)
printf #context.return_format, "#{#context.inspect_last_value[0..MAX_LEN]} <- Truncated"
else
printf #context.return_format, #context.inspect_last_value
end
end
end
end
If you'd like to customize your output more, check irb's source at https://github.com/Ruby/Ruby/blob/trunk/lib/irb.rb
I sometimes modify the objects themselves (via a module called BoringInspect which I include into the relevant classes) so that exception messages are also manageable.

Where does 'color_puts' come from?

I can't find where the color_puts would come from in the mod_passenger installer... any ideas?
Looked up myself just what you meant, since it's more fun than working.
Check the require statements at the top of the file - you'll see that there are only 3 possible sources, given that color_puts is not a Ruby standard.
Turns out, it's in abstract_installer.rb.
def color_print(text)
STDOUT.write(ConsoleTextTemplate.new(:text => text).result)
STDOUT.flush
end
def color_puts(text)
color_print("#{text}\n")
end

Resources