Tag Archives: sexp

RubyScope

grep and ack are indispensable tools, but they don’t know ruby. That’s where
RubyScope comes in. RubyScope lets you search your codebase for classes, methods, variables, and more. It understands ruby.

For instance you could use it to find all of your test methods:

ruby_scope -R --def '/^test/' ~/SecretProject

That would match:

def test_failing
  assert false
end

But it would skip this:

# TODO: test this later
def suspect_method
  test = 1 / 0
end

Installing

RubyScope is available as a normal gem:

gem install ruby_scope

Alternatively, you can get the source from GitHub:

git clone git://github.com/adamsanderson/ruby_scope.git

If you pull the source, install with rake install.

Examples

Here are some other examples of things you can do with RubyScope.

Find all the places the variable name is assigned:

ruby_scope -R --assign 'name' .

Find any place that cake is mentioned in the code base, this could be a variable, a method call, a symbol, or any other bit of ruby code:

ruby_scope -R --any 'cake' .

I often want to know where a method was defined:

ruby_scope -R --def 'save_or_fail' .

If you’re not afraid of writing a SexpPath query you can even define custom queries:

ruby_scope --custom 's(:call, s(:ivar, atom), :save, _)'

That will find all of the places save is called on an instance variable,
@post.save for instance.

Why

Ruby is a great language, it’s very flexible, but sometimes that can be annoying. These are all the same:

a = 1 / 0.0
(a = 1/0.0)
a =
  1 / 0.0

Meanwhile it can be quite frustrating to be looking for where a got assigned this ridiculous value, and have to wade through all the different places a is used.

This also served as a first step towards common code completion and refactoring tools that could be used by any ruby editor.

Hacking RubyScope

Want to play with ruby_scope? Take a look at cli.rb, this is where all the queries are actually generated. Have an idea for a better caching mechanism? Look at sexp_cache.rb. Want to change how the hits are reported? Take a look at scanner.rb

The source is on GitHub, so go ahead and branch it:

http://github.com/adamsanderson/ruby_scope

2 Comments

Filed under ruby

SexpPath 0.4

SexpPath 0.4 is complete now, and a gem is available from GitHub:

sudo gem install adamsanderson-sexp_path

New Features

Two new matchers have been added:

Negation
Matches any time a SexpPath query would normally not match, for instance this Q?{ -s(:a) } would match s(:b), but not s(:a).

Sibling
Matches any pair of siblings in sequential order. For instance this Q?{ s(:a) >> s(:c) } would match s( s(:a), s(:b), s(:c) ), but not s( s(:c), s(:a), s(:b) ).

I am also pretty excited that Magnus Holm has started playing with SexpPath, take a look at sexp_template and sexp_builder, they’re both starting to take shape.

Leave a comment

Filed under ruby

SexpPath

SexpPath is a ruby DSL for pattern matching S-Expressions. Think of it as XPath or Regular Expressions for Ruby code, and you’re most of the way there.

Here is an example of an S-Expression in Ruby:

pets = s(:pets, 
  s(:cat, :fluffy, s(:color, :white)),
  s(:cat, :snuggles, s(:color, :grey)),
  s(:fish, :bubbles, s(:drinks, :water)
)

This query extracts all the cats from our example:

pattern = Q?{ s(:cat, atom, _ ) }
cats = pets.search pattern

The SexpPath query above looks for expressions that start with the symbol :cat. The atom part says that we will match any symbol, so in the example above it would match both :fluffy and :snuggles. The underscore at the end will match anything at all, in this case, the S-Expression for color.

You can also match nested expressions with SexpPath:

pattern = Q?{ s(:cat, atom, s(:color, grey) ) }
grey_cats = pets.search pattern

SexpPath also has a notion of named matches. This query will place each cat’s name in the query result:

pattern = Q?{ s(:cat, atom % 'name', _ ) }
cat_matches = pets.search pattern
cat_names = cat_matches.map{|match| match['name']}

The % operator tells SexpPath where to stash a matching value.

Here is an example of using SexpPath with Ryan Davis’ excellent ParseTree library to extract all the methods in a given file:

# read in a ruby file
code = File.read('pony_factory.rb')
# parse the file with ParseTree
sexp = Sexp.from_array(ParseTree.new.parse_tree_for_string(code))
# create a SexpPath to find all the methods
pattern = Q?{ s(:defn, atom % 'name', _ ) }
# print all the methods in the ruby file
sexp.search(pattern).each do |match|
  puts match['name']
end

Now you’re doing static analysis on Ruby code! To learn more take a look at the Readme on GitHub, or skim over some of the examples.

If you have any suggestions or critiques of the code, API, etc. I would love to hear from you so comment, fork, or open an issue on GitHub.

4 Comments

Filed under ruby, ruby_diff, Uncategorized

RubyDiff 0.2, now with Subversion support

I saw a post over on Polishing Ruby about using the proper version requirements, and figured that it was about time for me to push out a new version of RubyDiff.  The major new feature is support for Subversion:

ruby_diff --svn http://project.svnserver.org/lib --svn BASE:lib

There are also some bugfixes dealing with classes referenced like: Module::SubModule::Class.

Leave a comment

Filed under ruby, ruby_diff