radar-lookup 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.md +25 -0
- data/Rakefile +63 -0
- data/TODO +2 -0
- data/bin/lookup +3 -0
- data/lib/config.rb +5 -0
- data/lib/lookup.rb +233 -0
- data/spec/lookup_spec.rb +32 -0
- data/spec/spec_helper.rb +3 -0
- metadata +63 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Ryan Bigg
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Lazy Man's ri (lookup)
|
|
2
|
+
|
|
3
|
+
## Example usage:
|
|
4
|
+
|
|
5
|
+
* `lookup ActiveRecord::Base#new` (returns a single method, since the method name is right)
|
|
6
|
+
* `lookup ActiveRecord::Base#destroy` (returns two methods, since there's two methods with that name)
|
|
7
|
+
* `lookup ActiveRecord::Base#destro` (returns three methods, uses methods beginning with "destroy")
|
|
8
|
+
* `lookup ActiveRecord::Base#d` (tells you to be more specific, because it can't open 35 tabs at once)
|
|
9
|
+
* `lookup ActiveRecord::Base` (returns a single consant)
|
|
10
|
+
* `lookup Acv::Base` (returns six constants, because it does a fuzzy match)
|
|
11
|
+
|
|
12
|
+
## Options
|
|
13
|
+
|
|
14
|
+
It also takes options:
|
|
15
|
+
|
|
16
|
+
* `-c or --clear` will delete the database and update the api again. This can take a minute or two.
|
|
17
|
+
|
|
18
|
+
## How it finds them
|
|
19
|
+
|
|
20
|
+
1. Checks if there's constants/methods with that exact name.
|
|
21
|
+
2. Checks if there's constants/methods with names beginning with that name.
|
|
22
|
+
3. Does a "fuzzy match" splitting the name and getting anything containing those letters in that order.
|
|
23
|
+
4. Opens your browser if you're running a DECENT_OPERATING_SYSTEM (may add support for things other than Mac later on)
|
|
24
|
+
5. ???
|
|
25
|
+
6. Profit
|
data/Rakefile
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake/gempackagetask'
|
|
3
|
+
require 'rubygems/specification'
|
|
4
|
+
require 'date'
|
|
5
|
+
require 'spec/rake/spectask'
|
|
6
|
+
|
|
7
|
+
GEM = "lookup"
|
|
8
|
+
GEM_VERSION = "0.1.0"
|
|
9
|
+
AUTHOR = "Ryan Bigg"
|
|
10
|
+
EMAIL = "radarlistener@gmail.com"
|
|
11
|
+
HOMEPAGE = "http://gitpilot.com"
|
|
12
|
+
SUMMARY = "A gem that provides a lazy man's ri"
|
|
13
|
+
|
|
14
|
+
spec = Gem::Specification.new do |s|
|
|
15
|
+
s.name = GEM
|
|
16
|
+
s.version = GEM_VERSION
|
|
17
|
+
s.platform = Gem::Platform::RUBY
|
|
18
|
+
s.has_rdoc = true
|
|
19
|
+
s.extra_rdoc_files = ["README.md", "LICENSE", 'TODO']
|
|
20
|
+
s.summary = SUMMARY
|
|
21
|
+
s.description = s.summary
|
|
22
|
+
s.author = AUTHOR
|
|
23
|
+
s.email = EMAIL
|
|
24
|
+
s.homepage = HOMEPAGE
|
|
25
|
+
s.executables << "lookup"
|
|
26
|
+
|
|
27
|
+
# Uncomment this to add a dependency
|
|
28
|
+
# s.add_dependency "foo"
|
|
29
|
+
|
|
30
|
+
s.require_path = 'lib'
|
|
31
|
+
s.autorequire = GEM
|
|
32
|
+
(Dir.entries("doc") - ['..', '.']).each do |file|
|
|
33
|
+
FileUtils.rm("doc/#{file}")
|
|
34
|
+
end
|
|
35
|
+
Dir.delete("doc")
|
|
36
|
+
Dir.mkdir("doc")
|
|
37
|
+
s.files = %w(LICENSE README.md Rakefile TODO) + Dir.glob("{lib,spec,bin,doc}/**/*")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
task :default => :spec
|
|
41
|
+
|
|
42
|
+
desc "Run specs"
|
|
43
|
+
Spec::Rake::SpecTask.new do |t|
|
|
44
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
|
45
|
+
t.spec_opts = %w(-fs --color)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
|
50
|
+
pkg.gem_spec = spec
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
desc "install the gem locally"
|
|
54
|
+
task :install => [:package] do
|
|
55
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
desc "create a gemspec file"
|
|
59
|
+
task :make_spec do
|
|
60
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
|
61
|
+
file.puts spec.to_ruby
|
|
62
|
+
end
|
|
63
|
+
end
|
data/bin/lookup
ADDED
data/lib/config.rb
ADDED
data/lib/lookup.rb
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'activerecord'
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
MAC = !!/darwin/.match(PLATFORM)
|
|
6
|
+
WINDOWS = !!/win/.match(PLATFORM)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# How many methods / constants to return.
|
|
10
|
+
THRESHOLD = 5
|
|
11
|
+
|
|
12
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => File.join(File.dirname(__FILE__), "lookup.sqlite3"))
|
|
13
|
+
class Lookup
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def update
|
|
17
|
+
require 'hpricot'
|
|
18
|
+
require 'net/http'
|
|
19
|
+
puts "Updating API, this may take a minute or two. Please be patient!"
|
|
20
|
+
Constant.delete_all
|
|
21
|
+
Entry.delete_all
|
|
22
|
+
# Ruby on Rails Classes & Methods
|
|
23
|
+
update_api("Rails", "http://api.rubyonrails.org")
|
|
24
|
+
# Ruby Classes & Methods
|
|
25
|
+
update_api("Ruby", "http://www.ruby-doc.org/core")
|
|
26
|
+
|
|
27
|
+
weight_results
|
|
28
|
+
puts "Updated API index! Use the lookup <method> or lookup <class> <method> to find what you're after"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update_api(name, url)
|
|
32
|
+
puts "Updating API for #{name}"
|
|
33
|
+
Api.find_or_create_by_name_and_url(name, url)
|
|
34
|
+
update_methods(Hpricot(Net::HTTP.get(URI.parse("#{url}/fr_method_index.html"))), url)
|
|
35
|
+
update_classes(Hpricot(Net::HTTP.get(URI.parse("#{url}/fr_class_index.html"))), url)
|
|
36
|
+
puts "Updated API for #{name}!"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def update_methods(doc, prefix)
|
|
40
|
+
doc.search("a").each do |a|
|
|
41
|
+
names = a.inner_html.split(" ")
|
|
42
|
+
method = names[0]
|
|
43
|
+
name = names[1].gsub(/[\(|\)]/, "")
|
|
44
|
+
# The same constant can be defined twice in different APIs, be wary!
|
|
45
|
+
url = prefix + "/classes/" + name.gsub("::", "/") + ".html"
|
|
46
|
+
constant = Constant.find_or_create_by_name_and_url(name, url)
|
|
47
|
+
constant.entries.create!(:name => method, :url => prefix + "/" + a["href"])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def update_classes(doc, prefix)
|
|
52
|
+
doc.search("a").each do |a|
|
|
53
|
+
constant = Constant.find_or_create_by_name_and_url(a.inner_html, prefix + "/" + a["href"])
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Weights the results so the ones more likely to be used by people come up first.
|
|
58
|
+
def weight_results
|
|
59
|
+
e = Constant.find_by_name("ActiveRecord::Associations::ClassMethods").entries.find_by_name("belongs_to")
|
|
60
|
+
e.increment!(:weighting)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def find_constant(name, entry=nil)
|
|
64
|
+
# Find by specific name.
|
|
65
|
+
constants = Constant.find_all_by_name(name, :include => "entries")
|
|
66
|
+
# Find by name beginning with <blah>.
|
|
67
|
+
constants = Constant.all(:conditions => ["name LIKE ?", name + "%"], :include => "entries") if constants.empty?
|
|
68
|
+
# Find by fuzzy.
|
|
69
|
+
constants = Constant.find_by_sql("select * from constants where name LIKE '%#{name.split("").join("%")}%'") if constants.empty?
|
|
70
|
+
if constants.size > 1
|
|
71
|
+
# Narrow it down to the constants that only contain the entry we are looking for.
|
|
72
|
+
if !entry.nil?
|
|
73
|
+
constants = constants.select { |constant| !constant.entries.find_by_name(entry).nil? }
|
|
74
|
+
return [constants, constants.size]
|
|
75
|
+
else
|
|
76
|
+
display_constants(constants)
|
|
77
|
+
end
|
|
78
|
+
if constants.size == 1
|
|
79
|
+
if entry.nil?
|
|
80
|
+
display_constants(constants)
|
|
81
|
+
else
|
|
82
|
+
return [[constants.first], 1]
|
|
83
|
+
end
|
|
84
|
+
elsif constants.size == 0
|
|
85
|
+
if entry
|
|
86
|
+
puts "There are no constants that match #{name} and contain #{entry}."
|
|
87
|
+
else
|
|
88
|
+
puts "There are no constants that match #{name}"
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
return [constants, constants.size]
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
if entry.nil?
|
|
95
|
+
display_constants(constants)
|
|
96
|
+
else
|
|
97
|
+
return [[constants.first], 1]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Find an entry.
|
|
103
|
+
# If the constant argument is passed, look it up within the scope of the constant.
|
|
104
|
+
def find_method(name, constant=nil)
|
|
105
|
+
if constant
|
|
106
|
+
constants, number = find_constant(constant, name)
|
|
107
|
+
end
|
|
108
|
+
methods = []
|
|
109
|
+
methods = Entry.find_all_by_name(name.to_s)
|
|
110
|
+
methods = Entry.all(:conditions => ["name LIKE ?", name.to_s + "%"]) if methods.empty?
|
|
111
|
+
methods = Entry.find_by_sql("select * from entries where name LIKE '%#{name.split("").join("%")}%'") if methods.empty?
|
|
112
|
+
|
|
113
|
+
# Weight the results, last result is the first one we want shown first
|
|
114
|
+
methods = methods.sort_by(&:weighting)
|
|
115
|
+
|
|
116
|
+
if constant
|
|
117
|
+
methods = methods.select { |m| constants.include?(m.constant) }
|
|
118
|
+
end
|
|
119
|
+
count = 0
|
|
120
|
+
if methods.size == 1
|
|
121
|
+
method = methods.first
|
|
122
|
+
if OPTIONS[:text]
|
|
123
|
+
puts display_method(method)
|
|
124
|
+
elsif MAC
|
|
125
|
+
`open #{method.url}`
|
|
126
|
+
elsif WINDOWS
|
|
127
|
+
`start #{method.url}`
|
|
128
|
+
else
|
|
129
|
+
puts display_method(method)
|
|
130
|
+
end
|
|
131
|
+
elsif methods.size <= THRESHOLD
|
|
132
|
+
for method in methods
|
|
133
|
+
if OPTIONS[:text]
|
|
134
|
+
puts "#{count += 1}. #{display_method(method)}"
|
|
135
|
+
elsif MAC
|
|
136
|
+
`open #{method.url}`
|
|
137
|
+
elsif WINDOWS
|
|
138
|
+
`start #{method.url}`
|
|
139
|
+
else
|
|
140
|
+
puts "#{count += 1}. #{display_method(method)}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
methods
|
|
144
|
+
else
|
|
145
|
+
puts "Please refine your query, we found #{methods.size} methods. Threshold is #{THRESHOLD}."
|
|
146
|
+
end
|
|
147
|
+
return nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def display_method(method)
|
|
151
|
+
"(#{method.constant.name}) #{method.name} #{method.url}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def display_constants(constants)
|
|
156
|
+
count = 0
|
|
157
|
+
if constants.size == 1
|
|
158
|
+
constant = constants.first
|
|
159
|
+
if OPTIONS[:text]
|
|
160
|
+
puts "#{constant.name} #{constant.url}"
|
|
161
|
+
elsif MAC && !OPTIONS[:text]
|
|
162
|
+
`open #{constant.url}`
|
|
163
|
+
elsif WINDOWS && !OPTIONS[:text]
|
|
164
|
+
`start #{constant.url}`
|
|
165
|
+
else
|
|
166
|
+
puts "#{constant.name} #{constant.url}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
elsif constants.size <= THRESHOLD
|
|
170
|
+
for constant in constants
|
|
171
|
+
if OPTIONS[:text]
|
|
172
|
+
puts "#{count+=1}. #{constant.name} #{constant.url}"
|
|
173
|
+
elsif MAC
|
|
174
|
+
`open #{constant.url}`
|
|
175
|
+
elsif WINDOWS
|
|
176
|
+
`open #{constant.url}`
|
|
177
|
+
else
|
|
178
|
+
puts "#{count+=1}. #{constant.name} #{constant.url}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
puts "Please refine your query, we found #{constants.size} constants (threshold is #{THRESHOLD})."
|
|
183
|
+
end
|
|
184
|
+
return nil
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def do(msg)
|
|
188
|
+
msg = msg.split(" ")[0..-1].flatten.map { |a| a.split("#") }.flatten!
|
|
189
|
+
|
|
190
|
+
# It's a constant! Oh... and there's nothing else in the string!
|
|
191
|
+
if /^[A-Z]/.match(msg.first) && msg.size == 1
|
|
192
|
+
object = find_constant(msg.first)
|
|
193
|
+
# It's a method!
|
|
194
|
+
else
|
|
195
|
+
# Right, so they only specified one argument. Therefore, we look everywhere.
|
|
196
|
+
if msg.first == msg.last
|
|
197
|
+
object = find_method(msg)
|
|
198
|
+
# Left, so they specified two arguments. First is probably a constant, so let's find that!
|
|
199
|
+
else
|
|
200
|
+
object = find_method(msg.last, msg.first)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def setup
|
|
206
|
+
file = File.join(File.dirname(__FILE__), "lookup.sqlite3")
|
|
207
|
+
FileUtils.rm(file) if File.exists?(file) && OPTIONS[:clear]
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
OPTIONS = {}
|
|
213
|
+
unless $TESTING
|
|
214
|
+
OptionParser.new do |opts|
|
|
215
|
+
opts.banner = "Usage: lookup <constant|method> [method] [OPTIONS]"
|
|
216
|
+
|
|
217
|
+
opts.on("-c", "--clear", "Clear database") do
|
|
218
|
+
OPTIONS[:clear] = true
|
|
219
|
+
end
|
|
220
|
+
opts.on("-t", "--text", "Text only") do
|
|
221
|
+
OPTIONS[:text] = true
|
|
222
|
+
end
|
|
223
|
+
end.parse!
|
|
224
|
+
end
|
|
225
|
+
# Ready!
|
|
226
|
+
Lookup.setup
|
|
227
|
+
|
|
228
|
+
# Set!
|
|
229
|
+
require File.join(File.dirname(__FILE__), 'models')
|
|
230
|
+
|
|
231
|
+
# Go!
|
|
232
|
+
Lookup.do(ARGV[0..-1])
|
|
233
|
+
|
data/spec/lookup_spec.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Lookup" do
|
|
4
|
+
before do
|
|
5
|
+
# So it outputs text for us.
|
|
6
|
+
OPTIONS.merge!({ :text => true })
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should be able to find a constant" do
|
|
10
|
+
Lookup.do("ActiveRecord::Base")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should be able to find a constant and a method (using hash symbol)" do
|
|
14
|
+
Lookup.do("ActiveRecord::Base#new")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should be able to find a constant and a method (using space)" do
|
|
18
|
+
Lookup.do("ActiveRecord::Base new")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should be able to do a fuzzy match on the method" do
|
|
22
|
+
Lookup.do("ActiveRecord::Base#destry")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should prompt the user to be more specific" do
|
|
26
|
+
Lookup.do("be")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should be able to do a fuzzy match on the constant and method" do
|
|
30
|
+
Lookup.do("AR::B#destroy")
|
|
31
|
+
end
|
|
32
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: radar-lookup
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ryan Bigg
|
|
8
|
+
autorequire: lookup
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-02-07 00:00:00 -08:00
|
|
13
|
+
default_executable: lookup
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: A gem that provides a lazy man's ri
|
|
17
|
+
email: radarlistener@gmail.com
|
|
18
|
+
executables:
|
|
19
|
+
- lookup
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files:
|
|
23
|
+
- README.md
|
|
24
|
+
- LICENSE
|
|
25
|
+
- TODO
|
|
26
|
+
files:
|
|
27
|
+
- LICENSE
|
|
28
|
+
- README.md
|
|
29
|
+
- Rakefile
|
|
30
|
+
- TODO
|
|
31
|
+
- lib/config.rb
|
|
32
|
+
- lib/lookup.rb
|
|
33
|
+
- spec/lookup_spec.rb
|
|
34
|
+
- spec/spec_helper.rb
|
|
35
|
+
- bin/lookup
|
|
36
|
+
has_rdoc: true
|
|
37
|
+
homepage: http://gitpilot.com
|
|
38
|
+
post_install_message:
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: "0"
|
|
48
|
+
version:
|
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: "0"
|
|
54
|
+
version:
|
|
55
|
+
requirements: []
|
|
56
|
+
|
|
57
|
+
rubyforge_project:
|
|
58
|
+
rubygems_version: 1.2.0
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 2
|
|
61
|
+
summary: A gem that provides a lazy man's ri
|
|
62
|
+
test_files: []
|
|
63
|
+
|