bowline 0.4.6 → 0.5.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.
Files changed (63) hide show
  1. data/Rakefile +1 -0
  2. data/TODO +11 -0
  3. data/VERSION +1 -1
  4. data/assets/bowline.js +194 -0
  5. data/assets/json2.js +479 -0
  6. data/assets/osx/Info.plist.erb +45 -0
  7. data/assets/osx/bowline.png +0 -0
  8. data/assets/osx/makeicns +0 -0
  9. data/bowline.gemspec +47 -11
  10. data/lib/bowline.rb +35 -15
  11. data/lib/bowline/binders.rb +168 -131
  12. data/lib/bowline/commands/build.rb +3 -0
  13. data/lib/bowline/commands/generate.rb +3 -1
  14. data/lib/bowline/commands/run.rb +7 -4
  15. data/lib/bowline/desktop.rb +26 -0
  16. data/lib/bowline/desktop/app.rb +9 -0
  17. data/lib/bowline/desktop/bridge.rb +97 -0
  18. data/lib/bowline/desktop/clipboard.rb +9 -0
  19. data/lib/bowline/desktop/dialog.rb +28 -0
  20. data/lib/bowline/desktop/dock.rb +9 -0
  21. data/lib/bowline/desktop/host.rb +10 -0
  22. data/lib/bowline/desktop/js.rb +92 -0
  23. data/lib/bowline/desktop/misc.rb +9 -0
  24. data/lib/bowline/desktop/network.rb +7 -0
  25. data/lib/bowline/desktop/proxy.rb +94 -0
  26. data/lib/bowline/desktop/sound.rb +8 -0
  27. data/lib/bowline/desktop/window.rb +31 -0
  28. data/lib/bowline/desktop/window_manager.rb +72 -0
  29. data/lib/bowline/desktop/window_methods.rb +70 -0
  30. data/lib/bowline/generators.rb +3 -2
  31. data/lib/bowline/generators/application.rb +8 -5
  32. data/lib/bowline/generators/binder.rb +10 -6
  33. data/lib/bowline/generators/model.rb +9 -0
  34. data/lib/bowline/generators/window.rb +28 -0
  35. data/lib/bowline/helpers.rb +0 -3
  36. data/lib/bowline/initializer.rb +65 -11
  37. data/lib/bowline/library.rb +31 -0
  38. data/lib/bowline/local_model.rb +116 -0
  39. data/lib/bowline/logging.rb +23 -0
  40. data/lib/bowline/platform.rb +73 -0
  41. data/lib/bowline/tasks/app.rake +80 -148
  42. data/lib/bowline/tasks/libs.rake +59 -0
  43. data/lib/bowline/version.rb +2 -2
  44. data/lib/bowline/watcher.rb +91 -0
  45. data/templates/binder.rb +2 -8
  46. data/templates/config/environment.rb +3 -2
  47. data/templates/main_window.rb +7 -0
  48. data/templates/model.rb +1 -1
  49. data/templates/public/index.html +2 -1
  50. data/templates/script/build +3 -0
  51. data/templates/script/generate +3 -0
  52. data/templates/script/init +0 -16
  53. data/templates/window.rb +3 -0
  54. data/vendor/pathname.rb +1099 -0
  55. data/vendor/progressbar.rb +236 -0
  56. metadata +48 -9
  57. data/assets/jquery.bowline.js +0 -156
  58. data/lib/bowline/async.rb +0 -29
  59. data/lib/bowline/binders/collection.rb +0 -59
  60. data/lib/bowline/binders/singleton.rb +0 -31
  61. data/lib/bowline/jquery.rb +0 -31
  62. data/lib/bowline/observer.rb +0 -66
  63. data/lib/bowline/window.rb +0 -19
@@ -0,0 +1,59 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
3
+ require 'net/http'
4
+ require 'progressbar'
5
+ require 'zip/zip'
6
+
7
+ def download(url)
8
+ name = url.split("/").last
9
+ file = Tempfile.new("download_#{name}")
10
+ url = URI.parse(url)
11
+ req = Net::HTTP::Get.new(url.path)
12
+ res = Net::HTTP.start(url.host, url.port) {|http|
13
+ http.request(req) {|resp|
14
+ pbar = ProgressBar.new(name, resp.content_length)
15
+ resp.read_body {|seg|
16
+ pbar.inc(seg.length)
17
+ file.write(seg)
18
+ }
19
+ pbar.finish
20
+ }
21
+ }
22
+ file.rewind
23
+ file
24
+ end
25
+
26
+ namespace :libs do
27
+ task :download => :environment do
28
+ puts "Downloading libraries. This may take a while..."
29
+ FileUtils.mkdir_p(Bowline::Library.path)
30
+
31
+ desktop_path = Bowline::Library.desktop_path
32
+ unless File.exist?(desktop_path)
33
+ desktop_tmp = download(Bowline::Library::DESKTOP_URL)
34
+ desktop_tmp.close
35
+ FileUtils.mv(desktop_tmp.path, desktop_path)
36
+ FileUtils.chmod(0755, desktop_path)
37
+ end
38
+
39
+ unless File.directory?(Bowline::Library.rubylib_path)
40
+ rubylib_tmp = download(Bowline::Library::RUBYLIB_URL)
41
+ rubylib_tmp.close
42
+ puts "Extracting..."
43
+ Zip::ZipFile.open(rubylib_tmp.path) { |zfile|
44
+ zfile.each {|file|
45
+ file_path = File.join(Bowline::Library.path, file.name)
46
+ FileUtils.mkdir_p(File.dirname(file_path))
47
+ zfile.extract(file, file_path)
48
+ }
49
+ }
50
+ end
51
+ puts "Finished downloading"
52
+ end
53
+
54
+ task :update => :environment do
55
+ FileUtils.rm_rf(Bowline::Library.desktop_path)
56
+ FileUtils.rm_rf(Bowline::Library.rubylib_path)
57
+ Rake::Task["libs:download"].invoke
58
+ end
59
+ end
@@ -1,8 +1,8 @@
1
1
  module Bowline
2
2
  module Version #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 4
5
- TINY = 6
4
+ MINOR = 5
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
 
