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