Class: File

Inherits:
Object show all
Defined in:
lib/ruby_archive/handlers/rubyzip/zip/stdrubyext.rb,
lib/ruby_archive/patch/file.rb

Overview

:nodoc:all

Direct Known Subclasses

BugFix::Tempfile

Constant Summary collapse

@@ruby_archive_file_class_bind =
binding

Class Method Summary collapse

Class Method Details

.forward_method_multi(method, min_arguments = 1, first_path_arg_index = 0) ⇒ Object

See forward_method_single. Does nearly the same thing, but assumes the argument list ends with a list of files to operate on rather than a single file to operate on.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ruby_archive/patch/file.rb', line 81

def forward_method_multi method, min_arguments=1, first_path_arg_index=0
  alias_name = "ruby_archive_original_#{method}".intern
  eval_line = __LINE__; eval %{
    unless File.respond_to?(:#{alias_name})
      alias_method(:#{alias_name}, :#{method})
    end
    protected(:#{alias_name})

    define_method(:#{method}) do |*args|
      if (#{min_arguments} > 0 && args.size == 0)
        raise ArgumentError, "wrong number of arguments (0 for #{min_arguments})"
      end

      # grab args before the list of filepaths, and the list of filepaths
      args_before_path = nil
      if #{first_path_arg_index} == 0
        args_before_path = []
      else
        args_before_path = args[0..(#{first_path_arg_index - 1})]
      end
      path_list = args[#{first_path_arg_index}..(args.size - 1)]

      path_list.each do |path|
        location_info = File.in_archive?(path)
        if location_info == false
          args_to_send = args_before_path + [path]
          File.send(:#{alias_name},*args_to_send)
          next
        else
          begin
            file_handler = RubyArchive.get(location_info[0]).file
            raise NotImplementedError unless file_handler.respond_to?(:#{method})
            args_to_send = args_before_path + [location_info[1]]
            file_handler.send(:#{method},*args_to_send)
            next
          rescue NotImplementedError
            raise NotImplementedError, "#{method} not implemented in handler for specified archive"
          end
        end         
      end
      return path_list.size
    end
  }, @@ruby_archive_file_class_bind, __FILE__, eval_line
end

.forward_method_single(method, min_arguments = 1, path_arg_index = 0, on_load_error = nil) ⇒ Object

Automates creating class methods that operate on either normal files or files within archives, given a symbol with the name of the method and the index of the argument containing the file name.

It performs tasks in the following order:

  • alias the original method to ruby_archive_original_name, marks the alias as protected.

  • overrides the class method so that it tries, in order:

    1. returns the result of the original class method if the given file exists
    2. returns the result of the original class method if an archive is not specified
    3. returns the result of the archive handler method if an archive is specified
    

The current method of doing this is kind of ugly and involves ‘eval’, but it works(tm). This method is definitely a candidate for improvement.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruby_archive/patch/file.rb', line 33

def forward_method_single method, min_arguments=1, path_arg_index=0, on_load_error=nil
  alias_name = "ruby_archive_original_#{method}".intern
  eval_line = __LINE__; eval %{
    unless File.respond_to?(:#{alias_name})
      alias_method(:#{alias_name}, :#{method})
    end
    protected(:#{alias_name})

    define_method(:#{method}) do |*args|
      if (#{min_arguments} > 0 && args.size == 0)
        raise ArgumentError, "wrong number of arguments (0 for #{min_arguments})"
      end

      # grab args before and after the filepath
      args_before_path = nil
      if #{path_arg_index} == 0
        args_before_path = []
      else
        args_before_path = args[0..(#{path_arg_index - 1})]
      end
      path = args[#{path_arg_index}]
      args_after_path = args[(#{path_arg_index + 1})..(args.size-1)]
    
      # get file location info and forward it to the appropriate method
      location_info = File.in_archive?(path)
      if location_info == false
        return File.send(:#{alias_name},*args)
      else
        begin
          file_handler = RubyArchive.get(location_info[0]).file
          raise NotImplementedError unless file_handler.respond_to?(:#{method})
          args_to_send = args_before_path + [location_info[1]] + args_after_path
          return file_handler.send(:#{method},*args_to_send)
        rescue LoadError
          raise if #{on_load_error.inspect}.nil?
          return #{on_load_error.inspect}
        rescue NotImplementedError
          raise NotImplementedError, "#{method} not implemented in handler for specified archive"
        end
      end
    end
  }, @@ruby_archive_file_class_bind, __FILE__, eval_line
end

.forward_method_unsupported(method, min_arguments = 1, path_args = [0], return_instead = :raise) ⇒ Object

Marks a function as unsupported by archives. path_args should be an array of argument indices containing filepaths to check.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ruby_archive/patch/file.rb', line 129

def forward_method_unsupported method, min_arguments=1, path_args=[0], return_instead=:raise
  alias_name = "ruby_archive_original_#{method}".intern
  eval_line = __LINE__; eval %{
    unless File.respond_to?(:#{alias_name})
      alias_method(:#{alias_name}, :#{method})
    end
    protected(:#{alias_name})

    define_method(:#{method}) do |*args|
      if (#{min_arguments} > 0 && args.size == 0)
        raise ArgumentError, "wrong number of arguments (0 for #{min_arguments})"
      end

      # grab args before the list of filepaths, and the list of filepaths
      #{path_args.inspect}.each do |i|
        if File.in_archive?(args[i]) != false
          unless #{return_instead.inspect} == :raise
            warn "Dir.#{method} is not supported for files within archives"
            return #{return_instead.inspect}
          end
          raise NotImplementedError, "File.#{method} is not supported for files within archives (yet)"
        end
      end
      
      File.send(:#{alias_name},*args)
    end
  }, @@ruby_archive_file_class_bind, __FILE__, eval_line
end

.in_archive?(path) ⇒ Boolean

Determines whether a specified location appears to be within an archive. It first checks if the given location exists on the filesystem, and if not check if there is an ‘!’ mark in the filename.

Returns an array of [archive_path, file_inside_archive] if location appears to be in an archive, false otherwise.

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
# File 'lib/ruby_archive/patch/file.rb', line 11

def in_archive? path
  return false if File.ruby_archive_original_exist?(path)
  sp = path.split('!')
  return sp if sp.size == 2
  raise ArgumentError, "only one archive deep supported (for now)" if sp.size > 2
  return false
end

.open(path, mode = 'r', perm = 0666, &block) ⇒ Object

Open a file, either from the filesystem or from within an archive. If the given path exists on the filesystem, it will be opened from there. Otherwise the path will be split by delimiter ! and checked again.

TODO: ‘perm’ is currently ignored by anything sent to an archive, due to incompatibility with rubyzip.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/ruby_archive/patch/file.rb', line 172

def open path,mode='r',perm=0666,&block # 0666 from io.c in MRI
  if File.ruby_archive_original_exist?(path)
    return File.ruby_archive_original_open(path,mode,perm,&block)
  end
  location_info = File.in_archive?(path)
  if location_info == false
    return File.ruby_archive_original_open(path,mode,perm,&block)
  else
    f = RubyArchive.get(location_info[0]).file.open(location_info[1],mode)
    return f if block.nil?
    begin
      return yield(f)
    ensure
      f.close
    end
  end
end

.read(fileName) ⇒ Object

singleton method read does not exist in 1.6.x



192
193
194
195
196
197
198
199
# File 'lib/ruby_archive/patch/file.rb', line 192

def read name,length=nil,offset=nil
  ret = nil
  self.open(name) do |f|
    f.seek(offset) unless offset.nil?
    ret = f.read(length)
  end
  ret
end