@@ -0,0 +1,91 @@
1
+ module Bowline
2
+ class Watcher
3
+ # Callbacks on steroids.
4
+ # Add callbacks as class methods, or instance ones.
5
+ #
6
+ # class MyClass
7
+ # extend Bowline::Watcher::Base
8
+ # watch :update, :create
9
+ #
10
+ # def self.update
11
+ # watcher.call(:update)
12
+ # end
13
+ #
14
+ # def create
15
+ # watcher.call(:create)
16
+ # end
17
+ # end
18
+ #
19
+ # MyClass.on_update { puts 'update' }
20
+ # MyClass.new.on_create { puts 'create' }
21
+
22
+ module Base
23
+ def watch(*names)
24
+ names.each do |name|
25
+ # Because define_method only takes a block,
26
+ # which doesn't accept multiple arguments
27
+ script = <<-RUBY
28
+ def #{name}(*args, &block)
29
+ watcher.append(:#{name}, *args, &block)
30
+ end
31
+ RUBY
32
+ instance_eval script
33
+ class_eval script
34
+ end
35
+ end
36
+
37
+ def watcher
38
+ @watcher ||= Watcher.new
39
+ end
40
+ end
41
+
42
+ class Callback
43
+ attr_reader :event, :prok
44
+
45
+ def initialize(watcher, event, prok)
46
+ @watcher, @event, @prok = watcher, event, prok
47
+ end
48
+
49
+ def call(*args)
50
+ @prok.call(*args)
51
+ end
52
+
53
+ def remove
54
+ @watcher.remove(@event, @prok)
55
+ end
56
+ end
57
+
58
+ def initialize
59
+ @listeners = {}
60
+ end
61
+
62
+ def append(event, method = nil, &block)
63
+ callback = Callback.new(self, event, method||block)
64
+ (@listeners[event] ||= []) << callback
65
+ callback
66
+ end
67
+
68
+ def call(event, *args)
69
+ return unless @listeners[event]
70
+ @listeners[event].each do |callback|
71
+ callback.call(*args)
72
+ end
73
+ end
74
+
75
+ def remove(event, value=nil)
76
+ return unless @listeners[event]
77
+ if value
78
+ @listeners[event].delete(value)
79
+ if @listeners[event].empty?
80
+ @listeners.delete(event)
81
+ end
82
+ else
83
+ @listeners.delete(event)
84
+ end
85
+ end
86
+
87
+ def clear
88
+ @listeners = {}
89
+ end
90
+ end
91
+ end
data/templates/binder.rb CHANGED
@@ -1,9 +1,3 @@
1
- <%- with_modules(modules) do -%>
2
1
  class <%= class_name %>
3
- class << self
4
- def index
5
- end
6
- end
7
-
8
- end
9
- <%- end -%>
2
+ # expose <%= expose_name %>
3
+ end
@@ -4,10 +4,11 @@ require File.join(File.dirname(__FILE__), 'boot')
4
4
  Bowline::Initializer.run do |config|
5
5
  config.name = <%= full_name.inspect %>
6
6
  config.id = "com.<%= full_name.underscore %>"
7
- config.version = "0.1.0"
7
+ config.version = "0.0.1"
8
8
  config.publisher = "Example"
9
9
  config.url = "http://example.com"
10
-
10
+
11
+ # config.gem "activerecord"
11
12
  # config.gem "net-mdns", :lib => 'net/dns/mdns'
12
13
  # config.gem "rack"
13
14
  # config.gem "rubyzip", :lib => 'zip/zip'
@@ -0,0 +1,7 @@
1
+ class MainWindow < Bowline::Desktop::WindowManager
2
+ setup
3
+ self.width = 300
4
+ self.height = 400
5
+ center
6
+ on_load { show }
7
+ end
data/templates/model.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  <%- with_modules(modules) do -%>
2
- class <%= class_name %> < ActiveRecord::Base
2
+ class <%= class_name %>
3
3
  end
4
4
  <%- end -%>
@@ -6,7 +6,8 @@
6
6
  <link rel="stylesheet" href="stylesheets/application.css" type="text/css" charset="utf-8">
7
7
  <script src="javascripts/jquery.js" type="text/javascript" charset="utf-8"></script>
8
8
  <script src="javascripts/jquery.chain.js" type="text/javascript" charset="utf-8"></script>
9
- <script src="javascripts/jquery.bowline.js" type="text/javascript" charset="utf-8"></script>
9
+ <script src="javascripts/json2.js" type="text/javascript" charset="utf-8"></script>
10
+ <script src="javascripts/bowline.js" type="text/javascript" charset="utf-8"></script>
10
11
  <script src="javascripts/application.js" type="text/javascript" charset="utf-8"></script>
11
12
  <script type="text/javascript" charset="utf-8">
12
13
  // Example:
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.join(File.dirname(__FILE__), *%w[.. config boot])
3
+ require "bowline/commands/build"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.join(File.dirname(__FILE__), *%w[.. config boot])
3
+ require "bowline/commands/generate"
@@ -1,18 +1,2 @@
1
1
  #!/usr/bin/env ruby
2
-
3
- unless defined?(Titanium)
4
- raise "You can't execute this file directly - it's for Titanium"
5
- end
6
-
7
- # Hack for load paths - Titanium doesn't add .
8
- app_resources = Titanium.App.appURLToPath("app://index.html")
9
- APP_ROOT = File.dirname(app_resources)
10
-
11
- $LOAD_PATH << APP_ROOT
12
- $LOAD_PATH.uniq!
13
-
14
- # The 'window' function is only
15
- # available in this scope
16
- $app_window = window
17
-
18
2
  require File.join(*%w[config environment])
@@ -0,0 +1,3 @@
1
+ class <%= class_name %> < Bowline::Desktop::WindowManager
2
+ setup
3
+ end
@@ -0,0 +1,1099 @@
1
+ #
2
+ # = pathname.rb
3
+ #
4
+ # Object-Oriented Pathname Class
5
+ #
6
+ # Author:: Tanaka Akira <akr@m17n.org>
7
+ # Documentation:: Author and Gavin Sinclair
8
+ #
9
+ # For documentation, see class Pathname.
10
+ #
11
+ # <tt>pathname.rb</tt> is distributed with Ruby since 1.8.0.
12
+ #
13
+
14
+ #
15
+ # == Pathname
16
+ #
17
+ # Pathname represents a pathname which locates a file in a filesystem.
18
+ # The pathname depends on OS: Unix, Windows, etc.
19
+ # Pathname library works with pathnames of local OS.
20
+ # However non-Unix pathnames are supported experimentally.
21
+ #
22
+ # It does not represent the file itself.
23
+ # A Pathname can be relative or absolute. It's not until you try to
24
+ # reference the file that it even matters whether the file exists or not.
25
+ #
26
+ # Pathname is immutable. It has no method for destructive update.
27
+ #
28
+ # The value of this class is to manipulate file path information in a neater
29
+ # way than standard Ruby provides. The examples below demonstrate the
30
+ # difference. *All* functionality from File, FileTest, and some from Dir and
31
+ # FileUtils is included, in an unsurprising way. It is essentially a facade for
32
+ # all of these, and more.
33
+ #
34
+ # == Examples
35
+ #
36
+ # === Example 1: Using Pathname
37
+ #
38
+ # require 'pathname'
39
+ # p = Pathname.new("/usr/bin/ruby")
40
+ # size = p.size # 27662
41
+ # isdir = p.directory? # false
42
+ # dir = p.dirname # Pathname:/usr/bin
43
+ # base = p.basename # Pathname:ruby
44
+ # dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
45
+ # data = p.read
46
+ # p.open { |f| _ }
47
+ # p.each_line { |line| _ }
48
+ #
49
+ # === Example 2: Using standard Ruby
50
+ #
51
+ # p = "/usr/bin/ruby"
52
+ # size = File.size(p) # 27662
53
+ # isdir = File.directory?(p) # false
54
+ # dir = File.dirname(p) # "/usr/bin"
55
+ # base = File.basename(p) # "ruby"
56
+ # dir, base = File.split(p) # ["/usr/bin", "ruby"]
57
+ # data = File.read(p)
58
+ # File.open(p) { |f| _ }
59
+ # File.foreach(p) { |line| _ }
60
+ #
61
+ # === Example 3: Special features
62
+ #
63
+ # p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib
64
+ # p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8
65
+ # p3 = p1.parent # Pathname:/usr
66
+ # p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8
67
+ # pwd = Pathname.pwd # Pathname:/home/gavin
68
+ # pwd.absolute? # true
69
+ # p5 = Pathname.new "." # Pathname:.
70
+ # p5 = p5 + "music/../articles" # Pathname:music/../articles
71
+ # p5.cleanpath # Pathname:articles
72
+ # p5.realpath # Pathname:/home/gavin/articles
73
+ # p5.children # [Pathname:/home/gavin/articles/linux, ...]
74
+ #
75
+ # == Breakdown of functionality
76
+ #
77
+ # === Core methods
78
+ #
79
+ # These methods are effectively manipulating a String, because that's all a path
80
+ # is. Except for #mountpoint?, #children, and #realpath, they don't access the
81
+ # filesystem.
82
+ #
83
+ # - +
84
+ # - #join
85
+ # - #parent
86
+ # - #root?
87
+ # - #absolute?
88
+ # - #relative?
89
+ # - #relative_path_from
90
+ # - #each_filename
91
+ # - #cleanpath
92
+ # - #realpath
93
+ # - #children
94
+ # - #mountpoint?
95
+ #
96
+ # === File status predicate methods
97
+ #
98
+ # These methods are a facade for FileTest:
99
+ # - #blockdev?
100
+ # - #chardev?
101
+ # - #directory?
102
+ # - #executable?
103
+ # - #executable_real?
104
+ # - #exist?
105
+ # - #file?
106
+ # - #grpowned?
107
+ # - #owned?
108
+ # - #pipe?
109
+ # - #readable?
110
+ # - #world_readable?
111
+ # - #readable_real?
112
+ # - #setgid?
113
+ # - #setuid?
114
+ # - #size
115
+ # - #size?
116
+ # - #socket?
117
+ # - #sticky?
118
+ # - #symlink?
119
+ # - #writable?
120
+ # - #world_writable?
121
+ # - #writable_real?
122
+ # - #zero?
123
+ #
124
+ # === File property and manipulation methods
125
+ #
126
+ # These methods are a facade for File:
127
+ # - #atime
128
+ # - #ctime
129
+ # - #mtime
130
+ # - #chmod(mode)
131
+ # - #lchmod(mode)
132
+ # - #chown(owner, group)
133
+ # - #lchown(owner, group)
134
+ # - #fnmatch(pattern, *args)
135
+ # - #fnmatch?(pattern, *args)
136
+ # - #ftype
137
+ # - #make_link(old)
138
+ # - #open(*args, &block)
139
+ # - #readlink
140
+ # - #rename(to)
141
+ # - #stat
142
+ # - #lstat
143
+ # - #make_symlink(old)
144
+ # - #truncate(length)
145
+ # - #utime(atime, mtime)
146
+ # - #basename(*args)
147
+ # - #dirname
148
+ # - #extname
149
+ # - #expand_path(*args)
150
+ # - #split
151
+ #
152
+ # === Directory methods
153
+ #
154
+ # These methods are a facade for Dir:
155
+ # - Pathname.glob(*args)
156
+ # - Pathname.getwd / Pathname.pwd
157
+ # - #rmdir
158
+ # - #entries
159
+ # - #each_entry(&block)
160
+ # - #mkdir(*args)
161
+ # - #opendir(*args)
162
+ #
163
+ # === IO
164
+ #
165
+ # These methods are a facade for IO:
166
+ # - #each_line(*args, &block)
167
+ # - #read(*args)
168
+ # - #readlines(*args)
169
+ # - #sysopen(*args)
170
+ #
171
+ # === Utilities
172
+ #
173
+ # These methods are a mixture of Find, FileUtils, and others:
174
+ # - #find(&block)
175
+ # - #mkpath
176
+ # - #rmtree
177
+ # - #unlink / #delete
178
+ #
179
+ #
180
+ # == Method documentation
181
+ #
182
+ # As the above section shows, most of the methods in Pathname are facades. The
183
+ # documentation for these methods generally just says, for instance, "See
184
+ # FileTest.writable?", as you should be familiar with the original method
185
+ # anyway, and its documentation (e.g. through +ri+) will contain more
186
+ # information. In some cases, a brief description will follow.
187
+ #
188
+ class Pathname
189
+
190
+ # :stopdoc:
191
+ if RUBY_VERSION < "1.9"
192
+ TO_PATH = :to_str
193
+ else
194
+ # to_path is implemented so Pathname objects are usable with File.open, etc.
195
+ TO_PATH = :to_path
196
+ end
197
+
198
+ SAME_PATHS = if File::FNM_SYSCASE
199
+ proc {|a, b| a.casecmp(b).zero?}
200
+ else
201
+ proc {|a, b| a == b}
202
+ end
203
+
204
+ # :startdoc:
205
+
206
+ #
207
+ # Create a Pathname object from the given String (or String-like object).
208
+ # If +path+ contains a NUL character (<tt>\0</tt>), an ArgumentError is raised.
209
+ #
210
+ def initialize(path)
211
+ path = path.__send__(TO_PATH) if path.respond_to? TO_PATH
212
+ @path = path.dup
213
+
214
+ if /\0/ =~ @path
215
+ raise ArgumentError, "pathname contains \\0: #{@path.inspect}"
216
+ end
217
+
218
+ self.taint if @path.tainted?
219
+ end
220
+
221
+ def freeze() super; @path.freeze; self end
222
+ def taint() super; @path.taint; self end
223
+ def untaint() super; @path.untaint; self end
224
+
225
+ #
226
+ # Compare this pathname with +other+. The comparison is string-based.
227
+ # Be aware that two different paths (<tt>foo.txt</tt> and <tt>./foo.txt</tt>)
228
+ # can refer to the same file.
229
+ #
230
+ def ==(other)
231
+ return false unless Pathname === other
232
+ other.to_s == @path
233
+ end
234
+ alias === ==
235
+ alias eql? ==
236
+
237
+ # Provides for comparing pathnames, case-sensitively.
238
+ def <=>(other)
239
+ return nil unless Pathname === other
240
+ @path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
241
+ end
242
+
243
+ def hash # :nodoc:
244
+ @path.hash
245
+ end
246
+
247
+ # Return the path as a String.
248
+ def to_s
249
+ @path.dup
250
+ end
251
+
252
+ # to_path is implemented so Pathname objects are usable with File.open, etc.
253
+ alias_method TO_PATH, :to_s
254
+
255
+ def inspect # :nodoc:
256
+ "#<#{self.class}:#{@path}>"
257
+ end
258
+
259
+ # Return a pathname which is substituted by String#sub.
260
+ def sub(pattern, *rest, &block)
261
+ if block
262
+ path = @path.sub(pattern, *rest) {|*args|
263
+ begin
264
+ old = Thread.current[:pathname_sub_matchdata]
265
+ Thread.current[:pathname_sub_matchdata] = $~
266
+ eval("$~ = Thread.current[:pathname_sub_matchdata]", block.binding)
267
+ ensure
268
+ Thread.current[:pathname_sub_matchdata] = old
269
+ end
270
+ yield *args
271
+ }
272
+ else
273
+ path = @path.sub(pattern, *rest)
274
+ end
275
+ self.class.new(path)
276
+ end
277
+
278
+ if File::ALT_SEPARATOR
279
+ SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}"
280
+ SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/
281
+ else
282
+ SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}"
283
+ SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
284
+ end
285
+
286
+ # Return a pathname which the extension of the basename is substituted by
287
+ # <i>repl</i>.
288
+ #
289
+ # If self has no extension part, <i>repl</i> is appended.
290
+ def sub_ext(repl)
291
+ ext = File.extname(@path)
292
+ self.class.new(@path.chomp(ext) + repl)
293
+ end
294
+
295
+ # chop_basename(path) -> [pre-basename, basename] or nil
296
+ def chop_basename(path)
297
+ base = File.basename(path)
298
+ if /\A#{SEPARATOR_PAT}?\z/ =~ base
299
+ return nil
300
+ else
301
+ return path[0, path.rindex(base)], base
302
+ end
303
+ end
304
+ private :chop_basename
305
+
306
+ # split_names(path) -> prefix, [name, ...]
307
+ def split_names(path)
308
+ names = []
309
+ while r = chop_basename(path)
310
+ path, basename = r
311
+ names.unshift basename
312
+ end
313
+ return path, names
314
+ end
315
+ private :split_names
316
+
317
+ def prepend_prefix(prefix, relpath)
318
+ if relpath.empty?
319
+ File.dirname(prefix)
320
+ elsif /#{SEPARATOR_PAT}/ =~ prefix
321
+ prefix = File.dirname(prefix)
322
+ prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
323
+ prefix + relpath
324
+ else
325
+ prefix + relpath
326
+ end
327
+ end
328
+ private :prepend_prefix
329
+
330
+ # Returns clean pathname of +self+ with consecutive slashes and useless dots
331
+ # removed. The filesystem is not accessed.
332
+ #
333
+ # If +consider_symlink+ is +true+, then a more conservative algorithm is used
334
+ # to avoid breaking symbolic linkages. This may retain more <tt>..</tt>
335
+ # entries than absolutely necessary, but without accessing the filesystem,
336
+ # this can't be avoided. See #realpath.
337
+ #
338
+ def cleanpath(consider_symlink=false)
339
+ if consider_symlink
340
+ cleanpath_conservative
341
+ else
342
+ cleanpath_aggressive
343
+ end
344
+ end
345
+
346
+ #
347
+ # Clean the path simply by resolving and removing excess "." and ".." entries.
348
+ # Nothing more, nothing less.
349
+ #
350
+ def cleanpath_aggressive
351
+ path = @path
352
+ names = []
353
+ pre = path
354
+ while r = chop_basename(pre)
355
+ pre, base = r
356
+ case base
357
+ when '.'
358
+ when '..'
359
+ names.unshift base
360
+ else
361
+ if names[0] == '..'
362
+ names.shift
363
+ else
364
+ names.unshift base
365
+ end
366
+ end
367
+ end
368
+ if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
369
+ names.shift while names[0] == '..'
370
+ end
371
+ self.class.new(prepend_prefix(pre, File.join(*names)))
372
+ end
373
+ private :cleanpath_aggressive
374
+
375
+ # has_trailing_separator?(path) -> bool
376
+ def has_trailing_separator?(path)
377
+ if r = chop_basename(path)
378
+ pre, basename = r
379
+ pre.length + basename.length < path.length
380
+ else
381
+ false
382
+ end
383
+ end
384
+ private :has_trailing_separator?
385
+
386
+ # add_trailing_separator(path) -> path
387
+ def add_trailing_separator(path)
388
+ if File.basename(path + 'a') == 'a'
389
+ path
390
+ else
391
+ File.join(path, "") # xxx: Is File.join is appropriate to add separator?
392
+ end
393
+ end
394
+ private :add_trailing_separator
395
+
396
+ def del_trailing_separator(path)
397
+ if r = chop_basename(path)
398
+ pre, basename = r
399
+ pre + basename
400
+ elsif /#{SEPARATOR_PAT}+\z/o =~ path
401
+ $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
402
+ else
403
+ path
404
+ end
405
+ end
406
+ private :del_trailing_separator
407
+
408
+ def cleanpath_conservative
409
+ path = @path
410
+ names = []
411
+ pre = path
412
+ while r = chop_basename(pre)
413
+ pre, base = r
414
+ names.unshift base if base != '.'
415
+ end
416
+ if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
417
+ names.shift while names[0] == '..'
418
+ end
419
+ if names.empty?
420
+ self.class.new(File.dirname(pre))
421
+ else
422
+ if names.last != '..' && File.basename(path) == '.'
423
+ names << '.'
424
+ end
425
+ result = prepend_prefix(pre, File.join(*names))
426
+ if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path)
427
+ self.class.new(add_trailing_separator(result))
428
+ else
429
+ self.class.new(result)
430
+ end
431
+ end
432
+ end
433
+ private :cleanpath_conservative
434
+
435
+ def realpath_rec(prefix, unresolved, h)
436
+ resolved = []
437
+ until unresolved.empty?
438
+ n = unresolved.shift
439
+ if n == '.'
440
+ next
441
+ elsif n == '..'
442
+ resolved.pop
443
+ else
444
+ path = prepend_prefix(prefix, File.join(*(resolved + [n])))
445
+ if h.include? path
446
+ if h[path] == :resolving
447
+ raise Errno::ELOOP.new(path)
448
+ else
449
+ prefix, *resolved = h[path]
450
+ end
451
+ else
452
+ s = File.lstat(path)
453
+ if s.symlink?
454
+ h[path] = :resolving
455
+ link_prefix, link_names = split_names(File.readlink(path))
456
+ if link_prefix == ''
457
+ prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h)
458
+ else
459
+ prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h)
460
+ end
461
+ else
462
+ resolved << n
463
+ h[path] = [prefix, *resolved]
464
+ end
465
+ end
466
+ end
467
+ end
468
+ return prefix, *resolved
469
+ end
470
+ private :realpath_rec
471
+
472
+ #
473
+ # Returns a real (absolute) pathname of +self+ in the actual filesystem.
474
+ # The real pathname doesn't contain symlinks or useless dots.
475
+ #
476
+ # No arguments should be given; the old behaviour is *obsoleted*.
477
+ #
478
+ def realpath
479
+ path = @path
480
+ prefix, names = split_names(path)
481
+ if prefix == ''
482
+ prefix, names2 = split_names(Dir.pwd)
483
+ names = names2 + names
484
+ end
485
+ prefix, *names = realpath_rec(prefix, names, {})
486
+ self.class.new(prepend_prefix(prefix, File.join(*names)))
487
+ end
488
+
489
+ # #parent returns the parent directory.
490
+ #
491
+ # This is same as <tt>self + '..'</tt>.
492
+ def parent
493
+ self + '..'
494
+ end
495
+
496
+ # #mountpoint? returns +true+ if <tt>self</tt> points to a mountpoint.
497
+ def mountpoint?
498
+ begin
499
+ stat1 = self.lstat
500
+ stat2 = self.parent.lstat
501
+ stat1.dev == stat2.dev && stat1.ino == stat2.ino ||
502
+ stat1.dev != stat2.dev
503
+ rescue Errno::ENOENT
504
+ false
505
+ end
506
+ end
507
+
508
+ #
509
+ # #root? is a predicate for root directories. I.e. it returns +true+ if the
510
+ # pathname consists of consecutive slashes.
511
+ #
512
+ # It doesn't access actual filesystem. So it may return +false+ for some
513
+ # pathnames which points to roots such as <tt>/usr/..</tt>.
514
+ #
515
+ def root?
516
+ !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
517
+ end
518
+
519
+ # Predicate method for testing whether a path is absolute.
520
+ # It returns +true+ if the pathname begins with a slash.
521
+ def absolute?
522
+ !relative?
523
+ end
524
+
525
+ # The opposite of #absolute?
526
+ def relative?
527
+ path = @path
528
+ while r = chop_basename(path)
529
+ path, basename = r
530
+ end
531
+ path == ''
532
+ end
533
+
534
+ #
535
+ # Iterates over each component of the path.
536
+ #
537
+ # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
538
+ # # yields "usr", "bin", and "ruby".
539
+ #
540
+ def each_filename # :yield: filename
541
+ return to_enum(__method__) unless block_given?
542
+ prefix, names = split_names(@path)
543
+ names.each {|filename| yield filename }
544
+ nil
545
+ end
546
+
547
+ # Iterates over and yields a new Pathname object
548
+ # for each element in the given path in descending order.
549
+ #
550
+ # Pathname.new('/path/to/some/file.rb').descend {|v| p v}
551
+ # #<Pathname:/>
552
+ # #<Pathname:/path>
553
+ # #<Pathname:/path/to>
554
+ # #<Pathname:/path/to/some>
555
+ # #<Pathname:/path/to/some/file.rb>
556
+ #
557
+ # Pathname.new('path/to/some/file.rb').descend {|v| p v}
558
+ # #<Pathname:path>
559
+ # #<Pathname:path/to>
560
+ # #<Pathname:path/to/some>
561
+ # #<Pathname:path/to/some/file.rb>
562
+ #
563
+ # It doesn't access actual filesystem.
564
+ #
565
+ # This method is available since 1.8.5.
566
+ #
567
+ def descend
568
+ vs = []
569
+ ascend {|v| vs << v }
570
+ vs.reverse_each {|v| yield v }
571
+ nil
572
+ end
573
+
574
+ # Iterates over and yields a new Pathname object
575
+ # for each element in the given path in ascending order.
576
+ #
577
+ # Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
578
+ # #<Pathname:/path/to/some/file.rb>
579
+ # #<Pathname:/path/to/some>
580
+ # #<Pathname:/path/to>
581
+ # #<Pathname:/path>
582
+ # #<Pathname:/>
583
+ #
584
+ # Pathname.new('path/to/some/file.rb').ascend {|v| p v}
585
+ # #<Pathname:path/to/some/file.rb>
586
+ # #<Pathname:path/to/some>
587
+ # #<Pathname:path/to>
588
+ # #<Pathname:path>
589
+ #
590
+ # It doesn't access actual filesystem.
591
+ #
592
+ # This method is available since 1.8.5.
593
+ #
594
+ def ascend
595
+ path = @path
596
+ yield self
597
+ while r = chop_basename(path)
598
+ path, name = r
599
+ break if path.empty?
600
+ yield self.class.new(del_trailing_separator(path))
601
+ end
602
+ end
603
+
604
+ #
605
+ # Pathname#+ appends a pathname fragment to this one to produce a new Pathname
606
+ # object.
607
+ #
608
+ # p1 = Pathname.new("/usr") # Pathname:/usr
609
+ # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
610
+ # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
611
+ #
612
+ # This method doesn't access the file system; it is pure string manipulation.
613
+ #
614
+ def +(other)
615
+ other = Pathname.new(other) unless Pathname === other
616
+ Pathname.new(plus(@path, other.to_s))
617
+ end
618
+
619
+ def plus(path1, path2) # -> path
620
+ prefix2 = path2
621
+ index_list2 = []
622
+ basename_list2 = []
623
+ while r2 = chop_basename(prefix2)
624
+ prefix2, basename2 = r2
625
+ index_list2.unshift prefix2.length
626
+ basename_list2.unshift basename2
627
+ end
628
+ return path2 if prefix2 != ''
629
+ prefix1 = path1
630
+ while true
631
+ while !basename_list2.empty? && basename_list2.first == '.'
632
+ index_list2.shift
633
+ basename_list2.shift
634
+ end
635
+ break unless r1 = chop_basename(prefix1)
636
+ prefix1, basename1 = r1
637
+ next if basename1 == '.'
638
+ if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
639
+ prefix1 = prefix1 + basename1
640
+ break
641
+ end
642
+ index_list2.shift
643
+ basename_list2.shift
644
+ end
645
+ r1 = chop_basename(prefix1)
646
+ if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
647
+ while !basename_list2.empty? && basename_list2.first == '..'
648
+ index_list2.shift
649
+ basename_list2.shift
650
+ end
651
+ end
652
+ if !basename_list2.empty?
653
+ suffix2 = path2[index_list2.first..-1]
654
+ r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
655
+ else
656
+ r1 ? prefix1 : File.dirname(prefix1)
657
+ end
658
+ end
659
+ private :plus
660
+
661
+ #
662
+ # Pathname#join joins pathnames.
663
+ #
664
+ # <tt>path0.join(path1, ..., pathN)</tt> is the same as
665
+ # <tt>path0 + path1 + ... + pathN</tt>.
666
+ #
667
+ def join(*args)
668
+ args.unshift self
669
+ result = args.pop
670
+ result = Pathname.new(result) unless Pathname === result
671
+ return result if result.absolute?
672
+ args.reverse_each {|arg|
673
+ arg = Pathname.new(arg) unless Pathname === arg
674
+ result = arg + result
675
+ return result if result.absolute?
676
+ }
677
+ result
678
+ end
679
+
680
+ #
681
+ # Returns the children of the directory (files and subdirectories, not
682
+ # recursive) as an array of Pathname objects. By default, the returned
683
+ # pathnames will have enough information to access the files. If you set
684
+ # +with_directory+ to +false+, then the returned pathnames will contain the
685
+ # filename only.
686
+ #
687
+ # For example:
688
+ # p = Pathname("/usr/lib/ruby/1.8")
689
+ # p.children
690
+ # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
691
+ # Pathname:/usr/lib/ruby/1.8/Env.rb,
692
+ # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
693
+ # p.children(false)
694
+ # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
695
+ #
696
+ # Note that the result never contain the entries <tt>.</tt> and <tt>..</tt> in
697
+ # the directory because they are not children.
698
+ #
699
+ # This method has existed since 1.8.1.
700
+ #
701
+ def children(with_directory=true)
702
+ with_directory = false if @path == '.'
703
+ result = []
704
+ Dir.foreach(@path) {|e|
705
+ next if e == '.' || e == '..'
706
+ if with_directory
707
+ result << self.class.new(File.join(@path, e))
708
+ else
709
+ result << self.class.new(e)
710
+ end
711
+ }
712
+ result
713
+ end
714
+
715
+ #
716
+ # #relative_path_from returns a relative path from the argument to the
717
+ # receiver. If +self+ is absolute, the argument must be absolute too. If
718
+ # +self+ is relative, the argument must be relative too.
719
+ #
720
+ # #relative_path_from doesn't access the filesystem. It assumes no symlinks.
721
+ #
722
+ # ArgumentError is raised when it cannot find a relative path.
723
+ #
724
+ # This method has existed since 1.8.1.
725
+ #
726
+ def relative_path_from(base_directory)
727
+ dest_directory = self.cleanpath.to_s
728
+ base_directory = base_directory.cleanpath.to_s
729
+ dest_prefix = dest_directory
730
+ dest_names = []
731
+ while r = chop_basename(dest_prefix)
732
+ dest_prefix, basename = r
733
+ dest_names.unshift basename if basename != '.'
734
+ end
735
+ base_prefix = base_directory
736
+ base_names = []
737
+ while r = chop_basename(base_prefix)
738
+ base_prefix, basename = r
739
+ base_names.unshift basename if basename != '.'
740
+ end
741
+ unless SAME_PATHS[dest_prefix, base_prefix]
742
+ raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
743
+ end
744
+ while !dest_names.empty? &&
745
+ !base_names.empty? &&
746
+ SAME_PATHS[dest_names.first, base_names.first]
747
+ dest_names.shift
748
+ base_names.shift
749
+ end
750
+ if base_names.include? '..'
751
+ raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
752
+ end
753
+ base_names.fill('..')
754
+ relpath_names = base_names + dest_names
755
+ if relpath_names.empty?
756
+ Pathname.new('.')
757
+ else
758
+ Pathname.new(File.join(*relpath_names))
759
+ end
760
+ end
761
+ end
762
+
763
+ class Pathname # * IO *
764
+ #
765
+ # #each_line iterates over the line in the file. It yields a String object
766
+ # for each line.
767
+ #
768
+ # This method has existed since 1.8.1.
769
+ #
770
+ def each_line(*args, &block) # :yield: line
771
+ IO.foreach(@path, *args, &block)
772
+ end
773
+
774
+ # Pathname#foreachline is *obsoleted* at 1.8.1. Use #each_line.
775
+ def foreachline(*args, &block)
776
+ warn "Pathname#foreachline is obsoleted. Use Pathname#each_line."
777
+ each_line(*args, &block)
778
+ end
779
+
780
+ # See <tt>IO.read</tt>. Returns all the bytes from the file, or the first +N+
781
+ # if specified.
782
+ def read(*args) IO.read(@path, *args) end
783
+
784
+ # See <tt>IO.readlines</tt>. Returns all the lines from the file.
785
+ def readlines(*args) IO.readlines(@path, *args) end
786
+
787
+ # See <tt>IO.sysopen</tt>.
788
+ def sysopen(*args) IO.sysopen(@path, *args) end
789
+ end
790
+
791
+
792
+ class Pathname # * File *
793
+
794
+ # See <tt>File.atime</tt>. Returns last access time.
795
+ def atime() File.atime(@path) end
796
+
797
+ # See <tt>File.ctime</tt>. Returns last (directory entry, not file) change time.
798
+ def ctime() File.ctime(@path) end
799
+
800
+ # See <tt>File.mtime</tt>. Returns last modification time.
801
+ def mtime() File.mtime(@path) end
802
+
803
+ # See <tt>File.chmod</tt>. Changes permissions.
804
+ def chmod(mode) File.chmod(mode, @path) end
805
+
806
+ # See <tt>File.lchmod</tt>.
807
+ def lchmod(mode) File.lchmod(mode, @path) end
808
+
809
+ # See <tt>File.chown</tt>. Change owner and group of file.
810
+ def chown(owner, group) File.chown(owner, group, @path) end
811
+
812
+ # See <tt>File.lchown</tt>.
813
+ def lchown(owner, group) File.lchown(owner, group, @path) end
814
+
815
+ # See <tt>File.fnmatch</tt>. Return +true+ if the receiver matches the given
816
+ # pattern.
817
+ def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
818
+
819
+ # See <tt>File.fnmatch?</tt> (same as #fnmatch).
820
+ def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
821
+
822
+ # See <tt>File.ftype</tt>. Returns "type" of file ("file", "directory",
823
+ # etc).
824
+ def ftype() File.ftype(@path) end
825
+
826
+ # See <tt>File.link</tt>. Creates a hard link.
827
+ def make_link(old) File.link(old, @path) end
828
+
829
+ # See <tt>File.open</tt>. Opens the file for reading or writing.
830
+ def open(*args, &block) # :yield: file
831
+ File.open(@path, *args, &block)
832
+ end
833
+
834
+ # See <tt>File.readlink</tt>. Read symbolic link.
835
+ def readlink() self.class.new(File.readlink(@path)) end
836
+
837
+ # See <tt>File.rename</tt>. Rename the file.
838
+ def rename(to) File.rename(@path, to) end
839
+
840
+ # See <tt>File.stat</tt>. Returns a <tt>File::Stat</tt> object.
841
+ def stat() File.stat(@path) end
842
+
843
+ # See <tt>File.lstat</tt>.
844
+ def lstat() File.lstat(@path) end
845
+
846
+ # See <tt>File.symlink</tt>. Creates a symbolic link.
847
+ def make_symlink(old) File.symlink(old, @path) end
848
+
849
+ # See <tt>File.truncate</tt>. Truncate the file to +length+ bytes.
850
+ def truncate(length) File.truncate(@path, length) end
851
+
852
+ # See <tt>File.utime</tt>. Update the access and modification times.
853
+ def utime(atime, mtime) File.utime(atime, mtime, @path) end
854
+
855
+ # See <tt>File.basename</tt>. Returns the last component of the path.
856
+ def basename(*args) self.class.new(File.basename(@path, *args)) end
857
+
858
+ # See <tt>File.dirname</tt>. Returns all but the last component of the path.
859
+ def dirname() self.class.new(File.dirname(@path)) end
860
+
861
+ # See <tt>File.extname</tt>. Returns the file's extension.
862
+ def extname() File.extname(@path) end
863
+
864
+ # See <tt>File.expand_path</tt>.
865
+ def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end
866
+
867
+ # See <tt>File.split</tt>. Returns the #dirname and the #basename in an
868
+ # Array.
869
+ def split() File.split(@path).map {|f| self.class.new(f) } end
870
+
871
+ # Pathname#link is confusing and *obsoleted* because the receiver/argument
872
+ # order is inverted to corresponding system call.
873
+ def link(old)
874
+ warn 'Pathname#link is obsoleted. Use Pathname#make_link.'
875
+ File.link(old, @path)
876
+ end
877
+
878
+ # Pathname#symlink is confusing and *obsoleted* because the receiver/argument
879
+ # order is inverted to corresponding system call.
880
+ def symlink(old)
881
+ warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.'
882
+ File.symlink(old, @path)
883
+ end
884
+ end
885
+
886
+
887
+ class Pathname # * FileTest *
888
+
889
+ # See <tt>FileTest.blockdev?</tt>.
890
+ def blockdev?() FileTest.blockdev?(@path) end
891
+
892
+ # See <tt>FileTest.chardev?</tt>.
893
+ def chardev?() FileTest.chardev?(@path) end
894
+
895
+ # See <tt>FileTest.executable?</tt>.
896
+ def executable?() FileTest.executable?(@path) end
897
+
898
+ # See <tt>FileTest.executable_real?</tt>.
899
+ def executable_real?() FileTest.executable_real?(@path) end
900
+
901
+ # See <tt>FileTest.exist?</tt>.
902
+ def exist?() FileTest.exist?(@path) end
903
+
904
+ # See <tt>FileTest.grpowned?</tt>.
905
+ def grpowned?() FileTest.grpowned?(@path) end
906
+
907
+ # See <tt>FileTest.directory?</tt>.
908
+ def directory?() FileTest.directory?(@path) end
909
+
910
+ # See <tt>FileTest.file?</tt>.
911
+ def file?() FileTest.file?(@path) end
912
+
913
+ # See <tt>FileTest.pipe?</tt>.
914
+ def pipe?() FileTest.pipe?(@path) end
915
+
916
+ # See <tt>FileTest.socket?</tt>.
917
+ def socket?() FileTest.socket?(@path) end
918
+
919
+ # See <tt>FileTest.owned?</tt>.
920
+ def owned?() FileTest.owned?(@path) end
921
+
922
+ # See <tt>FileTest.readable?</tt>.
923
+ def readable?() FileTest.readable?(@path) end
924
+
925
+ # See <tt>FileTest.world_readable?</tt>.
926
+ def world_readable?() FileTest.world_readable?(@path) end
927
+
928
+ # See <tt>FileTest.readable_real?</tt>.
929
+ def readable_real?() FileTest.readable_real?(@path) end
930
+
931
+ # See <tt>FileTest.setuid?</tt>.
932
+ def setuid?() FileTest.setuid?(@path) end
933
+
934
+ # See <tt>FileTest.setgid?</tt>.
935
+ def setgid?() FileTest.setgid?(@path) end
936
+
937
+ # See <tt>FileTest.size</tt>.
938
+ def size() FileTest.size(@path) end
939
+
940
+ # See <tt>FileTest.size?</tt>.
941
+ def size?() FileTest.size?(@path) end
942
+
943
+ # See <tt>FileTest.sticky?</tt>.
944
+ def sticky?() FileTest.sticky?(@path) end
945
+
946
+ # See <tt>FileTest.symlink?</tt>.
947
+ def symlink?() FileTest.symlink?(@path) end
948
+
949
+ # See <tt>FileTest.writable?</tt>.
950
+ def writable?() FileTest.writable?(@path) end
951
+
952
+ # See <tt>FileTest.world_writable?</tt>.
953
+ def world_writable?() FileTest.world_writable?(@path) end
954
+
955
+ # See <tt>FileTest.writable_real?</tt>.
956
+ def writable_real?() FileTest.writable_real?(@path) end
957
+
958
+ # See <tt>FileTest.zero?</tt>.
959
+ def zero?() FileTest.zero?(@path) end
960
+ end
961
+
962
+
963
+ class Pathname # * Dir *
964
+ # See <tt>Dir.glob</tt>. Returns or yields Pathname objects.
965
+ def Pathname.glob(*args) # :yield: p
966
+ if block_given?
967
+ Dir.glob(*args) {|f| yield self.new(f) }
968
+ else
969
+ Dir.glob(*args).map {|f| self.new(f) }
970
+ end
971
+ end
972
+
973
+ # See <tt>Dir.getwd</tt>. Returns the current working directory as a Pathname.
974
+ def Pathname.getwd() self.new(Dir.getwd) end
975
+ class << self; alias pwd getwd end
976
+
977
+ # Pathname#chdir is *obsoleted* at 1.8.1.
978
+ def chdir(&block)
979
+ warn "Pathname#chdir is obsoleted. Use Dir.chdir."
980
+ Dir.chdir(@path, &block)
981
+ end
982
+
983
+ # Pathname#chroot is *obsoleted* at 1.8.1.
984
+ def chroot
985
+ warn "Pathname#chroot is obsoleted. Use Dir.chroot."
986
+ Dir.chroot(@path)
987
+ end
988
+
989
+ # Return the entries (files and subdirectories) in the directory, each as a
990
+ # Pathname object.
991
+ def entries() Dir.entries(@path).map {|f| self.class.new(f) } end
992
+
993
+ # Iterates over the entries (files and subdirectories) in the directory. It
994
+ # yields a Pathname object for each entry.
995
+ #
996
+ # This method has existed since 1.8.1.
997
+ def each_entry(&block) # :yield: p
998
+ Dir.foreach(@path) {|f| yield self.class.new(f) }
999
+ end
1000
+
1001
+ # Pathname#dir_foreach is *obsoleted* at 1.8.1.
1002
+ def dir_foreach(*args, &block)
1003
+ warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry."
1004
+ each_entry(*args, &block)
1005
+ end
1006
+
1007
+ # See <tt>Dir.mkdir</tt>. Create the referenced directory.
1008
+ def mkdir(*args) Dir.mkdir(@path, *args) end
1009
+
1010
+ # See <tt>Dir.rmdir</tt>. Remove the referenced directory.
1011
+ def rmdir() Dir.rmdir(@path) end
1012
+
1013
+ # See <tt>Dir.open</tt>.
1014
+ def opendir(&block) # :yield: dir
1015
+ Dir.open(@path, &block)
1016
+ end
1017
+ end
1018
+
1019
+
1020
+ class Pathname # * Find *
1021
+ #
1022
+ # Pathname#find is an iterator to traverse a directory tree in a depth first
1023
+ # manner. It yields a Pathname for each file under "this" directory.
1024
+ #
1025
+ # Since it is implemented by <tt>find.rb</tt>, <tt>Find.prune</tt> can be used
1026
+ # to control the traverse.
1027
+ #
1028
+ # If +self+ is <tt>.</tt>, yielded pathnames begin with a filename in the
1029
+ # current directory, not <tt>./</tt>.
1030
+ #
1031
+ def find(&block) # :yield: p
1032
+ require 'find'
1033
+ if @path == '.'
1034
+ Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
1035
+ else
1036
+ Find.find(@path) {|f| yield self.class.new(f) }
1037
+ end
1038
+ end
1039
+ end
1040
+
1041
+
1042
+ class Pathname # * FileUtils *
1043
+ # See <tt>FileUtils.mkpath</tt>. Creates a full path, including any
1044
+ # intermediate directories that don't yet exist.
1045
+ def mkpath
1046
+ require 'fileutils'
1047
+ FileUtils.mkpath(@path)
1048
+ nil
1049
+ end
1050
+
1051
+ # See <tt>FileUtils.rm_r</tt>. Deletes a directory and all beneath it.
1052
+ def rmtree
1053
+ # The name "rmtree" is borrowed from File::Path of Perl.
1054
+ # File::Path provides "mkpath" and "rmtree".
1055
+ require 'fileutils'
1056
+ FileUtils.rm_r(@path)
1057
+ nil
1058
+ end
1059
+ end
1060
+
1061
+
1062
+ class Pathname # * mixed *
1063
+ # Removes a file or directory, using <tt>File.unlink</tt> or
1064
+ # <tt>Dir.unlink</tt> as necessary.
1065
+ def unlink()
1066
+ begin
1067
+ Dir.unlink @path
1068
+ rescue Errno::ENOTDIR
1069
+ File.unlink @path
1070
+ end
1071
+ end
1072
+ alias delete unlink
1073
+
1074
+ # This method is *obsoleted* at 1.8.1. Use #each_line or #each_entry.
1075
+ def foreach(*args, &block)
1076
+ warn "Pathname#foreach is obsoleted. Use each_line or each_entry."
1077
+ if FileTest.directory? @path
1078
+ # For polymorphism between Dir.foreach and IO.foreach,
1079
+ # Pathname#foreach doesn't yield Pathname object.
1080
+ Dir.foreach(@path, *args, &block)
1081
+ else
1082
+ IO.foreach(@path, *args, &block)
1083
+ end
1084
+ end
1085
+ end
1086
+
1087
+ class Pathname
1088
+ undef =~
1089
+ end
1090
+
1091
+ module Kernel
1092
+ # create a pathname object.
1093
+ #
1094
+ # This method is available since 1.8.5.
1095
+ def Pathname(path) # :doc:
1096
+ Pathname.new(path)
1097
+ end
1098
+ private :Pathname
1099
+ end