coypond 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+
4
+
5
+ group :development do
6
+ gem "shoulda", ">= 0"
7
+ gem "bundler", "~> 1.0.0"
8
+ gem "jeweler", "~> 1.6.4"
9
+ gem "rcov", ">= 0"
10
+ end
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2)
10
+ rcov (0.9.10)
11
+ shoulda (2.11.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ jeweler (~> 1.6.4)
19
+ rcov
20
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Amit Levy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ = coypond
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to coypond
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Amit Levy. See LICENSE.txt for
18
+ further details.
19
+
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "coypond"
18
+ gem.homepage = "http://github.com/alevy/coypond"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Semantic grep for Ruby}
21
+ gem.description = %Q{Semantic grep for Ruby}
22
+ gem.email = "aalevy@gmail.com"
23
+ gem.authors = ["Amit Levy"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "coypond #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'coypond'
4
+
5
+ def print_help
6
+ puts "Usage:"
7
+ puts " coypond search_term [options] file1 [file2 [file3 [...]]]"
8
+ puts ""
9
+ puts "Options:"
10
+ puts " -m search method names"
11
+ puts " -c search class names"
12
+ puts " -M search Module names"
13
+ puts " -a search all available type names (default: true unless another search option was given)"
14
+ end
15
+
16
+ if $*.first == "-h" || $*.first == "--help"
17
+ print_help
18
+ exit(0)
19
+ end
20
+ options = {:search_term => $*.shift}
21
+ while !$*.empty? and $*.first.start_with?("-")
22
+ option = $*.shift
23
+ case option
24
+ when "-r"
25
+ options[:regexp] = true
26
+ when "-i"
27
+ options[:ignore_case] = true
28
+ when "-m"
29
+ options[:method] = true
30
+ when "-c"
31
+ options[:class] = true
32
+ when "-M"
33
+ options[:module] = true
34
+ when "-a"
35
+ options[:all] = true
36
+ end
37
+ end
38
+
39
+ options[:all] ||= (options.keys & [:class, :method, :module]).empty?
40
+
41
+ coypond = Coypond::CoypondRunner.new($*)
42
+ results = coypond.search(options)
43
+
44
+ results.each do |file_name, res|
45
+ unless res.empty?
46
+ puts "#{file_name}:"
47
+ res.each do |dec, location, context|
48
+ puts " #{location.join(",")}: #{context.strip}"
49
+ end
50
+ puts ""
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ require 'ripper'
2
+ require 'coypond/parser'
3
+
4
+ module Coypond
5
+ class CoypondRunner
6
+
7
+ def initialize(files)
8
+ @files = files
9
+ end
10
+
11
+ def search(options)
12
+ final_result = {}
13
+ each_file do |file_name, source, parser|
14
+ final_result[file_name] = []
15
+ classes = modules = methods = {}
16
+ classes = search_in_collection(parser.classes, options) if options[:class] || options[:all]
17
+ modules = search_in_collection(parser.modules, options) if options[:module] || options[:all]
18
+ methods = search_in_collection(parser.methods, options) if options[:method] || options[:all]
19
+ result = classes.merge(modules.merge(methods))
20
+ result.each do |dec, location|
21
+ context = source.lines.to_a[location.first - 1]
22
+ final_result[file_name] << [dec, location, context]
23
+ end
24
+ end
25
+ return final_result
26
+ end
27
+
28
+ private
29
+ def search_in_collection(collection, options)
30
+ search_term = options[:search_term]
31
+ search_term = search_term.downcase if options[:ignore_case]
32
+ collection.select do |k,v|
33
+ k = k.downcase if options[:ignore_case]
34
+ if options[:regexp]
35
+ Regexp.new("#{search_term}$").match(k)
36
+ else
37
+ k.end_with?(search_term)
38
+ end
39
+ end
40
+ end
41
+
42
+ def each_file
43
+ @files.each do |file|
44
+ source = File.read(file)
45
+ parse_tree = Ripper::SexpBuilder.new(source).parse
46
+ parser = Coypond::Parser.new.parse(parse_tree)
47
+ yield(file, source, parser)
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ module Coypond
2
+ class Parser
3
+
4
+ attr_reader :classes, :modules, :methods
5
+
6
+ def initialize
7
+ @classes = {}
8
+ @modules = {}
9
+ @methods = {}
10
+ end
11
+
12
+ def extract_ref(type, *tail)
13
+ case type
14
+ when :const_path_ref
15
+ var_ref = extract_ref(*(tail.first)).first
16
+ name, location = extract_const_and_location(tail[1])
17
+ return [[var_ref, name].join("::"), location]
18
+ else
19
+ return extract_const_and_location(tail.first)
20
+ end
21
+ end
22
+
23
+ def parse(parse_tree, prefix=nil)
24
+ return self unless parse_tree.is_a? Array
25
+ case parse_tree.first
26
+ when :module
27
+ name, location = extract_ref(*(parse_tree[1]))
28
+ prefix = [prefix, name].compact.join("::")
29
+ @modules[prefix] = location
30
+ parse_tree[2..-1].each do |pt|
31
+ parse(pt, prefix)
32
+ end
33
+ when :class
34
+ name, location = extract_ref(*(parse_tree[1]))
35
+ prefix = [prefix, name].compact.join("::")
36
+ @classes[prefix] = location
37
+ parse_tree[2..-1].each do |pt|
38
+ parse(pt, prefix)
39
+ end
40
+ when :def
41
+ name, location = extract_const_and_location(parse_tree[1])
42
+ prefix = [prefix, name].compact.join("#")
43
+ @methods[prefix] = location
44
+ else
45
+ parse_tree[1..-1].each do |pt|
46
+ parse(pt, prefix)
47
+ end
48
+ end
49
+ return self
50
+ end
51
+
52
+ private
53
+ def extract_const_and_location(tuple)
54
+ return tuple[1..2]
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,101 @@
1
+ require 'helper'
2
+ require 'coypond/parser'
3
+ require 'ripper'
4
+
5
+ class TestParser < Test::Unit::TestCase
6
+ should "correctly extract const_ref" do
7
+ result = Coypond::Parser.new.extract_ref(*[:const_ref, [:@const, "Baz", [1, 22]]])
8
+ assert_equal ["Baz", [1, 22]], result
9
+ end
10
+
11
+ should "correctly extract var_ref" do
12
+ result = Coypond::Parser.new.extract_ref(*[:var_ref, [:@const, "Baz", [1, 22]]])
13
+ assert_equal ["Baz", [1, 22]], result
14
+ end
15
+
16
+ should "correctly extract const_path_ref" do
17
+ result = Coypond::Parser.new.extract_ref(*[:const_path_ref,
18
+ [:var_ref, [:@const, "Bar", [1, 22]]],
19
+ [:@const, "Baz", [1, 24]]])
20
+ assert_equal ["Bar::Baz", [1, 24]], result
21
+ end
22
+
23
+ should "capture instances of `class` in classes instance variable" do
24
+ parse_tree = [:program,
25
+ [:stmts_add,
26
+ [:stmts_add,
27
+ [:stmts_new],
28
+ [:class, [:const_ref, [:@const, "Foo", [1, 6]]], nil,
29
+ [:bodystmt,
30
+ [:stmts_add,
31
+ [:stmts_new],
32
+ [:void_stmt]], nil, nil, nil]]],
33
+ [:class, [:const_ref, [:@const, "Baz", [1, 22]]], nil,
34
+ [:bodystmt,
35
+ [:stmts_add,
36
+ [:stmts_new],
37
+ [:void_stmt]], nil, nil, nil]]]]
38
+ parser = Coypond::Parser.new.parse(parse_tree)
39
+
40
+ assert_equal ["Foo", "Baz"], parser.classes.keys
41
+ end
42
+
43
+ should "use fully qualified class name in classes instance variable" do
44
+ parse_tree = [:class,
45
+ [:const_path_ref,
46
+ [:var_ref,
47
+ [:@const, "Bar", [1, 6]]],
48
+ [:@const, "Foo", [1, 11]]],
49
+ nil,
50
+ [:bodystmt, [:stmts_add, [:stmts_new], [:void_stmt]], nil, nil, nil]]
51
+ parser = Coypond::Parser.new.parse(parse_tree)
52
+
53
+ assert_equal ["Bar::Foo"], parser.classes.keys
54
+ end
55
+
56
+ should "use multi-level fully qualified class name in classes instance variable" do
57
+ parse_tree = [:class,
58
+ [:const_path_ref,
59
+ [:const_path_ref,
60
+ [:var_ref,
61
+ [:@const, "Bar", [1, 6]]],
62
+ [:@const, "Baz", [1, 11]]],
63
+ [:@const, "Foo", [1, 16]]],
64
+ nil,
65
+ [:bodystmt, [:stmts_add, [:stmts_new], [:void_stmt]], nil, nil, nil]]
66
+ parser = Coypond::Parser.new.parse(parse_tree)
67
+
68
+ assert_equal ["Bar::Baz::Foo"], parser.classes.keys
69
+ end
70
+
71
+ should "use fully qualified class name with module in classes instance variable" do
72
+ parse_tree = [:module, [:const_ref, [:@const, "Bar", [1, 7]]],
73
+ [:bodystmt, [:stmts_add, [:stmts_add, [:stmts_new], [:void_stmt]],
74
+ [:class, [:const_ref, [:@const, "Foo", [1, 17]]], nil,
75
+ [:bodystmt,
76
+ [:stmts_add,
77
+ [:stmts_new],
78
+ [:void_stmt]], nil, nil, nil]]], nil, nil, nil]]
79
+ parser = Coypond::Parser.new.parse(parse_tree)
80
+
81
+ assert_equal ["Bar::Foo"], parser.classes.keys
82
+ end
83
+
84
+ should "capture instances of `module` in modules instance variable" do
85
+ parse_tree = [:module, [:const_ref, [:@const, "Bar", [1, 7]]],
86
+ [:bodystmt, [:stmts_add, [:stmts_new], [:void_stmt]], nil, nil, nil]]
87
+ parser = Coypond::Parser.new.parse(parse_tree)
88
+
89
+ assert_equal ["Bar"], parser.modules.keys
90
+ end
91
+
92
+ should "capture instances of `def` in methods instance variable" do
93
+ parse_tree = [:def, [:@ident, "foo", [1, 4]],
94
+ [:params, nil, nil, nil, nil, nil],
95
+ [:bodystmt, [:stmts_add, [:stmts_new], [:void_stmt]], nil, nil, nil]]
96
+ parser = Coypond::Parser.new.parse(parse_tree)
97
+
98
+ assert_equal ["foo"], parser.methods.keys
99
+ end
100
+
101
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'coypond'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestCoypond < Test::Unit::TestCase
4
+ # should "probably rename this file and start testing for real" do
5
+ # flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ # end
7
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coypond
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Amit Levy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-10 00:00:00.000000000 -07:00
13
+ default_executable: coypond
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda
17
+ requirement: &2157206000 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2157206000
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: &2157205080 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2157205080
37
+ - !ruby/object:Gem::Dependency
38
+ name: jeweler
39
+ requirement: &2157204160 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 1.6.4
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2157204160
48
+ - !ruby/object:Gem::Dependency
49
+ name: rcov
50
+ requirement: &2157203060 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2157203060
59
+ description: Semantic grep for Ruby
60
+ email: aalevy@gmail.com
61
+ executables:
62
+ - coypond
63
+ extensions: []
64
+ extra_rdoc_files:
65
+ - LICENSE.txt
66
+ - README.rdoc
67
+ files:
68
+ - .document
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE.txt
72
+ - README.rdoc
73
+ - Rakefile
74
+ - VERSION
75
+ - bin/coypond
76
+ - lib/coypond.rb
77
+ - lib/coypond/parser.rb
78
+ - test/coypond/test_parser.rb
79
+ - test/helper.rb
80
+ - test/test_coypond.rb
81
+ has_rdoc: true
82
+ homepage: http://github.com/alevy/coypond
83
+ licenses:
84
+ - MIT
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ segments:
96
+ - 0
97
+ hash: 3557283599531574218
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.6.2
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Semantic grep for Ruby
110
+ test_files: []