pik 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +35 -1
  3. data/README.rdoc +99 -39
  4. data/Rakefile +49 -8
  5. data/bin/pik_install +23 -0
  6. data/features/add_command.feature +28 -0
  7. data/features/checkup_command.feature +0 -0
  8. data/features/config_command.feature +12 -0
  9. data/features/default_command.feature +12 -0
  10. data/features/env.rb +52 -0
  11. data/features/gemsync_command.feature +0 -0
  12. data/features/help_command.feature +13 -0
  13. data/features/implode_command.feature +12 -0
  14. data/features/install_command.feature +13 -0
  15. data/features/list_command.feature +18 -0
  16. data/features/remove_command.feature +18 -0
  17. data/features/run_command.feature +22 -0
  18. data/features/step_definitions/pik_commands.rb +140 -0
  19. data/features/switch_command.feature +35 -0
  20. data/features/tag_command.feature +18 -0
  21. data/features/version.feature +9 -0
  22. data/lib/pik.rb +17 -3
  23. data/lib/pik/commands/add_command.rb +6 -6
  24. data/lib/pik/commands/batch_file_editor.rb +22 -8
  25. data/lib/pik/commands/checkup_command.rb +5 -2
  26. data/lib/pik/commands/command.rb +30 -26
  27. data/lib/pik/commands/config_command.rb +54 -2
  28. data/lib/pik/commands/default_command.rb +19 -5
  29. data/lib/pik/commands/help_command.rb +1 -1
  30. data/lib/pik/commands/implode_command.rb +12 -1
  31. data/lib/pik/commands/install_command.rb +182 -0
  32. data/lib/pik/commands/list_command.rb +3 -2
  33. data/lib/pik/commands/remove_command.rb +6 -6
  34. data/lib/pik/commands/run_command.rb +70 -10
  35. data/lib/pik/commands/switch_command.rb +10 -10
  36. data/lib/pik/commands/tag_command.rb +56 -0
  37. data/lib/pik/config_file.rb +26 -2
  38. data/lib/pik/contrib/progressbar.rb +237 -0
  39. data/lib/pik/contrib/unzip.rb +14 -0
  40. data/lib/pik/contrib/uri_ext.rb +296 -0
  41. data/lib/pik/contrib/zip/ioextras.rb +155 -0
  42. data/lib/pik/contrib/zip/stdrubyext.rb +111 -0
  43. data/lib/pik/contrib/zip/tempfile_bugfixed.rb +195 -0
  44. data/lib/pik/contrib/zip/zip.rb +1846 -0
  45. data/lib/pik/contrib/zip/zipfilesystem.rb +609 -0
  46. data/lib/pik/contrib/zip/ziprequire.rb +90 -0
  47. data/lib/pik/core_ext/pathname.rb +20 -7
  48. data/lib/pik/search_path.rb +21 -13
  49. data/lib/pik/which.rb +52 -0
  50. data/lib/pik/windows_env.rb +64 -25
  51. data/spec/add_command_spec.rb +0 -2
  52. data/spec/batch_file_spec.rb +3 -3
  53. data/spec/command_spec.rb +0 -7
  54. data/spec/gemsync_command_spec.rb +1 -1
  55. data/spec/help_command_spec.rb +1 -1
  56. data/spec/list_command_spec.rb +1 -1
  57. data/spec/pathname_spec.rb +30 -0
  58. data/spec/remove_command_spec.rb +6 -6
  59. data/spec/run_command_spec.rb +2 -30
  60. data/spec/search_path_spec.rb +9 -0
  61. data/spec/switch_command_spec.rb +14 -2
  62. data/spec/which_spec.rb +7 -0
  63. data/tools/pik.bat +2 -0
  64. data/tools/pik/pik +45 -0
  65. data/tools/pik/pik.exe +0 -0
  66. data/tools/pik/pik.exy +198 -0
  67. metadata +50 -21
  68. data/bin/pik +0 -33
