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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9a366fb5dfef7a6f12ee74c4e5e4d1bb39d810a0
|
4
|
+
data.tar.gz: b20b96931842cfb6a35ee986a1fa7e327d9882f2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7921ced6e4e20baae692c7a7255e1f4dc1ab9afa8cf743660fe4d4bc1968ebf4184e191c878026e8f0c88bfe40a5c80ceee07165b3e40a191631ca667c021318
|
7
|
+
data.tar.gz: afd679c13f190f0e4a40ce2a533cdf5563e58b4580e20a24f2ede733cc59025dd70fafad3cbf1f359eaa8ffdb9bae92f57c405d601f40700a6eea8a81d018828
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.DS_Store
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
moped-gridfs
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 topac
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# moped-gridfs
|
2
|
+
|
3
|
+
Add [GridFS support](http://docs.mongodb.org/manual/core/gridfs) to the [Moped driver](https://github.com/mongoid/moped).
|
4
|
+
|
5
|
+
Moped is a fast MongoDB driver for Ruby,
|
6
|
+
but it does not implement the GridFS specifications
|
7
|
+
(while [mongo-ruby-driver](https://github.com/mongodb/mongo-ruby-driver) does).
|
8
|
+
|
9
|
+
## Bucket
|
10
|
+
|
11
|
+
GridFS places the collections in a common bucket by prefixing each with the bucket name.
|
12
|
+
By default, GridFS uses two collections with names prefixed by "fs" bucket: _fs.files_ and _fs.chunks_.
|
13
|
+
|
14
|
+
You can choose a different bucket name than "fs", and create multiple buckets in a single database.
|
15
|
+
Access the default bucket (named "fs") this way:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
require 'moped'
|
19
|
+
require 'moped/gridfs'
|
20
|
+
|
21
|
+
session = Moped::Session.new(["127.0.0.1:27017"])
|
22
|
+
session.use("test")
|
23
|
+
bucket = session.bucket #<Moped::GridFS::Bucket:7ffbdbd4e160 name=fs>
|
24
|
+
```
|
25
|
+
or
|
26
|
+
```ruby
|
27
|
+
bucket = Moped::GridFS::Bucket.new(session) #<Moped::GridFS::Bucket:7fc06db72c00 name=fs>
|
28
|
+
```
|
29
|
+
|
30
|
+
A list of all the buckets can be retrieved with `Session#buckets`.
|
31
|
+
For example, you can access the _photos_ bucket with `session.buckets['photos']`.
|
32
|
+
|
33
|
+
To open a file call `Bucket#open`, with the filename (or the _id) and the open mode.
|
34
|
+
A more generic selector can be also given instead of the filename.
|
35
|
+
|
36
|
+
## File
|
37
|
+
|
38
|
+
The GridFS::File class exposes an API similar to the ruby File class.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
file = bucket.open("myfile", "w+") #<Moped::GridFS::File:7f88599a58b0 bucket=fs _id=539c532ddb13a973ed000001 mode=w+ filename=myfile length=0>
|
42
|
+
file.write("foobar") # 6
|
43
|
+
file.seek(3) # 3
|
44
|
+
file.read # "bar"
|
45
|
+
```
|
46
|
+
|
47
|
+
All the [open modes](http://www.ruby-doc.org/core-2.1.2/IO.html#method-c-new-label-IO+Open+Mode) are supported:
|
48
|
+
r, r+, w, w+, a and a+.
|
49
|
+
|
50
|
+
GridFS::File attributes are: _id, length, chunk_size, filename, content_type, md5, aliases, metadata and uploadDate.
|
51
|
+
Some of them may be changed if the file is opened in write/append mode.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
file.content_type # "application/octet-stream"
|
55
|
+
file.md5 # "3858f62230ac3c915f300c664312c63f"
|
56
|
+
file.filename = "test"
|
57
|
+
file.filename # "test"
|
58
|
+
```
|
59
|
+
|
60
|
+
## Thread safe?
|
61
|
+
It depends on what you're doing.
|
62
|
+
You may face race conditions if many threads are writing on the same file a buffer that have to be splitted onto multiple chunks. This is due to how the GridFS specs have been designed: [read this](https://jira.mongodb.org/browse/NODE-157).
|
63
|
+
|
64
|
+
## Performance
|
65
|
+
|
66
|
+
Are pretty much the same of mongo-ruby-driver (run the script perf/compare.rb).
|
67
|
+
|
68
|
+
|
69
|
+
## Installation
|
70
|
+
|
71
|
+
Add this line to your application's Gemfile:
|
72
|
+
|
73
|
+
gem 'moped-gridfs'
|
74
|
+
|
75
|
+
And then execute:
|
76
|
+
|
77
|
+
$ bundle
|
78
|
+
|
79
|
+
Or install it yourself as:
|
80
|
+
|
81
|
+
$ gem install moped-gridfs
|
82
|
+
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/moped/gridfs.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Moped
|
2
|
+
module GridFS
|
3
|
+
module AccessModes
|
4
|
+
|
5
|
+
attr_reader :mode
|
6
|
+
|
7
|
+
ACCESS_MODES = %w[r r+ w w+ a a+]
|
8
|
+
|
9
|
+
def readable?
|
10
|
+
mode =~ /r|\+/
|
11
|
+
end
|
12
|
+
|
13
|
+
def writable?
|
14
|
+
mode =~ /w|\+|a/
|
15
|
+
end
|
16
|
+
|
17
|
+
def append?
|
18
|
+
mode =~ /a/
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_only?
|
22
|
+
mode == 'r'
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_only?
|
26
|
+
mode == 'w' or mode == 'a'
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_write?
|
30
|
+
mode =~ /\+/
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def append_only?
|
36
|
+
mode == 'a'
|
37
|
+
end
|
38
|
+
|
39
|
+
def need_file?
|
40
|
+
mode == 'r' or mode == 'r+'
|
41
|
+
end
|
42
|
+
|
43
|
+
def truncate?
|
44
|
+
mode == 'w' or mode == 'w+'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "moped/gridfs/files"
|
2
|
+
require "moped/gridfs/file"
|
3
|
+
require "moped/gridfs/inspectable"
|
4
|
+
require "moped/gridfs/bucketable"
|
5
|
+
|
6
|
+
module Moped
|
7
|
+
module GridFS
|
8
|
+
class Bucket
|
9
|
+
include Bucketable
|
10
|
+
include Inspectable
|
11
|
+
|
12
|
+
attr_reader :name, :session
|
13
|
+
|
14
|
+
DEFAULT_NAME = 'fs'
|
15
|
+
|
16
|
+
def initialize(session, name = DEFAULT_NAME)
|
17
|
+
@name = name.to_s.strip
|
18
|
+
@session = session
|
19
|
+
|
20
|
+
raise ArgumentError.new("Bucket name cannot be empty") if @name.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def open(selector, mode)
|
24
|
+
ensure_indexes
|
25
|
+
file = File.new(self, mode, selector)
|
26
|
+
block_given? ? yield(file) : file
|
27
|
+
end
|
28
|
+
|
29
|
+
def ensure_indexes
|
30
|
+
@indexes_ensured ||= begin
|
31
|
+
chunks_collection.indexes.create(files_id: 1, n: 1)
|
32
|
+
# Optional index on filename
|
33
|
+
files_collection.indexes.create({filename: 1}, {background: true})
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def files
|
39
|
+
Files.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def md5(file_id)
|
43
|
+
session.command(filemd5: file_id, root: name)['md5']
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(selector)
|
47
|
+
document = files_collection.find(parse_selector(selector)).first
|
48
|
+
return unless document
|
49
|
+
chunks_collection.find(files_id: document['_id']).remove_all
|
50
|
+
files_collection.find(_id: document['_id']).remove_all
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
alias :remove :delete
|
55
|
+
|
56
|
+
def drop
|
57
|
+
[files_collection, chunks_collection].map(&:drop)
|
58
|
+
@indexes_ensured = false
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
build_inspect_string(name: name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Moped
|
2
|
+
module GridFS
|
3
|
+
module Bucketable
|
4
|
+
def files_collection
|
5
|
+
if self.respond_to?(:session)
|
6
|
+
session[:"#{name}.files"]
|
7
|
+
else
|
8
|
+
bucket.files_collection
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def chunks_collection
|
13
|
+
if self.respond_to?(:session)
|
14
|
+
session[:"#{name}.chunks"]
|
15
|
+
else
|
16
|
+
bucket.chunks_collection
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse_selector(selector)
|
23
|
+
if selector.kind_of?(String)
|
24
|
+
{filename: selector}
|
25
|
+
elsif selector.kind_of?(BSON::ObjectId)
|
26
|
+
{_id: selector}
|
27
|
+
else
|
28
|
+
selector
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "moped/gridfs/bucket"
|
2
|
+
|
3
|
+
module Moped
|
4
|
+
module GridFS
|
5
|
+
class Buckets
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :session
|
9
|
+
|
10
|
+
def initialize(session)
|
11
|
+
@session = session
|
12
|
+
end
|
13
|
+
|
14
|
+
def names
|
15
|
+
collections.map { |collection| collection.name.gsub('.files', '') }
|
16
|
+
end
|
17
|
+
|
18
|
+
def count
|
19
|
+
collections.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](name)
|
23
|
+
Bucket.new(session, name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(&block)
|
27
|
+
names.each { |name| yield(self[name]) }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def collections
|
33
|
+
session.collections.select { |collection| collection.name =~ /.+\.files\z/ }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "moped"
|
2
|
+
require "moped/gridfs/bucket"
|
3
|
+
require "moped/gridfs/buckets"
|
4
|
+
|
5
|
+
module Moped
|
6
|
+
module GridFS
|
7
|
+
module Extensions
|
8
|
+
module Session
|
9
|
+
def bucket
|
10
|
+
Bucket.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def buckets
|
14
|
+
Buckets.new(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Moped::Session.__send__(:include, Moped::GridFS::Extensions::Session)
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require "moped/gridfs/inspectable"
|
2
|
+
require "moped/gridfs/bucketable"
|
3
|
+
require "moped/gridfs/access_modes"
|
4
|
+
|
5
|
+
module Moped
|
6
|
+
module GridFS
|
7
|
+
class File
|
8
|
+
include Bucketable
|
9
|
+
include Inspectable
|
10
|
+
include AccessModes
|
11
|
+
|
12
|
+
attr_reader :mode, :attributes, :bucket, :pos
|
13
|
+
|
14
|
+
def initialize(bucket, mode, selector)
|
15
|
+
selector = parse_selector(selector)
|
16
|
+
@mode = mode
|
17
|
+
@bucket = bucket
|
18
|
+
@cached_chunk = nil
|
19
|
+
@pos = 0
|
20
|
+
|
21
|
+
raise ArgumentError.new("Invalid access mode #{mode}") unless ACCESS_MODES.include?(mode)
|
22
|
+
|
23
|
+
document = files_collection.find(selector).first
|
24
|
+
|
25
|
+
raise "No such file" if need_file? and !document
|
26
|
+
|
27
|
+
if document and truncate?
|
28
|
+
chunks_collection.find(files_id: document['_id']).remove_all
|
29
|
+
files_collection.find(_id: document['_id']).remove_all
|
30
|
+
|
31
|
+
@attributes = normalize_attributes(selector)
|
32
|
+
else
|
33
|
+
@attributes = normalize_attributes(document || selector)
|
34
|
+
@attributes.freeze if read_only?
|
35
|
+
end
|
36
|
+
|
37
|
+
define_dynamic_accessors
|
38
|
+
|
39
|
+
file_query.upsert(attributes) if writable?
|
40
|
+
|
41
|
+
@pos = length if append_only?
|
42
|
+
end
|
43
|
+
|
44
|
+
alias :tell :pos
|
45
|
+
|
46
|
+
def pos=(value)
|
47
|
+
check_negative_value(value)
|
48
|
+
|
49
|
+
@pos = (append_only? and value < length) ? length : value
|
50
|
+
end
|
51
|
+
|
52
|
+
alias :seek :pos=
|
53
|
+
|
54
|
+
def rewind
|
55
|
+
self.pos = 0
|
56
|
+
end
|
57
|
+
|
58
|
+
def eof?
|
59
|
+
raise "Not opened for reading" if write_only?
|
60
|
+
|
61
|
+
pos >= length
|
62
|
+
end
|
63
|
+
|
64
|
+
EMPTINESS = ''.force_encoding('BINARY')
|
65
|
+
|
66
|
+
def read(size = length)
|
67
|
+
raise "Not opened for reading" if write_only?
|
68
|
+
|
69
|
+
check_negative_value(size)
|
70
|
+
|
71
|
+
chunk_number = pos / chunk_size
|
72
|
+
chunk_offset = pos % chunk_size
|
73
|
+
|
74
|
+
data = EMPTINESS
|
75
|
+
|
76
|
+
loop do
|
77
|
+
break if data.size >= size
|
78
|
+
break unless read_chunk(chunk_number)
|
79
|
+
buffer = @cached_chunk[:data][chunk_offset..-1]
|
80
|
+
data.empty? ? (data = buffer) : (data << buffer)
|
81
|
+
chunk_number += 1
|
82
|
+
chunk_offset = 0
|
83
|
+
end
|
84
|
+
|
85
|
+
data = data[0..size - 1]
|
86
|
+
@pos += data.size
|
87
|
+
data
|
88
|
+
end
|
89
|
+
|
90
|
+
def write(data)
|
91
|
+
raise "Not opened for writing" if read_only?
|
92
|
+
|
93
|
+
data.force_encoding('BINARY') if data.respond_to?(:force_encoding)
|
94
|
+
|
95
|
+
@pos = length if @pos > length
|
96
|
+
@pos = length if append?
|
97
|
+
|
98
|
+
chunk_number = pos / chunk_size
|
99
|
+
chunk_offset = pos % chunk_size
|
100
|
+
written = data.size
|
101
|
+
new_length = 0
|
102
|
+
|
103
|
+
loop do
|
104
|
+
if buffer = read_chunk(chunk_number)
|
105
|
+
data = (chunk_offset.zero? ? EMPTINESS : buffer[0..chunk_offset - 1]) + data + (buffer[chunk_offset + data.size..-1] || EMPTINESS)
|
106
|
+
end
|
107
|
+
|
108
|
+
to_write = data[0..chunk_size - 1] || EMPTINESS
|
109
|
+
|
110
|
+
break if to_write.empty?
|
111
|
+
|
112
|
+
new_length = chunk_number * chunk_size + write_chunk(chunk_number, to_write)
|
113
|
+
|
114
|
+
data = data[chunk_size..-1] || EMPTINESS
|
115
|
+
|
116
|
+
break if data.empty?
|
117
|
+
|
118
|
+
chunk_number += 1
|
119
|
+
chunk_offset = 0
|
120
|
+
end
|
121
|
+
|
122
|
+
# Update internal position
|
123
|
+
@pos += written
|
124
|
+
|
125
|
+
# Calculate new md5 (if needed)
|
126
|
+
md5 = bucket.md5(@attributes[:_id]) if written > 0
|
127
|
+
|
128
|
+
# Update if something changed
|
129
|
+
updates = {}
|
130
|
+
updates[:md5] = md5 if md5
|
131
|
+
updates[:length] = new_length if new_length > length
|
132
|
+
change_attributes(updates) if updates.any?
|
133
|
+
|
134
|
+
written
|
135
|
+
end
|
136
|
+
|
137
|
+
DEFAULT_CHUNK_SIZE = 255 * 1024
|
138
|
+
|
139
|
+
def default_chunk_size
|
140
|
+
DEFAULT_CHUNK_SIZE
|
141
|
+
end
|
142
|
+
|
143
|
+
def inspect
|
144
|
+
build_inspect_string(bucket: bucket.name, _id: _id, mode: mode, filename: filename, length: length)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def normalize_attributes(provided)
|
150
|
+
provided.keys.each do |key|
|
151
|
+
provided[key.to_sym] = provided.delete(key)
|
152
|
+
end
|
153
|
+
|
154
|
+
attrs = {}
|
155
|
+
attrs[:_id] = provided[:_id] ? BSON::ObjectId.from_string(provided[:_id]) : BSON::ObjectId.new
|
156
|
+
attrs[:length] = (provided[:length] || 0).to_i
|
157
|
+
attrs[:chunkSize] = (provided[:chunkSize] || provided[:chunk_size] || default_chunk_size).to_i
|
158
|
+
attrs[:filename] = provided[:filename] || attrs[:_id].to_s
|
159
|
+
attrs[:contentType] = provided[:content_type] || provided[:contentType] || 'application/octet-stream'
|
160
|
+
attrs[:md5] = provided[:md5]
|
161
|
+
attrs[:aliases] = provided[:aliases] || []
|
162
|
+
attrs[:metadata] = provided[:metadata] || {}
|
163
|
+
attrs[:uploadDate] = provided[:upload_date] || provided[:uploadDate] || Time.now.utc
|
164
|
+
attrs
|
165
|
+
end
|
166
|
+
|
167
|
+
PROTECTED_ATTRIBUTES = [:_id, :length, :chunkSize, :md5]
|
168
|
+
|
169
|
+
def define_dynamic_accessors
|
170
|
+
attributes.keys.each do |attrname|
|
171
|
+
method_name = underscorize(attrname)
|
172
|
+
|
173
|
+
__send__(:define_singleton_method, method_name) { attributes[attrname] }
|
174
|
+
|
175
|
+
if writable? and !PROTECTED_ATTRIBUTES.include?(attrname)
|
176
|
+
__send__(:define_singleton_method, :"#{method_name}=") do |value|
|
177
|
+
change_attributes(:"#{attrname}" => value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def underscorize(name)
|
184
|
+
name.to_s.gsub(/([A-Z])/, '_\1').downcase.to_sym
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_negative_value(value)
|
188
|
+
raise ArgumentError.new("negative value #{value} given") if value < 0
|
189
|
+
end
|
190
|
+
|
191
|
+
def change_attributes(hash)
|
192
|
+
file_query.update('$set' => hash)
|
193
|
+
@attributes.merge!(hash)
|
194
|
+
end
|
195
|
+
|
196
|
+
def file_query
|
197
|
+
files_collection.find(_id: attributes[:_id])
|
198
|
+
end
|
199
|
+
|
200
|
+
def chunk_query(n)
|
201
|
+
chunks_collection.find(files_id: attributes[:_id], n: n)
|
202
|
+
end
|
203
|
+
|
204
|
+
def chunk(n)
|
205
|
+
chunk_query(n).first
|
206
|
+
end
|
207
|
+
|
208
|
+
def write_chunk(n, data)
|
209
|
+
chunk_query(n).upsert('$set' => {data: binarize(data)})
|
210
|
+
@cached_chunk = {n: n, data: data}
|
211
|
+
data.size
|
212
|
+
end
|
213
|
+
|
214
|
+
def read_chunk(n)
|
215
|
+
return @cached_chunk[:data] if @cached_chunk and @cached_chunk[:n] == n
|
216
|
+
readed = chunk(n)
|
217
|
+
return unless readed
|
218
|
+
@cached_chunk = {n: readed['n'], data: readed['data'].data}
|
219
|
+
@cached_chunk[:data]
|
220
|
+
end
|
221
|
+
|
222
|
+
if Moped::VERSION < '2.0.0'
|
223
|
+
def binarize(data)
|
224
|
+
BSON::Binary.new(:generic, data)
|
225
|
+
end
|
226
|
+
else
|
227
|
+
def binarize(data)
|
228
|
+
BSON::Binary.new(data, :generic)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|