wycats-textmate 0.9.2 → 0.9.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.
Files changed (3) hide show
  1. data/Rakefile +1 -1
  2. data/bin/textmate +108 -62
  3. metadata +7 -5
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rake/gempackagetask'
3
3
  require 'date'
4
4
 
5
5
  GEM = "textmate"
6
- GEM_VERSION = "0.9.2"
6
+ GEM_VERSION = "0.9.6"
7
7
  AUTHOR = "Yehuda Katz"
8
8
  EMAIL = "wycats@gmail.com"
9
9
  HOMEPAGE = "http://yehudakatz.com"
data/bin/textmate CHANGED
@@ -5,17 +5,18 @@ require "rubygems"
5
5
  require "thor"
6
6
  require "open-uri"
7
7
  require "yaml"
8
+ require "cgi"
8
9
 
9
10
  class TextmateInstaller < Thor
10
-
11
+
11
12
  # CHANGED: renamed list to remote. Could there be a better name?
12
- desc "remote [SEARCH]", "Lists all the matching remote bundles"
13
- def remote(search_term = "")
13
+ desc "search [SEARCH]", "Lists all the matching remote bundles"
14
+ def search(search_term = "")
14
15
  search_term = Regexp.new(".*#{search_term}.*", "i")
15
-
16
- remote_bundle_locations.each do |name,location|
16
+
17
+ REMOTE_LOCATIONS.each do |name,location|
17
18
  puts "\n" << name.to_s << " Remote Bundles\n" << name.to_s.gsub(/./,'-') << '---------------'
18
-
19
+
19
20
  results = case location[:scm]
20
21
  when :svn
