radar-lookup 0.1.0 → 0.2.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.
Files changed (6) hide show
  1. data/LICENSE +2 -0
  2. data/README.md +1 -0
  3. data/bin/lookup +78 -2
  4. data/lib/lookup.rb +51 -145
  5. data/lib/models.rb +70 -0
  6. metadata +3 -2
data/LICENSE CHANGED
@@ -1,5 +1,7 @@
1
1
  Copyright (c) 2009 Ryan Bigg
2
2
 
3
+ Contributions by: Josh Goebel / Dreamer3
4
+
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
5
7
  "Software"), to deal in the Software without restriction, including
data/README.md CHANGED
@@ -14,6 +14,7 @@
14
14
  It also takes options:
15
15
 
16
16
  * `-c or --clear` will delete the database and update the api again. This can take a minute or two.
17
+ * `-t or --text` is useful for when you don't want lookup to spam tabs into your browser willy-nilly.
17
18
 
18
19
  ## How it finds them
19
20
 
data/bin/lookup CHANGED
@@ -1,3 +1,79 @@
1
1
  #!/usr/bin/ruby
2
- require 'rubygems'
3
- require 'lookup'
2
+ require 'optparse'
3
+
4
+ # How many methods / constants to return.
5
+ THRESHOLD = 5
6
+ MAC = !!/darwin/.match(PLATFORM)
7
+ WINDOWS = !!/win/.match(PLATFORM) if not MAC
8
+
9
+ def clear_database
10
+ file = File.join(File.dirname(__FILE__), "lookup.sqlite3")
11
+ FileUtils.rm(file) if File.exists?(file)
12
+ end
13
+
14
+ def display_url(result, options={})
15
+ # if we're not on mac or windows we default to text output
16
+ if OPTIONS[:text] or not (MAC || WINDOWS)
17
+ s = options[:number] ? options[:number].to_s + ". " : ""
18
+ # if we're a method then show the constant in parans
19
+ s += "(#{result.constant.name}) " if result.respond_to?(:constant)
20
+ s += "#{result.name} #{result.url}"
21
+ puts s
22
+ elsif MAC
23
+ `open #{result.url}`
24
+ elsif WINDOWS
25
+ `start #{result.url}`
26
+ end
27
+ end
28
+
29
+ def display_results(results, search_string)
30
+ if results.empty?
31
+ puts "There were no results matching #{search_string}."
32
+ # if entry
33
+ # puts "There are no constants that match #{name} and contain #{entry}."
34
+ # else
35
+ # puts "There are no constants that match #{name}"
36
+ # end
37
+ elsif results.size == 1
38
+ display_url(results.first)
39
+ elsif results.size <= THRESHOLD
40
+ results.each_with_index do |result, i|
41
+ display_url(result, :number => i+1)
42
+ end
43
+ else
44
+ puts "Please refine your query, we found #{results.size} results (threshold is #{THRESHOLD})."
45
+ end
46
+ end
47
+
48
+ OPTIONS = {}
49
+ op=OptionParser.new do |opts|
50
+ opts.banner = "Usage: lookup <constant|method> [method] [OPTIONS]"
51
+
52
+ opts.on("-c", "--clear", "Clear database") do
53
+ OPTIONS[:clear] = true
54
+ end
55
+ opts.on("-t", "--text", "Text only") do
56
+ OPTIONS[:text] = true
57
+ end
58
+ end
59
+ op.parse!
60
+
61
+ clear_database if OPTIONS[:clear]
62
+
63
+
64
+ local_lib=File.join(File.dirname(__FILE__), "../lib/lookup")
65
+ if File.exists?(local_lib + ".rb")
66
+ require local_lib
67
+ else
68
+ require 'rubygems'
69
+ require 'lookup'
70
+ end
71
+
72
+ search_string=ARGV[0..-1].join(" ")
73
+ if search_string.strip.empty?
74
+ puts op.help
75
+ puts
76
+ else
77
+ results=APILookup.search(ARGV[0..-1])
78
+ display_results(results, search_string)
79
+ end
data/lib/lookup.rb CHANGED
@@ -1,16 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'activerecord'
3
- require 'optparse'
4
3
 
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
4
+ class APILookup
14
5
 