@@ -0,0 +1,14 @@
1
+ $: << File.dirname(__FILE__)
2
+ require 'zip/zip'
3
+
4
+ module Zip
5
+ def self.fake_unzip(zipfilename, regexp, target_dir)
6
+ Zip::ZipFile.open(zipfilename) do |zipfile|
7
+ Zip::ZipFile.foreach(zipfilename) do |entry|
8
+ if regexp =~ entry.name
9
+ zipfile.extract(entry, File.join(target_dir, File.basename(entry.name))) { true }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,296 @@
1
+ #
2
+ # I've striped down dependencies on Net::SSH and Facets to
3
+ # stay as simple as possible.
4
+ #
5
+ # Original code from Assaf Arkin, released under Apache License
6
+ # (http://buildr.rubyforge.org/license.html)
7
+ #
8
+ require 'cgi'
9
+ require 'uri'
10
+ require 'net/http'
11
+ require 'net/https'
12
+ require 'tempfile'
13
+ require 'fileutils'
14
+
15
+ # show progress of download
16
+ # require File.join(File.dirname(__FILE__), 'progressbar')
17
+
18
+ # Not quite open-uri, but similar. Provides read and write methods for the resource represented by the URI.
19
+ # Currently supports reads for URI::HTTP and writes for URI::SFTP. Also provides convenience methods for
20
+ # downloads and uploads.
21
+ module URI
22
+ # Raised when trying to read/download a resource that doesn't exist.
23
+ class NotFoundError < RuntimeError; end
24
+
25
+ class << self
26
+
27
+ # :call-seq:
28
+ # read(uri, options?) => content
29
+ # read(uri, options?) { |chunk| ... }
30
+ #
31
+ # Reads from the resource behind this URI. The first form returns the content of the resource,
32
+ # the second form yields to the block with each chunk of content (usually more than one).
33
+ #
34
+ # For example:
35
+ # File.open "image.jpg", "w" do |file|
36
+ # URI.read("http://example.com/image.jpg") { |chunk| file.write chunk }
37
+ # end
38
+ # Shorter version:
39
+ # File.open("image.jpg", "w") { |file| file.write URI.read("http://example.com/image.jpg") }
40
+ #
41
+ # Supported options:
42
+ # * :modified -- Only download if file modified since this timestamp. Returns nil if not modified.
43
+ # * :progress -- Show the progress bar while reading.
44
+ def read(uri, options = nil, &block)
45
+ uri = URI.parse(uri.to_s) unless URI === uri
46
+ uri.read(options, &block)
47
+ end
48
+
49
+ # :call-seq:
50
+ # download(uri, target, options?)
51
+ #
52
+ # Downloads the resource to the target.
53
+ #
54
+ # The target may be a file name (string or task), in which case the file is created from the resource.
55
+ # The target may also be any object that responds to +write+, e.g. File, StringIO, Pipe.
56
+ #
57
+ # Use the progress bar when running in verbose mode.
58
+ def download(uri, target, options = nil)
59
+ uri = URI.parse(uri.to_s) unless URI === uri
60
+ uri.download(target, options)
61
+ end
62
+
63
+ # :call-seq:
64
+ # write(uri, content, options?)
65
+ # write(uri, options?) { |bytes| .. }
66
+ #
67
+ # Writes to the resource behind the URI. The first form writes the content from a string or an object
68
+ # that responds to +read+ and optionally +size+. The second form writes the content by yielding to the
69
+ # block. Each yield should return up to the specified number of bytes, the last yield returns nil.
70
+ #
71
+ # For example:
72
+ # File.open "killer-app.jar", "rb" do |file|
73
+ # write("sftp://localhost/jars/killer-app.jar") { |chunk| file.read(chunk) }
74
+ # end
75
+ # Or:
76
+ # write "sftp://localhost/jars/killer-app.jar", File.read("killer-app.jar")
77
+ #
78
+ # Supported options:
79
+ # * :progress -- Show the progress bar while reading.
80
+ def write(uri, *args, &block)
81
+ uri = URI.parse(uri.to_s) unless URI === uri
82
+ uri.write(*args, &block)
83
+ end
84
+ end
85
+
86
+ class Generic
87
+
88
+ # :call-seq:
89
+ # read(options?) => content
90
+ # read(options?) { |chunk| ... }
91
+ #
92
+ # Reads from the resource behind this URI. The first form returns the content of the resource,
93
+ # the second form yields to the block with each chunk of content (usually more than one).
94
+ #
95
+ # For options, see URI::read.
96
+ def read(options = nil, &block)
97
+ fail "This protocol doesn't support reading (yet, how about helping by implementing it?)"
98
+ end
99
+
100
+ # :call-seq:
101
+ # download(target, options?)
102
+ #
103
+ # Downloads the resource to the target.
104
+ #
105
+ # The target may be a file name (string or task), in which case the file is created from the resource.
106
+ # The target may also be any object that responds to +write+, e.g. File, StringIO, Pipe.
107
+ #
108
+ # Use the progress bar when running in verbose mode.
109
+ def download(target, options = {})
110
+ case target
111
+ when String
112
+ # If download breaks we end up with a partial file which is
113
+ # worse than not having a file at all, so download to temporary
114
+ # file and then move over.
115
+ modified = File.stat(target).mtime if File.exist?(target)
116
+ temp = nil
117
+ result = nil
118
+ Tempfile.open(File.basename(target)) do |tf|
119
+ tf.binmode
120
+ result = read(options.merge(:modified => modified)) { |chunk| tf.write chunk }
121
+ temp = tf
122
+ end
123
+ FileUtils.mkpath(File.dirname(target))
124
+ FileUtils.move(temp.path, target) if result
125
+ when File
126
+ read(options.merge(:modified => target.mtime)) { |chunk| target.write chunk }
127
+ target.flush
128
+ else
129
+ raise ArgumentError, "Expecting a target that is either a file name (string, task) or object that responds to write (file, pipe)." unless target.respond_to?(:write)
130
+ read(options) { |chunk| target.write chunk }
131
+ target.flush
132
+ end
133
+ end
134
+
135
+ # :call-seq:
136
+ # write(content, options?)
137
+ # write(options?) { |bytes| .. }
138
+ #
139
+ # Writes to the resource behind the URI. The first form writes the content from a string or an object
140
+ # that responds to +read+ and optionally +size+. The second form writes the content by yielding to the
141
+ # block. Each yield should return up to the specified number of bytes, the last yield returns nil.
142
+ #
143
+ # For options, see URI::write.
144
+ def write(*args, &block)
145
+ options = args.pop if Hash === args.last
146
+ options ||= {}
147
+ if String === args.first
148
+ ios = StringIO.new(args.first, "r")
149
+ write(options.merge(:size => args.first.size)) { |bytes| ios.read(bytes) }
150
+ elsif args.first.respond_to?(:read)
151
+ size = args.first.size rescue nil
152
+ write({ :size => size }.merge(options)) { |bytes| args.first.read(bytes) }
153
+ elsif args.empty? && block
154
+ write_internal(options, &block)
155
+ else
156
+ raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?"
157
+ end
158
+ end
159
+
160
+ protected
161
+
162
+ # :call-seq:
163
+ # with_progress_bar(enable, file_name, size) { |progress| ... }
164
+ #
165
+ # Displays a progress bar while executing the block. The first argument must be true for the
166
+ # progress bar to show (TTY output also required), as a convenient for selectively using the
167
+ # progress bar from a single block.
168
+ #
169
+ # The second argument provides a filename to display, the third its size in bytes.
170
+ #
171
+ # The block is yielded with a progress object that implements a single method.
172
+ # Call << for each block of bytes down/uploaded.
173
+ def with_progress_bar(enable, file_name, size) #:nodoc:
174
+ if enable && $stdout.isatty
175
+ progress_bar = Console::ProgressBar.new(file_name, size)
176
+ # Extend the progress bar so we can display count/total.
177
+ class << progress_bar
178
+ def total()
179
+ convert_bytes(@total)
180
+ end
181
+ end
182
+ # Squeeze the filename into 30 characters.
183
+ if file_name.size > 30
184
+ base, ext = File.basename(file_name), File.extname(file_name)
185
+ truncated = "#{base[0..26-ext.to_s.size]}..#{ext}"
186
+ else
187
+ truncated = file_name
188
+ end
189
+ progress_bar.format = "#{CGI.unescape(truncated)}: %3d%% %s %s/%s %s"
190
+ progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
191
+ progress_bar.bar_mark = "o"
192
+
193
+ begin
194
+ class << progress_bar
195
+ def <<(bytes)
196
+ inc bytes.respond_to?(:size) ? bytes.size : bytes
197
+ end
198
+ end
199
+ yield progress_bar
200
+ ensure
201
+ progress_bar.finish
202
+ end
203
+ else
204
+ progress_bar = Object.new
205
+ class << progress_bar
206
+ def <<(bytes)
207
+ end
208
+ end
209
+ yield progress_bar
210
+ end
211
+ end
212
+
213
+ # :call-seq:
214
+ # proxy_uri() => URI?
215
+ #
216
+ # Returns the proxy server to use. Obtains the proxy from the relevant environment variable (e.g. HTTP_PROXY).
217
+ # Supports exclusions based on host name and port number from environment variable NO_PROXY.
218
+ def proxy_uri()
219
+ proxy = ENV["#{scheme.upcase}_PROXY"]
220
+ proxy = URI.parse(proxy) if String === proxy
221
+ excludes = (ENV["NO_PROXY"] || "").split(/\s*,\s*/).compact
222
+ excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" }
223
+ return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") }
224
+ end
225
+
226
+ def write_internal(options, &block) #:nodoc:
227
+ fail "This protocol doesn't support writing (yet, how about helping by implementing it?)"
228
+ end
229
+ end
230
+
231
+ class HTTP #:nodoc:
232
+
233
+ def verbose
234
+ false
235
+ end
236
+
237
+ # See URI::Generic#read
238
+ def read(options = nil, &block)
239
+ options ||= {}
240
+ connect do |http|
241
+ puts "Requesting #{self}" if verbose
242
+ headers = { 'If-Modified-Since' => CGI.rfc1123_date(options[:modified].utc) } if options[:modified]
243
+ request = Net::HTTP::Get.new(request_uri.empty? ? '/' : request_uri, headers)
244
+ request.basic_auth self.user, self.password if self.user
245
+ http.request request do |response|
246
+ case response
247
+ when Net::HTTPNotModified
248
+ # No modification, nothing to do.
249
+ puts 'Not modified since last download' if verbose
250
+ return nil
251
+ when Net::HTTPRedirection
252
+ # Try to download from the new URI, handle relative redirects.
253
+ puts "Redirected to #{response['Location']}" if verbose
254
+ return (self + URI.parse(response['location'])).read(options, &block)
255
+ when Net::HTTPOK
256
+ puts "Downloading #{self}" if verbose
257
+ result = nil
258
+ with_progress_bar options[:progress], path.split('/').last, response.content_length do |progress|
259
+ if block
260
+ response.read_body do |chunk|
261
+ block.call chunk
262
+ progress << chunk
263
+ end
264
+ return true
265
+ else
266
+ result = ''
267
+ response.read_body do |chunk|
268
+ result << chunk
269
+ progress << chunk
270
+ end
271
+ return result
272
+ end
273
+ end
274
+ when Net::HTTPNotFound
275
+ raise NotFoundError, "Looking for #{self} and all I got was a 404!"
276
+ else
277
+ raise RuntimeError, "Failed to download #{self}: #{response.message}"
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ private
284
+
285
+ def connect
286
+ if proxy = proxy_uri
287
+ proxy = URI.parse(proxy) if String === proxy
288
+ http = Net::HTTP.new(host, port, proxy.host, proxy.port, proxy.user, proxy.password)
289
+ else
290
+ http = Net::HTTP.new(host, port)
291
+ end
292
+ http.use_ssl = true if self.instance_of? URI::HTTPS
293
+ yield http
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,155 @@
1
+ module IOExtras #:nodoc:
2
+
3
+ CHUNK_SIZE = 32768
4
+
5
+ RANGE_ALL = 0..-1
6
+
7
+ def self.copy_stream(ostream, istream)
8
+ s = ''
9
+ ostream.write(istream.read(CHUNK_SIZE, s)) until istream.eof?
10
+ end
11
+
12
+
13
+ # Implements kind_of? in order to pretend to be an IO object
14
+ module FakeIO
15
+ def kind_of?(object)
16
+ object == IO || super
17
+ end
18
+ end
19
+
20
+ # Implements many of the convenience methods of IO
21
+ # such as gets, getc, readline and readlines
22
+ # depends on: input_finished?, produce_input and read
23
+ module AbstractInputStream
24
+ include Enumerable
25
+ include FakeIO
26
+
27
+ def initialize
28
+ super
29
+ @lineno = 0
30
+ @outputBuffer = ""
31
+ end
32
+
33
+ attr_accessor :lineno
34
+
35
+ def read(numberOfBytes = nil, buf = nil)
36
+ tbuf = nil
37
+
38
+ if @outputBuffer.length > 0
39
+ if numberOfBytes <= @outputBuffer.length
40
+ tbuf = @outputBuffer.slice!(0, numberOfBytes)
41
+ else
42
+ numberOfBytes -= @outputBuffer.length if (numberOfBytes)
43
+ rbuf = sysread(numberOfBytes, buf)
44
+ tbuf = @outputBuffer
45
+ tbuf << rbuf if (rbuf)
46
+ @outputBuffer = ""
47
+ end
48
+ else
49
+ tbuf = sysread(numberOfBytes, buf)
50
+ end
51
+
52
+ return nil unless (tbuf)
53
+
54
+ if buf
55
+ buf.replace(tbuf)
56
+ else
57
+ buf = tbuf
58
+ end
59
+
60
+ buf
61
+ end
62
+
63
+ def readlines(aSepString = $/)
64
+ retVal = []
65
+ each_line(aSepString) { |line| retVal << line }
66
+ return retVal
67
+ end
68
+
69
+ def gets(aSepString=$/)
70
+ @lineno = @lineno.next
71
+ return read if aSepString == nil
72
+ aSepString="#{$/}#{$/}" if aSepString == ""
73
+
74
+ bufferIndex=0
75
+ while ((matchIndex = @outputBuffer.index(aSepString, bufferIndex)) == nil)
76
+ bufferIndex=@outputBuffer.length
77
+ if input_finished?
78
+ return @outputBuffer.empty? ? nil : flush
79
+ end
80
+ @outputBuffer << produce_input
81
+ end
82
+ sepIndex=matchIndex + aSepString.length
83
+ return @outputBuffer.slice!(0...sepIndex)
84
+ end
85
+
86
+ def flush
87
+ retVal=@outputBuffer
88
+ @outputBuffer=""
89
+ return retVal
90
+ end
91
+
92
+ def readline(aSepString = $/)
93
+ retVal = gets(aSepString)
94
+ raise EOFError if retVal == nil
95
+ return retVal
96
+ end
97
+
98
+ def each_line(aSepString = $/)
99
+ while true
100
+ yield readline(aSepString)
101
+ end
102
+ rescue EOFError
103
+ end
104
+
105
+ alias_method :each, :each_line
106
+ end
107
+
108
+
109
+ # Implements many of the output convenience methods of IO.
110
+ # relies on <<
111
+ module AbstractOutputStream
112
+ include FakeIO
113
+
114
+ def write(data)
115
+ self << data
116
+ data.to_s.length
117
+ end
118
+
119
+
120
+ def print(*params)
121
+ self << params.to_s << $\.to_s
122
+ end
123
+
124
+ def printf(aFormatString, *params)
125
+ self << sprintf(aFormatString, *params)
126
+ end
127
+
128
+ def putc(anObject)
129
+ self << case anObject
130
+ when Fixnum then anObject.chr
131
+ when String then anObject
132
+ else raise TypeError, "putc: Only Fixnum and String supported"
133
+ end
134
+ anObject
135
+ end
136
+
137
+ def puts(*params)
138
+ params << "\n" if params.empty?
139
+ params.flatten.each {
140
+ |element|
141
+ val = element.to_s
142
+ self << val
143
+ self << "\n" unless val[-1,1] == "\n"
144
+ }
145
+ end
146
+
147
+ end
148
+
149
+ end # IOExtras namespace module
150
+
151
+
152
+
153
+ # Copyright (C) 2002-2004 Thomas Sondergaard
154
+ # rubyzip is free software; you can redistribute it and/or
155
+ # modify it under the terms of the ruby license.