ddollar-textmate 0.9.8 → 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.
data/README.markdown CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  A binary that provides package management for TextMate.
4
4
 
5
+ # Example
6
+
7
+ $ textmate install javascript
8
+ Please select a bundle from the following list:
9
+
10
+ Github
11
+ ------
12
+ 1. Javascript
13
+
14
+ Macromates
15
+ ----------
16
+ 2. JavaScript
17
+ 3. JavaScript Flash
18
+ 4. JavaScript JSDoc
19
+ 5. JavaScript MooTools
20
+ 6. JavaScript Objective-J
21
+ 7. JavaScript Prototype & Script_aculo_us
22
+ 8. JavaScript YUI
23
+ 9. JavaScript jQuery
24
+
25
+ Which bundle would you like to install? 1
26
+
27
+ Please select a repository from the following list:
28
+
29
+ 1. subtleGradient/javascript.tmbundle watchers:16 updated:2009-04-29
30
+ 2. textmate/javascript.tmbundle watchers:6 updated:2009-06-09
31
+ 3. rdougan/javascript.tmbundle watchers:2 updated:2009-01-28
32
+ 4. cohitre/javascript-tmbundle watchers:1 updated:2009-01-29
33
+
34
+ Which repository would you like to install? 1
35
+
36
+ Javascript bundle installed
37
+
38
+ Reloading bundles...
39
+
5
40
  # Usage
6
41
 
7
42
  `textmate [COMMAND] [*PARAMS]`
@@ -10,7 +45,7 @@ Textmate bundles are automatically reloaded after install or uninstall operation
10
45
 
11
46
  ## List available remote bundles
12
47
 
13
- `textmate remote [SEARCH]`
48
+ `textmate search [SEARCH]`
14
49
 
15
50
  List all of the available bundles in the remote repository, optionally filtering by `search`.
16
51
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.8
1
+ 1.0.0
data/bin/textmate CHANGED
@@ -1,236 +1,94 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "fileutils"
4
- require "rubygems"
5
- require "thor"
6
- require "open-uri"
7
- require "yaml"
3
+ require 'thor'
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6
+ require 'textmate'
8
7
 
9
8
  class TextmateInstaller < Thor
10
-
11
- # CHANGED: renamed list to remote. Could there be a better name?
9
+
12
10
  desc "search [SEARCH]", "Lists all the matching remote bundles"
