Class: EM::File

Inherits:
Object
  • Object
show all
Defined in:
lib/em-files.rb

Overview

Sequenced file reader and writer.

Constant Summary collapse

RWSIZE =

Holds the default size of block operated during one tick.

65536

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filepath, mode = "r", rwsize = self.class::RWSIZE) ⇒ File

Constructor. If IO object is given instead of filepath, uses it as native one and mode argument is ignored.

Parameters:

  • filepath (String, IO, StringIO)

    path to file or IO object

  • mode (String) (defaults to: "r")

    file access mode (see equivalent Ruby method)

  • rwsize (Integer) (defaults to: self.class::RWSIZE)

    size of block operated during one tick



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/em-files.rb', line 137

def initialize(filepath, mode = "r", rwsize = self.class::RWSIZE)
    @mode = mode
    @rw_len = rwsize
    
    rwsize = self::RWSIZE if rwsize.nil?
    
    # If filepath is directly IO, uses it
    if filepath.io?
        @native = filepath
    else
        @native = ::File::open(filepath, mode)
    end
    
end

Instance Attribute Details

#modeString (readonly)

Holds mode of the object.

Returns:

  • (String)


125
126
127
# File 'lib/em-files.rb', line 125

def mode
  @mode
end

#nativeIO

Holds file object.

Returns:

  • (IO)


109
110
111
# File 'lib/em-files.rb', line 109

def native
  @native
end

#rw_lenInteger

Indicates block size for operate with in one tick.

Returns:

  • (Integer)


117
118
119
# File 'lib/em-files.rb', line 117

def rw_len
  @rw_len
end

Class Method Details

.open(filepath, mode = "r", rwsize = self::RWSIZE, &block) ⇒ File

Opens the file.

In opposite to appropriate Ruby method, “block syntax” is only syntactic sugar, file isn’t closed after return from block because processing is asynchronous so it doesn’t know when is convenient to close the file.

Parameters:

  • filepath (String, IO, StringIO)

    path to file or IO object

  • mode (String) (defaults to: "r")

    file access mode (see equivalent Ruby method)

  • rwsize (Integer) (defaults to: self::RWSIZE)

    size of block operated during one tick

  • block (Proc)

    syntactic sugar for wrapping File access object

Returns:

  • (File)

    file access object



44
45
46
47
48
49
50
51
52
53
# File 'lib/em-files.rb', line 44

def self.open(filepath, mode = "r", rwsize = self::RWSIZE, &block)   # 64 kilobytes
    rwsize = self::RWSIZE if rwsize.nil?
    
    file = self::new(filepath, mode, rwsize)
    if not block.nil?
        block.call(file)
    end
    
    return file
end

.read(filepath, rwsize = self::RWSIZE, filter = nil, &block) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/em-files.rb', line 68

def self.read(filepath, rwsize = self::RWSIZE, filter = nil, &block)
    rwsize = self::RWSIZE if rwsize.nil?
    self::open(filepath, "rb", rwsize) do |io|
        io.read(nil, filter) do |out|
            io.close()
            block.call(out)
        end
    end
end

.write(filepath, data = "", rwsize = self::RWSIZE, filter = nil, &block) ⇒ Object

Writes data to file and closes it. Writes them in binary mode. If IO object is given instead of filepath, uses it as native one and mode argument is ignored.

Parameters:

  • filepath (String, IO, StringIO)

    path to file or IO object

  • data (String) (defaults to: "")

    data for write

  • rwsize (Integer) (defaults to: self::RWSIZE)

    size of block operated during one tick

  • filter (Proc) (defaults to: nil)

    filter which for preprocessing each written chunk

  • block (Proc)

    block called when writing is finished with written bytes size count as parameter



92
93
94
95
96
97
98
99
100
# File 'lib/em-files.rb', line 92

def self.write(filepath, data = "", rwsize = self::RWSIZE, filter = nil, &block)
    rwsize = self::RWSIZE if rwsize.nil?
    self::open(filepath, "wb", rwsize) do |io|
        io.write(data, filter) do |length|
            io.close()
            block.call(length)
        end
    end
end

Instance Method Details

#closeObject

Closes the file.



295
296
297
# File 'lib/em-files.rb', line 295

def close
    @native.close
end

#read(length, &block) ⇒ Object #read(&block) ⇒ Object

Reads data from file.

It will reopen the file if EBADF: Bad file descriptor of File class IO object will occur.

Overloads:

  • #read(length, &block) ⇒ Object

    Reads specified amount of data from file.

    Parameters:

    • length (Integer)

      length for read from file

    • filter (Proc)

      filter which for postprocessing each read chunk

    • block (Proc)

      callback for returning the result

  • #read(&block) ⇒ Object

    Reads whole content of file.

    Parameters:

    • filter (Proc)

      filter which for processing each block

    • block (Proc)

      callback for returning the result



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/em-files.rb', line 170

def read(length = nil, filter = nil, &block)
    buffer = ""
    pos = 0
    
    # Arguments
    if length.kind_of? Proc
        filter = length
    end
    
    
    worker = Proc::new do
    
        # Sets length for read
        if not length.nil?
            rlen = length - buffer.length
            if rlen > @rw_len
                rlen = @rw_len
            end
        else
            rlen = @rw_len
        end
        
        # Reads
        begin
            chunk = @native.read(rlen)
            if not filter.nil?
                chunk = filter.call(chunk)
            end
            buffer << chunk
        rescue Errno::EBADF
            if @native.kind_of? ::File
                self.reopen!
                @native.seek(pos)
                redo
            else
                raise
            end
        end
        
        pos = @native.pos
        
        # Returns or continues work
        if @native.eof? or (buffer.length == length)
            if not block.nil?
                block.call(buffer)              # returns result
            end
        else
            EM::next_tick { worker.call() }     # continues work
        end
        
    end
    
    worker.call()
end

#reopen!Object

Reopens the file with the original mode.



229
230
231
# File 'lib/em-files.rb', line 229

def reopen!
    @native = ::File.open(@native.path, @mode)
end

#write(data, filter = nil, &block) ⇒ Object

Writes data to file. Supports writing both strings or copying from another IO object.

It will reopen the file if EBADF: Bad file descriptor of File class IO object will occur on File object.

Parameters:

  • data (String, IO, StringIO)

    data for write or IO object

  • filter (Proc) (defaults to: nil)

    filter which for preprocessing each written chunk

  • block (Proc)

    callback called when finish and for giving back the length of written data



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/em-files.rb', line 247

def write(data, filter = nil, &block)
    pos = 0
    
    if data.io?
        io = data
    else
        io = StringIO::new(data)
    end
    
    worker = Proc::new do
    
        # Writes
        begin
            chunk = io.read(@rw_len)
            if not filter.nil?
                chunk = filter.call(chunk)
            end
            @native.write(chunk)
        rescue Errno::EBADF
            if @native.kind_of? File
                self.reopen!
                @native.seek(pos)
                redo
            else
                raise
            end
        end
    
        pos = @native.pos
        
        # Returns or continues work
        if io.eof?
            if not block.nil?
                block.call(pos)                 # returns result
            end
        else
            EM::next_tick { worker.call() }     # continues work
        end
        
    end
    
    worker.call()
end