em-files 0.1.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem "eventmachine", ">= 0.12.10"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.5.2"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ eventmachine (0.12.10)
5
+ git (1.2.5)
6
+ jeweler (1.5.2)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler (~> 1.0.0)
17
+ eventmachine (>= 0.12.10)
18
+ jeweler (~> 1.5.2)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Martin Kozák (martinkozak@martinkozak.net)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ EventMachine Files
2
+ ==================
3
+
4
+ **em-files** solve problem of blocking disk IO while operating with
5
+ large files. Use [EventMachine][4] for multiplexing reads and writes
6
+ to small blocks performed in standalone EM ticks. They speed down the
7
+ file IO operations of sure, but allow running other tasks with them
8
+ simultaneously (from EM point of view).
9
+
10
+ API is similar to classic Ruby file IO represented by [File][1] class.
11
+ See an example:
12
+
13
+ require "em-files"
14
+ EM::run do
15
+ EM::File::open("some_file.txt", "r") do |io|
16
+ io.read(1024) do |data| # writing works by very similar way, of sure
17
+ puts data
18
+ io.close() # it's necessary to do it in block too, because reading is evented
19
+ end
20
+ end
21
+ end
22
+
23
+ Support of Ruby API is limited to `#open`, `#close`, `#read` and `#write`
24
+ methods only, so for special operations use simply:
25
+
26
+ EM::File::open("some_file.txt", "r") do |io|
27
+ io.native # returns native Ruby File class object
28
+ end
29
+
30
+
31
+ Contributing
32
+ ------------
33
+
34
+ 1. Fork it.
35
+ 2. Create a branch (`git checkout -b 20101220-my-change`).
36
+ 3. Commit your changes (`git commit -am "Added something"`).
37
+ 4. Push to the branch (`git push origin 20101220-my-change`).
38
+ 5. Create an [Issue][2] with a link to your branch.
39
+ 6. Enjoy a refreshing Diet Coke and wait.
40
+
41
+ Copyright
42
+ ---------
43
+
44
+ Copyright © 2011 [Martin Kozák][3]. See `LICENSE.txt` for
45
+ further details.
46
+
47
+ [1]: http://www.ruby-doc.org/core/classes/File.html
48
+ [2]: http://github.com/martinkozak/em-sequence/issues
49
+ [3]: http://www.martinkozak.net/
50
+ [4]: http://rubyeventmachine.com/
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "em-files"
17
+ gem.homepage = "https://github.com/martinkozak/em-files"
18
+ gem.license = "MIT"
19
+ gem.summary = "Sequenced file reader and writer through EventMachine. Solves problem of blocking disk IO while operating with large files."
20
+ gem.email = "martinkozak@martinkozak.net"
21
+ gem.authors = ["Martin Kozák"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/rdoctask'
30
+ Rake::RDocTask.new do |rdoc|
31
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
32
+
33
+ rdoc.rdoc_dir = 'rdoc'
34
+ rdoc.title = "qrpc #{version}"
35
+ rdoc.rdoc_files.include('README*')
36
+ rdoc.rdoc_files.include('lib/**/*.rb')
37
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/em-file.gemspec ADDED
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{em-file}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Martin Kozák"]
12
+ s.date = %q{2011-02-27}
13
+ s.email = %q{martinkozak@martinkozak.net}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE.txt",
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/em-files.rb",
27
+ "test.rb"
28
+ ]
29
+ s.homepage = %q{https://github.com/martinkozak/em-file}
30
+ s.licenses = ["MIT"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.5.3}
33
+ s.summary = %q{Sequenced file reader and writer through EventMachine. Solves problem of blocking disk IO while operating with large files.}
34
+
35
+ if s.respond_to? :specification_version then
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
39
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.10"])
40
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
41
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
42
+ else
43
+ s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
44
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
45
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
46
+ end
47
+ else
48
+ s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
49
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
51
+ end
52
+ end
53
+
data/lib/em-files.rb ADDED
@@ -0,0 +1,193 @@
1
+ # encoding: utf-8
2
+ # (c) 2011 Martin Kozák (martinkozak@martinkozak.net)
3
+
4
+ require "eventmachine"
5
+
6
+ ##
7
+ # Main EventMachine module.
8
+ # @see http://rubyeventmachine.com/
9
+ #
10
+
11
+ module EM
12
+
13
+ ##
14
+ # Sequenced file reader and writer.
15
+ #
16
+
17
+ class File
18
+
19
+ ##
20
+ # Opens the file.
21
+ #
22
+ # In opposite to appropriate Ruby method, "block syntax" is only
23
+ # syntactic sugar, file isn't closed after return from block
24
+ # because processing is asynchronous so it doesn't know when
25
+ # is convenient to close the file.
26
+ #
27
+ # @param [String] filepath path to file
28
+ # @param [String] mode file access mode (see equivalent Ruby method)
29
+ # @param [Integer] rwsize size of block operated during one tick
30
+ # @param [Proc] block syntactic sugar for wrapping File access object
31
+ # @return [File] file access object
32
+ #
33
+
34
+ def self.open(filepath, mode = "r", rwsize = 65536, &block) # 64 kilobytes
35
+ file = self::new(filepath, mode, rwsize)
36
+ if not block.nil?
37
+ block.call(file)
38
+ end
39
+
40
+ return file
41
+ end
42
+
43
+ ##
44
+ # Reads wholoe content of the file.
45
+ #
46
+ # @param [String] filepath path to file
47
+ # @param [Integer] rwsize size of block operated during one tick
48
+ # @param [Proc] block block for giving back the result
49
+ #
50
+
51
+
52
+ def self.read(filepath, rwsize = 65536, &block)
53
+ self::open(filepath, "r", rwsize) do |io|
54
+ io.read do |out|
55
+ block.call(out)
56
+ io.close()
57
+ end
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Writes data to file and closes it.
63
+ #
64
+ # @param [String] filepath path to file
65
+ # @param [String] data data for write
66
+ # @param [Integer] rwsize size of block operated during one tick
67
+ # @param [Proc] block block called when writing is finished with
68
+ # written bytes size count as parameter
69
+ #
70
+
71
+ def self.write(filepath, data = "", rwsize = 65536, &block)
72
+ self::open(filepath, "w", rwsize) do |io|
73
+ io.write(data) do |length|
74
+ block.call(length)
75
+ io.close()
76
+ end
77
+ end
78
+ end
79
+
80
+ ###
81
+
82
+ ##
83
+ # Holds file object.
84
+ # @return [::File]
85
+ #
86
+
87
+ attr_accessor :native
88
+ @native
89
+
90
+ ##
91
+ # Indicates block size for operate with in one tick.
92
+ # @return [Integer]
93
+ #
94
+
95
+ attr_accessor :rw_len
96
+ @rw_len
97
+
98
+ ##
99
+ # Constructor.
100
+ #
101
+ # @param [String] filepath path to file
102
+ # @param [String] mode file access mode (see equivalent Ruby method)
103
+ # @param [Integer] rwsize size of block operated during one tick
104
+ #
105
+
106
+ def initialize(filepath, mode = "r", rwsize = 65536)
107
+ @native = ::File::open(filepath, mode)
108
+ @rw_len = rwsize
109
+ end
110
+
111
+ ##
112
+ # Reads data from file.
113
+ #
114
+ # @overload read(length, &block)
115
+ # Reads specified amount of data from file.
116
+ # @param [Integer] length length for read from file
117
+ # @param [Proc] block callback for returning the result
118
+ # @overload read(&block)
119
+ # Reads whole content of file.
120
+ # @param [Proc] block callback for returning the result
121
+ #
122
+
123
+ def read(length = nil, &block)
124
+ buffer = ""
125
+
126
+ worker = Proc::new do
127
+
128
+ # Sets length for read
129
+ if not length.nil?
130
+ rlen = length - buffer.length
131
+ if rlen > @rw_len
132
+ rlen = @rw_len
133
+ end
134
+ else
135
+ rlen = @rw_len
136
+ end
137
+
138
+ # Reads
139
+ buffer << @native.read(rlen)
140
+
141
+ # Returns or continues work
142
+ if @native.eof? or (buffer.length == length)
143
+ if not block.nil?
144
+ block.call(buffer) # returns result
145
+ end
146
+ else
147
+ EM::next_tick { worker.call() } # continues work
148
+ end
149
+
150
+ end
151
+
152
+ worker.call()
153
+ end
154
+
155
+ ##
156
+ # Writes data to file.
157
+ #
158
+ # @param [String] data data for write
159
+ # @param [Proc] block callback called when finish and for giving
160
+ # back the length of written data
161
+ #
162
+
163
+ def write(data, &block)
164
+ written = 0
165
+
166
+ worker = Proc::new do
167
+
168
+ # Writes
169
+ written += @native.write(data[written...(written + @rw_len)])
170
+
171
+ # Returns or continues work
172
+ if written >= data.bytesize
173
+ if not block.nil?
174
+ block.call(written) # returns result
175
+ end
176
+ else
177
+ EM::next_tick { worker.call() } # continues work
178
+ end
179
+
180
+ end
181
+
182
+ worker.call()
183
+ end
184
+
185
+ ##
186
+ # Closes the file.
187
+ #
188
+
189
+ def close
190
+ @native.close
191
+ end
192
+ end
193
+ end
data/test.rb ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+ # (c) 2011 Martin Kozák
4
+
5
+ $:.push("./lib")
6
+ require 'em-files'
7
+ require "riot"
8
+
9
+ test = Array::new(5)
10
+
11
+ EM::run do
12
+
13
+ # Test 1
14
+ test[0] = EM::File::open("./~test1", "w")
15
+ test[0].close()
16
+
17
+ # Test 2
18
+ EM::File::open("./~test1", "w") do |io|
19
+ io.write("x" * 300000) do |len|
20
+ test[1] = len
21
+ io.close()
22
+ end
23
+ end
24
+
25
+ EM::add_timer(1) do
26
+ # Test 3
27
+ EM::File::open("./~test1", "r") do |io|
28
+ io.read do |data|
29
+ test[2] = data
30
+ io.close()
31
+ end
32
+ end
33
+ end
34
+
35
+ # Test 4
36
+ EM::File::write("./~test2", "x" * 300000) do |len|
37
+ test[3] = len
38
+ end
39
+
40
+ EM::add_timer(1) do
41
+ # Test 5
42
+ EM::File::read("./~test2") do |data|
43
+ test[4] = data
44
+ end
45
+ end
46
+
47
+ EM::add_timer(2) do
48
+ EM::stop
49
+ end
50
+
51
+ end
52
+
53
+
54
+ context "EM::Files (instance methods)" do
55
+ setup { test }
56
+
57
+ asserts("#open returns EM::File object") do
58
+ topic[0].kind_of? EM::File
59
+ end
60
+ asserts("file size produced by #write is equivalent to reported written data length") do
61
+ topic[1] == File.size?("./~test1")
62
+ end
63
+ asserts("file content produced by #write is correct and #read works well") do
64
+ topic[2] == "x" * 300000
65
+ end
66
+
67
+ teardown do
68
+ File.unlink("./~test1")
69
+ end
70
+ end
71
+
72
+ context "EM::Files (class methods)" do
73
+ setup { test }
74
+
75
+ asserts("file size produced by #write is equivalent to reported written data length") do
76
+ topic[1] == File.size?("./~test2")
77
+ end
78
+ asserts("file content produced by #write is correct and #read works well") do
79
+ topic[2] == "x" * 300000
80
+ end
81
+
82
+ teardown do
83
+ File.unlink("./~test2")
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-files
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - "Martin Koz\xC3\xA1k"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-27 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: eventmachine
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.12.10
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: jeweler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.5.2
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ description:
50
+ email: martinkozak@martinkozak.net
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - LICENSE.txt
57
+ - README.md
58
+ files:
59
+ - .document
60
+ - Gemfile
61
+ - Gemfile.lock
62
+ - LICENSE.txt
63
+ - README.md
64
+ - Rakefile
65
+ - VERSION
66
+ - em-file.gemspec
67
+ - lib/em-files.rb
68
+ - test.rb
69
+ has_rdoc: true
70
+ homepage: https://github.com/martinkozak/em-files
71
+ licenses:
72
+ - MIT
73
+ post_install_message:
74
+ rdoc_options: []
75
+
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 2457539952407610451
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ requirements: []
94
+
95
+ rubyforge_project:
96
+ rubygems_version: 1.5.3
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: Sequenced file reader and writer through EventMachine. Solves problem of blocking disk IO while operating with large files.
100
+ test_files: []
101
+