moped-gridfs 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +90 -0
- data/Rakefile +6 -0
- data/gemfiles/moped15.gemfile +4 -0
- data/lib/moped/gridfs.rb +2 -0
- data/lib/moped/gridfs/access_modes.rb +48 -0
- data/lib/moped/gridfs/bucket.rb +66 -0
- data/lib/moped/gridfs/bucketable.rb +33 -0
- data/lib/moped/gridfs/buckets.rb +37 -0
- data/lib/moped/gridfs/extensions/session.rb +21 -0
- data/lib/moped/gridfs/file.rb +233 -0
- data/lib/moped/gridfs/files.rb +29 -0
- data/lib/moped/gridfs/inspectable.rb +15 -0
- data/lib/moped/gridfs/version.rb +5 -0
- data/moped-gridfs.gemspec +26 -0
- data/perf/compare.rb +143 -0
- data/perf/perf_helper.rb +33 -0
- data/spec/lib/moped/gridfs/bucket_spec.rb +119 -0
- data/spec/lib/moped/gridfs/buckets_spec.rb +45 -0
- data/spec/lib/moped/gridfs/file_spec.rb +83 -0
- data/spec/lib/moped/gridfs/getters.rb +78 -0
- data/spec/lib/moped/gridfs/read.rb +106 -0
- data/spec/lib/moped/gridfs/setters.rb +113 -0
- data/spec/lib/moped/gridfs/write.rb +152 -0
- data/spec/spec_helper.rb +33 -0
- metadata +152 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require "moped/gridfs/bucketable"
|
2
|
+
require "moped/gridfs/file"
|
3
|
+
|
4
|
+
module Moped
|
5
|
+
module GridFS
|
6
|
+
class Files
|
7
|
+
include Enumerable
|
8
|
+
include Bucketable
|
9
|
+
|
10
|
+
attr_reader :bucket
|
11
|
+
|
12
|
+
def initialize(bucket)
|
13
|
+
@bucket = bucket
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](id)
|
17
|
+
bucket.open(id, 'r')
|
18
|
+
end
|
19
|
+
|
20
|
+
def count
|
21
|
+
files_collection.find.count
|
22
|
+
end
|
23
|
+
|
24
|
+
def each(&block)
|
25
|
+
files_collection.find.each { |document| yield(self[document['_id']]) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Moped
|
2
|
+
module GridFS
|
3
|
+
module Inspectable
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def build_inspect_string(hash)
|
8
|
+
memaddr = (__send__(:object_id) << 1).to_s(16)
|
9
|
+
string = "#<#{self.class.name}:#{memaddr}"
|
10
|
+
hash.each { |k, v| string << " #{k}=#{v}" }
|
11
|
+
string << ">"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'moped/gridfs/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "moped-gridfs"
|
7
|
+
spec.version = Moped::GridFS::VERSION
|
8
|
+
spec.authors = ["topac"]
|
9
|
+
spec.email = ["dani.m.mobile@gmail.com"]
|
10
|
+
spec.summary = %q{mongoDB GridFS implementation for Moped}
|
11
|
+
spec.description = %q{mongoDB GridFS implementation for Moped}
|
12
|
+
spec.homepage = "https://www.github.com/topac/moped-gridfs"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency("moped")
|
21
|
+
|
22
|
+
spec.add_development_dependency("bundler", "~> 1.6")
|
23
|
+
spec.add_development_dependency("rake")
|
24
|
+
spec.add_development_dependency("rspec")
|
25
|
+
spec.add_development_dependency("pry")
|
26
|
+
end
|
data/perf/compare.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# Compare with mongo-ruby-driver
|
2
|
+
# $ gem install mongo
|
3
|
+
# $ gem install bson_ext
|
4
|
+
|
5
|
+
$INCLUDE_MONGO = true
|
6
|
+
|
7
|
+
require_relative 'perf_helper'
|
8
|
+
|
9
|
+
def reset
|
10
|
+
purge
|
11
|
+
|
12
|
+
@bucket = moped_session.bucket
|
13
|
+
@grid = Mongo::GridFileSystem.new(mongo_connection)
|
14
|
+
end
|
15
|
+
|
16
|
+
def content(size = 0.5) # 0.5 mb
|
17
|
+
"\xDF\x00\xAB\xFA" * (1024 * 1024 * size)
|
18
|
+
end
|
19
|
+
|
20
|
+
profile("Create an empty file", n: [1000, 5_000]) do |bm, n|
|
21
|
+
reset
|
22
|
+
|
23
|
+
bm.report do
|
24
|
+
n.times { |i| @bucket.open("file#{i}", 'w') }
|
25
|
+
end
|
26
|
+
|
27
|
+
reset
|
28
|
+
|
29
|
+
bm.report do
|
30
|
+
n.times { |i| @grid.open( "file#{i}", 'w').close }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
profile("Create and write a file", n: [10, 100]) do |bm, n|
|
35
|
+
reset
|
36
|
+
|
37
|
+
bm.report do
|
38
|
+
n.times { |i| @bucket.open("file#{i}", 'w').write(content) }
|
39
|
+
end
|
40
|
+
|
41
|
+
reset
|
42
|
+
|
43
|
+
bm.report do
|
44
|
+
n.times do |i|
|
45
|
+
file = @grid.open("file", "w")
|
46
|
+
file.write(content)
|
47
|
+
file.close
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
profile("Sequentially write on a file", n: [1000, 5_000]) do |bm, n|
|
53
|
+
reset
|
54
|
+
file = @bucket.open("file", 'w')
|
55
|
+
|
56
|
+
bm.report do
|
57
|
+
n.times { |i| file.write("foobar") }
|
58
|
+
end
|
59
|
+
|
60
|
+
reset
|
61
|
+
file = @grid.open("file", "w")
|
62
|
+
|
63
|
+
bm.report do
|
64
|
+
(n-1).times { |i| file.write("foobar") }
|
65
|
+
file.close
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
profile("Open a file in r mode", n: [1000, 5_000]) do |bm, n|
|
70
|
+
reset
|
71
|
+
@bucket.open("file", 'w')
|
72
|
+
|
73
|
+
bm.report do
|
74
|
+
n.times { |i| @bucket.open("file", "r") }
|
75
|
+
end
|
76
|
+
|
77
|
+
reset
|
78
|
+
@grid.open("file", 'w').close
|
79
|
+
|
80
|
+
bm.report do
|
81
|
+
n.times { |i| @grid.open("file", "r") }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
profile("Open a file in w mode", n: [1000, 5_000]) do |bm, n|
|
86
|
+
reset
|
87
|
+
|
88
|
+
bm.report do
|
89
|
+
n.times { |i| @bucket.open("file", "w") }
|
90
|
+
end
|
91
|
+
|
92
|
+
reset
|
93
|
+
|
94
|
+
bm.report do
|
95
|
+
n.times { |i| @grid.open("file", "w") }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_sample_file(klass)
|
100
|
+
file = klass.open("file", 'w')
|
101
|
+
file.write(content)
|
102
|
+
file.close if file.respond_to?(:close)
|
103
|
+
|
104
|
+
klass.open("file", "r")
|
105
|
+
end
|
106
|
+
|
107
|
+
profile("Read a whole file from the beginning", n: 100) do |bm, n|
|
108
|
+
reset
|
109
|
+
file = write_sample_file(@bucket)
|
110
|
+
|
111
|
+
bm.report do
|
112
|
+
n.times { |i| file.seek(0); file.read }
|
113
|
+
end
|
114
|
+
|
115
|
+
reset
|
116
|
+
file = write_sample_file(@grid)
|
117
|
+
|
118
|
+
bm.report do
|
119
|
+
n.times { |i| file.seek(0); file.read }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
profile("Read 100 bytes a time till the end", n: [10, 100]) do |bm, n|
|
124
|
+
reset
|
125
|
+
file = write_sample_file(@bucket)
|
126
|
+
|
127
|
+
bm.report do
|
128
|
+
n.times do |i|
|
129
|
+
file.seek(0)
|
130
|
+
while !file.eof?; file.read(100); end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
reset
|
135
|
+
file = write_sample_file(@grid)
|
136
|
+
|
137
|
+
bm.report do
|
138
|
+
n.times do |i|
|
139
|
+
file.seek(0)
|
140
|
+
while !file.eof?; file.read(100); end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/perf/perf_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../spec/spec_helper'
|
3
|
+
|
4
|
+
if $INCLUDE_MONGO
|
5
|
+
require 'mongo'
|
6
|
+
|
7
|
+
def mongo_connection
|
8
|
+
client = Mongo::MongoClient.new(ENV["MOPED-GRIDFS_SPEC_HOST"], ENV["MOPED-GRIDFS_SPEC_PORT"])
|
9
|
+
client[ENV["MOPED-GRIDFS_SPEC_DB"]]
|
10
|
+
end
|
11
|
+
|
12
|
+
module Mongo
|
13
|
+
class GridIO
|
14
|
+
def warn(*args); end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def purge
|
20
|
+
drop_all_collections
|
21
|
+
end
|
22
|
+
|
23
|
+
def profile(message, options = {})
|
24
|
+
ary = [options[:n] || 1].flatten
|
25
|
+
|
26
|
+
ary.each do |n|
|
27
|
+
puts message+" (#{n} times)"
|
28
|
+
|
29
|
+
Benchmark.bm do |bm|
|
30
|
+
yield(bm, n)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'moped/gridfs/bucket'
|
3
|
+
|
4
|
+
describe Moped::GridFS::Bucket do
|
5
|
+
|
6
|
+
let(:session) { moped_session }
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
|
10
|
+
context 'when the given name is empty' do
|
11
|
+
|
12
|
+
it 'raises an error' do
|
13
|
+
expect { described_class.new(session, '') }.to raise_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when the given name is nil' do
|
18
|
+
|
19
|
+
it 'raises an error' do
|
20
|
+
expect { described_class.new(session, nil) }.to raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when the given name is not empty nor nil' do
|
25
|
+
|
26
|
+
it 'does not raise any error' do
|
27
|
+
expect { described_class.new(session, 'foo') }.not_to raise_error
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
let(:subject) { described_class.new(session, 'bar') }
|
33
|
+
|
34
|
+
describe '#files' do
|
35
|
+
|
36
|
+
it 'returns an enumerable' do
|
37
|
+
expect(subject.files).to respond_to(:each)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#drop' do
|
42
|
+
|
43
|
+
before do
|
44
|
+
subject.files_collection.insert(foo: 'bar')
|
45
|
+
subject.chunks_collection.insert(foo: 'bar')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'drops the two collections' do
|
49
|
+
subject.drop
|
50
|
+
|
51
|
+
expect(subject.files_collection.find.count).to eq(0)
|
52
|
+
expect(subject.chunks_collection.find.count).to eq(0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#delete' do
|
57
|
+
|
58
|
+
context 'when a file is missing' do
|
59
|
+
|
60
|
+
it 'returns nil' do
|
61
|
+
expect(subject.delete("file")).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when a file exists' do
|
66
|
+
|
67
|
+
before do
|
68
|
+
subject.open("file", "w").write("buffer")
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'not to returns nil' do
|
72
|
+
expect(subject.delete("file")).not_to be_nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'deletes the file' do
|
76
|
+
subject.delete("file")
|
77
|
+
expect { subject.open("foo", "r") }.to raise_error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#open' do
|
83
|
+
|
84
|
+
context 'when the mode is w' do
|
85
|
+
|
86
|
+
it 'returns a writable file' do
|
87
|
+
expect(subject.open("foo", "w")).to be_writable
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when the mode is r' do
|
92
|
+
|
93
|
+
context 'and the file is missing' do
|
94
|
+
|
95
|
+
it 'raises an error' do
|
96
|
+
expect { subject.open("foo", "r") }.to raise_error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'and the file exists' do
|
101
|
+
|
102
|
+
before do
|
103
|
+
subject.open("bar", "w").write("buffer")
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns a readable file' do
|
107
|
+
expect(subject.open("bar", "r")).to be_readable
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when the mode is a' do
|
113
|
+
|
114
|
+
it 'returns a writable file' do
|
115
|
+
expect(subject.open("foo", "a")).to be_writable
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'moped/gridfs/buckets'
|
3
|
+
|
4
|
+
describe Moped::GridFS::Buckets do
|
5
|
+
|
6
|
+
let(:session) { moped_session }
|
7
|
+
|
8
|
+
let(:subject) { described_class.new(session) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
Moped::GridFS::Bucket.new(session, "bar").open("file", "w")
|
12
|
+
Moped::GridFS::Bucket.new(session, "foo").open("file", "w")
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#names' do
|
16
|
+
|
17
|
+
it 'returns the bucket names' do
|
18
|
+
expect(subject.names.sort).to eq(%w[bar foo])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#count' do
|
23
|
+
|
24
|
+
it 'returns the buckets count' do
|
25
|
+
expect(subject.count).to eq(2)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#[]' do
|
30
|
+
|
31
|
+
it 'returns a bucket with the given name' do
|
32
|
+
bucket = subject['test']
|
33
|
+
expect(bucket.name).to eq('test')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#each' do
|
38
|
+
|
39
|
+
it 'iterates over the buckets' do
|
40
|
+
names = []
|
41
|
+
subject.each { |bucket| names << bucket.name }
|
42
|
+
expect(names.sort).to eq(%w[bar foo])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'moped/gridfs/file'
|
3
|
+
|
4
|
+
require_relative 'write'
|
5
|
+
require_relative 'read'
|
6
|
+
require_relative 'getters'
|
7
|
+
require_relative 'setters'
|
8
|
+
|
9
|
+
describe Moped::GridFS::File do
|
10
|
+
|
11
|
+
$chunk_size = 5
|
12
|
+
|
13
|
+
before do
|
14
|
+
described_class.any_instance.stub(:default_chunk_size).and_return($chunk_size)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:session) { moped_session }
|
18
|
+
|
19
|
+
let(:bucket) { session.bucket }
|
20
|
+
|
21
|
+
context 'when mode is r' do
|
22
|
+
|
23
|
+
let(:access_mode) { 'r' }
|
24
|
+
|
25
|
+
include_examples :read, :getters
|
26
|
+
|
27
|
+
let(:file) do
|
28
|
+
bucket.open("file", "w").write("foobar")
|
29
|
+
bucket.open("file", access_mode)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'cannot be written' do
|
33
|
+
expect { file.write("foo") }.to raise_error
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not have setters' do
|
37
|
+
%w[content_type metadata aliases filename upload_date].each do |name|
|
38
|
+
expect(file).not_to respond_to(:"#{name}=")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when mode is r+' do
|
44
|
+
|
45
|
+
let(:access_mode) { 'r+' }
|
46
|
+
|
47
|
+
include_examples :read, :getters, :setters
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when mode is w' do
|
51
|
+
|
52
|
+
let(:access_mode) { 'w' }
|
53
|
+
|
54
|
+
include_examples :write, :getters, :setters
|
55
|
+
|
56
|
+
it 'cannot be readed' do
|
57
|
+
bucket.open("file", "w").write("foobar")
|
58
|
+
file = bucket.open("file", access_mode)
|
59
|
+
expect { file.read }.to raise_error
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when mode is w+' do
|
64
|
+
|
65
|
+
let(:access_mode) { 'w+' }
|
66
|
+
|
67
|
+
include_examples :write, :getters, :setters
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when mode is a' do
|
71
|
+
|
72
|
+
let(:access_mode) { 'a' }
|
73
|
+
|
74
|
+
include_examples :write, :getters, :setters
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when mode is a+' do
|
78
|
+
|
79
|
+
let(:access_mode) { 'a+' }
|
80
|
+
|
81
|
+
include_examples :write, :read, :getters, :setters
|
82
|
+
end
|
83
|
+
end
|