dakrone-fastri 0.3.1

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/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: