croc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-01-17
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
@@ -0,0 +1,26 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/croc
6
+ lib/croc.rb
7
+ public/about.html
8
+ public/index.html
9
+ public/jquery.dimensions.js
10
+ public/jquery.js
11
+ spec/croc_spec.rb
12
+ spec/spec_helper.rb
13
+ tasks/ann.rake
14
+ tasks/bones.rake
15
+ tasks/gem.rake
16
+ tasks/git.rake
17
+ tasks/manifest.rake
18
+ tasks/notes.rake
19
+ tasks/post_load.rake
20
+ tasks/rdoc.rake
21
+ tasks/rubyforge.rake
22
+ tasks/setup.rb
23
+ tasks/spec.rake
24
+ tasks/svn.rake
25
+ tasks/test.rake
26
+ test/test_croc.rb
@@ -0,0 +1,53 @@
1
+ croc
2
+ by Dan Hensgen
3
+ http://www.methodhead.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Croc indexes your locally installed rdocs and generates an HTML page with a
8
+ quick search box. The search works a bit like gotapi.com.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * Indexes all locally installed gems
13
+ * Installs missing rdocs when possible
14
+ * Optionally installs Ruby and Standard Library rdocs
15
+ * Untested on everything except Mac
16
+
17
+ == SYNOPSIS:
18
+
19
+ $ croc
20
+ $ open file:///Users/dan/.croc/index.html
21
+
22
+ == REQUIREMENTS:
23
+
24
+ RubyGems 1.2+
25
+
26
+ == INSTALL:
27
+
28
+ $ sudo gem install croc
29
+
30
+ == LICENSE:
31
+
32
+ (The MIT License)
33
+
34
+ Copyright (c) 2008 Dan Hensgen
35
+
36
+ Permission is hereby granted, free of charge, to any person obtaining
37
+ a copy of this software and associated documentation files (the
38
+ 'Software'), to deal in the Software without restriction, including
39
+ without limitation the rights to use, copy, modify, merge, publish,
40
+ distribute, sublicense, and/or sell copies of the Software, and to
41
+ permit persons to whom the Software is furnished to do so, subject to
42
+ the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be
45
+ included in all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
48
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
49
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
50
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
51
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
52
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
53
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'croc'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'croc'
22
+ PROJ.authors = 'Dan Hensgen'
23
+ PROJ.email = 'dan@methodhead.com'
24
+ PROJ.url = 'https://rubyforge.org/projects/croc/'
25
+ PROJ.version = "1.0.0"
26
+ PROJ.rubyforge.name = 'croc'
27
+
28
+ PROJ.spec.opts << '--color'
29
+ PROJ.exclude << "bugs" << "\.sw.$" << ".ditz-config"
30
+
31
+ # EOF
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "croc"
4
+
5
+ $gems = []
6
+ $classes = {}
7
+ $methods = []
8
+
9
+ install_assets
10
+
11
+ specs = get_specs
12
+
13
+ homes = {}
14
+ specs.each do |spec|
15
+ spec.loaded_from =~ /^(.+)\/specifications/
16
+ homes[$1] = true
17
+ end
18
+
19
+ # find gem home
20
+ $home = homes.keys.detect { |home| home !~ /^\/System/ }
21
+ $rdoc_home = File.join($home, "doc")
22
+ $gem_home = File.join($home, "gems")
23
+ $croc_home = File.join(user_home_dir, ".croc")
24
+
25
+ puts "Indexing gems in #{$gem_home}"
26
+
27
+ # install system gems
28
+ gems_to_install = specs.select { |spec| spec.loaded_from =~ /^\/System/ }
29
+
30
+ unless gems_to_install.empty?
31
+ puts "It looks like you're using a Mac and these gems are installed under /System:"
32
+ puts
33
+ puts " #{gems_to_install.collect {|spec| "#{spec.name}-#{spec.version}"}.join(" ")}"
34
+ puts
35
+ puts "Croc can't index these, but installing them in your main gems directory"
36
+ puts "should solve the problem. Install them (may require sudo)? (y/n)"
37
+ if gets.strip == "y"
38
+ gems_to_install.each do |spec|
39
+ puts "Installing #{spec.name}-#{spec.version}"
40
+ `gem install -v #{spec.version} #{spec.name}`
41
+ end
42
+ else
43
+ specs = specs - gems_to_install
44
+ end
45
+ end
46
+
47
+ # install rdocs
48
+ rdocs_to_install = specs.select { |spec| !File.exists?(File.join($rdoc_home, "#{spec.name}-#{spec.version}", "rdoc"))}
49
+
50
+ unless rdocs_to_install.empty?
51
+ puts "These gems have rdocs, but they're not installed:"
52
+ puts
53
+ puts " #{rdocs_to_install.collect {|spec| "#{spec.name}-#{spec.version}"}.join(" ")}"
54
+ puts
55
+ puts "Install them (may require sudo)? (y/n)"
56
+ if gets.strip == "y"
57
+ rdocs_to_install.each do |spec|
58
+ puts "Installing rdocs for #{spec.name}-#{spec.version}"
59
+ `sudo gem rdoc -v #{spec.version} #{spec.name}`
60
+ end
61
+ else
62
+ specs = specs - rdocs_to_install
63
+ end
64
+ end
65
+
66
+ croc_rdoc_dir = File.join($croc_home, "rdoc")
67
+ unless File.exist?(File.join(croc_rdoc_dir, "ruby"))
68
+ puts "Install Ruby and Stdlib rdocs from www.ruby-doc.org? (y/n)"
69
+ if gets.strip == "y"
70
+ install_rdocs("http://www.ruby-doc.org/download/ruby-1.8.6-core-rdocs.tgz", "core-1.8.6_HEAD", "ruby")
71
+
72
+ Dir.chdir(croc_rdoc_dir)
73
+ puts "Installing http://www.ruby-doc.org/download/stdlib/ruby-doc-stdlib-0.10.1.tgz"
74
+ tgz_file = File.join(croc_rdoc_dir, "dest.tgz")
75
+ download("http://www.ruby-doc.org/download/stdlib/ruby-doc-stdlib-0.10.1.tgz", tgz_file)
76
+ `tar xzf #{tgz_file}`
77
+ src_file = File.join(croc_rdoc_dir, "ruby-doc-stdlib-0.10.1")
78
+ libdoc_dir = File.join(src_file, "stdlib", "libdoc")
79
+ Dir.chdir(libdoc_dir)
80
+ Dir.glob("**/index.html").each do |f|
81
+ f = File.split(f)[0]
82
+ move_to = File.split(f)[0].split(File::SEPARATOR).join('-')
83
+ File.move(File.join(libdoc_dir, f), File.join(croc_rdoc_dir, move_to))
84
+ end
85
+ File.delete(tgz_file)
86
+ FileUtils.rm_r(src_file)
87
+ else
88
+ specs = specs - rdocs_to_install
89
+ end
90
+ end
91
+
92
+ Dir.chdir(File.join(user_home_dir, ".croc", "rdoc"))
93
+ Dir.glob("*").each do |name|
94
+ next if name == "." || name == ".."
95
+ index_rdoc(name, File.join(Dir.pwd, name))
96
+ end
97
+
98
+ specs.each { |spec| index_rdoc(spec.name, File.join($rdoc_home, "#{spec.name}-#{spec.version}", "rdoc")) }
99
+
100
+ idx = -1
101
+ File.open(File.join(user_home_dir, ".croc", "data.js"), "w") do |f|
102
+ f.puts "objs = ["
103
+ $gems.each do |gem|
104
+ f.puts " {t: 'g', p: null, n: '#{gem[:name]}', l: '#{gem[:name].downcase}', u: '#{gem[:dir]}'},"
105
+ idx += 1
106
+ gidx = idx
107
+ gem[:classes].each_pair do |key, value|
108
+ f.puts " {t: 'c', p: #{gidx}, n: '#{key}', l: '#{key.downcase}', u: '#{value[:url]}'},"
109
+ idx += 1
110
+ cidx = idx
111
+ value[:methods].each do |method|
112
+ f.puts " {t: 'm', p: #{cidx}, n: '#{method[:name]}', l: '#{method[:name].downcase}', u: '#{method[:url]}'},"
113
+ idx += 1
114
+ end
115
+ end
116
+ end
117
+ f.puts "];"
118
+ end
119
+
120
+ puts "Bookmark file://#{File.join(user_home_dir, ".croc", "index.html")}"
@@ -0,0 +1,130 @@
1
+ require "rubygems"
2
+ require "hpricot"
3
+ require "ftools"
4
+ require "net/http"
5
+
6
+
7
+ # Figure out where rdocs are installed.
8
+ def find_gem_home
9
+ if File.exists?("/Library/Ruby/Gems/1.8/gems")
10
+ return "/Library/Ruby/Gems/1.8/gems"
11
+ end
12
+ end
13
+
14
+ # Returns the user's home directory as a string.
15
+ def user_home_dir
16
+ ENV["HOME"]
17
+ end
18
+
19
+ # Creates ~/.croc and installs assets from public directory.
20
+ def install_assets
21
+ croc_dir = File.join(user_home_dir, ".croc")
22
+ unless File.exists?(croc_dir)
23
+ Dir.mkdir(croc_dir)
24
+ Dir.mkdir(File.join(croc_dir, "rdoc"))
25
+ end
26
+
27
+ public_dir = File.join(File.dirname(__FILE__), "..", "public")
28
+ Dir.new(public_dir).each do |f|
29
+ next if f == "." || f == ".."
30
+ File.copy(File.join(public_dir, f), File.join(croc_dir, f))
31
+ end
32
+ end
33
+
34
+ # Print installed gems and whether rdocs are available and indeed installed.
35
+ def print_gems
36
+ Gem.source_index.search(Gem::Dependency.new("", nil)).each do |spec|
37
+ puts "#{spec.name} #{spec.version} #{spec.has_rdoc? ? "has rdoc" : ""} #{File.exists?(rdoc_dir) ? "and it exists" : "but it doesn't exist"}"
38
+ end
39
+ end
40
+
41
+ # Collect specs for most recent version of all installed gems.
42
+ def get_specs
43
+ specs = {}
44
+ Gem.source_index.search(Gem::Dependency.new("", nil)).each do |spec|
45
+ next unless spec.has_rdoc?
46
+ if specs[spec.name]
47
+ specs[spec.name] = spec if (spec.version <=> specs[spec.name].version) > 0
48
+ else
49
+ specs[spec.name] = spec
50
+ end
51
+ end
52
+ specs.values
53
+ end
54
+
55
+ def index_rdoc(name, rdoc_dir)
56
+ unless File.exists?(rdoc_dir)
57
+ puts "WARN Couldn't find rdoc dir #{rdoc_dir}"
58
+ return
59
+ end
60
+
61
+ methods_file = File.join(rdoc_dir, "fr_method_index.html")
62
+ unless File.exists?(methods_file)
63
+ puts "ERROR Couldn't find #{methods_file}"
64
+ end
65
+
66
+ classes_file = File.join(rdoc_dir, "fr_class_index.html")
67
+ unless File.exists?(classes_file)
68
+ puts "ERROR Couldn't find #{classes_file}"
69
+ end
70
+
71
+ # collect gem
72
+ gem = {:name => name, :dir => rdoc_dir, :classes => {}}
73
+ $gems << gem
74
+
75
+ # collect classes
76
+ doc = Hpricot(open(classes_file))
77
+ (doc/"#index-entries a").each do |el|
78
+ klass = el.inner_html
79
+
80
+ if $classes[klass]
81
+ puts "Already have class #{klass}"
82
+ else
83
+ gem[:classes][klass] = {:name => klass, :url => el["href"], :methods => []}
84
+ end
85
+ end
86
+
87
+ # collect methods
88
+ doc = Hpricot(open(methods_file))
89
+ (doc/"#index-entries a").each do |el|
90
+ if el.inner_html =~ /(\S+)\s+\((\S+)\)/
91
+ method = $1
92
+ if klass = gem[:classes][$2]
93
+ el["href"] =~ /#(.*)$/
94
+ url = $1
95
+ klass[:methods] << {:name => method, :class => klass, :url => url}
96
+ else
97
+ # puts %Q(Couldn't find class "#{$2} for method #{method}")
98
+ end
99
+ else
100
+ # puts %Q(Couldn't get method and class from "#{el.inner_html}")
101
+ end
102
+ end
103
+
104
+ puts "Indexed #{name}"
105
+ end
106
+
107
+ def download(url, dest)
108
+ File.open(dest, "w") do |f|
109
+ url = URI.parse(url)
110
+ http = Net::HTTP.new(url.host, url.port)
111
+ http.request_get(url.path) do |resp|
112
+ resp.read_body do |s|
113
+ f.write(s)
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def install_rdocs(url, src_dir_name, dest_dir_name)
120
+ croc_rdoc_dir = File.join($croc_home, "rdoc")
121
+ Dir.chdir(croc_rdoc_dir)
122
+ puts "Installing #{url}"
123
+ tgz_file = File.join(croc_rdoc_dir, "dest.tgz")
124
+ src_file = File.join(croc_rdoc_dir, src_dir_name)
125
+ dest_file = File.join(croc_rdoc_dir, dest_dir_name)
126
+ download(url, tgz_file)
127
+ `tar xzf #{tgz_file}`
128
+ File.rename(src_file, dest_file)
129
+ File.delete(tgz_file)
130
+ end
@@ -0,0 +1,74 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ body, th, td {
5
+ font: normal 12px sans-serif;
6
+ }
7
+
8
+ #main {
9
+ width: 640px;
10
+ margin: auto;
11
+ }
12
+
13
+ th {
14
+ font-weight: bold;
15
+ text-align: left;
16
+ }
17
+
18
+ h1 {
19
+ font-size: 1.7em;
20
+ color: #800;
21
+ border-bottom: 1px solid #888;
22
+ margin: 0 0 2px 0;
23
+ }
24
+
25
+ h2 {
26
+ font-size: 1.2em;
27
+ color: #a00;
28
+ }
29
+
30
+ #deck {
31
+ text-align: right;
32
+ color: #a00;
33
+ }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <div id="main">
38
+ <h1>croc</h1>
39
+ <div id="deck">
40
+ Quick access to your locally installed rdocs
41
+ </div>
42
+
43
+ <h2>Examples with rspec</h2>
44
+ <table>
45
+ <tr>
46
+ <th>test</th>
47
+ <td>Gems, classes and methods that contain <em>test</em></td>
48
+ </tr>
49
+ <tr>
50
+ <th>test_finished.</th>
51
+ <td>Classes and methods named exactly <em>test_finished</em></td>
52
+ </tr>
53
+ <tr>
54
+ <th>rsp test</th>
55
+ <td>Classes and methods that contain <em>test</em> in gems that contain <em>rsp</em></td>
56
+ </tr>
57
+ <tr>
58
+ <th>rsp test_finished.</th>
59
+ <td>Classes and methods named exactly <em>test_finished</em> in gems that contain <em>rsp</em></td>
60
+ </tr>
61
+ </table>
62
+
63
+ <h2>Install your own rdocs</h2>
64
+ <p>
65
+ Copy your rdocs directory to ~/.croc/rdoc and make sure it's named
66
+ something sensible. Rerun croc.
67
+ </p>
68
+
69
+ <h2>About</h2>
70
+ Copyright 2008 <a href="http://blog.methodhead.com">Dan Hensgen</a>.
71
+ Feedback appreciated at <a href="mailto:dan@methodhead.com">dan@methodhead.com</a>.
72
+ </div>
73
+ </body>
74
+ </html>
@@ -0,0 +1,202 @@
1
+ <html>
2
+ <head>
3
+ <title>Croc</title>
4
+ <script type="text/javascript" src="jquery.js"></script>
5
+ <script type="text/javascript" src="jquery.dimensions.js"></script>
6
+ <script type="text/javascript" src="data.js"></script>
7
+ <script>
8
+ suggestions = [];
9
+
10
+ function find(q) {
11
+ if (q == null || jQuery.trim(q).length == 0) {
12
+ return [];
13
+ }
14
+
15
+ gemPart = null;
16
+ if (q.indexOf(' ') > 0) {
17
+ a = q.split(/\s+/)
18
+ gemPart = a[0];
19
+ q = a[1];
20
+ }
21
+
22
+ var sugs = [];
23
+ var includedGems = {};
24
+ var parentGems = {};
25
+
26
+ if (gemPart) {
27
+ for (i in objs) {
28
+ o = objs[i];
29
+ if (o.t == "g") {
30
+ if (o.l.indexOf(gemPart) >= 0) {
31
+ parentGems[i] = true;
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ for (i in objs) {
38
+ o = objs[i];
39
+ if ((q[q.length - 1] == '.' && o.l == q.substr(0, q.length - 1)) || o.l.indexOf(q) >= 0) {
40
+ if (o.t == "c") {
41
+ if (!gemPart || parentGems[o.p]) {
42
+ if (!includedGems[o.p]) {
43
+ includedGems[o.p] = true;
44
+ sugs.push(objs[o.p]);
45
+ }
46
+ sugs.push(o);
47
+ }
48
+ }
49
+ else if (o.t == "m") {
50
+ if (!gemPart || parentGems[objs[o.p].p]) {
51
+ if (!includedGems[objs[o.p].p]) {
52
+ includedGems[objs[o.p].p] = true;
53
+ sugs.push(objs[objs[o.p].p])
54
+ }
55
+ sugs.push(o);
56
+ }
57
+ }
58
+ else {
59
+ if (!gemPart || parentGems[i]) {
60
+ includedGems[i] = true;
61
+ sugs.push(o);
62
+ }
63
+ }
64
+
65
+ if (sugs.length > 50) {
66
+ sugs.push(0)
67
+ break;
68
+ }
69
+ }
70
+ }
71
+
72
+ return sugs;
73
+ }
74
+
75
+ function resultsToHtml(results) {
76
+ html = '';
77
+ jQuery.each(results, function(i, o) {
78
+ if (o == 0) {
79
+ html += '<div>...</div>';
80
+ }
81
+ else {
82
+ switch (o.t) {
83
+ case "g":
84
+ html += '<div class="gem"><a target="iframe" href="file://' + o.u + '/index.html">' + o.n + '</a></div>'; break;
85
+ case "c":
86
+ html += '<div class="class-or-method"><a target="iframe" href="file://' + objs[o.p].u + '/' + o.u + '">' + o.n + '</a></div>'; break;
87
+ case "m":
88
+ html += '<div class="class-or-method"><a target="iframe" href="file://' + objs[objs[o.p].p].u + '/' + objs[o.p].u + '#' + o.u + '">' + o.n + '</a>&nbsp;(' + objs[o.p].n + ')</div>'; break;
89
+ }
90
+ }
91
+ });
92
+
93
+ return html;
94
+ }
95
+
96
+ function sizeElements() {
97
+ $('#suggestions').css('top', $('input').position()['top'] + $('input').outerHeight() + 'px');
98
+ $('#iframe').css('height', $(window).height() - $('input').outerHeight() - 5 + 'px' );
99
+ $('#iframe').css('width', $(window).width() - 5 + 'px' );
100
+ }
101
+
102
+ $(document).ready(function(){
103
+ sizeElements();
104
+
105
+ $(document).keyup(function(e) {
106
+ if (e.keyCode == 27) {
107
+ $('#suggestions').hide();
108
+ }
109
+ });
110
+
111
+ $('iframe').keyup(function(e) {
112
+ if (e.keyCode == 27) {
113
+ $('#suggestions').hide();
114
+ }
115
+ });
116
+
117
+ // search and update results
118
+ $('input').keyup(function(e) {
119
+ if (e.keyCode == 27) {
120
+ return;
121
+ }
122
+
123
+ matches = find(e.target.value.toLowerCase());
124
+
125
+ html = resultsToHtml(matches);
126
+
127
+ if (matches.length > 0) {
128
+ $('#suggestions').html(html).show();
129
+ }
130
+ else {
131
+ $('#suggestions').hide();
132
+ }
133
+ });
134
+
135
+ // hide suggestions if user clicks on a link
136
+ $('#suggestions').click(function(e) {
137
+ if (e.target.nodeName == "A") {
138
+ $('#suggestions').hide();
139
+ $('#recents').prepend('&nbsp;&nbsp;<a target="iframe" href="' + e.target.href + '">' + e.target.innerHTML + '</a>');
140
+ }
141
+ });
142
+
143
+ $('input').focus();
144
+
145
+ $('input').focus(function() {
146
+ if (!$('#suggestions').text().match(/^\s*$/)) {
147
+ $('#suggestions').show();
148
+ }
149
+ });
150
+ });
151
+ </script>
152
+
153
+ <style>
154
+ * {
155
+ margin: 0;
156
+ font: 9pt sans-serif;
157
+ }
158
+ body {
159
+ margin: 0;
160
+ padding: 0;
161
+ }
162
+ input {
163
+ float: left;
164
+ width: 10%
165
+ }
166
+
167
+ #suggestions {
168
+ border: 1px solid #bbb;
169
+ position: absolute;
170
+ background-color: #fff;
171
+ color: #aaa;
172
+ padding: 3px;
173
+ }
174
+
175
+ #suggestions div.gem {
176
+ font-weight: bold;
177
+ }
178
+
179
+ #suggestions div.class-or-method {
180
+ padding-left: 1em;
181
+ }
182
+
183
+ #recents {
184
+ float: left;
185
+ width: 89%;
186
+ overflow: hidden;
187
+ padding: 5px 0 0 3px;
188
+ }
189
+ </style>
190
+ </head>
191
+ <body>
192
+ <form>
193
+ <input>
194
+ <div id="recents"></div>
195
+ <div id="suggestions" style="display:none">
196
+
197
+ </div>
198
+ </form>
199
+ <iframe id="iframe" name="iframe" src="about.html">
200
+ </iframe>
201
+ </body>
202
+ </html>