bowline 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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