15
6
  class << self
16
7
  def update
@@ -29,11 +20,11 @@ class Lookup
29
20
  end
30
21
 
31
22
  def update_api(name, url)
32
- puts "Updating API for #{name}"
23
+ puts "Updating API for #{name}..."
33
24
  Api.find_or_create_by_name_and_url(name, url)
34
25
  update_methods(Hpricot(Net::HTTP.get(URI.parse("#{url}/fr_method_index.html"))), url)
35
26
  update_classes(Hpricot(Net::HTTP.get(URI.parse("#{url}/fr_class_index.html"))), url)
36
- puts "Updated API for #{name}!"
27
+ puts "DONE (with #{name})!"
37
28
  end
38
29
 
39
30
  def update_methods(doc, prefix)
@@ -63,49 +54,52 @@ class Lookup
63
54
  def find_constant(name, entry=nil)
64
55
  # Find by specific name.
65
56
  constants = Constant.find_all_by_name(name, :include => "entries")
57
+ # search for class methods, which is prolly what we want if we can find it
58
+ constants = Constant.find_all_by_name("#{name}::ClassMethods", :include => "entries") if constants.empty?
66
59
  # Find by name beginning with <blah>.
67
60
  constants = Constant.all(:conditions => ["name LIKE ?", name + "%"], :include => "entries") if constants.empty?
68
61
  # 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
62
+ match="%#{name.split("").join("%")}%"
63
+ constants = Constant.find_by_sql("select * from constants where name LIKE '#{match}'") if constants.empty?
64
+ regex=build_regex_from_constant(name)
65
+ constants = constants.select { |x| x.name =~ regex }
66
+ # Narrow it down to the constants that only contain the entry we are looking for.
67
+ if entry
68
+ constants = constants.select { |constant| !constant.entries.find_by_name(entry).nil? }
69
+ end
70
+ constants
71
+ end
72
+
73
+ # this uses a regex to lock down our SQL finds even more
74
+ # so that things like AR::Base will not match
75
+ # ActiveRecord::ConnectionAdapters::DatabaseStatements
76
+ def build_regex_from_constant(name)
77
+ parts=name.split("::").map do |c|
78
+ c.split("").join("[^:]*")+"[^:]*"
99
79
  end
100
- end
80
+ /#{parts.join("::")}/i
81
+ end
82
+
83
+ def smart_rails_constant_substitutions(name)
84
+ parts=name.split("::").map { |x| x.split(":")}.flatten
85
+ rep = case parts.first.downcase
86
+ # so it falls back on fuzzy and matches AR as well as ActiveResource
87
+ when "ar": "ActiveRecord"
88
+ when "ares": "ActiveResource"
89
+ when "am": "ActionMailer"
90
+ when "as": "ActiveSupport"
91
+ when "ac": "ActionController"
92
+ when "av": "ActionView"
93
+ else
94
+ parts.first
95
+ end
96
+ ([rep] + parts[1..-1]).join("::")
97
+ end
101
98
 
102
99
  # Find an entry.
103
100
  # If the constant argument is passed, look it up within the scope of the constant.
104
101
  def find_method(name, constant=nil)
105
- if constant
106
- constants, number = find_constant(constant, name)
107
- end
108
- methods = []
102
+ methods = []
109
103
  methods = Entry.find_all_by_name(name.to_s)
110
104
  methods = Entry.all(:conditions => ["name LIKE ?", name.to_s + "%"]) if methods.empty?
111
105
  methods = Entry.find_by_sql("select * from entries where name LIKE '%#{name.split("").join("%")}%'") if methods.empty?
@@ -114,120 +108,32 @@ class Lookup
114
108
  methods = methods.sort_by(&:weighting)
115
109
 
116
110
  if constant
