grid_attachment 0.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.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: []