13
- def search(search_term = "")
14
- search_term = Regexp.new(".*#{search_term}.*", "i")
15
-
16
- remote_bundle_locations.keys.map { |key| key.to_s }.sort.each do |name|
17
- location = remote_bundle_locations[name.to_sym]
18
- puts "\n" << name.to_s << " Remote Bundles\n" << name.to_s.gsub(/./,'-') << '---------------'
19
-
20
- results = case location[:scm]
21
- when :svn
22
- %x[svn list #{e_sh location[:url]}].select {|x| x =~ search_term}.map do |result|
23
- "%s - %s" % [
24
- result.split('.').first,
25
- "#{location[:url]}/#{result.chomp}"
26
- ]
27
- end.join("\n")
28
- when :git
29
- 'git remotes not implemented yet'
30
- when :github
31
- find_github_bundles(search_term).map {|result|
32
- "%s - %s" %
33
- [
34
- normalize_github_repo_name(result['name']).split('.').first,
35
- git_url_from_github_result(result)
36
- ]
37
- }
38
- end
39
-
40
- puts results
41
- end
11
+ def search(search = "")
12
+ display_hash remote.bundles(search)
42
13
  end
43
-
14
+
44
15
  desc "list [SEARCH]", "lists all the bundles installed locally"
45
- def list(search_term = "")
46
- search_term = Regexp.new(".*#{search_term}.*", "i")
47
-
48
- local_bundle_paths.each do |name,bundles_path|
49
- puts "\n" << name.to_s << " Bundles\n" << name.to_s.gsub(/./,'-') << '--------'
50
- puts Dir["#{e_sh bundles_path}/*.tmbundle"].map {|x| x.split("/").last.split(".").first}.
51
- select {|x| x =~ search_term}.join("\n")
52
- end
16
+ def list(search = "")
17
+ display_hash local.bundles(search)
53
18
  end
54
-
19
+
55
20
  desc "install NAME [SOURCE]", "install a bundle"
56
- def install(bundle_name, remote_bundle_location_name=nil)
57
- FileUtils.mkdir_p install_bundles_path
58
- puts "Checking out #{bundle_name}..."
59
-
60
- # CHANGED: It's faster to just try and fail for each repo than to search them all first
61
- installed=false
62
- remote_bundle_locations.each do |remote_name,location|
63
- next unless remote_name.to_s.downcase.include? remote_bundle_location_name.to_s.downcase if remote_bundle_location_name
64
-
65
- cmd = case location[:scm]
66
- when :git
67
- 'echo "git remotes not implemented yet"'
68
- when :svn
69
- %[svn co #{e_sh location[:url]}/#{e_sh bundle_name}.tmbundle #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
70
- when :github
71
- repos = find_github_bundles(denormalize_github_repo_name(bundle_name))
72
-
73
- # Handle possible multiple Repos with the same name
74
- case repos.size
75
- when 0
76
- 'echo "Sorry, no such bundle found"'
77
- when 1
78
- %[git clone git://github.com/#{repos.first['username']}/#{repos.first['name']}.git #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
79
- else
80
- puts "Multiple bundles with that name found. Please choose which one you want to install:"
81
- repos.each_with_index {|repo, idx|
82
- puts "%d: %s by %s" %
83
- [
84
- idx + 1,
85
- normalize_github_repo_name(repo['name']),
86
- repo['username']
87
- ]
88
- }
89
- print "Your choice: "
90
-
91
- # Since to_i defaults to 0, we have to use Integer
92
- choice = Integer(STDIN.gets.chomp) rescue nil
93
- until choice && (0...repos.size).include?( choice - 1 ) do
94
- print "Sorry, invalid choice. Please enter a valid number or Ctrl+C to stop: "
95
- choice = Integer(STDIN.gets.chomp) rescue nil
96
- end
97
-
98
- %[git clone git://github.com/#{repos[choice - 1]['username']}/#{repos.first['name']}.git #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
99
- end
100
- end
101
-
102
- res = %x{#{cmd}}
103
-
104
- puts cmd, res.gsub(/^/,' ')
105
-
106
- installed=true and break if res =~ /Checked out revision|Initialized empty Git repository/
21
+ def install(bundle)
22
+ remotes = remote.bundles(bundle)
23
+
24
+ case remotes.values.flatten.length
25
+ when 0 then abort("Unknown bundle: #{bundle}")
26
+ when 1 then local.install(bundle, remotes.keys.detect { |k| remotes[k].length == 1 })
27
+ else local.install(*select_remote(remotes))
107
28
  end
108
- abort 'Not Installed' unless installed
109
-
110
- reload :verbose => true
29
+
30
+ reload
111
31
  end
112
32
 
113
33
  desc "uninstall NAME", "uninstall a bundle"
114
- def uninstall(bundle_name)
34
+ def uninstall(bundle)
115
35
  puts "Removing bundle..."
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
118
- # the same bundle in multiple bundle paths
119
- local_bundle_paths.each do |name,bundles_path|
120
- bundle_path = "#{bundles_path}/#{bundle_name}.tmbundle"
121
- if File.exist? bundle_path
122
- %x[osascript -e 'tell application "Finder" to move the POSIX file "#{bundle_path}" to trash']
123
- end
124
- end
125
-
126
- reload :verbose => true
36
+ local.uninstall(bundle)
37
+ reload
127
38
  end
128
-
39
+
129
40
  desc "reload", "Reloads TextMate Bundles"
130
- method_options :verbose => :boolean
131
- def reload(opts = {})
132
- puts "Reloading bundles..." if opts[:verbose]
133
- %x[osascript -e 'tell app "TextMate" to reload bundles']
134
- puts "Done." if opts[:verbose]
135
- end
136
-
137
- private
138
- def remote_bundle_locations
139
- { :'Macromates Trunk' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Bundles'},
140
- :'Macromates Review' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Review/Bundles'},
141
-
142
- # :'Bunch of Git Bundles' => {:scm => :git, :url => 'git://NotImplemented'},
143
-
144
- :'GitHub' => {:scm => :github, :url => 'http://github.com/search?q=tmbundle'},
145
- }
146
- end
147
-
148
- def local_bundle_paths
149
- { :Application => '/Applications/TextMate.app/Contents/SharedSupport/Bundles',
150
- :User => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles",
151
- :System => '/Library/Application Support/TextMate/Bundles',
152
- :'User Pristine' => "#{ENV["HOME"]}/Library/Application Support/TextMate/Pristine Copy/Bundles",
153
- :'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy/Bundles',
154
- }
155
- end
156
-
157
- def install_bundles_path
158
- local_bundle_paths[:'User Pristine']
159
- end
160
-
161
- # Copied from http://macromates.com/svn/Bundles/trunk/Support/lib/escape.rb
162
- # escape text to make it useable in a shell script as one “word” (string)
163
- def e_sh(str)
164
- str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
41
+ def reload
42
+ puts "Reloading bundles..."
43
+ %x{ osascript -e 'tell app "TextMate" to reload bundles' }
165
44
  end
166
-
167
- CAPITALIZATION_EXCEPTIONS = %w[tmbundle on]
168
- # Convert a GitHub repo name into a "normal" TM bundle name
169
- # e.g. ruby-on-rails-tmbundle => Ruby on Rails.tmbundle
170
- def normalize_github_repo_name(name)
171
- name = name.gsub("-", " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
172
- name[-9] = ?. if name =~ / tmbundle$/
173
- name
174
- end
175
-
176
- # Does the opposite of normalize_github_repo_name
177
- def denormalize_github_repo_name(name)
178
- name += " tmbundle" unless name =~ / tmbundle$/
179
- name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
180
- end
181
-
182
- def find_github_bundles(search_term)
183
- # Until GitHub fixes http://support.github.com/discussions/feature-requests/11-api-search-results,
184
- # we need to account for multiple pages of results:
185
- page = 1
186
- repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
187
- results = []
188
- until repositories.empty?
189
- results += repositories.find_all{|result| result['name'].match(search_term)}
190
- page += 1
191
- repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
45
+
46
+ end
47
+
48
+ def local
49
+ @local ||= Textmate::Local.new
50
+ end
51
+
52
+ def remote
53
+ @remote ||= Textmate::Remote.new
54
+ end
55
+
56
+ def display_hash(hash)
57
+ puts
58
+ hash.keys.sort.each do |key|
59
+ value = hash[key]
60
+ key = key.name if key.respond_to?(:name)
61
+ puts key
62
+ puts '-' * key.length
63
+ case value
64
+ when Array then puts value.join("\n") unless value.empty?
65
+ else puts value
192
66
  end
193
- results.sort{|a,b| a['name'] <=> b['name']}
67
+ puts
194
68
  end
195
-
196
- def git_url_from_github_result(result)
197
- "git://github.com/#{result['username']}/#{result['name']}.git"
69
+ end
70
+
71
+ def select_remote(remotes)
72
+ puts "Please select a bundle from the following list:"
73
+ puts
74
+ bundle_index = {}
75
+ remotes.keys.sort.inject(0) do |index, remote|
76
+ bundles = remotes[remote]
77
+ puts remote.name
78
+ puts '-' * remote.name.length
79
+ bundles.each do |bundle|
80
+ bundle_index[index += 1] = [bundle, remote]
81
+ puts "#{index}. #{bundle}"
82
+ end
83
+ puts
84
+ index
198
85
  end
199
86
 
200
- end
87
+ index = ask('Which bundle would you like to install?').to_i
88
+ puts
89
+ abort("Unknown option: #{index}") unless bundle_index[index]
201
90
 
202
- # TODO: create a "monument to personal cleverness" by class-izing everything?
203
- # class TextMateBundle
204
- # def self.find_local(bundle_name)
205
- #
206
- # end
207
- #
208
- # def self.find_remote(bundle_name)
209
- #
210
- # end
211
- # attr_reader :name
212
- # attr_reader :location
213
- # attr_reader :scm
214
- # def initialize(name, location, scm)
215
- # @name = name
216
- # @location = location
217
- # @scm = scm
218
- # end
219
- #
220
- # def install!
221
- #
222
- # end
223
- #
224
- # def uninstall!
225
- #
226
- # end
227
- #
228
- #
229
- # def installed?
230
- # # List all the installed versions, and where they're at
231
- # end
232
- #
233
- # # TODO: dirty? method to show if there are any deltas
234
- # end
91
+ bundle_index[index]
92
+ end
235
93
 
236
94
  TextmateInstaller.start
@@ -0,0 +1,55 @@
1
+ class Textmate::Local
2
+
3
+ def self.bundle_paths
4
+ { 'Application' => '/Applications/TextMate.app/Contents/SharedSupport/Bundles',
5
+ 'User' => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles",
6
+ 'System' => '/Library/Application Support/TextMate/Bundles',
7
+ 'User Pristine' => "#{ENV["HOME"]}/Library/Application Support/TextMate/Pristine Copy/Bundles",
8
+ 'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy/Bundles',
9
+ }
10
+ end
11
+
12
+ def self.bundle_install_path
13
+ bundle_paths['User Pristine']
14
+ end
15
+
16
+ def bundle_paths
17
+ self.class.bundle_paths
18
+ end
19
+
20
+ def bundle_install_path
21
+ self.class.bundle_install_path
22
+ end
23
+
24
+ def bundles(search = '')
25
+ bundle_paths.inject({}) do |hash, (name, path)|
26
+ hash.update(name => find_bundles(name, search))
27
+ end
28
+ end
29
+
30
+ def install(bundle, remote)
31
+ remote.install(bundle)
32
+ end
33
+
34
+ def uninstall(bundle)
35
+ bundle_paths.values.each do |path|
36
+ bundle_path = File.join(path, "#{bundle}.tmbundle")
37
+ if File.exist? bundle_path
38
+ %x[osascript -e 'tell application "Finder" to move the POSIX file "#{bundle_path}" to trash']
39
+ end
40
+ end
41
+ end
42
+
43
+ private ######################################################################
44
+
45
+ def find_bundles(location, search='')
46
+ search_term = Regexp.new(".*#{search}.*", 'i')
47
+
48
+ Dir[File.join(bundle_paths[location], '*.tmbundle')].map do |bundle|
49
+ File.basename(bundle, '.*')
50
+ end.select do |bundle|
51
+ bundle =~ search_term
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,21 @@
1
+ class Textmate::Remote
2
+
3
+ def self.register_source(klass)
4
+ sources << klass.new
5
+ end
6
+
7
+ def self.sources
8
+ @sources ||= []
9
+ end
10
+
11
+ def sources
12
+ self.class.sources
13
+ end
14
+
15
+ def bundles(search = '')
16
+ sources.inject({}) do |hash, source|
17
+ hash.update(source => source.bundles(search))
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,31 @@
1
+ class Textmate::Source::Base
2
+
3
+ def self.name(name = nil)
4
+ @name = name if name
5
+ @name
6
+ end
7
+
8
+ def name
9
+ self.class.name
10
+ end
11
+
12
+ def <=>(other)
13
+ name <=> other.name
14
+ end
15
+
16
+ def clear_existing(bundle)
17
+ FileUtils.rm_rf(local_path_for(bundle))
18
+ end
19
+
20
+ def local_path_for(bundle)
21
+ File.join(Textmate::Local.bundle_install_path, "#{bundle}.tmbundle")
22
+ end
23
+
24
+ def around_install(bundle, &block)
25
+ clear_existing(bundle)
26
+ yield
27
+ puts "#{bundle} bundle installed"
28
+ puts
29
+ end
30
+
31
+ end
@@ -0,0 +1,72 @@
1
+ require 'octopi'
2
+
3
+ class Textmate::Source::Github < Textmate::Source::Base
4
+
5
+ name 'Github'
6
+
7
+ def bundles(search = '')
8
+ github_search(search).map do |repository|
9
+ normalize_github_repo_name(repository.name).split('.').first
10
+ end.uniq.sort
11
+ end
12
+
13
+ def install(bundle)
14
+ repositories = github_search(bundle).sort_by { |r| r.followers }.reverse
15
+
16
+ case repositories.length
17
+ when 0 then abort("Can't find Github bundle to install: #{bundle}")
18
+ when 1 then install_bundle_from_github(repositories.first, bundle)
19
+ else install_bundle_from_github(select_repository(repositories), bundle)
20
+ end
21
+ end
22
+
23
+ private ######################################################################
24
+
25
+ CAPITALIZATION_EXCEPTIONS = %w[tmbundle on]
26
+
27
+ def github_search(bundle)
28
+ Octopi::Repository.find_all(bundle, 'tmbundle')
29
+ end
30
+
31
+ def normalize_github_repo_name(name)
32
+ name = name.gsub("-", " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
33
+ name[-9] = ?. if name =~ / tmbundle$/
34
+ name
35
+ end
36
+
37
+ def select_repository(repositories)
38
+ puts "Please select a repository from the following list:"
39
+ puts
40
+ repository_index = {}
41
+ repositories.inject(0) do |index, repository|
42
+ repository_index[index += 1] = repository
43
+ puts "%d. %-50s watchers:%-3d updated:%s" % [
44
+ index,
45
+ "#{repository.username}/#{repository.name}",
46
+ repository.followers,
47
+ repository.pushed.split('T').first
48
+ ]
49
+ index
50
+ end
51
+
52
+ puts
53
+ index = Thor.new.ask('Which repository would you like to install?').to_i
54
+ puts
55
+ abort("Unknown option: #{index}") unless repository_index[index]
56
+
57
+ repository_index[index]
58
+ end
59
+
60
+ def install_bundle_from_github(repository, bundle)
61
+ around_install(bundle) do
62
+ %x{ git clone "#{git_clone_url(repository)}" "#{local_path_for(bundle)}" }
63
+ end
64
+ end
65
+
66
+ def git_clone_url(repository)
67
+ "git://github.com/#{repository.username}/#{repository.name}.git"
68
+ end
69
+
70
+ end
71
+
72
+ Textmate::Remote.register_source Textmate::Source::Github
@@ -0,0 +1,45 @@
1
+ class Textmate::Source::Macromates < Textmate::Source::Base
2
+
3
+ name 'Macromates'
4
+
5
+ def bundles(search = '')
6
+ repositories.map do |repository|
7
+ repository_bundles(repository, search)
8
+ end.flatten.uniq.sort
9
+ end
10
+
11
+ def install(bundle)
12
+ repository = repositories.detect do |repository|
13
+ repository_bundles(repository, bundle, :exact)
14
+ end
15
+ install_bundle_from_svn("#{repository}/#{bundle}.tmbundle", bundle)
16
+ end
17
+
18
+ private ######################################################################
19
+
20
+ def repositories
21
+ repositories = []
22
+ repositories << 'http://svn.textmate.org/trunk/Bundles/'
23
+ repositories << 'http://svn.textmate.org/trunk/Review/Bundles/'
24
+ end
25
+
26
+ def repository_bundles(repository, search = '', match = :partial)
27
+ search_term = Regexp.new(".*#{search}.*", 'i')
28
+
29
+ %x{ svn list #{repository} }.map do |bundle|
30
+ bundle.split('.').first
31
+ end.select do |bundle|
32
+ match == :partial ? bundle =~ search_term : bundle == search
33
+ end
34
+ end
35
+
36
+ def install_bundle_from_svn(svn_path, bundle)
37
+ around_install(bundle) do
38
+ %x{ svn co "#{svn_path}" "#{local_path_for(bundle)}" }
39
+ puts "#{bundle} installed"
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ Textmate::Remote.register_source Textmate::Source::Macromates
@@ -0,0 +1,5 @@
1
+ module Textmate::Source; end
2
+
3
+ require 'textmate/source/base'
4
+ require 'textmate/source/github'
5
+ require 'textmate/source/macromates'
data/lib/textmate.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Textmate; end
2
+
3
+ require 'textmate/local'
4
+ require 'textmate/remote'
5
+ require 'textmate/source'
data/textmate.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{textmate}
8
- s.version = "0.9.8"
8
+ s.version = "1.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["David Dollar"]
@@ -25,6 +25,13 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "bin/textmate",
28
+ "lib/textmate.rb",
29
+ "lib/textmate/local.rb",
30
+ "lib/textmate/remote.rb",
31
+ "lib/textmate/source.rb",
32
+ "lib/textmate/source/base.rb",
33
+ "lib/textmate/source/github.rb",
34
+ "lib/textmate/source/macromates.rb",
28
35
  "textmate.gemspec"
29
36
  ]
30
37
  s.homepage = %q{http://github.com/ddollar/textmate}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddollar-textmate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Dollar
@@ -38,6 +38,13 @@ files:
38
38
  - Rakefile
39
39
  - VERSION
40
40
  - bin/textmate
41
+ - lib/textmate.rb
42
+ - lib/textmate/local.rb
43
+ - lib/textmate/remote.rb
44
+ - lib/textmate/source.rb
45
+ - lib/textmate/source/base.rb
46
+ - lib/textmate/source/github.rb
47
+ - lib/textmate/source/macromates.rb
41
48
  - textmate.gemspec
42
49
  has_rdoc: false
43
50
  homepage: http://github.com/ddollar/textmate