111
+ constants = find_constant(constant, name)
117
112
  methods = methods.select { |m| constants.include?(m.constant) }
118
113
  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
114
+ methods
185
115
  end
186
-
187
- def do(msg)
116
+
117
+ def search(msg)
188
118
  msg = msg.split(" ")[0..-1].flatten.map { |a| a.split("#") }.flatten!
189
119
 
190
120
  # 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)
121
+ first=smart_rails_constant_substitutions(msg.first)
122
+ if /^[A-Z]/.match(first) && msg.size == 1
123
+ find_constant(first)
193
124
  # It's a method!
194
125
  else
195
126
  # Right, so they only specified one argument. Therefore, we look everywhere.
196
- if msg.first == msg.last
197
- object = find_method(msg)
127
+ if msg.size == 1
128
+ find_method(msg)
198
129
  # Left, so they specified two arguments. First is probably a constant, so let's find that!
199
130
  else
200
- object = find_method(msg.last, msg.first)
131
+ find_method(msg.last, first)
201
132
  end
202
133
  end
203
134
  end
204
135
 
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
136
  end
210
137
  end
211
138
 
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
139
  require File.join(File.dirname(__FILE__), 'models')
230
-
231
- # Go!
232
- Lookup.do(ARGV[0..-1])
233
-
data/lib/models.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'fileutils'
2
+
3
+ class APILookup
4
+
5
+ class MissingHome < StandardError
6
+ end
7
+
8
+ class LookupBase < ActiveRecord::Base
9
+ end
10
+ if ENV["HOME"].nil?
11
+ puts "The HOME environment variable should be set so lookup knows where"
12
+ puts "to store it's lookup database."
13
+ raise MissingHome, "HOME must be set to know where to store our local db."
14
+ end
15
+
16
+ LookupBase.establish_connection(:adapter => "sqlite3",
17
+ :database => File.join(ENV["HOME"],".lookup", "lookup.sqlite3"))
18
+
19
+ class Api < LookupBase
20
+ set_table_name "apis"
21
+ has_many :constants
22
+ end
23
+
24
+ class Constant < LookupBase
25
+ set_table_name "constants"
26
+ belongs_to :api
27
+ has_many :entries
28
+ end
29
+
30
+ class Entry < LookupBase
31
+ set_table_name "entries"
32
+ belongs_to :constant
33
+ end
34
+
35
+ end
36
+
37
+ class SetupTables < ActiveRecord::Migration
38
+ def self.connection
39
+ APILookup::Api.connection
40
+ end
41
+
42
+ def self.up
43
+ create_table :apis do |t|
44
+ t.string :name, :url
45
+ end
46
+
47
+ create_table :entries do |t|
48
+ t.string :name, :url
49
+ t.references :constant
50
+ t.integer :weighting, :default => 0
51
+ t.integer :count, :default => 0
52
+ end
53
+
54
+ create_table :constants do |t|
55
+ t.string :name, :url
56
+ t.references :api
57
+ t.integer :weighting, :default => 0
58
+ t.integer :count, :default => 0
59
+ end
60
+ end
61
+ end
62
+
63
+ FileUtils.mkdir_p(File.join(ENV["HOME"],".lookup"))
64
+
65
+ if !APILookup::Api.table_exists? &&
66
+ !APILookup::Constant.table_exists? &&
67
+ !APILookup::Entry.table_exists?
68
+ SetupTables.up
69
+ APILookup.update
70
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radar-lookup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Bigg
@@ -9,7 +9,7 @@ autorequire: lookup
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-07 00:00:00 -08:00
12
+ date: 2009-03-30 00:00:00 -07:00
13
13
  default_executable: lookup
14
14
  dependencies: []
15
15
 
@@ -30,6 +30,7 @@ files:
30
30
  - TODO
31
31
  - lib/config.rb
32
32
  - lib/lookup.rb
33
+ - lib/models.rb
33
34
  - spec/lookup_spec.rb
34
35
  - spec/spec_helper.rb
35
36
  - bin/lookup