dakrone-fastri 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.en ADDED
@@ -0,0 +1,102 @@
1
+ FastRI Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
2
+ Inspired by ri-emacs.rb by Kristof Bastiaensen <kristof@vleeuwen.org>
3
+
4
+ Overview
5
+ ========
6
+ FastRI is an alternative to the ri command-line tool. It is *much* faster, and
7
+ also allows you to offer RI lookup services over DRb. FastRI is a bit smarter
8
+ than ri, and can find classes anywhere in the hierarchy without specifying the
9
+ "full path". It also knows about gems, and can tell you e.g. which extensions
10
+ to a core class were added by a specific gem.
11
+
12
+ Install
13
+ =======
14
+ Just run
15
+ setup.rb
16
+
17
+ Usage
18
+ =====
19
+ FastRI can be used either standalone or with a DRb server, for extra speed.
20
+
21
+ The standalone client is qri and is used the same way as ri; run
22
+ $ qri -h
23
+ to list the options.
24
+
25
+ There are two parts to FastRI over DRb:
26
+ * the server: fastri-server
27
+ * the client: fri
28
+
29
+ FastRI uses a Rinda Ring to allow servers to be discovered automatically
30
+ without needing to indicate the DRb URIs manually. It can work across
31
+ machines if you make sure the ring server is bound to the correct interface,
32
+ and the ACL permissions are correct.
33
+
34
+ qri and fri are nearly identical, the only difference being that fri tries to
35
+ use a FastRI service over DRb by default.
36
+
37
+ fri Quickstart
38
+ --------------
39
+ $ fastri-server (blocks)
40
+ Later,
41
+ $ fri String
42
+ ---------------------------------------------------------- Class: String
43
+ A String object holds and manipulates an arbitrary sequence of
44
+ bytes, typically representing characters. String objects may be
45
+ created using String::new or as literals.
46
+ ...
47
+
48
+ Read on for more information, including how to make FastRI work across
49
+ machines.
50
+
51
+ Launching the server
52
+ --------------------
53
+ For local usage, just
54
+ $ fastri-server
55
+ will do. The DRb service will bind to 127.0.0.1, and only connections from
56
+ 127.0.0.1 will be allowed. If you want to allow fri to be used from other
57
+ machines, you have to specify which interface to bind to, and allow incoming
58
+ connections from the desired hosts. For example, if your network is
59
+ 192.168.1.0, and your IP is 192.168.1.2, you can do
60
+ $ fastri-server -a 192.168.1.0/24 -s 192.168.1.2
61
+ FastRI 0.0.1 listening on druby://192.168.1.2:41217
62
+ ACL:
63
+ deny all
64
+ allow localhost
65
+ allow 192.168.1.0/24
66
+
67
+ Further options are documented in
68
+ $ fastri-server -h
69
+
70
+ Using fri
71
+ ---------
72
+ Running fri with no options (or -h/--help) will explain the command-line options.
73
+ If you are using fri locally (i.e. on the same server as fastri-server), just use
74
+ it as follows:
75
+ $ fri Array
76
+ ------------------------------------------------------- Class: Array
77
+ Arrays are ordered, integer-indexed collections of any object.
78
+ ...
79
+
80
+ If you're on a different machine, you'll probably have to specify which
81
+ address the DRb service should be attached to. You can either specify it with
82
+ -s ADDRESS (or --bind ADDRESS), or set the FASTRI_ADDR environment variable.
83
+ For example, if you're on 192.168.1.101, both
84
+ export FASTRI_ADDR="192.168.1.101"
85
+ fri Array
86
+ and
87
+ fri -s 192.168.1.101 Array
88
+ will work. Note that FASTRI_ADDR is the *local* address: the server will be
89
+ discovered automatically. (The reason why the local address must be specified
90
+ is that it will be given to the ring server, which must be able to establish
91
+ a reverse connection through the local DRb service.)
92
+
93
+ Feedback
94
+ ========
95
+ Bug reports, patches, comments... are appreciated.
96
+ You can contact the author via <mfp@acm.org>. Please add "fastri" to the
97
+ subject in order to bypass the spam filters.
98
+
99
+ License
100
+ =======
101
+ FastRI is licensed under the same terms as Ruby. See LICENSE.
102
+
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ $:.unshift "lib" if File.directory? "lib"
2
+ require 'rake/testtask'
3
+
4
+ desc "Run the functional and unit tests."
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.test_files = FileList['test/test*.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ require 'rcov/rcovtask'
11
+ desc "Run rcov."
12
+ Rcov::RcovTask.new do |t|
13
+ t.test_files = FileList['test/test_*.rb'].to_a.reject{|x| /test_functional/ =~ x}
14
+ t.verbose = true
15
+ end
16
+
17
+ desc "Save current coverage state for later comparisons."
18
+ Rcov::RcovTask.new(:rcovsave) do |t|
19
+ t.rcov_opts << "--save"
20
+ t.test_files = FileList['test/test_*.rb'].to_a.reject{|x| /test_functional/ =~ x}
21
+ t.verbose = true
22
+ end
23
+
24
+ task :default => :test
25
+
26
+ # vim: set sw=2 ft=ruby:
data/THANKS ADDED
@@ -0,0 +1,36 @@
1
+
2
+ Tomasz Wegrzanowski
3
+ ===================
4
+ * contributed the code that allows fri to find a method in the ancestors
5
+ for core classes
6
+
7
+ Dee Zsombor
8
+ ===========
9
+ * reported problem with ri-emacs setting $stdout.sync to true
10
+
11
+ Greg Weber
12
+ ==========
13
+ * came up with the idea to load optparse only when needed,
14
+ saving some 10-30ms per run.
15
+
16
+ Steven Lumos
17
+ ============
18
+ * found bug triggered by hyphens in Gem.path
19
+
20
+ Lee Marlow
21
+ ==========
22
+ * helped fix FastRI on Leopard
23
+
24
+ Steve Madsen
25
+ ============
26
+ * reported the problems on Leopard and tested the patches
27
+
28
+ Nikolai Lugovoi
29
+ ===============
30
+ * implemented -a (--all)
31
+
32
+ rubikitch
33
+ =========
34
+ * ri-emacs work
35
+ * -1 (--exact)
36
+ * fixes
data/bin/fastri-server ADDED
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env ruby
2
+ # fastri-server: serve RI documentation over DRb
3
+ # Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
4
+
5
+ require 'fastri/version'
6
+ require 'fastri/ri_index'
7
+ require 'fastri/ri_service'
8
+ require 'fastri/util'
9
+ require 'fastri/full_text_indexer'
10
+ require 'enumerator'
11
+
12
+ FASTRI_SERVER_VERSION = "0.0.1"
13
+
14
+ def make_index(index_file)
15
+ # The local environment is trusted --- what we don't trust is what would come
16
+ # from the DRb connection. This way the RiService will be untainted, and we
17
+ # will be able to use it with $SAFE = 1.
18
+ ObjectSpace.each_object{|obj| obj.untaint unless obj.frozen? }
19
+
20
+ paths = [ ::RDoc::RI::Paths::SYSDIR, ::RDoc::RI::Paths::SITEDIR, ::RDoc::RI::Paths::HOMEDIR ].find_all do |p|
21
+ p && File.directory?(p)
22
+ end
23
+ FastRI::Util.gem_directories_unique.each do |name, version, path|
24
+ paths << path
25
+ puts "Indexing RI docs for #{name} version #{version || "unknown"}."
26
+ end
27
+
28
+ puts "Building index."
29
+ t0 = Time.new
30
+ #ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
31
+ ri_reader = FastRI::RiIndex.new_from_paths(paths)
32
+ open(index_file, "wb"){|io| Marshal.dump ri_reader, io}
33
+ puts <<EOF
34
+ Indexed:
35
+ * #{ri_reader.num_methods} methods
36
+ * #{ri_reader.num_namespaces} classes/modules
37
+ Needed #{Time.new - t0} seconds
38
+ EOF
39
+ ri_reader
40
+ end
41
+
42
+ def linearize(comment)
43
+ case s = comment["body"]
44
+ when String; s
45
+ else
46
+ if Array === (y = comment["contents"])
47
+ y.map{|z| linearize(z)}.join("\n")
48
+ elsif s = comment["text"]
49
+ s
50
+ else
51
+ nil
52
+ end
53
+ end
54
+ end
55
+
56
+ def make_full_text_index(dir)
57
+ paths = [ RI::Paths::SYSDIR, RI::Paths::SITEDIR, RI::Paths::HOMEDIR ].find_all do |p|
58
+ p && File.directory?(p)
59
+ end
60
+ FastRI::Util.gem_directories_unique.each do |name, version, path|
61
+ paths << path
62
+ puts "Indexing RI docs for #{name} version #{version || "unknown"}."
63
+ end
64
+ unless File.exist?(dir)
65
+ Dir.mkdir(dir)
66
+ end
67
+ indexer = FastRI::FullTextIndexer.new(40)
68
+ bad = 0
69
+ paths.each do |path|
70
+ Dir["#{path}/**/*.yaml"].each do |yamlfile|
71
+ yaml = File.read(yamlfile)
72
+ begin
73
+ data = YAML.load(yaml.gsub(/ \!.*/, ''))
74
+ rescue Exception
75
+ bad += 1
76
+ #puts "Couldn't load #{yamlfile}"
77
+ next
78
+ end
79
+
80
+ desc = (data['comment']||[]).map{|x| linearize(x)}.join("\n")
81
+ desc.gsub!(/<\/?(em|b|tt|ul|ol|table)>/, "")
82
+ desc.gsub!(/&quot;/, "'")
83
+ desc.gsub!(/&lt;/, "<")
84
+ desc.gsub!(/&gt;/, ">")
85
+ desc.gsub!(/&amp;/, "&")
86
+ unless desc.empty?
87
+ indexer.add_document(yamlfile, desc)
88
+ end
89
+ end
90
+ end
91
+
92
+ File.open(File.join(dir, "full_text.dat"), "wb") do |fulltextIO|
93
+ File.open(File.join(dir, "suffixes.dat"), "wb") do |suffixesIO|
94
+ indexer.build_index(fulltextIO, suffixesIO)
95
+ end
96
+ end
97
+ end
98
+
99
+ #{{{ Main program
100
+
101
+ require 'optparse'
102
+
103
+ home = FastRI::Util.find_home
104
+ options = {:allowed_hosts => ["127.0.0.1"], :addr => "127.0.0.1",
105
+ :index_file => File.join(home, ".fastri-index"),
106
+ :do_full_text => false,
107
+ :full_text_dir => File.join(home, ".fastri-fulltext"),
108
+ }
109
+ OptionParser.new do |opts|
110
+ opts.version = FastRI::FASTRI_VERSION
111
+ opts.release = FastRI::FASTRI_RELEASE_DATE
112
+ opts.banner = "Usage: fastri-server.rb [options]"
113
+
114
+ opts.on("-a", "--allow HOST", "Allow connections from HOST.",
115
+ "(default: 127.0.0.1)") do |host|
116
+ options[:allowed_hosts] << host
117
+ end
118
+
119
+ opts.on("-s", "--bind ADDR", "Listen to connections on ADDR.",
120
+ "(default: 127.0.0.1)") do |addr|
121
+ options[:addr] = addr
122
+ end
123
+
124
+ opts.on("--index-file=FILE", "Use index file.",
125
+ "(default: #{options[:index_file]})") do |file|
126
+ options[:index_file] = file
127
+ end
128
+
129
+ opts.on("-b", "--rebuild-index", "Only rebuild index.") do
130
+ make_index(options[:index_file])
131
+ exit 0
132
+ end
133
+
134
+ opts.on("-F", "--full-text-dir DIR", "Place full-text index in DIR",
135
+ "(default: #{options[:full_text_dir]})") do |dir|
136
+ options[:full_text_dir] = dir if dir
137
+ options[:do_full_text] = true
138
+ end
139
+
140
+ opts.on("-B", "--rebuild-full-text", "Rebuild full-text index.") do
141
+ make_full_text_index(options[:full_text_dir])
142
+ exit 0
143
+ end
144
+
145
+ opts.on("-h", "--help", "Show this help message") do
146
+ puts opts
147
+ exit
148
+ end
149
+ end.parse!
150
+
151
+ if File.exist?(options[:index_file])
152
+ ri_reader = open(options[:index_file], "rb"){|io| Marshal.load io } rescue nil
153
+ end
154
+
155
+ ri_reader ||= make_index(options[:index_file])
156
+
157
+ service = FastRI::RiService.new(ri_reader)
158
+ GC.start
159
+
160
+ require 'rinda/ring'
161
+ require 'rinda/tuplespace'
162
+ require 'drb/acl'
163
+
164
+ class FastRI::RiService
165
+ include DRbUndumped
166
+ end
167
+
168
+ #{{{ start DRb service
169
+ acl_opt = ["deny", "all"]
170
+ options[:allowed_hosts].each{|host| acl_opt.concat ["allow", host.strip]}
171
+ acl = ACL.new(acl_opt)
172
+ DRb.install_acl(acl)
173
+
174
+ ip = options[:addr][/^[^:]+/] || "127.0.0.1"
175
+ port = options[:addr][/:(\d+)/, 1] || 0
176
+ drb_addr = "druby://#{ip}:#{port}"
177
+ DRb.start_service(drb_addr)
178
+
179
+ $SAFE = 1
180
+
181
+ $stdout.sync = true
182
+ begin
183
+ puts "Looking for Ring server..."
184
+ finder = Rinda::RingFinger.new
185
+ service_ts = finder.lookup_ring_any(2)
186
+ puts "Located Ring server at #{service_ts.__drburi}"
187
+ rescue Exception
188
+ puts "No Ring server found, starting my own."
189
+ service_ts = Rinda::TupleSpace.new
190
+ ring_server = Rinda::RingServer.new(service_ts)
191
+ end
192
+
193
+ begin
194
+ service_ts.write([:name, :FastRI, service, 'FastRI documentation'], Rinda::SimpleRenewer.new)
195
+ rescue Exception
196
+ puts <<EOF
197
+ The FastRI service could not be registered in the Ring.
198
+ This is probably due to the Ring being bound to an unreachable address.
199
+ You can specify which address the Ring should be bound to with
200
+ fastri-server --bind ADDRESS --allow ADDRESS
201
+ EOF
202
+ exit
203
+ end
204
+
205
+ # {{{ Init message
206
+ puts "fastri-server #{FASTRI_SERVER_VERSION} (FastRI #{FastRI::FASTRI_VERSION}) listening on #{DRb.uri}"
207
+ puts "ACL:"
208
+ acl_opt.each_slice(2){|action, host| puts "%-5s %s" % [action, host]}
209
+
210
+ # {{{ GC periodically
211
+ # keeps the process hot too :)
212
+ Thread.new do
213
+ loop do
214
+ GC.start
215
+ sleep 300
216
+ end
217
+ end
218
+
219
+ if $DEBUG
220
+ # trying to see where our memory is going
221
+ population = Hash.new{|h,k| h[k] = [0,0]}
222
+ array_sizes = Hash.new{|h,k| h[k] = 0}
223
+ ObjectSpace.each_object do |object|
224
+ size = case object # rough estimates
225
+ when Array
226
+ array_sizes[object.size / 10] += 1
227
+ case object.size
228
+ when 0..16
229
+ 20 + 64
230
+ else
231
+ 20 + 4 * object.size * 1.5
232
+ end
233
+ when Hash; 40 + 4 * [object.size / 5, 11].max + 16 * object.size
234
+ when String; 30 + object.size
235
+ else 120 # the iv_tbl, etc
236
+ end
237
+ count, tsize = population[object.class]
238
+ population[object.class] = [count + 1, tsize + size]
239
+ end
240
+
241
+ population.sort_by{|k,(c,s)| s}.reverse[0..10].each do |klass, (count, bytes)|
242
+ puts "%-20s %7d %9d" % [klass, count, bytes]
243
+ end
244
+
245
+ puts "Array sizes:"
246
+ array_sizes.sort.each{|k,v| puts "%5d %6d" % [k * 10, v]}
247
+ end
248
+
249
+ DRb.thread.join
250
+
251
+ # vi: set sw=2 expandtab:
data/bin/fri ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env ruby
2
+ # fri: access RI documentation through DRb
3
+ # Copyright (C) 2006 Mauricio Fernandez <mfp@acm.org>
4
+ #
5
+
6
+ require 'fastri/util'
7
+ require 'fastri/full_text_index'
8
+
9
+ default_local_mode = File.basename($0)[/^qri/] ? true : false
10
+
11
+ # we bind to 127.0.0.1 by default, because otherwise Ruby will try with
12
+ # 0.0.0.0, which results in a DNS request, adding way too much latency
13
+ options = {
14
+ :addr => "127.0.0.1",
15
+ :format =>
16
+ case RUBY_PLATFORM
17
+ when /win/
18
+ if /darwin|cygwin/ =~ RUBY_PLATFORM
19
+ "ansi"
20
+ else
21
+ "plain"
22
+ end
23
+ else
24
+ "ansi"
25
+ end,
26
+ :width => 72,
27
+ :lookup_order => [
28
+ :exact, :exact_ci, :nested, :nested_ci, :partial, :partial_ci,
29
+ :nested_partial, :nested_partial_ci,
30
+ ],
31
+ :show_matches => false,
32
+ :do_full_text => false,
33
+ :full_text_dir => File.join(FastRI::Util.find_home, ".fastri-fulltext"),
34
+ :use_pager => nil,
35
+ :pager => nil,
36
+ :list_classes => nil,
37
+ :list_methods => nil,
38
+ :extended => false,
39
+ :index_file => File.join(FastRI::Util.find_home, ".fastri-index"),
40
+ :local_mode => default_local_mode,
41
+ :do_second_guess => true,
42
+ :expand_choices => false,
43
+ }
44
+
45
+ override_addr_env = false
46
+
47
+
48
+ # only load optparse if actually needed
49
+ # saves ~0.01-0.04s.
50
+ if (arg = ARGV[0]) && arg[0, 1] == "-" or ARGV.empty?
51
+ require 'optparse'
52
+ optparser = OptionParser.new do |opts|
53
+ opts.version = FastRI::FASTRI_VERSION
54
+ opts.release = FastRI::FASTRI_RELEASE_DATE
55
+ opts.banner = "Usage: #{File.basename($0)} [options] <query>"
56
+
57
+ opts.on("-L", "--local", "Try to use local index instead of DRb service.",
58
+ *[("(default)" if default_local_mode)].compact) do
59
+ options[:local_mode] = true
60
+ end
61
+ opts.on("--index-file=FILE", "Use index file (forces --local mode).",
62
+ "(default: #{options[:index_file]})") do |file|
63
+ options[:index_file] = file
64
+ options[:local_mode] = true
65
+ end
66
+ opts.on("-R", "--remote", "Use DRb service. #{'(default)' unless default_local_mode}") do
67
+ options[:local_mode] = false
68
+ end
69
+ opts.on("-s", "--bind ADDR", "Bind to ADDR for incoming DRb connections.",
70
+ "(default: 127.0.0.1)") do |addr|
71
+ options[:addr] = addr
72
+ override_addr_env = true
73
+ end
74
+
75
+ order_mapping = {
76
+ 'e' => :exact, 'E' => :exact_ci, 'n' => :nested, 'N' => :nested_ci,
77
+ 'p' => :partial, 'P' => :partial_ci, 'x' => :nested_partial,
78
+ 'X' => :nested_partial_ci, 'a' => :anywhere, 'A' => :anywhere_ci,
79
+ 'm' => :namespace_partial, 'M' => :namespace_partial_ci,
80
+ 'f' => :full_partial, 'F' => :full_partial_ci,
81
+ }
82
+ opts.on("-O", "--order ORDER", "Specify lookup order.",
83
+ "(default: eEnNpPxX)", "Uppercase: case-indep.",
84
+ "e:exact n:nested p:partial (completion)",
85
+ "x:nested and partial m:complete namespace",
86
+ "f:complete both class and method",
87
+ "a:match method name anywhere") do |order|
88
+ options[:lookup_order] = order.split(//).map{|x| order_mapping[x]}.compact
89
+ end
90
+
91
+ opts.on("-1", "--exact", "Does not do second guess(exact query).") do
92
+ options[:do_second_guess] = false
93
+ options[:lookup_order] = [ :exact ]
94
+ end
95
+ opts.on("-a", "--all", "Show info for all methods in Multiple choices",
96
+ "(default: don't)") do
97
+ options[:expand_choices] = true
98
+ end
99
+
100
+ opts.on("-e", "--extended", "Show all methods for given namespace.") do
101
+ options[:extended] = true
102
+ options[:use_pager] = true
103
+ options[:format] = "plain"
104
+ end
105
+
106
+ opts.on("--show-matches", "Only show matching entries."){ options[:show_matches] = true }
107
+
108
+ opts.on("--classes", "List all known classes/modules.") do
109
+ options[:use_pager] = true
110
+ options[:list_classes] = true
111
+ end
112
+ opts.on("--methods", "List all known methods.") do
113
+ options[:use_pager] = true
114
+ options[:list_methods] = true
115
+ end
116
+ opts.on("-l", "--list-names", "List all known namespaces/methods.") do
117
+ options[:use_pager] = true
118
+ options[:list_classes] = true
119
+ options[:list_methods] = true
120
+ end
121
+
122
+ opts.on("-S", "--full-text", "Perform full-text search.") do
123
+ options[:do_full_text] = true
124
+ options[:use_pager] = true if options[:use_pager].nil?
125
+ options[:format] = "plain"
126
+ end
127
+
128
+ opts.on("-F", "--full-text-dir DIR", "Use full-text index in DIR",
129
+ "(default: #{options[:full_text_dir]})") do |dir|
130
+ options[:full_text_dir] = dir if dir
131
+ options[:do_full_text] = true
132
+ options[:use_pager] = true
133
+ options[:format] = "plain"
134
+ end
135
+
136
+ opts.on("-f", "--format FMT", "Format to use when displaying output:",
137
+ " ansi, plain (default: #{options[:format]})") do |format|
138
+ options[:format] = format
139
+ end
140
+
141
+ opts.on("-P", "--[no-]pager", "Use pager.", "(default: don't)") do |usepager|
142
+ options[:use_pager] = usepager
143
+ options[:format] = "plain" if usepager
144
+ end
145
+
146
+ opts.on("-T", "Don't use a pager."){ options[:use_pager] = false }
147
+
148
+ opts.on("--pager-cmd PAGER", "Use pager PAGER.", "(default: don't)") do |pager|
149
+ options[:pager] = pager
150
+ options[:use_pager] = true
151
+ options[:format] = "plain"
152
+ end
153
+
154
+ opts.on("-w", "--width WIDTH", "Set the width of the output.") do |width|
155
+ w = width.to_i
156
+ options[:width] = w > 0 ? w : options[:width]
157
+ end
158
+
159
+ opts.on("-h", "--help", "Show this help message") do
160
+ puts opts
161
+ exit
162
+ end
163
+ end
164
+ optparser.parse!
165
+
166
+ if !options[:list_classes] && !options[:list_methods] && ARGV.empty?
167
+ puts optparser
168
+ exit
169
+ end
170
+ end # lazy optparse loading
171
+
172
+ # {{{ try to find where the method comes from exactly
173
+ include FastRI::Util::MagicHelp
174
+
175
+ MAX_CONTEXT_LINES = 20
176
+ def context_wrap(text, width)
177
+ "... " +
178
+ text.gsub(/(.{1,#{width-4}})( +|$\n?)|(.{1,#{width-4}})/, "\\1\\3\n").chomp
179
+ end
180
+
181
+ def display_fulltext_search_results(results, gem_dir_info = FastRI::Util.gem_directories_unique,
182
+ width = 78)
183
+ return if results.empty?
184
+ path = File.expand_path(results[0].path)
185
+ gem_name, version, gem_path = FastRI::Util.gem_info_for_path(path, gem_dir_info)
186
+ if gem_name
187
+ rel_path = path[/#{Regexp.escape(gem_path)}\/(.*)/, 1]
188
+ if rel_path
189
+ entry_name = FastRI::Util.gem_relpath_to_full_name(rel_path)
190
+ end
191
+ puts "Found in #{gem_name} #{version} #{entry_name}"
192
+ else
193
+ rdoc_system_path = File.expand_path(RI::Paths::SYSDIR)
194
+ if path.index(rdoc_system_path)
195
+ rel_path = path[/#{Regexp.escape(rdoc_system_path)}\/(.*)/, 1]
196
+ puts "Found in system #{FastRI::Util.gem_relpath_to_full_name(rel_path)}"
197
+ else
198
+ puts "Found in #{path}:"
199
+ end
200
+ end
201
+ text = results.map do |result|
202
+ context = result.context(120)
203
+ from = (context.rindex("\n", context.index(result.query)) || -1) + 1
204
+ to = (context.index("\n", context.index(result.query)) || 0) - 1
205
+ context_wrap(context[from..to], width)
206
+ end
207
+ puts
208
+ puts text.uniq[0...MAX_CONTEXT_LINES]
209
+ puts
210
+ end
211
+
212
+ def perform_fulltext_search(options)
213
+ fulltext = File.join(options[:full_text_dir], "full_text.dat")
214
+ suffixes = File.join(options[:full_text_dir], "suffixes.dat")
215
+ begin
216
+ index = FastRI::FullTextIndex.new_from_filenames(fulltext, suffixes)
217
+ rescue Exception
218
+ puts <<EOF
219
+ Couldn't open the full-text index:
220
+ #{fulltext}
221
+ #{suffixes}
222
+
223
+ The index needs to be rebuilt with
224
+ fastri-server -B
225
+ EOF
226
+ exit(-1)
227
+ end
228
+ gem_dir_info = FastRI::Util.gem_directories_unique
229
+ match_sets = ARGV.map do |query|
230
+ result = index.lookup(query)
231
+ if result
232
+ index.next_matches(result) + [result]
233
+ else
234
+ []
235
+ end
236
+ end
237
+ path_map = Hash.new{|h,k| h[k] = []}
238
+ match_sets.each{|matches| matches.each{|m| path_map[m.path] << m} }
239
+ paths = match_sets[1..-1].inject(match_sets[0].map{|x| x.path}.uniq) do |s,x|
240
+ s & x.map{|y| y.path}.uniq
241
+ end
242
+ if paths.empty?
243
+ puts "nil"
244
+ else
245
+ puts "#{paths.size} hits"
246
+ paths.sort_by{|path| 1.0 * -path_map[path].size / path_map[path].first.metadata[:size] ** 0.5}.map do |path|
247
+ puts "=" * options[:width]
248
+ puts 1.0 * path_map[path].size / path_map[path].first.metadata[:size] ** 0.5
249
+ display_fulltext_search_results(path_map[path], gem_dir_info, options[:width])
250
+ end
251
+ end
252
+
253
+ exit 0
254
+ end
255
+
256
+ #{{{ set up pager
257
+ if options[:use_pager]
258
+ [options[:pager], ENV["PAGER"], "less", "more", "pager"].compact.uniq.each do |pager|
259
+ begin
260
+ $stdout = IO.popen(pager, "w")
261
+ at_exit{ $stdout.close }
262
+ break
263
+ rescue Exception
264
+ end
265
+ end
266
+ end
267
+
268
+ #{{{ perform full text search if asked to
269
+ perform_fulltext_search(options) if options[:do_full_text]
270
+
271
+ #{{{ normal query
272
+ if options[:local_mode]
273
+ require 'fastri/ri_service'
274
+ ri_reader = open(options[:index_file], "rb"){|io| Marshal.load io } rescue nil
275
+ unless ri_reader
276
+ puts <<EOF
277
+ Couldn't open the index:
278
+ #{options[:index_file]}
279
+
280
+ The index needs to be rebuilt with
281
+ fastri-server -b
282
+ EOF
283
+ exit(-1)
284
+ end
285
+ service = FastRI::RiService.new(ri_reader)
286
+ else # remote
287
+ require 'rinda/ring'
288
+
289
+ #{{{ determine the address to bind to
290
+ if override_addr_env
291
+ addr_spec = options[:addr]
292
+ else
293
+ addr_spec = ENV["FASTRI_ADDR"] || options[:addr]
294
+ end
295
+
296
+ ip = addr_spec[/^[^:]+/] || "127.0.0.1"
297
+ port = addr_spec[/:(\d+)/, 1] || 0
298
+ addr = "druby://#{ip}:#{port}"
299
+
300
+ #{{{ start DRb and perform request
301
+ begin
302
+ DRb.start_service(addr)
303
+ ring_server = Rinda::RingFinger.primary
304
+ rescue Exception
305
+ $stderr.puts <<EOF
306
+ Couldn't initialize DRb and locate the Ring server.
307
+
308
+ Please make sure that:
309
+ * the fastri-server is running, the server is bound to the correct interface,
310
+ and the ACL setup allows connections from this host
311
+ * fri is using the correct interface for incoming DRb requests:
312
+ either set the FASTRI_ADDR environment variable, or use --bind ADDR, e.g
313
+ export FASTRI_ADDR="192.168.1.12"
314
+ fri Array
315
+ EOF
316
+ exit(-1)
317
+ end
318
+ service = ring_server.read([:name, :FastRI, nil, nil])[2]
319
+ end
320
+
321
+
322
+ info_options = {
323
+ :formatter => options[:format],
324
+ :width => options[:width],
325
+ :lookup_order => options[:lookup_order],
326
+ :extended => options[:extended],
327
+ :expand_choices => options[:expand_choices],
328
+ }
329
+
330
+ # {{{ list classes or methods
331
+ puts service.all_classes if options[:list_classes]
332
+ puts service.all_methods if options[:list_methods]
333
+ exit if options[:list_classes] || options[:list_methods]
334
+
335
+ # {{{ normal query
336
+
337
+ ARGV.each do |term|
338
+ help_query = magic_help(term)
339
+ if options[:show_matches]
340
+ puts service.matches(help_query, info_options).sort
341
+ else
342
+ result = service.info(help_query, info_options)
343
+ # second-guess the correct method type only as the last resort.
344
+ if result
345
+ puts result
346
+ elsif options[:do_second_guess] and (new_query = FastRI::Util.change_query_method_type(help_query)) != help_query
347
+ puts service.info(new_query)
348
+ else
349
+ puts nil
350
+ end
351
+ end
352
+ end
353
+ # vi: set sw=2 expandtab: