fastri 0.1.0.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.
@@ -0,0 +1,95 @@
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
+ # {{{ Package tasks
27
+
28
+ require 'fastri/version'
29
+
30
+ PKG_REVISION = ".0"
31
+ PKG_FILES = FileList[
32
+ "bin/fri", "bin/fastri-server", "bin/ri-emacs",
33
+ "lib/**/*.rb",
34
+ "COPYING", "LEGAL", "LICENSE", "Rakefile", "README.*", "test/*.rb",
35
+ "setup.rb",
36
+ ]
37
+
38
+ require 'rake/gempackagetask'
39
+ Spec = Gem::Specification.new do |s|
40
+ s.name = "fastri"
41
+ s.version = FastRI::FASTRI_VERSION + PKG_REVISION
42
+ s.summary = "RI docs across machines, faster and smarter than ri."
43
+ s.description = <<EOF
44
+ FastRI is an alternative to the ri command-line tool. It is *much* faster, and
45
+ also allows you to offer RI lookup services over DRb. FastRI is a bit smarter
46
+ than ri, and can find classes anywhere in the hierarchy without specifying the
47
+ "full path". It also knows about gems, and can tell you e.g. which extensions
48
+ to a core class were added by a specific gem.
49
+ EOF
50
+ s.files = PKG_FILES.to_a
51
+ s.require_path = 'lib'
52
+ s.author = "Mauricio Fernandez"
53
+ s.email = "mfp@acm.org"
54
+ s.homepage = "http://eigenclass.org/hiki.rb?fastri"
55
+ s.bindir = "bin"
56
+ s.executables = %w[fri fastri-server ri-emacs]
57
+ s.has_rdoc = true
58
+ #s.extra_rdoc_files = %w[]
59
+ s.rdoc_options << "--title" << 'FastRI: better, faster ri'
60
+ s.test_files = Dir["test/test_*.rb"]
61
+ s.post_install_message = <<EOF
62
+
63
+ A small note about the RubyGems + FastRI.
64
+ =========================================
65
+ RubyGems adds a noticable overhead to fri, making it run slower than if you
66
+ installed it directly from the tarball with setup.rb.
67
+
68
+ Compare the execution time when installed with RubyGems:
69
+ $ time fri -f plain String > /dev/null
70
+
71
+ real 0m0.385s
72
+ user 0m0.244s
73
+ sys 0m0.036s
74
+
75
+ to the time fri actually takes to run, without the overhead introduced by
76
+ RubyGems:
77
+ $ time ruby bin/fri -f plain String > /dev/null
78
+
79
+ real 0m0.088s
80
+ user 0m0.040s
81
+ sys 0m0.008s
82
+
83
+ If you care about those extra 300ms (and there are situations where they will
84
+ matter, e.g. when using fri for method completion), get FastRI from the
85
+ tarballs.
86
+
87
+ EOF
88
+ end
89
+
90
+ task :gem => [:test]
91
+ Rake::GemPackageTask.new(Spec) do |p|
92
+ p.need_tar = true
93
+ end
94
+
95
+ # vim: set sw=2 ft=ruby:
@@ -0,0 +1,188 @@
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
+
9
+ FASTRI_SERVER_VERSION = "0.0.1"
10
+
11
+ def make_index(index_file)
12
+ # The local environment is trusted --- what we don't trust is what would come
13
+ # from the DRb connection. This way the RiService will be untainted, and we
14
+ # will be able to use it with $SAFE = 1.
15
+ ObjectSpace.each_object{|obj| obj.untaint unless obj.frozen? }
16
+
17
+ paths = [ RI::Paths::SYSDIR, RI::Paths::SITEDIR, RI::Paths::HOMEDIR ].find_all do |p|
18
+ p && File.directory?(p)
19
+ end
20
+ begin
21
+ require 'rubygems'
22
+ gemdirs = Dir["#{Gem.path}/doc/*/ri"]
23
+ gems = Hash.new{|h,k| h[k] = []}
24
+ gemdirs.each do |path|
25
+ gemname, version = %r{/([^/]+)-(.*)/ri$}.match(path).captures
26
+ if gemname.nil? # doesn't follow any conventions :(
27
+ gems[path[%r{/([^/]+)/ri$}, 1]] << ["unknown", path]
28
+ else
29
+ gems[gemname] << [version, path]
30
+ end
31
+ end
32
+ gems.sort_by{|name, _| name}.each do |name, versions|
33
+ version, path = versions.sort.last
34
+ puts "Indexing RI docs for #{name} version #{version}."
35
+ paths << path
36
+ end
37
+ rescue LoadError
38
+ end
39
+
40
+ puts "Building index."
41
+ t0 = Time.new
42
+ #ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
43
+ ri_reader = FastRI::RiIndex.new_from_paths(paths)
44
+ open(index_file, "wb"){|io| Marshal.dump ri_reader, io}
45
+ puts <<EOF
46
+ Indexed:
47
+ * #{ri_reader.num_methods} methods
48
+ * #{ri_reader.num_namespaces} classes/modules
49
+ Needed #{Time.new - t0} seconds
50
+ EOF
51
+ ri_reader
52
+ end
53
+
54
+ #{{{ Main program
55
+
56
+ require 'optparse'
57
+
58
+ options = {:allowed_hosts => ["127.0.0.1"], :addr => "127.0.0.1",
59
+ :index_file => File.expand_path("~/.fastri-index")}
60
+ OptionParser.new do |opts|
61
+ opts.banner = "Usage: fastri-server.rb [options]"
62
+
63
+ opts.on("-a", "--allow HOST", "Allow connections from HOST.",
64
+ "(default: 127.0.0.1)") do |host|
65
+ options[:allowed_hosts] << host
66
+ end
67
+
68
+ opts.on("-s", "--bind ADDR", "Listen to connections on ADDR.",
69
+ "(default: 127.0.0.1)") do |addr|
70
+ options[:addr] = addr
71
+ end
72
+
73
+ opts.on("--index-file=FILE", "Use index file.",
74
+ "(default: #{options[:index_file]})") do |file|
75
+ options[:index_file] = file
76
+ end
77
+
78
+ opts.on("-b", "--rebuild-index", "Only rebuild index.") do
79
+ make_index(options[:index_file])
80
+ exit 0
81
+ end
82
+
83
+ opts.on("-h", "--help", "Show this help message") do
84
+ puts opts
85
+ exit
86
+ end
87
+ end.parse!
88
+
89
+ if File.exist?(options[:index_file])
90
+ ri_reader = open(options[:index_file], "rb"){|io| Marshal.load io } rescue nil
91
+ end
92
+
93
+ ri_reader ||= make_index(options[:index_file])
94
+
95
+ service = FastRI::RiService.new(ri_reader)
96
+ GC.start
97
+
98
+ require 'rinda/ring'
99
+ require 'rinda/tuplespace'
100
+ require 'drb/acl'
101
+
102
+ class FastRI::RiService
103
+ include DRbUndumped
104
+ end
105
+
106
+ #{{{ start DRb service
107
+ acl_opt = ["deny", "all"]
108
+ options[:allowed_hosts].each{|host| acl_opt.concat ["allow", host.strip]}
109
+ acl = ACL.new(acl_opt)
110
+ DRb.install_acl(acl)
111
+
112
+ drb_addr = "druby://#{options[:addr]}:0"
113
+ DRb.start_service(drb_addr)
114
+
115
+ $SAFE = 1
116
+
117
+ $stdout.sync = true
118
+ begin
119
+ puts "Looking for Ring server..."
120
+ finder = Rinda::RingFinger.new
121
+ service_ts = finder.lookup_ring_any(2)
122
+ puts "Located Ring server at #{service_ts.__drburi}"
123
+ rescue Exception
124
+ puts "No Ring server found, starting my own."
125
+ service_ts = Rinda::TupleSpace.new
126
+ ring_server = Rinda::RingServer.new(service_ts)
127
+ end
128
+
129
+ begin
130
+ service_ts.write([:name, :FastRI, service, 'FastRI documentation'], Rinda::SimpleRenewer.new)
131
+ rescue Exception
132
+ puts <<EOF
133
+ The FastRI service could not be registered in the Ring.
134
+ This is probably due to the Ring being bound to an unreachable address.
135
+ You can specify which address the Ring should be bound to with
136
+ fastri-server --bind ADDRESS --allow ADDRESS
137
+ EOF
138
+ exit
139
+ end
140
+
141
+ # {{{ Init message
142
+ require 'enumerator'
143
+ puts "fastri-server #{FASTRI_SERVER_VERSION} (FastRI #{FastRI::FASTRI_VERSION}) listening on #{DRb.uri}"
144
+ puts "ACL:"
145
+ acl_opt.each_slice(2){|action, host| puts "%-5s %s" % [action, host]}
146
+
147
+ # {{{ GC periodically
148
+ # keeps the process hot too :)
149
+ Thread.new do
150
+ loop do
151
+ GC.start
152
+ sleep 300
153
+ end
154
+ end
155
+
156
+ if $DEBUG
157
+ # trying to see where our memory is going
158
+ population = Hash.new{|h,k| h[k] = [0,0]}
159
+ array_sizes = Hash.new{|h,k| h[k] = 0}
160
+ ObjectSpace.each_object do |object|
161
+ size = case object # rough estimates
162
+ when Array
163
+ array_sizes[object.size / 10] += 1
164
+ case object.size
165
+ when 0..16
166
+ 20 + 64
167
+ else
168
+ 20 + 4 * object.size * 1.5
169
+ end
170
+ when Hash; 40 + 4 * [object.size / 5, 11].max + 16 * object.size
171
+ when String; 30 + object.size
172
+ else 120 # the iv_tbl, etc
173
+ end
174
+ count, tsize = population[object.class]
175
+ population[object.class] = [count + 1, tsize + size]
176
+ end
177
+
178
+ population.sort_by{|k,(c,s)| s}.reverse[0..10].each do |klass, (count, bytes)|
179
+ puts "%-20s %7d %9d" % [klass, count, bytes]
180
+ end
181
+
182
+ puts "Array sizes:"
183
+ array_sizes.sort.each{|k,v| puts "%5d %6d" % [k * 10, v]}
184
+ end
185
+
186
+ DRb.thread.join
187
+
188
+ # vi: set sw=2 expandtab:
data/bin/fri ADDED
@@ -0,0 +1,67 @@
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 'rinda/ring'
7
+ require 'optparse'
8
+
9
+ # we bind to 127.0.0.1 by default, because otherwise Ruby will try with
10
+ # 0.0.0.0, which results in a DNS request, adding way too much latency
11
+ options = {
12
+ :addr => "127.0.0.1",
13
+ :format => "ansi"
14
+ }
15
+ override_addr_env = false
16
+ optparser = OptionParser.new do |opts|
17
+ opts.banner = "Usage: fri [options] <query>"
18
+
19
+ opts.on("-s", "--bind ADDR", "Bind to ADDR for incoming DRb connections.",
20
+ "(default: 127.0.0.1)") do |addr|
21
+ options[:addr] = addr
22
+ override_addr_env = true
23
+ end
24
+
25
+ opts.on("-f", "--format FMT", "Format to use when displaying output:",
26
+ " ansi, plain (default: ansi)") do |format|
27
+ options[:format] = format
28
+ end
29
+
30
+ opts.on("-h", "--help", "Show this help message") do
31
+ puts opts
32
+ exit
33
+ end
34
+ end
35
+ optparser.parse!
36
+
37
+ if ARGV.empty?
38
+ puts optparser
39
+ exit
40
+ end
41
+
42
+ if override_addr_env
43
+ addr = "druby://#{options[:addr]}:0"
44
+ else
45
+ addr = "druby://#{ENV["FASTRI_ADDR"]||options[:addr]}:0"
46
+ end
47
+
48
+ begin
49
+ DRb.start_service(addr)
50
+ ring_server = Rinda::RingFinger.primary
51
+ rescue Exception
52
+ puts <<EOF
53
+ Couldn't initialize DRb and locate the Ring server.
54
+
55
+ Please make sure that:
56
+ * the fastri-server is running, the server is bound to the correct interface,
57
+ and the ACL setup allows connections from this host
58
+ * fri is using the correct interface for incoming DRb requests:
59
+ either set the FASTRI_ADDR environment variable, or use --bind ADDR, e.g
60
+ export FASTRI_ADDR="192.168.1.12"
61
+ fri Array
62
+ EOF
63
+ exit(-1)
64
+ end
65
+ service = ring_server.read([:name, :FastRI, nil, nil])[2]
66
+ puts service.info(ARGV[0], options[:format])
67
+ # vi: set sw=2 expandtab:
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env ruby
2
+ ## drop-in replacement for the ri-emacs helper script for use
3
+ # with ri-ruby.el, using the FastRI service via DRb
4
+ #
5
+ # Based on ri-emacs.rb by Kristof Bastiaensen <kristof@vleeuwen.org>
6
+ #
7
+ # Copyright (C) 2004,2006 Kristof Bastiaensen
8
+ # 2006 Mauricio Fernandez <mfp@acm.org>
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this program; if not, write to the Free Software
22
+ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
+ #----------------------------------------------------------------------
24
+
25
+ require 'rinda/ring'
26
+ require 'optparse'
27
+
28
+ # {{{ cmdline parsing and service discovery
29
+ # we bind to 127.0.0.1 by default, because otherwise Ruby will try with
30
+ # 0.0.0.0, which results in a DNS request, adding way too much latency
31
+ options = {:addr => "127.0.0.1"}
32
+ override_addr_env = false
33
+ optparser = OptionParser.new do |opts|
34
+ opts.banner = "Usage: ri-emacs [options] <query>"
35
+
36
+ opts.on("-s", "--bind [ADDR]", "Bind to ADDR for incoming DRb connections.",
37
+ "(default: 127.0.0.1)") do |addr|
38
+ options[:addr] = addr
39
+ override_addr_env = true
40
+ end
41
+
42
+ opts.on("-h", "--help", "Show this help message") do
43
+ puts opts
44
+ exit
45
+ end
46
+ end
47
+ optparser.parse!
48
+
49
+ if override_addr_env
50
+ addr = "druby://#{options[:addr]}:0"
51
+ else
52
+ addr = "druby://#{ENV["FASTRI_ADDR"]||options[:addr]}:0"
53
+ end
54
+
55
+ begin
56
+ DRb.start_service(addr)
57
+ ring_server = Rinda::RingFinger.primary
58
+ rescue Exception
59
+ puts <<EOF
60
+ Couldn't initialize DRb and locate the Ring server.
61
+
62
+ Please make sure that:
63
+ * the fastri-server is running, the server is bound to the correct interface,
64
+ and the ACL setup allows connections from this host
65
+ * fri is using the correct interface for incoming DRb requests:
66
+ either set the FASTRI_ADDR environment variable, or use --bind ADDR, e.g
67
+ export FASTRI_ADDR="192.168.1.12"
68
+ fri Array
69
+ EOF
70
+ exit(-1)
71
+ end
72
+ service = ring_server.read([:name, :FastRI, nil, nil])[2]
73
+
74
+ class EventLoop
75
+ def initialize(ri)
76
+ @ri = ri
77
+ end
78
+
79
+ def run
80
+ puts "READY"
81
+ loop do
82
+ line = $stdin.gets
83
+ cmd, p = /(\w+)(.*)$/.match(line)[1..2]
84
+ p.strip!
85
+ case cmd
86
+ when "TRY_COMPLETION"; puts complete_try(p)
87
+ when "COMPLETE_ALL"; puts complete_all(p)
88
+ when "LAMBDA"; puts complete_lambda(p)
89
+ when "CLASS_LIST"; puts class_list(p)
90
+ when "CLASS_LIST_WITH_FLAG"; puts class_list_with_flag(p)
91
+ when "DISPLAY_ARGS"; display_args(p)
92
+ when "DISPLAY_INFO"; display_info(p)
93
+ end
94
+ end
95
+ end
96
+
97
+ def complete_try(keyw)
98
+ list = @ri.completion_list(keyw)
99
+ if list.nil?
100
+ return "nil"
101
+ elsif list.size == 1 and
102
+ list[0].split(/(::)|#|\./) == keyw.split(/(::)|#|\./)
103
+ return "t"
104
+ end
105
+
106
+ first = list.shift;
107
+ if first =~ /(.*)((?:::)|(?:#))(.*)/
108
+ other = $1 + ($2 == "::" ? "#" : "::") + $3
109
+ end
110
+
111
+ len = first.size
112
+ match_both = false
113
+ list.each do |w|
114
+ while w[0, len] != first[0, len]
115
+ if other and w[0, len] == other[0, len]
116
+ match_both = true
117
+ break
118
+ end
119
+ len -= 1
120
+ end
121
+ end
122
+
123
+ if match_both
124
+ return other.sub(/(.*)((?:::)|(?:#))/) { $1 + "." }[0, len].inspect
125
+ else
126
+ return first[0, len].inspect
127
+ end
128
+ end
129
+
130
+ def complete_all(keyw)
131
+ list = @ri.completion_list(keyw)
132
+ if list.nil?
133
+ "nil"
134
+ else
135
+ "(" + list.map { |w| w.inspect }.join(" ") + ")"
136
+ end
137
+ end
138
+
139
+ def complete_lambda(keyw)
140
+ list = @ri.completion_list(keyw)
141
+ if list.nil?
142
+ "nil"
143
+ else
144
+ if list.find { |n| n.split(/(::)|#|\./) == keyw.split(/(::)|#|\./) }
145
+ "t"
146
+ else
147
+ "nil"
148
+ end
149
+ end
150
+ end
151
+
152
+ def class_list(keyw)
153
+ list = @ri.class_list(keyw)
154
+ if list
155
+ "(" + list.map{|x| "(#{x.inspect})"}.join(" ") + ")"
156
+ else
157
+ "nil"
158
+ end
159
+ end
160
+
161
+ def class_list_with_flag(keyw)
162
+ list = @ri.class_list_with_flag(keyw)
163
+ if list
164
+ "(" + list.map{|x| "(#{x.inspect})"}.join(" ") + ")"
165
+ else
166
+ "nil"
167
+ end
168
+ end
169
+
170
+ def display_args(keyw)
171
+ data = @ri.args(keyw)
172
+ puts data if data
173
+ puts "RI_EMACS_END_OF_INFO"
174
+ end
175
+
176
+ def display_info(keyw)
177
+ data = @ri.info(keyw)
178
+ puts data if data
179
+ puts "RI_EMACS_END_OF_INFO"
180
+ end
181
+ end
182
+
183
+
184
+ #{{{ event loop
185
+ $stdout.sync = true
186
+ EventLoop.new(service).run
187
+
188
+ # vi: set sw=2 expandtab: