grid_attachment 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt ADDED
@@ -0,0 +1,85 @@
1
+ Summary
2
+
3
+ GridAttachment is a GridFS plugin for MongoDB ORMs.
4
+ Supports MongoMapper, Mongoid, MongoODM, and Mongomatic.
5
+
6
+ Support is built in for rack_grid and rack_grid_thumb to generate URLS and thumbnails:
7
+ http://github.com/dusty/rack_grid
8
+ http://github.com/dusty/rack_grid_thumb
9
+
10
+ You can pass in a File or a Hash as received by Sinatra on file uploads.
11
+
12
+ Installation
13
+
14
+ # gem install grid_attachment
15
+
16
+ Usage
17
+
18
+ require 'grid_attachment/mongo_mapper'
19
+ class Monkey
20
+ include MongoMapper::Document
21
+ plugin GridAttachment::MongoMapper
22
+
23
+ attachment :image, :prefix => :grid
24
+ end
25
+
26
+ require 'grid_attachment/mongo_odm'
27
+ class Monkey
28
+ include MongoODM::Document
29
+ include GridAttachment::MongoODM
30
+
31
+ attachment :image, :prefix => :grid
32
+ end
33
+
34
+ require 'grid_attachment/mongomatic'
35
+ class Monkey < Mongomatic::Base
36
+ include GridAttachment::Mongomatic
37
+
38
+ attachment :image, :prefix => :grid
39
+ end
40
+
41
+ require 'grid_attachment/mongoid'
42
+ class Monkey
43
+ include Mongoid::Document
44
+ include GridAttachment::Mongoid
45
+
46
+ attachment :image, :prefix => :grid
47
+ end
48
+
49
+ m = Monkey.new(:name => 'name')
50
+ m.save
51
+
52
+ # To add an attachment from the filesystem
53
+ m.image = File.open('/tmp/me.jpg')
54
+ m.save
55
+
56
+ # To remove an attachment
57
+ m.image = nil
58
+ m.save
59
+
60
+ # To get the attachment
61
+ m.image.read
62
+
63
+ # To get the URL for rack_grid
64
+ m.image_url # /grid/4e049e7c69c3b27d53000005/me.jpg
65
+
66
+ # To get the thumbail URL for rack_grid_thumb
67
+ m.image_thumb('50x50') # /grid/4e049e7c69c3b27d53000005/me_50x50.jpg
68
+
69
+ # HTML form example
70
+ <form action = "/monkeys" method="post" enctype="multipart/form-data">
71
+ <input id="image" name="image" type="file" />
72
+ </form>
73
+
74
+ # Use the image hash provided in params with Sinatra
75
+ post '/monkeys' do
76
+ m = Monkey.new
77
+ m.image = params[:image]
78
+ m.save
79
+ # Or just Monkey.new(params).save
80
+ end
81
+
82
+
83
+ Inspired By
84
+ - http://github.com/jnunemaker/joint
85
+
@@ -0,0 +1,238 @@
1
+ require 'mime/types'
2
+ require 'uri'
3
+
4
+ module GridAttachment
5
+ module MongoMapper
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+
10
+ ##
11
+ # Declare an attachment for the object
12
+ #
13
+ # eg: attachment :image
14
+ def attachment(name,options={})
15
+ prefix = options[:prefix] ||= :grid
16
+
17
+ ##
18
+ # Callbacks to handle the attachment saving and deleting
19
+ after_save :create_attachments
20
+ after_save :delete_attachments
21
+ after_destroy :queue_delete_attachments
22
+ after_destroy :delete_attachments
23
+
24
+ ##
25
+ # Fields for the attachment.
26
+ #
27
+ # Only the _id is really needed, the others are helpful cached
28
+ # so you don't need to hit GridFS
29
+ key "#{name}_id".to_sym, BSON::ObjectId
30
+ key "#{name}_name".to_sym, String
31
+ key "#{name}_size".to_sym, Integer
32
+ key "#{name}_type".to_sym, String
33
+
34
+ ##
35
+ # Add this name to the attachment_types
36
+ attachment_types.push(name).uniq!
37
+
38
+ ##
39
+ # Return the Grid object.
40
+ # eg: image.filename, image.read
41
+ define_method(name) do
42
+ grid.get(read_attribute("#{name}_id")) if read_attribute("#{name}_id")
43
+ end
44
+
45
+ ##
46
+ # Create a method to set the attachment
47
+ # eg: object.image = File.open('/tmp/somefile.jpg')
48
+ define_method("#{name}=") do |file|
49
+ # delete the old file if it exists
50
+ unless read_attribute("#{name}_id").blank?
51
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
52
+ end
53
+ case
54
+ when file.is_a?(Hash) && file[:tempfile]
55
+ send(:create_attachment, name, file)
56
+ when file.respond_to?(:read)
57
+ send(:create_attachment, name, file)
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Create a method to set the attachment for binary string.
63
+ # eg: object.set_image(binary_string, "generated_filename.png")
64
+ define_method("set_#{name}") do |binary, filename|
65
+ if !binary.nil?
66
+ send(:create_attachment_raw, name, binary, filename)
67
+ else
68
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Unset the attachment, queue for removal
74
+ define_method("unset_#{name}") do
75
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
76
+ end
77
+
78
+ ##
79
+ # Return the relative URL to the attachment for use with Rack::Grid
80
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile.png
81
+ define_method("#{name}_url") do
82
+ _id = read_attribute("#{name}_id")
83
+ _name = read_attribute("#{name}_name")
84
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
85
+ end
86
+
87
+ ##
88
+ # Return the relative URL to the thumbnail for use with Rack::GridThumb
89
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile_50x.png
90
+ define_method("#{name}_thumb") do |thumb|
91
+ _id = read_attribute("#{name}_id")
92
+ _name = read_attribute("#{name}_name")
93
+ _ext = File.extname(_name)
94
+ _base = File.basename(_name,_ext)
95
+ _name = "#{_base}_#{thumb}#{_ext}"
96
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
97
+ end
98
+ end
99
+
100
+ ##
101
+ # Accessor to Grid
102
+ def grid
103
+ @grid ||= Mongo::Grid.new(::MongoMapper.database)
104
+ end
105
+
106
+ ##
107
+ # All the attachments types for this class
108
+ def attachment_types
109
+ @attachment_types ||= []
110
+ end
111
+
112
+ end
113
+
114
+ module InstanceMethods
115
+
116
+ ##
117
+ # Accessor to Grid
118
+ def grid
119
+ self.class.grid
120
+ end
121
+
122
+ private
123
+ ##
124
+ # Holds queue of attachments to create
125
+ def create_attachment_queue
126
+ @create_attachment_queue ||= {}
127
+ end
128
+
129
+ ##
130
+ # Holds queue of attachments to delete
131
+ def delete_attachment_queue
132
+ @delete_attachment_queue ||= {}
133
+ end
134
+
135
+ ##
136
+ # Attachments we need to add after save.
137
+ def create_attachment(name,file)
138
+ case
139
+ when file.is_a?(Hash)
140
+ filename = file[:filename]
141
+ size = File.size(file[:tempfile])
142
+ mime = file[:type]
143
+ unless mime
144
+ type = MIME::Types.type_for(filename).first
145
+ mime = type ? type.content_type : "application/octet-stream"
146
+ end
147
+ when file.respond_to?(:read)
148
+ filename = case
149
+ when file.respond_to?(:original_filename) && file.original_filename
150
+ file.original_filename
151
+ when file.respond_to?(:tempfile)
152
+ File.basename(file.tempfile.path)
153
+ else
154
+ File.basename(file.path)
155
+ end
156
+ size = File.size(file.respond_to?(:tempfile) ? file.tempfile : file)
157
+ type = MIME::Types.type_for(filename).first
158
+ mime = type ? type.content_type : "application/octet-stream"
159
+ else
160
+ return
161
+ end
162
+ write_attribute("#{name}_id", BSON::ObjectId.new)
163
+ write_attribute("#{name}_name", filename)
164
+ write_attribute("#{name}_size", size)
165
+ write_attribute("#{name}_type", mime)
166
+ create_attachment_queue[name] = file
167
+ end
168
+
169
+ ##
170
+ # Attachments we need to add after save.
171
+ # For binary String data.
172
+ def create_attachment_raw(name, binary, filename)
173
+ type = MIME::Types.type_for(filename).first
174
+ mime = type ? type.content_type : "application/octet-stream"
175
+ write_attribute("#{name}_id", BSON::ObjectId.new)
176
+ write_attribute("#{name}_name", filename)
177
+ write_attribute("#{name}_size", binary.size)
178
+ write_attribute("#{name}_type", mime)
179
+ create_attachment_queue[name] = binary
180
+ end
181
+
182
+ ##
183
+ # Save an attachment to Grid
184
+ def create_grid_attachment(name,file)
185
+ data = case
186
+ when file.is_a?(Hash)
187
+ file[:tempfile].read
188
+ else
189
+ file.respond_to?(:read) ? file.read : file
190
+ end
191
+ grid.put(
192
+ data,
193
+ :filename => read_attribute("#{name}_name"),
194
+ :content_type => read_attribute("#{name}_type"),
195
+ :_id => read_attribute("#{name}_id")
196
+ )
197
+ create_attachment_queue.delete(name)
198
+ end
199
+
200
+ ##
201
+ # Attachments we need to remove after save
202
+ def delete_attachment(name,id)
203
+ delete_attachment_queue[name] = id if id.is_a?(BSON::ObjectId)
204
+ write_attribute("#{name}_id", nil)
205
+ write_attribute("#{name}_name", nil)
206
+ write_attribute("#{name}_size", nil)
207
+ write_attribute("#{name}_type", nil)
208
+ end
209
+
210
+ ##
211
+ # Delete an attachment from Grid
212
+ def delete_grid_attachment(name,id)
213
+ grid.delete(id) if id.is_a?(BSON::ObjectId)
214
+ delete_attachment_queue.delete(name)
215
+ end
216
+
217
+ ##
218
+ # Create attachments marked for creation
219
+ def create_attachments
220
+ create_attachment_queue.each {|k,v| create_grid_attachment(k,v)}
221
+ end
222
+
223
+ ##
224
+ # Delete attachments marked for deletion
225
+ def delete_attachments
226
+ delete_attachment_queue.each {|k,v| delete_grid_attachment(k,v)}
227
+ end
228
+
229
+ ##
230
+ # Queues all attachments for deletion
231
+ def queue_delete_attachments
232
+ self.class.attachment_types.each do |name|
233
+ delete_attachment(name, read_attribute("#{name}_id"))
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,242 @@
1
+ require 'mime/types'
2
+ require 'uri'
3
+
4
+ module GridAttachment
5
+ module MongoODM
6
+
7
+ def self.included(base)
8
+ base.send(:include, InstanceMethods)
9
+ base.send(:extend, ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ ##
15
+ # Declare an attachment for the object
16
+ #
17
+ # eg: attachment :image
18
+ def attachment(name,options={})
19
+ prefix = options[:prefix] ||= :grid
20
+
21
+ ##
22
+ # Callbacks to handle the attachment saving and deleting
23
+ after_save :create_attachments
24
+ after_save :delete_attachments
25
+ after_destroy :queue_delete_attachments
26
+ after_destroy :delete_attachments
27
+
28
+ ##
29
+ # Fields for the attachment.
30
+ #
31
+ # Only the _id is really needed, the others are helpful cached
32
+ # so you don't need to hit GridFS
33
+ field "#{name}_id".to_sym, BSON::ObjectId
34
+ field "#{name}_name".to_sym, String
35
+ field "#{name}_size".to_sym, Integer
36
+ field "#{name}_type".to_sym, String
37
+
38
+ ##
39
+ # Add this name to the attachment_types
40
+ attachment_types.push(name).uniq!
41
+
42
+ ##
43
+ # Return the Grid object.
44
+ # eg: image.filename, image.read
45
+ define_method(name) do
46
+ grid.get(read_attribute("#{name}_id")) if read_attribute("#{name}_id")
47
+ end
48
+
49
+ ##
50
+ # Create a method to set the attachment
51
+ # eg: object.image = File.open('/tmp/somefile.jpg')
52
+ define_method("#{name}=") do |file|
53
+ # delete the old file if it exists
54
+ unless read_attribute("#{name}_id").blank?
55
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
56
+ end
57
+ case
58
+ when file.is_a?(Hash) && file[:tempfile]
59
+ send(:create_attachment, name, file)
60
+ when file.respond_to?(:read)
61
+ send(:create_attachment, name, file)
62
+ end
63
+ end
64
+
65
+ ##
66
+ # Create a method to set the attachment for binary string.
67
+ # eg: object.set_image(binary_string, "generated_filename.png")
68
+ define_method("set_#{name}") do |binary, filename|
69
+ if !binary.nil?
70
+ send(:create_attachment_raw, name, binary, filename)
71
+ else
72
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Unset the attachment, queue for removal
78
+ define_method("unset_#{name}") do
79
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
80
+ end
81
+
82
+ ##
83
+ # Return the relative URL to the attachment for use with Rack::Grid
84
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile.png
85
+ define_method("#{name}_url") do
86
+ _id = read_attribute("#{name}_id")
87
+ _name = read_attribute("#{name}_name")
88
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
89
+ end
90
+
91
+ ##
92
+ # Return the relative URL to the thumbnail for use with Rack::GridThumb
93
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile_50x.png
94
+ define_method("#{name}_thumb") do |thumb|
95
+ _id = read_attribute("#{name}_id")
96
+ _name = read_attribute("#{name}_name")
97
+ _ext = File.extname(_name)
98
+ _base = File.basename(_name,_ext)
99
+ _name = "#{_base}_#{thumb}#{_ext}"
100
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
101
+ end
102
+ end
103
+
104
+ ##
105
+ # Accessor to Grid
106
+ def grid
107
+ @grid ||= Mongo::Grid.new(::MongoODM.database)
108
+ end
109
+
110
+ ##
111
+ # All the attachments types for this class
112
+ def attachment_types
113
+ @attachment_types ||= []
114
+ end
115
+
116
+ end
117
+
118
+ module InstanceMethods
119
+
120
+ ##
121
+ # Accessor to Grid
122
+ def grid
123
+ self.class.grid
124
+ end
125
+
126
+ private
127
+ ##
128
+ # Holds queue of attachments to create
129
+ def create_attachment_queue
130
+ @create_attachment_queue ||= {}
131
+ end
132
+
133
+ ##
134
+ # Holds queue of attachments to delete
135
+ def delete_attachment_queue
136
+ @delete_attachment_queue ||= {}
137
+ end
138
+
139
+ ##
140
+ # Attachments we need to add after save.
141
+ def create_attachment(name,file)
142
+ case
143
+ when file.is_a?(Hash)
144
+ filename = file[:filename]
145
+ size = File.size(file[:tempfile])
146
+ mime = file[:type]
147
+ unless mime
148
+ type = MIME::Types.type_for(filename).first
149
+ mime = type ? type.content_type : "application/octet-stream"
150
+ end
151
+ when file.respond_to?(:read)
152
+ filename = case
153
+ when file.respond_to?(:original_filename) && file.original_filename
154
+ file.original_filename
155
+ when file.respond_to?(:tempfile)
156
+ File.basename(file.tempfile.path)
157
+ else
158
+ File.basename(file.path)
159
+ end
160
+ size = File.size(file.respond_to?(:tempfile) ? file.tempfile : file)
161
+ type = MIME::Types.type_for(filename).first
162
+ mime = type ? type.content_type : "application/octet-stream"
163
+ else
164
+ return
165
+ end
166
+ write_attribute("#{name}_id", BSON::ObjectId.new)
167
+ write_attribute("#{name}_name", filename)
168
+ write_attribute("#{name}_size", size)
169
+ write_attribute("#{name}_type", mime)
170
+ create_attachment_queue[name] = file
171
+ end
172
+
173
+ ##
174
+ # Attachments we need to add after save.
175
+ # For binary String data.
176
+ def create_attachment_raw(name, binary, filename)
177
+ type = MIME::Types.type_for(filename).first
178
+ mime = type ? type.content_type : "application/octet-stream"
179
+ write_attribute("#{name}_id", BSON::ObjectId.new)
180
+ write_attribute("#{name}_name", filename)
181
+ write_attribute("#{name}_size", binary.size)
182
+ write_attribute("#{name}_type", mime)
183
+ create_attachment_queue[name] = binary
184
+ end
185
+
186
+ ##
187
+ # Save an attachment to Grid
188
+ def create_grid_attachment(name,file)
189
+ data = case
190
+ when file.is_a?(Hash)
191
+ file[:tempfile].read
192
+ else
193
+ file.respond_to?(:read) ? file.read : file
194
+ end
195
+ grid.put(
196
+ data,
197
+ :filename => read_attribute("#{name}_name"),
198
+ :content_type => read_attribute("#{name}_type"),
199
+ :_id => read_attribute("#{name}_id")
200
+ )
201
+ create_attachment_queue.delete(name)
202
+ end
203
+
204
+ ##
205
+ # Attachments we need to remove after save
206
+ def delete_attachment(name,id)
207
+ delete_attachment_queue[name] = id if id.is_a?(BSON::ObjectId)
208
+ write_attribute("#{name}_id", nil)
209
+ write_attribute("#{name}_name", nil)
210
+ write_attribute("#{name}_size", nil)
211
+ write_attribute("#{name}_type", nil)
212
+ end
213
+
214
+ ##
215
+ # Delete an attachment from Grid
216
+ def delete_grid_attachment(name,id)
217
+ grid.delete(id) if id.is_a?(BSON::ObjectId)
218
+ delete_attachment_queue.delete(name)
219
+ end
220
+
221
+ ##
222
+ # Create attachments marked for creation
223
+ def create_attachments
224
+ create_attachment_queue.each {|k,v| create_grid_attachment(k,v)}
225
+ end
226
+
227
+ ##
228
+ # Delete attachments marked for deletion
229
+ def delete_attachments
230
+ delete_attachment_queue.each {|k,v| delete_grid_attachment(k,v)}
231
+ end
232
+
233
+ ##
234
+ # Queues all attachments for deletion
235
+ def queue_delete_attachments
236
+ self.class.attachment_types.each do |name|
237
+ delete_attachment(name, read_attribute("#{name}_id"))
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,238 @@
1
+ require 'mime/types'
2
+ require 'uri'
3
+
4
+ module GridAttachment
5
+ module Mongoid
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+
10
+ ##
11
+ # Declare an attachment for the object
12
+ #
13
+ # eg: attachment :image
14
+ def attachment(name,options={})
15
+ prefix = options[:prefix] ||= :grid
16
+
17
+ ##
18
+ # Callbacks to handle the attachment saving and deleting
19
+ after_save :create_attachments
20
+ after_save :delete_attachments
21
+ after_destroy :queue_delete_attachments
22
+ after_destroy :delete_attachments
23
+
24
+ ##
25
+ # Fields for the attachment.
26
+ #
27
+ # Only the _id is really needed, the others are helpful cached
28
+ # so you don't need to hit GridFS
29
+ field "#{name}_id".to_sym, :type => BSON::ObjectId
30
+ field "#{name}_name".to_sym, :type => String
31
+ field "#{name}_size".to_sym, :type => Integer
32
+ field "#{name}_type".to_sym, :type => String
33
+
34
+ ##
35
+ # Add this name to the attachment_types
36
+ attachment_types.push(name).uniq!
37
+
38
+ ##
39
+ # Return the Grid object.
40
+ # eg: image.filename, image.read
41
+ define_method(name) do
42
+ grid.get(read_attribute("#{name}_id")) if read_attribute("#{name}_id")
43
+ end
44
+
45
+ ##
46
+ # Create a method to set the attachment
47
+ # eg: object.image = File.open('/tmp/somefile.jpg')
48
+ define_method("#{name}=") do |file|
49
+ # delete the old file if it exists
50
+ unless read_attribute("#{name}_id").blank?
51
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
52
+ end
53
+ case
54
+ when file.is_a?(Hash) && file[:tempfile]
55
+ send(:create_attachment, name, file)
56
+ when file.respond_to?(:read)
57
+ send(:create_attachment, name, file)
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Create a method to set the attachment for binary string.
63
+ # eg: object.set_image(binary_string, "generated_filename.png")
64
+ define_method("set_#{name}") do |binary, filename|
65
+ if !binary.nil?
66
+ send(:create_attachment_raw, name, binary, filename)
67
+ else
68
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Unset the attachment, queue for removal
74
+ define_method("unset_#{name}") do
75
+ send(:delete_attachment, name, read_attribute("#{name}_id"))
76
+ end
77
+
78
+ ##
79
+ # Return the relative URL to the attachment for use with Rack::Grid
80
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile.png
81
+ define_method("#{name}_url") do
82
+ _id = read_attribute("#{name}_id")
83
+ _name = read_attribute("#{name}_name")
84
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
85
+ end
86
+
87
+ ##
88
+ # Return the relative URL to the thumbnail for use with Rack::GridThumb
89
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile_50x.png
90
+ define_method("#{name}_thumb") do |thumb|
91
+ _id = read_attribute("#{name}_id")
92
+ _name = read_attribute("#{name}_name")
93
+ _ext = File.extname(_name)
94
+ _base = File.basename(_name,_ext)
95
+ _name = "#{_base}_#{thumb}#{_ext}"
96
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
97
+ end
98
+ end
99
+
100
+ ##
101
+ # Accessor to Grid
102
+ def grid
103
+ @grid ||= Mongo::Grid.new(::Mongoid.database)
104
+ end
105
+
106
+ ##
107
+ # All the attachments types for this class
108
+ def attachment_types
109
+ @attachment_types ||= []
110
+ end
111
+
112
+ end
113
+
114
+ module InstanceMethods
115
+
116
+ ##
117
+ # Accessor to Grid
118
+ def grid
119
+ self.class.grid
120
+ end
121
+
122
+ private
123
+ ##
124
+ # Holds queue of attachments to create
125
+ def create_attachment_queue
126
+ @create_attachment_queue ||= {}
127
+ end
128
+
129
+ ##
130
+ # Holds queue of attachments to delete
131
+ def delete_attachment_queue
132
+ @delete_attachment_queue ||= {}
133
+ end
134
+
135
+ ##
136
+ # Attachments we need to add after save.
137
+ def create_attachment(name,file)
138
+ case
139
+ when file.is_a?(Hash)
140
+ filename = file[:filename]
141
+ size = File.size(file[:tempfile])
142
+ mime = file[:type]
143
+ unless mime
144
+ type = MIME::Types.type_for(filename).first
145
+ mime = type ? type.content_type : "application/octet-stream"
146
+ end
147
+ when file.respond_to?(:read)
148
+ filename = case
149
+ when file.respond_to?(:original_filename) && file.original_filename
150
+ file.original_filename
151
+ when file.respond_to?(:tempfile)
152
+ File.basename(file.tempfile.path)
153
+ else
154
+ File.basename(file.path)
155
+ end
156
+ size = File.size(file.respond_to?(:tempfile) ? file.tempfile : file)
157
+ type = MIME::Types.type_for(filename).first
158
+ mime = type ? type.content_type : "application/octet-stream"
159
+ else
160
+ return
161
+ end
162
+ write_attribute("#{name}_id", BSON::ObjectId.new)
163
+ write_attribute("#{name}_name", filename)
164
+ write_attribute("#{name}_size", size)
165
+ write_attribute("#{name}_type", mime)
166
+ create_attachment_queue[name] = file
167
+ end
168
+
169
+ ##
170
+ # Attachments we need to add after save.
171
+ # For binary String data.
172
+ def create_attachment_raw(name, binary, filename)
173
+ type = MIME::Types.type_for(filename).first
174
+ mime = type ? type.content_type : "application/octet-stream"
175
+ write_attribute("#{name}_id", BSON::ObjectId.new)
176
+ write_attribute("#{name}_name", filename)
177
+ write_attribute("#{name}_size", binary.size)
178
+ write_attribute("#{name}_type", mime)
179
+ create_attachment_queue[name] = binary
180
+ end
181
+
182
+ ##
183
+ # Save an attachment to Grid
184
+ def create_grid_attachment(name,file)
185
+ data = case
186
+ when file.is_a?(Hash)
187
+ file[:tempfile].read
188
+ else
189
+ file.respond_to?(:read) ? file.read : file
190
+ end
191
+ grid.put(
192
+ data,
193
+ :filename => read_attribute("#{name}_name"),
194
+ :content_type => read_attribute("#{name}_type"),
195
+ :_id => read_attribute("#{name}_id")
196
+ )
197
+ create_attachment_queue.delete(name)
198
+ end
199
+
200
+ ##
201
+ # Attachments we need to remove after save
202
+ def delete_attachment(name,id)
203
+ delete_attachment_queue[name] = id if id.is_a?(BSON::ObjectId)
204
+ write_attribute("#{name}_id", nil)
205
+ write_attribute("#{name}_name", nil)
206
+ write_attribute("#{name}_size", nil)
207
+ write_attribute("#{name}_type", nil)
208
+ end
209
+
210
+ ##
211
+ # Delete an attachment from Grid
212
+ def delete_grid_attachment(name,id)
213
+ grid.delete(id) if id.is_a?(BSON::ObjectId)
214
+ delete_attachment_queue.delete(name)
215
+ end
216
+
217
+ ##
218
+ # Create attachments marked for creation
219
+ def create_attachments
220
+ create_attachment_queue.each {|k,v| create_grid_attachment(k,v)}
221
+ end
222
+
223
+ ##
224
+ # Delete attachments marked for deletion
225
+ def delete_attachments
226
+ delete_attachment_queue.each {|k,v| delete_grid_attachment(k,v)}
227
+ end
228
+
229
+ ##
230
+ # Queues all attachments for deletion
231
+ def queue_delete_attachments
232
+ self.class.attachment_types.each do |name|
233
+ delete_attachment(name, read_attribute("#{name}_id"))
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,238 @@
1
+ require 'mime/types'
2
+ require 'uri'
3
+
4
+ class GridmaticObserver < Mongomatic::Observer
5
+ def after_insert_or_update(instance, opts)
6
+ instance.send(:create_attachments)
7
+ instance.send(:delete_attachments)
8
+ end
9
+ def before_remove(instance,opts)
10
+ instance.send(:queue_delete_attachments)
11
+ instance.send(:delete_attachments)
12
+ end
13
+ end
14
+
15
+ module GridAttachment
16
+ module Mongomatic
17
+
18
+ def self.included(base)
19
+ base.send(:include, Mongomatic::Observable)
20
+ base.send(:observer, :GridmaticObserver)
21
+ base.send(:include, InstanceMethods)
22
+ base.send(:extend, ClassMethods)
23
+ end
24
+
25
+ module ClassMethods
26
+
27
+ ##
28
+ # Declare an attachment for the object
29
+ #
30
+ # eg: attachment :image
31
+ def attachment(name,options={})
32
+ prefix = options[:prefix] ||= :grid
33
+
34
+ ##
35
+ # Add this name to the attachment_types
36
+ attachment_types.push(name).uniq!
37
+
38
+ ##
39
+ # Return the Grid object.
40
+ # eg: image.filename, image.read
41
+ define_method(name) do
42
+ grid.get(self["#{name}_id"]) if self["#{name}_id"]
43
+ end
44
+
45
+ ##
46
+ # Create a method to set the attachment
47
+ # eg: object.image = File.open('/tmp/somefile.jpg')
48
+ define_method("#{name}=") do |file|
49
+ # delete the old file if it exists
50
+ unless self["#{name}_id"].blank?
51
+ send(:delete_attachment, name, self["#{name}_id"])
52
+ end
53
+ case
54
+ when file.is_a?(Hash) && file[:tempfile]
55
+ send(:create_attachment, name, file)
56
+ when file.respond_to?(:read)
57
+ send(:create_attachment, name, file)
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Create a method to set the attachment for binary string.
63
+ # eg: object.set_image(binary_string, "generated_filename.png")
64
+ define_method("set_#{name}") do |binary, filename|
65
+ if !binary.nil?
66
+ send(:create_attachment_raw, name, binary, filename)
67
+ else
68
+ send(:delete_attachment, name, self["#{name}_id"])
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Unset the attachment, queue for removal
74
+ define_method("unset_#{name}") do
75
+ send(:delete_attachment, name, self["#{name}_id"])
76
+ end
77
+
78
+ ##
79
+ # Return the relative URL to the attachment for use with Rack::Grid
80
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile.png
81
+ define_method("#{name}_url") do
82
+ _id = self["#{name}_id"]
83
+ _name = self["#{name}_name"]
84
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
85
+ end
86
+
87
+ ##
88
+ # Return the relative URL to the thumbnail for use with Rack::GridThumb
89
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile_50x.png
90
+ define_method("#{name}_thumb") do |thumb|
91
+ _id = self["#{name}_id"]
92
+ _name = self["#{name}_name"]
93
+ _ext = File.extname(_name)
94
+ _base = File.basename(_name,_ext)
95
+ _name = "#{_base}_#{thumb}#{_ext}"
96
+ URI.escape(["/#{prefix}", _id, _name].join('/')) if _id && _name
97
+ end
98
+ end
99
+
100
+ ##
101
+ # Accessor to Grid
102
+ def grid
103
+ @grid ||= Mongo::Grid.new(::Mongomatic.db)
104
+ end
105
+
106
+ ##
107
+ # All the attachments types for this class
108
+ def attachment_types
109
+ @attachment_types ||= []
110
+ end
111
+
112
+ end
113
+
114
+ module InstanceMethods
115
+
116
+ ##
117
+ # Accessor to Grid
118
+ def grid
119
+ self.class.grid
120
+ end
121
+
122
+ private
123
+ ##
124
+ # Holds queue of attachments to create
125
+ def create_attachment_queue
126
+ @create_attachment_queue ||= {}
127
+ end
128
+
129
+ ##
130
+ # Holds queue of attachments to delete
131
+ def delete_attachment_queue
132
+ @delete_attachment_queue ||= {}
133
+ end
134
+
135
+ ##
136
+ # Attachments we need to add after save.
137
+ def create_attachment(name,file)
138
+ case
139
+ when file.is_a?(Hash)
140
+ filename = file[:filename]
141
+ size = File.size(file[:tempfile])
142
+ mime = file[:type]
143
+ unless mime
144
+ type = MIME::Types.type_for(filename).first
145
+ mime = type ? type.content_type : "application/octet-stream"
146
+ end
147
+ when file.respond_to?(:read)
148
+ filename = case
149
+ when file.respond_to?(:original_filename) && file.original_filename
150
+ file.original_filename
151
+ when file.respond_to?(:tempfile)
152
+ File.basename(file.tempfile.path)
153
+ else
154
+ File.basename(file.path)
155
+ end
156
+ size = File.size(file.respond_to?(:tempfile) ? file.tempfile : file)
157
+ type = MIME::Types.type_for(filename).first
158
+ mime = type ? type.content_type : "application/octet-stream"
159
+ else
160
+ return
161
+ end
162
+ self["#{name}_id"] = BSON::ObjectId.new
163
+ self["#{name}_name"] = filename
164
+ self["#{name}_size"] = size
165
+ self["#{name}_type"] = mime
166
+ create_attachment_queue[name] = file
167
+ end
168
+
169
+ ##
170
+ # Attachments we need to add after save.
171
+ # For binary String data.
172
+ def create_attachment_raw(name, binary, filename)
173
+ type = MIME::Types.type_for(filename).first
174
+ mime = type ? type.content_type : "application/octet-stream"
175
+ self["#{name}_id"] = BSON::ObjectId.new
176
+ self["#{name}_name"] = filename
177
+ self["#{name}_size"] = binary.size
178
+ self["#{name}_type"] = mime
179
+ create_attachment_queue[name] = binary
180
+ end
181
+
182
+ ##
183
+ # Save an attachment to Grid
184
+ def create_grid_attachment(name,file)
185
+ data = case
186
+ when file.is_a?(Hash)
187
+ file[:tempfile].read
188
+ else
189
+ file.respond_to?(:read) ? file.read : file
190
+ end
191
+ grid.put(
192
+ data,
193
+ :filename => self["#{name}_name"],
194
+ :content_type => self["#{name}_type"],
195
+ :_id => self["#{name}_id"]
196
+ )
197
+ create_attachment_queue.delete(name)
198
+ end
199
+
200
+ ##
201
+ # Attachments we need to remove after save
202
+ def delete_attachment(name,id)
203
+ delete_attachment_queue[name] = id if id.is_a?(BSON::ObjectId)
204
+ self["#{name}_id"] = nil
205
+ self["#{name}_name"] = nil
206
+ self["#{name}_size"] = nil
207
+ self["#{name}_type"] = nil
208
+ end
209
+
210
+ ##
211
+ # Delete an attachment from Grid
212
+ def delete_grid_attachment(name,id)
213
+ grid.delete(id) if id.is_a?(BSON::ObjectId)
214
+ delete_attachment_queue.delete(name)
215
+ end
216
+
217
+ ##
218
+ # Create attachments marked for creation
219
+ def create_attachments
220
+ create_attachment_queue.each {|k,v| create_grid_attachment(k,v)}
221
+ end
222
+
223
+ ##
224
+ # Delete attachments marked for deletion
225
+ def delete_attachments
226
+ delete_attachment_queue.each {|k,v| delete_grid_attachment(k,v)}
227
+ end
228
+
229
+ ##
230
+ # Queues all attachments for deletion
231
+ def queue_delete_attachments
232
+ self.class.attachment_types.each do |name|
233
+ delete_attachment(name, self["#{name}_id"])
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
File without changes
File without changes
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grid_attachment
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dusty Doris
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-15 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mime-types
16
+ requirement: &70132069007560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70132069007560
25
+ description: Plugin for various Mongo ODMs to attach files via GridFS
26
+ email: github@dusty.name
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - README.txt
31
+ files:
32
+ - README.txt
33
+ - lib/grid_attachment/mongo_mapper.rb
34
+ - lib/grid_attachment/mongo_odm.rb
35
+ - lib/grid_attachment/mongomatic.rb
36
+ - lib/grid_attachment/mongoid.rb
37
+ - test/test_mongo_mapper.rb
38
+ - test/test_mongo_odm.rb
39
+ - test/test_mongomatic.rb
40
+ - test/test_mongoid.rb
41
+ homepage: http://github.com/dusty/grid_attachment
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project: none
61
+ rubygems_version: 1.8.7
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Plugin for various Mongo ODMs to attach files via GridFS
65
+ test_files: []