21
22
  %x[svn list #{e_sh location[:url]}].map {|x| x.split(".")[0]}.select {|x| x =~ search_term}.join("\n")
@@ -26,50 +27,53 @@ class TextmateInstaller < Thor
26
27
  "%s (by %s)" %
27
28
  [
28
29
  normalize_github_repo_name(result['name']).split('.').first,
29
- result['url'][/github\.com\/([a-zA-Z0-9]+)\//, 1] # Extract the username out of the repo URL
30
+ result['username']
30
31
  ]
31
32
  }
32
33
  end
33
-
34
+
34
35
  puts results
35
36
  end
36
37
  end
37
-
38
+
38
39
  desc "list [SEARCH]", "lists all the bundles installed locally"
39
40
  def list(search_term = "")
40
41
  search_term = Regexp.new(".*#{search_term}.*", "i")
41
-
42
+
42
43
  local_bundle_paths.each do |name,bundles_path|
43
44
  puts "\n" << name.to_s << " Bundles\n" << name.to_s.gsub(/./,'-') << '--------'
44
45
  puts Dir["#{e_sh bundles_path}/*.tmbundle"].map {|x| x.split("/").last.split(".").first}.
45
46
  select {|x| x =~ search_term}.join("\n")
46
47
  end
47
48
  end
48
-
49
- desc "install NAME [SOURCE]", "install a bundle"
50
- def install(bundle_name, remote_bundle_location_name=nil)
49
+
50
+ desc "install NAME", "Install a bundle. Source must be one of trunk, review, or github. \n" \
51
+ "If multiple gems with the same name exist, you will be prompted to \n" \
52
+ "choose from the available list."
53
+ method_option :source
54
+ def install(bundle_name)
51
55
  FileUtils.mkdir_p install_bundles_path
52
56
  puts "Checking out #{bundle_name}..."
53
-
57
+
54
58
  # CHANGED: It's faster to just try and fail for each repo than to search them all first
55
59
  installed=false
56
- remote_bundle_locations.each do |remote_name,location|
57
- next unless remote_name.to_s.downcase.include? remote_bundle_location_name.to_s.downcase if remote_bundle_location_name
58
-
60
+ REMOTE_LOCATIONS.each do |remote_name,location|
61
+ next unless remote_bundle(options["source"]) == location if options.key?("source")
62
+
59
63
  cmd = case location[:scm]
60
64
  when :git
61
65
  'echo "git remotes not implemented yet"'
62
66
  when :svn
63
- %[svn co #{e_sh location[:url]}/#{e_sh bundle_name}.tmbundle #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
67
+ %[svn co "#{location[:url]}/#{url_escape bundle_name}.tmbundle" #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
64
68
  when :github
65
69
  repos = find_github_bundles(denormalize_github_repo_name(bundle_name))
66
-
70
+
67
71
  # Handle possible multiple Repos with the same name
68
72
  case repos.size
69
73
  when 0
70
74
  'echo "Sorry, no such bundle found"'
71
75
  when 1
72
- %[git clone #{e_sh repos.first['url'].sub('http', 'git') + '.git'} #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
76
+ git_clone(repos.first["username"], repos.first["name"])
73
77
  else
74
78
  puts "Multiple bundles with that name found. Please choose which one you want to install:"
75
79
  repos.each_with_index {|repo, idx|
@@ -77,49 +81,57 @@ class TextmateInstaller < Thor
77
81
  [
78
82
  idx + 1,
79
83
  normalize_github_repo_name(repo['name']),
80
- repo['url'][/github\.com\/([a-zA-Z0-9]+)\//, 1]
84
+ repo['username']
81
85
  ]
82
86
  }
83
87
  print "Your choice: "
84
-
88
+
85
89
  # Since to_i defaults to 0, we have to use Integer
86
90
  choice = Integer(STDIN.gets.chomp) rescue nil
87
91
  until choice && (0...repos.size).include?( choice - 1 ) do
88
92
  print "Sorry, invalid choice. Please enter a valid number or Ctrl+C to stop: "
89
93
  choice = Integer(STDIN.gets.chomp) rescue nil
90
94
  end
91
-
92
- %[git clone #{e_sh repos[choice - 1]['url'].sub('http', 'git') + '.git'} #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
95
+
96
+ git_clone(repos[choice - 1]["username"], repos[choice - 1]["name"])
93
97
  end
94
98
  end
95
-
99
+
96
100
  res = %x{#{cmd}}
97
-
101
+
98
102
  puts cmd, res.gsub(/^/,' ')
99
-
103
+
100
104
  installed=true and break if res =~ /Checked out revision|Initialized empty Git repository/
101
105
  end
102
106
  abort 'Not Installed' unless installed
103
-
107
+
104
108
  reload :verbose => true
105
109
  end
106
110
 
107
111
  desc "uninstall NAME", "uninstall a bundle"
108
112
  def uninstall(bundle_name)
113
+ removed = false
114
+
109
115
  puts "Removing bundle..."
110
- # When moving to the trash, maybe move the bundle into a trash/disabled_bundles subfolder
111
- # named as the bundles_path key. Just in case there are multiple versions of
116
+ # When moving to the trash, maybe move the bundle into a trash/disabled_bundles subfolder
117
+ # named as the bundles_path key. Just in case there are multiple versions of
112
118
  # the same bundle in multiple bundle paths
113
119
  local_bundle_paths.each do |name,bundles_path|
114
120
  bundle_path = "#{bundles_path}/#{bundle_name}.tmbundle"
115
121
  if File.exist? bundle_path
122
+ removed = true
116
123
  %x[osascript -e 'tell application "Finder" to move the POSIX file "#{bundle_path}" to trash']
117
124
  end
118
125
  end
119
-
120
- reload :verbose => true
126
+
127
+ unless removed
128
+ say "There is no bundle by that name in the system", :red
129
+ exit
130
+ else
131
+ reload :verbose => true
132
+ end
121
133
  end
122
-
134
+
123
135
  desc "reload", "Reloads TextMate Bundles"
124
136
  method_options :verbose => :boolean
125
137
  def reload(opts = {})
@@ -127,18 +139,24 @@ class TextmateInstaller < Thor
127
139
  %x[osascript -e 'tell app "TextMate" to reload bundles']
128
140
  puts "Done." if opts[:verbose]
129
141
  end
130
-
142
+
131
143
  private
132
- def remote_bundle_locations
133
- { :'Macromates Trunk' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Bundles'},
134
- :'Macromates Review' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Review/Bundles'},
135
-
144
+ REMOTE_LOCATIONS =
145
+ { :'Macromates Trunk' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Bundles'},
146
+ :'Macromates Review' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Review/Bundles'},
147
+
136
148
  # :'Bunch of Git Bundles' => {:scm => :git, :url => 'git://NotImplemented'},
137
-
149
+
138
150
  :'GitHub' => {:scm => :github, :url => 'http://github.com/search?q=tmbundle'},
139
151
  }
152
+
153
+ SHORT_LOCATIONS = {:trunk => :"Macromates Trunk", :review => :"Macromates Review", :github => :"GitHub"}
154
+
155
+ def remote_bundle(name)
156
+ name = name.to_sym
157
+ REMOTE_LOCATIONS[name] || REMOTE_LOCATIONS[SHORT_LOCATIONS[name]]
140
158
  end
141
-
159
+
142
160
  def local_bundle_paths
143
161
  { :Application => '/Applications/TextMate.app/Contents/SharedSupport/Bundles',
144
162
  :User => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles",
@@ -147,48 +165,76 @@ class TextmateInstaller < Thor
147
165
  :'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy/Bundles',
148
166
  }
149
167
  end
150
-
168
+
151
169
  def install_bundles_path
152
170
  local_bundle_paths[:'User Pristine']
153
171
  end
154
-
172
+
155
173
  # Copied from http://macromates.com/svn/Bundles/trunk/Support/lib/escape.rb
156
174
  # escape text to make it useable in a shell script as one “word” (string)
157
175
  def e_sh(str)
158
176
  str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
159
177
  end
160
-
178
+
179
+ def url_escape(str)
180
+ chars = ((33...47).to_a + (94...96).to_a + (123...126).to_a).map {|c| c.chr }.join + "\\[\\]\\\\"
181
+ str = str.to_s.gsub(%r{[#{chars}]}) {|m| CGI.escape(m) }
182
+ end
183
+
161
184
  CAPITALIZATION_EXCEPTIONS = %w[tmbundle on]
162
185
  # Convert a GitHub repo name into a "normal" TM bundle name
163
186
  # e.g. ruby-on-rails-tmbundle => Ruby on Rails.tmbundle
164
187
  def normalize_github_repo_name(name)
165
- name = name.gsub("-", " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
188
+ name = name.gsub(/[\-\.]/, " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
166
189
  name[-9] = ?. if name =~ / tmbundle$/
167
190
  name
168
191
  end
169
-
192
+
170
193
  # Does the opposite of normalize_github_repo_name
171
194
  def denormalize_github_repo_name(name)
172
- name += " tmbundle" unless name =~ / tmbundle$/
173
- name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
195
+ name = name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
196
+ name += ".tmbundle" unless name =~ /.tmbundle$/
197
+ name
174
198
  end
175
-
199
+
176
200
  def find_github_bundles(search_term)
177
- YAML.load(open('http://github.com/api/v1/yaml/search/tmbundle'))['repositories'].
178
- find_all{|result| result['name'].match(search_term)}.
179
- sort{|a,b| a['name'] <=> b['name']}
201
+ # Until GitHub fixes http://support.github.com/discussions/feature-requests/11-api-search-results,
202
+ # we need to account for multiple pages of results:
203
+ page = 1
204
+ repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
205
+ results = []
206
+ until repositories.empty?
207
+ results += repositories.find_all{|result| result['name'].match(search_term)}
208
+ page += 1
209
+ repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
210
+ end
211
+ results.sort{|a,b| a['name'] <=> b['name']}
212
+ end
213
+
214
+ def git_clone(repo, name)
215
+ bundle_name = normalize_github_repo_name(name)
216
+
217
+ path = "#{install_bundles_path}/#{bundle_name}"
218
+ escaped_path = "#{e_sh(install_bundles_path)}/#{e_sh(bundle_name)}"
219
+
220
+ if File.directory?(path)
221
+ say "Sorry, that bundle is already installed. Please uninstall it first.", :red
222
+ exit
223
+ end
224
+
225
+ %[git clone "git://github.com/#{url_escape(repo)}/#{url_escape(name)}.git" #{escaped_path} 2>&1]
180
226
  end
181
-
227
+
182
228
  end
183
229
 
184
230
  # TODO: create a "monument to personal cleverness" by class-izing everything?
185
231
  # class TextMateBundle
186
232
  # def self.find_local(bundle_name)
187
- #
233
+ #
188
234
  # end
189
- #
235
+ #
190
236
  # def self.find_remote(bundle_name)
191
- #
237
+ #
192
238
  # end
193
239
  # attr_reader :name
194
240
  # attr_reader :location
@@ -198,20 +244,20 @@ end
198
244
  # @location = location
199
245
  # @scm = scm
200
246
  # end
201
- #
247
+ #
202
248
  # def install!
203
- #
249
+ #
204
250
  # end
205
- #
251
+ #
206
252
  # def uninstall!
207
- #
253
+ #
208
254
  # end
209
- #
210
- #
255
+ #
256
+ #
211
257
  # def installed?
212
258
  # # List all the installed versions, and where they're at
213
259
  # end
214
- #
260
+ #
215
261
  # # TODO: dirty? method to show if there are any deltas
216
262
  # end
217
263
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wycats-textmate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yehuda Katz
@@ -9,11 +9,12 @@ autorequire: textmate
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-28 00:00:00 -07:00
12
+ date: 2009-08-03 00:00:00 -07:00
13
13
  default_executable: textmate
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: thor
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -35,8 +36,9 @@ files:
35
36
  - README.markdown
36
37
  - Rakefile
37
38
  - bin/textmate
38
- has_rdoc: true
39
+ has_rdoc: false
39
40
  homepage: http://yehudakatz.com
41
+ licenses:
40
42
  post_install_message:
41
43
  rdoc_options: []
42
44
 
@@ -57,9 +59,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
59
  requirements: []
58
60
 
59
61
  rubyforge_project:
60
- rubygems_version: 1.0.1
62
+ rubygems_version: 1.3.5
61
63
  signing_key:
62
- specification_version: 2
64
+ specification_version: 3
63
65
  summary: Command-line textmate package manager
64
66
  test_files: []
65
67