tmb 0.0.5 → 0.0.6
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.tar.gz.sig +1 -1
- data/CHANGELOG +1 -0
- data/Manifest +5 -1
- data/Rakefile +1 -1
- data/bin/tmb +3 -524
- data/lib/tmb.rb +27 -0
- data/lib/tmb/bundle.rb +376 -0
- data/lib/tmb/bundles.rb +205 -0
- data/lib/tmb/commands.rb +133 -0
- data/tmb.gemspec +4 -4
- metadata +13 -5
- metadata.gz.sig +0 -0
data/lib/tmb.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
|
|
7
|
+
module TMB
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ROOT = File.expand_path( File.join( File.dirname(__FILE__), ".."))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def require_lib(*paths)
|
|
17
|
+
paths.each do |path|
|
|
18
|
+
path = File.join([ ROOT, "lib", "tmb", path.to_s + ".rb" ].flatten)
|
|
19
|
+
require path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
TMB.require_lib(:bundle, :bundles, :commands)
|
data/lib/tmb/bundle.rb
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
module TM
|
|
2
|
+
|
|
3
|
+
TextWrap = 78
|
|
4
|
+
IndentSpacing = 10
|
|
5
|
+
Indent = (" " * IndentSpacing)
|
|
6
|
+
Justify = 15
|
|
7
|
+
Delimiter = ": "
|
|
8
|
+
BundleDirectory = "/Library/Application Support/TextMate/Bundles"
|
|
9
|
+
App = File.basename(__FILE__)
|
|
10
|
+
DB = ".tmbdb"
|
|
11
|
+
SearchDB = ".searchdb"
|
|
12
|
+
|
|
13
|
+
Help = <<-eos
|
|
14
|
+
|
|
15
|
+
\033[1m#{App}\033[0m is a utility to search for textmate bundles, download and install
|
|
16
|
+
them, all via a convenient command line interface, much like rubygems.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
======================================
|
|
21
|
+
|
|
22
|
+
# Search for bundles containing the word 'webrat' in
|
|
23
|
+
# the title, description, or author's name.
|
|
24
|
+
|
|
25
|
+
\033[1m#{App} search webrat\033[0m
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Search for bundles containing the word 'rspec' OR
|
|
29
|
+
# 'cucumber' OR 'shoulda' in their title, description,
|
|
30
|
+
# or author's name.
|
|
31
|
+
|
|
32
|
+
\033[1m#{App} search rspec cucumber shoulda\033[0m
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Install a bundle containing the word rspec in its
|
|
36
|
+
# search fields. If multiple matches exist, #{App}
|
|
37
|
+
# will let you choose which version to install.
|
|
38
|
+
|
|
39
|
+
\033[1m#{App} install rspec\033[0m
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# List all installed bundles
|
|
43
|
+
|
|
44
|
+
\033[1m#{App} list\033[0m
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Uninstall bundle matching 'json'. If you type in a
|
|
48
|
+
# fragment and there are multiple installed bundles that begin
|
|
49
|
+
# with that fragment, #{App} will let you choose which version
|
|
50
|
+
# you'd like to destroy
|
|
51
|
+
|
|
52
|
+
\033[1m#{App} uninstall json\033[0m
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Tell textmate (if it's open) to reload its bundle information,
|
|
56
|
+
# and update menus & controls accordingly
|
|
57
|
+
|
|
58
|
+
\033[1m#{App} reload\033[0m
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Print this help information
|
|
62
|
+
|
|
63
|
+
\033[1m#{App} help\033[0m
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
eos
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class String
|
|
73
|
+
|
|
74
|
+
def indent(indent=TM::Indent)
|
|
75
|
+
indent + self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def text_wrap(width=TM::TextWrap, indent=TM::Indent)
|
|
79
|
+
self.gsub /(.{1,#{width}})(\s+|\Z)/, "\\1\n".indent(indent)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def bold
|
|
83
|
+
"\033[1m" + self + "\033[0m"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
module TM
|
|
89
|
+
|
|
90
|
+
class Bundle
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
attr_accessor :result, :repository, :install_output, :before_hooks, :output
|
|
94
|
+
|
|
95
|
+
def self.before(instance, hook, method, args={})
|
|
96
|
+
instance.before_hooks[hook] = {:method => method, :args => args}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def before(hook, method, args=nil)
|
|
100
|
+
self.before_hooks[hook] ||= {}
|
|
101
|
+
self.before_hooks[hook] = { :method => method, :args => args }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def run_before(method)
|
|
105
|
+
self.send(self.before_hooks[method][:method], self.before_hooks[method][:args]) if self.before_hooks[method]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def initialize(result=nil, options={})
|
|
109
|
+
@before_hooks = @result = result
|
|
110
|
+
@repository = options[:repo] || git_repo
|
|
111
|
+
before(:git_install_script, :update_db)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.installed
|
|
115
|
+
Dir.glob("#{BundleDirectory}/*.tmbundle").sort{|a,b| a.downcase <=> b.downcase }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.db_filename
|
|
119
|
+
File.join(BundleDirectory, DB)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.db(settings=nil)
|
|
123
|
+
f = File.open(db_filename,"w+")
|
|
124
|
+
unless settings.nil?
|
|
125
|
+
parsed = settings.merge(YAML::parse( f.read ) || {})
|
|
126
|
+
f.puts YAML::dump( parsed )
|
|
127
|
+
end
|
|
128
|
+
db_content = f.read
|
|
129
|
+
f.close
|
|
130
|
+
YAML::parse( db_content )
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.info
|
|
134
|
+
File.read(db_filename)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.list
|
|
138
|
+
installed.each do |b|
|
|
139
|
+
puts File.basename(b)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.select(bundle)
|
|
144
|
+
installed.select{|b| b =~ Regexp.new(bundle + ".*\.tmbundle$") }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.uninstall(bundle)
|
|
148
|
+
bundle_dir = File.basename(bundle)
|
|
149
|
+
FileUtils.rm_r(File.join(BundleDirectory, bundle_dir))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def short_result
|
|
153
|
+
{:description => result["description"], :repo => repository }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def name
|
|
157
|
+
result["name"] || repository.scan(/(\w\-0-9)\.git$/)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def bundle_name
|
|
161
|
+
name.gsub("tmbundle",'').gsub(/^[[:punct:]]+|[[:punct:]]+$/,'')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def git_repo
|
|
165
|
+
"https://github.com/#{@result["username"]}/#{@result["name"]}.git"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def display_key(key, options={})
|
|
169
|
+
defaults = {:ljust => Justify, :rjust => Justify, :delimiter => Delimiter, :key_prefix => "", :key_suffix => ""}
|
|
170
|
+
options = defaults.merge options
|
|
171
|
+
|
|
172
|
+
if options[:bold_key]
|
|
173
|
+
options[:key_prefix] = "\033[1m" + options[:key_prefix]
|
|
174
|
+
options[:key_suffix] = options[:key_suffix] +"\033[0m"
|
|
175
|
+
end
|
|
176
|
+
if options[:title].nil? || options[:title].strip.length == 0
|
|
177
|
+
options[:title] = ""
|
|
178
|
+
options[:delimiter] = ""#(" " * options[:delimiter].length)
|
|
179
|
+
options[:ljust] = 0
|
|
180
|
+
end
|
|
181
|
+
options[:title] ||= key.to_s
|
|
182
|
+
options[:key_prefix] + (options[:title].capitalize + options[:delimiter]).ljust(options[:ljust]) + options[:key_suffix]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def display_keypair(key, options={})
|
|
186
|
+
defaults = {:title => key.to_s, :ljust => Justify, :rjust => Justify, :delimiter => Delimiter, :key_prefix => "", :key_suffix => "", :value_prefix => "", :value_suffix => ""}
|
|
187
|
+
options = defaults.merge(options)
|
|
188
|
+
if options[:bold]
|
|
189
|
+
options[:bold_value] ||= true
|
|
190
|
+
options[:bold_key] ||= true
|
|
191
|
+
end
|
|
192
|
+
if options[:bold_value]
|
|
193
|
+
options[:value_prefix] = "\033[1m" + options[:value_prefix]
|
|
194
|
+
options[:value_suffix] = options[:value_suffix] +"\033[0m"
|
|
195
|
+
end
|
|
196
|
+
[
|
|
197
|
+
display_key(options[:title], options),
|
|
198
|
+
options[:value_prefix] + ((options[:value] || (key.is_a?(Symbol) && self.methods.include?(key.to_s)) ? self.send(key) : @result[key.to_s]).to_s) + options[:value_suffix]
|
|
199
|
+
].compact.reject{|s| s.strip == "" }.join
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def display_value(key, options={})
|
|
205
|
+
display_keypair(key, options)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def extended_display(key, options={})
|
|
209
|
+
#options[:indent] ||= true
|
|
210
|
+
ed = display_value(key, options).to_s
|
|
211
|
+
unless options[:indent] == false
|
|
212
|
+
ed = ed.indent
|
|
213
|
+
end
|
|
214
|
+
if options[:wrap]
|
|
215
|
+
ed = ed.text_wrap
|
|
216
|
+
end
|
|
217
|
+
if options[:newline]
|
|
218
|
+
ed = ("\n" * options[:newline]) + ed
|
|
219
|
+
end
|
|
220
|
+
ed
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def stats
|
|
224
|
+
"\n" + [
|
|
225
|
+
display_value(:followers, :ljust => Justify ),
|
|
226
|
+
display_value(:forks, :ljust => Justify ),
|
|
227
|
+
display_value(:watchers, :ljust => Justify )
|
|
228
|
+
].map{|v| v.indent(" " * (IndentSpacing + Justify))}.join("\n")
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def popularity
|
|
232
|
+
result["watchers"].to_i || 0
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def short_stats
|
|
236
|
+
"(" + ["followers", "forks", "watchers"].map{|s| result[s.to_s].to_s + " " + s.to_s}.join(", ") + ")"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def as_selection(index)
|
|
240
|
+
"#{(index + 1).to_s}) #{git_repo.ljust(60)} \033[1m#{short_stats}\033[0m"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def display_map
|
|
244
|
+
[
|
|
245
|
+
{:v=> "name", :bold => true, :indent => true, :title => "", :value_prefix => "\e[1;32m"},
|
|
246
|
+
{:v => "username", :title => "Author", :bold_value => true, :newline => 1},
|
|
247
|
+
{:v => "homepage"},
|
|
248
|
+
{:v => :repository, :bold_value => true},
|
|
249
|
+
{:v => :stats, :delimiter => "", :title => ""},
|
|
250
|
+
{:v => "created_at", :newline => 1},
|
|
251
|
+
{:v=> "description", :indent => false, :newline => 2, :title => "", :wrap => true}
|
|
252
|
+
]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def to_s
|
|
256
|
+
display_map.map{|d| extended_display(d.delete(:v), d) }.join("\n") + "\n"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def destination
|
|
260
|
+
File.join(BundleDirectory, "#{bundle_name}.tmbundle")
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def common_install_script
|
|
264
|
+
<<-eos
|
|
265
|
+
bundle_dir="#{BundleDirectory}"
|
|
266
|
+
mkdir -p "$bundle_dir"
|
|
267
|
+
file_name=$(echo -e #{@repository} | grep -o -P "[-\w\.]+$")
|
|
268
|
+
bundle_name=$(echo -e $file_name | sed -E -e 's/\.[a-zA-Z0-9]+$//g' -e 's/\.tmbundle//g')
|
|
269
|
+
dest="#{destination}"
|
|
270
|
+
rm -Rf "$dest"
|
|
271
|
+
eos
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def git_install_script
|
|
276
|
+
<<-eos
|
|
277
|
+
#{common_install_script}
|
|
278
|
+
git clone #{@repository} "$dest"
|
|
279
|
+
eos
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def bundle_reload_script
|
|
283
|
+
"osascript -e 'tell app \"TextMate\" to reload bundles'"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def smart_extract_script(archive)
|
|
287
|
+
<<-eos
|
|
288
|
+
arch=#{archive}
|
|
289
|
+
if [ -f $arch ]; then
|
|
290
|
+
case $arch in
|
|
291
|
+
*.tar.bz2) tar -jxvf $arch ;;
|
|
292
|
+
*.tar.gz) tar -zxvf $arch ;;
|
|
293
|
+
*.bz2) bunzip2 $arch ;;
|
|
294
|
+
*.dmg) hdiutil mount $arch ;;
|
|
295
|
+
*.gz) gunzip $arch ;;
|
|
296
|
+
*.tar) tar -xvf $arch ;;
|
|
297
|
+
*.tbz2) tar -jxvf $arch ;;
|
|
298
|
+
*.tgz) tar -zxvf $arch ;;
|
|
299
|
+
*.zip) unzip $arch ;;
|
|
300
|
+
*.Z) uncompress $arch ;;
|
|
301
|
+
*) echo "'$arch' cannot be extracted/mounted via smartextract()" ;;
|
|
302
|
+
esac
|
|
303
|
+
else
|
|
304
|
+
echo "'$arch' is not a valid file"
|
|
305
|
+
fi
|
|
306
|
+
eos
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def archive_install_script
|
|
310
|
+
<<-eos
|
|
311
|
+
#{common_install_script}
|
|
312
|
+
cd $bundle_dir
|
|
313
|
+
if [[ -n $bundle_name ]]
|
|
314
|
+
then
|
|
315
|
+
rm -R "$bundle_dir/$bundle_name"*
|
|
316
|
+
fi
|
|
317
|
+
curl -o "$bundle_dir/$file_name" $1
|
|
318
|
+
#{ smart_extract_script('$bundle_dir/$file_name')}
|
|
319
|
+
bundle=$(find $bundle_dir/$bundle_name | grep -P "tmbundle$")
|
|
320
|
+
if [[ -n $bundle ]]
|
|
321
|
+
then
|
|
322
|
+
cp -R $bundle $bundle_dir
|
|
323
|
+
fi
|
|
324
|
+
non_bundles=$(find $bundle_dir -d 1 | grep -v -P "tmbundle$|^\.")
|
|
325
|
+
echo $non_bundles | xargs -Ixxx rm -Rf "xxx"
|
|
326
|
+
cd $current
|
|
327
|
+
osascript -e 'tell app "TextMate" to reload bundles'
|
|
328
|
+
eos
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def git_repo?
|
|
332
|
+
File.exists?(File.join(destination,".git"))
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def hash(digester="sha1")
|
|
336
|
+
if git_repo?
|
|
337
|
+
op = File.read(File.join(destination,".git","refs","heads","master"))
|
|
338
|
+
else
|
|
339
|
+
op = IO.popen("cat `ls -F #{destination} | grep -v -E '\/$'` | #{digester} | tr '\n' ''").read
|
|
340
|
+
end
|
|
341
|
+
op.strip
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def db_hash
|
|
345
|
+
{ name => {"url" => repository, "dir" => destination, "installed" => Time.now.to_s, "sha1" => hash}}
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def update_db
|
|
349
|
+
self.class.db(db_hash)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def run_script(method)
|
|
353
|
+
@output = IO.popen(self.send(method)).read
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def bundle_exists?
|
|
357
|
+
File.exists?(destination)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def install
|
|
361
|
+
if bundle_exists?
|
|
362
|
+
puts "That bundle already appears to be installed at #{destination}.\n\nReinstall? [Yn]"
|
|
363
|
+
answer = STDIN.gets.chomp
|
|
364
|
+
return if answer == "n"
|
|
365
|
+
end
|
|
366
|
+
puts "Installing from #{@repository}"
|
|
367
|
+
run_script(:git_install_script)
|
|
368
|
+
puts "Bundle installed to #{destination}. \n\nReloading bundles..."
|
|
369
|
+
run_script(:bundle_reload_script)
|
|
370
|
+
puts "All done!"
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
data/lib/tmb/bundles.rb
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
|
|
2
|
+
module TM
|
|
3
|
+
class Bundles
|
|
4
|
+
|
|
5
|
+
@@search_file = File.join(TM::BundleDirectory, TM::SearchDB)
|
|
6
|
+
|
|
7
|
+
attr_accessor :response, :results, :search_url, :search_terms, :full_set
|
|
8
|
+
|
|
9
|
+
def self.searchdb_file
|
|
10
|
+
@@search_file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.searchdb?
|
|
14
|
+
File.exists?(searchdb_file)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(search_terms=nil)
|
|
18
|
+
@search_terms = search_terms
|
|
19
|
+
@full_set = nil
|
|
20
|
+
@results = []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def search_db(new_entries=nil)
|
|
24
|
+
f = File.open(self.class.searchdb_file,"w+")
|
|
25
|
+
file_contents = f.read
|
|
26
|
+
if new_entries
|
|
27
|
+
|
|
28
|
+
entries = file_contents.length < 2 ? [] : JSON.parse(f.read)
|
|
29
|
+
all_entries = [new_entries, entries].flatten.uniq
|
|
30
|
+
to_file = JSON.generate(all_entries)
|
|
31
|
+
f.puts to_file
|
|
32
|
+
else
|
|
33
|
+
all_entries = file_contents.length < 2 ? [] : JSON.parse(f.read)
|
|
34
|
+
end
|
|
35
|
+
f.close
|
|
36
|
+
return all_entries
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.handle_connection_error(e, url)
|
|
40
|
+
error = e.class.name.split("::")
|
|
41
|
+
exception = error[-1]
|
|
42
|
+
lib = error[0]
|
|
43
|
+
lib = lib == exception ? "OpenURI" : lib
|
|
44
|
+
host = URI.parse(url).host
|
|
45
|
+
error_message = case exception
|
|
46
|
+
when "SocketError" : socket_error(e,url)
|
|
47
|
+
when "HTTPError" : http_error(e,url)
|
|
48
|
+
end
|
|
49
|
+
puts error_message.
|
|
50
|
+
gsub(/#URL/,url).
|
|
51
|
+
gsub(/#LIB/,lib).
|
|
52
|
+
gsub(/#MESSAGE/,e.message).
|
|
53
|
+
gsub(/#EXCEPTION/,exception).
|
|
54
|
+
gsub(/#HOST/,host)
|
|
55
|
+
exit 0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.socket_error(e, url)
|
|
59
|
+
<<-eos
|
|
60
|
+
|
|
61
|
+
#LIB is raising a #EXCEPTION: \033[1m#{e.message}\033[0m
|
|
62
|
+
|
|
63
|
+
Either \033[1m#HOST\033[0m is currently unavailable or your internet connection is down.
|
|
64
|
+
|
|
65
|
+
(We were trying to access \033[1m#URL\033[0m)
|
|
66
|
+
|
|
67
|
+
eos
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.http_code_messages
|
|
71
|
+
{
|
|
72
|
+
"404" => "the page you're attempting to access doesn't exist"
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.http_error(e, url)
|
|
77
|
+
<<-eos
|
|
78
|
+
|
|
79
|
+
#LIB is raising an #EXCEPTION: \033[1m#MESSAGE\033[0m
|
|
80
|
+
|
|
81
|
+
That means #{http_code_messages[e.message.match(/\d{3}/).to_s]}
|
|
82
|
+
|
|
83
|
+
(We were trying to access \033[1m#URL\033[0m)
|
|
84
|
+
|
|
85
|
+
eos
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def search_description(terms=@search_terms)
|
|
89
|
+
"Searching for \033[1m#{terms.to_a.join(', ')}\033[0m"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def put_search_description
|
|
93
|
+
puts "\n" + search_description + "\n"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def short_result(result)
|
|
98
|
+
{:description => result["description"], :repo => git_repo(result) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def sample_response
|
|
102
|
+
'{"repositories": [{"name": "haml", "username": "justin"}]}'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def get_search_pages(url, page_range=1)
|
|
106
|
+
result_set = []
|
|
107
|
+
page_range.times do |i|
|
|
108
|
+
page = i + 1
|
|
109
|
+
full_url = url + "?start_page=#{page}"
|
|
110
|
+
puts "Reading page #{full_url}"
|
|
111
|
+
begin
|
|
112
|
+
res = open(full_url).read
|
|
113
|
+
rescue => e
|
|
114
|
+
self.class.handle_connection_error(e,search_url)
|
|
115
|
+
end
|
|
116
|
+
json_res = JSON.parse(res)
|
|
117
|
+
repos = json_res["repositories"] || []
|
|
118
|
+
#puts repos.map{|r| r["name"]}.join(",")
|
|
119
|
+
puts "#{repos.size} more repositories found, #{result_set.size} total #{i==page_range ? '' : '(still searching...)'}"
|
|
120
|
+
if repos.size > 0
|
|
121
|
+
result_set << repos
|
|
122
|
+
result_set = result_set.flatten.uniq
|
|
123
|
+
|
|
124
|
+
else
|
|
125
|
+
puts "End of results"
|
|
126
|
+
break
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
result_set
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def search(search_terms = @search_terms, options={})
|
|
133
|
+
|
|
134
|
+
bundle_suffix = ["textmate"]
|
|
135
|
+
bundle_identifier = /bundle|tmbundle|textmate/
|
|
136
|
+
options[:additive] ||= true
|
|
137
|
+
# Don't add tmbundle term if its already in the users search
|
|
138
|
+
regex_terms = Regexp.new(search_terms.to_a.select{|s| s.length > 1 }.join("|"))
|
|
139
|
+
actual_search_terms = search_terms.nil? ? bundle_suffix : [ search_terms ]
|
|
140
|
+
formatted_terms = actual_search_terms.flatten.compact.uniq.join(' ')
|
|
141
|
+
search_url="http://github.com/api/v2/json/repos/search/#{URI.escape(formatted_terms)}"
|
|
142
|
+
#puts "Searching for #{formatted_terms}"
|
|
143
|
+
if search_terms.nil? && @full_set.nil?
|
|
144
|
+
#puts "Getting full set"
|
|
145
|
+
@full_set = get_search_pages(search_url, 20)
|
|
146
|
+
puts "#{@full_set.size} Repositories found"
|
|
147
|
+
search_db(@full_set)
|
|
148
|
+
@full_set.each_with_index do |r,i|
|
|
149
|
+
@full_set[i]["search_terms"] = r.values_at("username", "name", "description").join
|
|
150
|
+
end
|
|
151
|
+
@results = @full_set
|
|
152
|
+
else
|
|
153
|
+
# puts "Searching for matching results for #{search_terms.to_a.join(',')}..."
|
|
154
|
+
@results = @results.to_a.select{|r| r["search_terms"] =~ regex_terms}
|
|
155
|
+
end
|
|
156
|
+
@results = @results.uniq
|
|
157
|
+
current_results = search_terms.nil? ? @full_set : @results
|
|
158
|
+
return current_results.uniq
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def search_local(terms)
|
|
162
|
+
@results = @full_set = JSON.parse( File.read(@@search_file) )
|
|
163
|
+
puts "#{@full_set.size} local results"
|
|
164
|
+
@full_set.each_with_index do |r,i|
|
|
165
|
+
@full_set[i]["search_terms"] = r.values_at("username", "name", "description").join
|
|
166
|
+
end
|
|
167
|
+
@search_terms = terms
|
|
168
|
+
search(terms)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def display_results(res=nil)
|
|
173
|
+
res ||= results
|
|
174
|
+
to_display = res || search(search_terms)
|
|
175
|
+
to_display.sort{|a,b| a["name"].downcase <=> b["name"].downcase }.map{|r| TM::Bundle.new(r).to_s }.join("\n\n")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def bash_install_script_from_repo(repo)
|
|
179
|
+
<<-eos
|
|
180
|
+
bundle_dir="/Library/Application Support/TextMate/Bundles"
|
|
181
|
+
mkdir -p "$bundle_dir"
|
|
182
|
+
file_name=$(echo -e #{repo} | grep -o -P "[-\w\.]+$")
|
|
183
|
+
bundle_name=$(echo -e $file_name | sed -E -e 's/\.[a-zA-Z0-9]+$//g' -e 's/\.tmbundle//g')
|
|
184
|
+
dest=$bundle_dir/$bundle_name.tmbundle
|
|
185
|
+
rm -Rf "$dest"
|
|
186
|
+
git clone #{repo} "$dest"
|
|
187
|
+
osascript -e 'tell app "TextMate" to reload bundles'
|
|
188
|
+
eos
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def install(repo)
|
|
192
|
+
puts repo
|
|
193
|
+
if repo.match(/^(http|git|https):.*\.git$/)
|
|
194
|
+
matching_repo = TM::Bundle.new([], :repo => repo)
|
|
195
|
+
elsif repo.match(/^(http|ftp|https):.*\.(zip|gz|tar.gz|bz2|tar.bz2)$/)
|
|
196
|
+
matching_repo = TM::Bundle.new([], :repo => repo)
|
|
197
|
+
else
|
|
198
|
+
matching_repo = TM::Bundle.new(search(repo).first)
|
|
199
|
+
end
|
|
200
|
+
install_output = matching_repo.install
|
|
201
|
+
puts install_output
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
end
|