joint 0.1.1 → 0.2
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/README.rdoc +1 -6
- data/Rakefile +2 -2
- data/lib/joint.rb +48 -30
- data/lib/joint/version.rb +1 -1
- data/test/helper.rb +16 -11
- data/test/test_joint.rb +160 -62
- metadata +5 -6
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Joint
|
2
2
|
|
3
|
-
MongoMapper and GridFS
|
3
|
+
MongoMapper and GridFS joined in file upload love.
|
4
4
|
|
5
5
|
== Usage
|
6
6
|
|
@@ -22,11 +22,6 @@ Also, #image and #pdf are proxies so you can do stuff like:
|
|
22
22
|
|
23
23
|
If you call a method other than those in the proxy it calls it on the GridIO instance so you can still get at all the GridIO instance methods.
|
24
24
|
|
25
|
-
== Dependencies
|
26
|
-
|
27
|
-
* MongoMapper >= 0.7 - gem install mongo_mapper
|
28
|
-
* Wand >= 0.2.1 - gem install wand
|
29
|
-
|
30
25
|
== Note on Patches/Pull Requests
|
31
26
|
|
32
27
|
* Fork the project.
|
data/Rakefile
CHANGED
@@ -8,8 +8,8 @@ require File.join(File.dirname(__FILE__), 'lib', 'joint', 'version')
|
|
8
8
|
|
9
9
|
Jeweler::Tasks.new do |gem|
|
10
10
|
gem.name = "joint"
|
11
|
-
gem.summary = %Q{MongoMapper and GridFS
|
12
|
-
gem.description = %Q{MongoMapper and GridFS
|
11
|
+
gem.summary = %Q{MongoMapper and GridFS joined in file upload love.}
|
12
|
+
gem.description = %Q{MongoMapper and GridFS joined in file upload love.}
|
13
13
|
gem.email = "nunemaker@gmail.com"
|
14
14
|
gem.homepage = "http://github.com/jnunemaker/joint"
|
15
15
|
gem.authors = ["John Nunemaker"]
|
data/lib/joint.rb
CHANGED
@@ -1,68 +1,82 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'mime/types'
|
2
3
|
require 'wand'
|
3
4
|
|
4
5
|
module Joint
|
5
6
|
autoload :Version, 'joint/version'
|
6
7
|
|
7
|
-
def self.file_name(file)
|
8
|
-
file.respond_to?(:original_filename) ? file.original_filename : File.basename(file.path)
|
9
|
-
end
|
10
|
-
|
11
8
|
module ClassMethods
|
12
9
|
def attachment(name)
|
13
|
-
self.class.class_inheritable_accessor :attachment_names
|
10
|
+
self.class.class_inheritable_accessor :attachment_names unless self.class.respond_to?(:attachment_names)
|
14
11
|
self.class.attachment_names ||= []
|
15
12
|
self.class.attachment_names << name
|
16
13
|
|
17
14
|
after_save :save_attachments
|
18
|
-
|
15
|
+
after_save :destroy_nil_attachments
|
16
|
+
before_destroy :destroy_all_attachments
|
19
17
|
|
20
18
|
key "#{name}_id".to_sym, ObjectId
|
21
19
|
key "#{name}_name".to_sym, String
|
22
20
|
key "#{name}_size".to_sym, Integer
|
23
21
|
key "#{name}_type".to_sym, String
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
class_eval <<-EOC
|
24
|
+
def #{name}
|
25
|
+
@#{name} ||= AttachmentProxy.new(self, :#{name})
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
def #{name}?
|
29
|
+
self.send(:#{name}_id?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def #{name}=(file)
|
33
|
+
if file.nil?
|
34
|
+
nil_attachments << :#{name}
|
35
|
+
else
|
36
|
+
self["#{name}_id"] = Mongo::ObjectID.new
|
37
|
+
self["#{name}_size"] = File.size(file)
|
38
|
+
self["#{name}_type"] = Wand.wave(file.path)
|
39
|
+
self["#{name}_name"] = Joint.file_name(file)
|
40
|
+
assigned_attachments[:#{name}] = file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
EOC
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
39
47
|
module InstanceMethods
|
40
|
-
def attachment_assignments
|
41
|
-
@attachment_assignments ||= {}
|
42
|
-
end
|
43
|
-
|
44
48
|
def grid
|
45
49
|
@grid ||= Mongo::Grid.new(database)
|
46
50
|
end
|
47
51
|
|
48
52
|
private
|
53
|
+
def assigned_attachments
|
54
|
+
@assigned_attachments ||= {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def nil_attachments
|
58
|
+
@nil_attachments ||= Set.new
|
59
|
+
end
|
60
|
+
|
49
61
|
def save_attachments
|
50
|
-
|
62
|
+
assigned_attachments.each do |attachment|
|
51
63
|
name, file = attachment
|
52
|
-
content_type = self["#{name}_type"]
|
53
|
-
|
54
64
|
if file.respond_to?(:read)
|
55
|
-
|
65
|
+
file.rewind if file.respond_to?(:rewind)
|
66
|
+
grid.put(file.read, self["#{name}_name"], {
|
67
|
+
:_id => self["#{name}_id"],
|
68
|
+
:content_type => self["#{name}_type"],
|
69
|
+
})
|
56
70
|
end
|
57
|
-
end
|
71
|
+
end.tap(&:clear)
|
72
|
+
end
|
58
73
|
|
59
|
-
|
74
|
+
def destroy_nil_attachments
|
75
|
+
nil_attachments.each { |name| grid.delete(self["#{name}_id"]) }.tap(&:clear)
|
60
76
|
end
|
61
77
|
|
62
|
-
def
|
63
|
-
self.class.attachment_names.each
|
64
|
-
grid.delete(self["#{name}_id"])
|
65
|
-
end
|
78
|
+
def destroy_all_attachments
|
79
|
+
self.class.attachment_names.each { |name| grid.delete(self["#{name}_id"]) }
|
66
80
|
end
|
67
81
|
end
|
68
82
|
|
@@ -95,4 +109,8 @@ module Joint
|
|
95
109
|
grid_io.send(method, *args, &block)
|
96
110
|
end
|
97
111
|
end
|
112
|
+
|
113
|
+
def self.file_name(file)
|
114
|
+
file.respond_to?(:original_filename) ? file.original_filename : File.basename(file.path)
|
115
|
+
end
|
98
116
|
end
|
data/lib/joint/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,23 +1,28 @@
|
|
1
|
-
require 'test/unit'
|
2
1
|
require 'tempfile'
|
3
2
|
require 'pp'
|
4
3
|
require 'mongo_mapper'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'matchy'
|
6
|
+
require 'mocha'
|
5
7
|
|
6
8
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/joint')
|
7
9
|
|
8
10
|
MongoMapper.database = "testing"
|
9
11
|
|
10
12
|
class Test::Unit::TestCase
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
def assert_difference(expression, difference = 1, message = nil, &block)
|
14
|
+
b = block.send(:binding)
|
15
|
+
exps = Array.wrap(expression)
|
16
|
+
before = exps.map { |e| eval(e, b) }
|
17
|
+
yield
|
18
|
+
exps.each_with_index do |e, i|
|
19
|
+
error = "#{e.inspect} didn't change by #{difference}"
|
20
|
+
error = "#{message}.\n#{error}" if message
|
21
|
+
assert_equal(before[i] + difference, eval(e, b), error)
|
21
22
|
end
|
22
23
|
end
|
24
|
+
|
25
|
+
def assert_no_difference(expression, message = nil, &block)
|
26
|
+
assert_difference(expression, 0, message, &block)
|
27
|
+
end
|
23
28
|
end
|
data/test/test_joint.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class Asset
|
4
4
|
include MongoMapper::Document
|
5
5
|
plugin Joint
|
6
6
|
|
7
|
+
key :title, String
|
7
8
|
attachment :image
|
8
9
|
attachment :pdf
|
9
10
|
end
|
@@ -11,17 +12,14 @@ end
|
|
11
12
|
class JointTest < Test::Unit::TestCase
|
12
13
|
def setup
|
13
14
|
MongoMapper.database.collections.each(&:remove)
|
14
|
-
@grid = Mongo::Grid.new(MongoMapper.database)
|
15
15
|
|
16
|
-
dir
|
17
|
-
@pdf
|
18
|
-
@image
|
19
|
-
|
20
|
-
@
|
21
|
-
@
|
22
|
-
|
23
|
-
@doc = Foo.create(:image => @image, :pdf => @pdf)
|
24
|
-
@doc.reload
|
16
|
+
dir = File.dirname(__FILE__) + '/fixtures'
|
17
|
+
@pdf = File.open("#{dir}/unixref.pdf", 'r')
|
18
|
+
@image = File.open("#{dir}/mr_t.jpg", 'r')
|
19
|
+
@pdf_contents = File.read("#{dir}/unixref.pdf")
|
20
|
+
@image_contents = File.read("#{dir}/mr_t.jpg")
|
21
|
+
@grid = Mongo::Grid.new(MongoMapper.database)
|
22
|
+
@gridfs_collection = MongoMapper.database['fs.files']
|
25
23
|
end
|
26
24
|
|
27
25
|
def teardown
|
@@ -29,80 +27,180 @@ class JointTest < Test::Unit::TestCase
|
|
29
27
|
@image.close
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
context "Using Joint plugin" do
|
31
|
+
should "add each attachment to attachment_names" do
|
32
|
+
Asset.attachment_names.should == [:image, :pdf]
|
33
|
+
end
|
34
|
+
|
35
|
+
should "add keys for each attachment" do
|
36
|
+
[:image, :pdf].each do |attachment|
|
37
|
+
[:id, :name, :type, :size].each do |key|
|
38
|
+
Asset.keys.include?("#{attachment}_#{key}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
35
42
|
end
|
36
43
|
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
context "Assigning attachments to document" do
|
45
|
+
setup do
|
46
|
+
@doc = Asset.create(:image => @image, :pdf => @pdf)
|
47
|
+
@doc.reload
|
48
|
+
end
|
40
49
|
|
41
|
-
|
42
|
-
|
50
|
+
should "assign GridFS content_type" do
|
51
|
+
@grid.get(@doc.image_id).content_type.should == 'image/jpeg'
|
52
|
+
@grid.get(@doc.pdf_id).content_type.should == 'application/pdf'
|
53
|
+
end
|
43
54
|
|
44
|
-
|
45
|
-
|
55
|
+
should "assign joint keys" do
|
56
|
+
@doc.image_size.should == 13661
|
57
|
+
@doc.pdf_size.should == 68926
|
46
58
|
|
47
|
-
|
48
|
-
|
49
|
-
|
59
|
+
@doc.image_type.should == "image/jpeg"
|
60
|
+
@doc.pdf_type.should == "application/pdf"
|
61
|
+
|
62
|
+
@doc.image_id.should_not be_nil
|
63
|
+
@doc.pdf_id.should_not be_nil
|
64
|
+
|
65
|
+
@doc.image_id.should be_instance_of(Mongo::ObjectID)
|
66
|
+
@doc.pdf_id.should be_instance_of(Mongo::ObjectID)
|
67
|
+
end
|
50
68
|
|
51
|
-
|
52
|
-
|
53
|
-
|
69
|
+
should "allow accessing keys through attachment proxy" do
|
70
|
+
@doc.image.size.should == 13661
|
71
|
+
@doc.pdf.size.should == 68926
|
54
72
|
|
55
|
-
|
56
|
-
|
73
|
+
@doc.image.type.should == "image/jpeg"
|
74
|
+
@doc.pdf.type.should == "application/pdf"
|
57
75
|
|
58
|
-
|
59
|
-
|
76
|
+
@doc.image.id.should_not be_nil
|
77
|
+
@doc.pdf.id.should_not be_nil
|
60
78
|
|
61
|
-
|
62
|
-
|
79
|
+
@doc.image.id.should be_instance_of(Mongo::ObjectID)
|
80
|
+
@doc.pdf.id.should be_instance_of(Mongo::ObjectID)
|
81
|
+
end
|
82
|
+
|
83
|
+
should "proxy unknown methods to GridIO object" do
|
84
|
+
@doc.image.files_id.should == @doc.image_id
|
85
|
+
@doc.image.content_type.should == 'image/jpeg'
|
86
|
+
@doc.image.filename.should == 'mr_t.jpg'
|
87
|
+
@doc.image.file_length.should == 13661
|
88
|
+
end
|
89
|
+
|
90
|
+
should "assign file name from path if original file name not available" do
|
91
|
+
@doc.image_name.should == 'mr_t.jpg'
|
92
|
+
@doc.pdf_name.should == 'unixref.pdf'
|
93
|
+
end
|
94
|
+
|
95
|
+
should "save attachment contents correctly" do
|
96
|
+
@doc.pdf.read.should == @pdf_contents
|
97
|
+
@doc.image.read.should == @image_contents
|
98
|
+
end
|
99
|
+
|
100
|
+
should "know that attachment exists" do
|
101
|
+
@doc.image?.should be(true)
|
102
|
+
@doc.pdf?.should be(true)
|
103
|
+
end
|
104
|
+
|
105
|
+
should "clear assigned attachments so they don't get uploaded twice" do
|
106
|
+
Mongo::Grid.any_instance.expects(:put).never
|
107
|
+
@doc.save
|
108
|
+
end
|
63
109
|
end
|
64
110
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
111
|
+
context "Updating document but not attachments" do
|
112
|
+
setup do
|
113
|
+
@doc = Asset.create(:image => @image)
|
114
|
+
@doc.update_attributes(:title => 'Updated')
|
115
|
+
@doc.reload
|
116
|
+
end
|
117
|
+
|
118
|
+
should "not affect attachment" do
|
119
|
+
@doc.image.read.should == @image_contents
|
120
|
+
end
|
121
|
+
|
122
|
+
should "update document attributes" do
|
123
|
+
@doc.title.should == 'Updated'
|
124
|
+
end
|
70
125
|
end
|
71
126
|
|
72
|
-
|
73
|
-
|
74
|
-
|
127
|
+
context "Assigning file with where file pointer is not at beginning" do
|
128
|
+
setup do
|
129
|
+
@image.read
|
130
|
+
@doc = Asset.create(:image => @image)
|
131
|
+
@doc.reload
|
132
|
+
end
|
133
|
+
|
134
|
+
should "rewind and correctly store contents" do
|
135
|
+
@doc.image.read.should == @image_contents
|
136
|
+
end
|
75
137
|
end
|
76
138
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
139
|
+
context "Setting attachment to nil" do
|
140
|
+
setup do
|
141
|
+
@doc = Asset.create(:image => @image)
|
142
|
+
end
|
143
|
+
|
144
|
+
should "delete attachment after save" do
|
145
|
+
assert_no_difference '@gridfs_collection.find().count' do
|
146
|
+
@doc.image = nil
|
82
147
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
148
|
+
|
149
|
+
assert_difference '@gridfs_collection.find().count', -1 do
|
150
|
+
@doc.save
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
should "clear nil attachments after save and not attempt to delete again" do
|
155
|
+
@doc.image = nil
|
156
|
+
@doc.save
|
157
|
+
Mongo::Grid.any_instance.expects(:delete).never
|
158
|
+
@doc.save
|
87
159
|
end
|
88
160
|
end
|
89
161
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
162
|
+
context "Retrieving attachment that does not exist" do
|
163
|
+
setup do
|
164
|
+
@doc = Asset.create
|
165
|
+
end
|
166
|
+
|
167
|
+
should "know that the attachment is not present" do
|
168
|
+
@doc.image?.should be(false)
|
169
|
+
end
|
170
|
+
|
171
|
+
should "raise Mongo::GridError" do
|
172
|
+
assert_raises(Mongo::GridError) { @doc.image.read }
|
95
173
|
end
|
96
174
|
end
|
97
175
|
|
98
|
-
|
99
|
-
|
100
|
-
|
176
|
+
context "Destroying a document" do
|
177
|
+
setup do
|
178
|
+
@doc = Asset.create(:image => @image)
|
179
|
+
end
|
180
|
+
|
181
|
+
should "remove files from grid fs as well" do
|
182
|
+
assert_difference "@gridfs_collection.find().count", -1 do
|
183
|
+
@doc.destroy
|
184
|
+
end
|
185
|
+
end
|
101
186
|
end
|
102
187
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
188
|
+
context "Assigning file name" do
|
189
|
+
should "default to path" do
|
190
|
+
Asset.create(:image => @image).image.name.should == 'mr_t.jpg'
|
191
|
+
end
|
192
|
+
|
193
|
+
should "use original_filename if available" do
|
194
|
+
begin
|
195
|
+
file = Tempfile.new('testing.txt')
|
196
|
+
def file.original_filename
|
197
|
+
'testing.txt'
|
198
|
+
end
|
199
|
+
doc = Asset.create(:image => file)
|
200
|
+
assert_equal 'testing.txt', doc.image_name
|
201
|
+
ensure
|
202
|
+
file.close
|
203
|
+
end
|
204
|
+
end
|
107
205
|
end
|
108
206
|
end
|
metadata
CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.1.1
|
7
|
+
- 2
|
8
|
+
version: "0.2"
|
10
9
|
platform: ruby
|
11
10
|
authors:
|
12
11
|
- John Nunemaker
|
@@ -14,7 +13,7 @@ autorequire:
|
|
14
13
|
bindir: bin
|
15
14
|
cert_chain: []
|
16
15
|
|
17
|
-
date: 2010-03-
|
16
|
+
date: 2010-03-29 00:00:00 -04:00
|
18
17
|
default_executable:
|
19
18
|
dependencies:
|
20
19
|
- !ruby/object:Gem::Dependency
|
@@ -67,7 +66,7 @@ dependencies:
|
|
67
66
|
version: "0"
|
68
67
|
type: :development
|
69
68
|
version_requirements: *id004
|
70
|
-
description: MongoMapper and GridFS
|
69
|
+
description: MongoMapper and GridFS joined in file upload love.
|
71
70
|
email: nunemaker@gmail.com
|
72
71
|
executables: []
|
73
72
|
|
@@ -113,7 +112,7 @@ rubyforge_project:
|
|
113
112
|
rubygems_version: 1.3.6
|
114
113
|
signing_key:
|
115
114
|
specification_version: 3
|
116
|
-
summary: MongoMapper and GridFS
|
115
|
+
summary: MongoMapper and GridFS joined in file upload love.
|
117
116
|
test_files:
|
118
117
|
- test/fixtures/mr_t.jpg
|
119
118
|
- test/fixtures/unixref.pdf
|