mm-attach-it 0.1.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/Gemfile ADDED
@@ -0,0 +1 @@
1
+ source "http://rubygems.org"
data/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) 2010 Adilson Chacon
2
+
3
+ Absolutely free. You can have the code, change, build another from this, whatever you want.
4
+
data/README ADDED
@@ -0,0 +1,172 @@
1
+ = Mm-attach-it
2
+
3
+ Attach files (images, videos, pdfs, txts, zips and etc) to a MongoMapper record. You can choose if you to store it on file system or GridFS.
4
+
5
+ == Install
6
+
7
+ sudo gem install mm-attach-it
8
+
9
+ == Usage
10
+
11
+ === Model
12
+
13
+ Declare the plugin and use the attachment method to make attachments.
14
+
15
+ class Foo
16
+ include MongoMapper::Document
17
+ plugin AttachIt
18
+
19
+ has_attachment :photo
20
+ end
21
+
22
+ The default storage is the file system, if you want to change for GridFS you should do:
23
+
24
+ class Bar
25
+ include MongoMapper::Document
26
+ plugin AttachIt
27
+
28
+ has_attachment :photo, { :storage => 'gridfs' }
29
+ end
30
+
31
+ If you want to resize the images (you can store resized images on both: file system or GridFS)
32
+
33
+ class Foo
34
+ include MongoMapper::Document
35
+ plugin AttachIt
36
+
37
+ has_attachment :photo, { :styles => { :small => '100x100>', :medium => '200x200>' } }
38
+ end
39
+
40
+ If you want to validate the attached file (again you can validate attaches on both: file system or GridFS)
41
+
42
+ class Foo
43
+ include MongoMapper::Document
44
+ plugin AttachIt
45
+
46
+ has_attch :photo
47
+
48
+ validates_attachment_presence :photo
49
+ validates_attachment_content_type: photo, :content_type => ['image/jpeg', 'image/gif', 'image/png']
50
+ validates_attachment_size :photo, { :less_than => 1.megabyte, :greater_than => 500.kilobytes }
51
+ end
52
+
53
+ OBS: But remember, you can attach whatever kind of file!
54
+
55
+ If you are using the file system to store the files you can specify the storage directory
56
+
57
+ class Foo
58
+ include MongoMapper::Document
59
+ plugin AttachIt
60
+
61
+ has_attachment :photo, { :path => '/:rails_root/public/images/foos/:id/:style/:filename' }
62
+ end
63
+
64
+ OBS: The default directory is '/:rails_root/public/system/:attachment/:id/:style/:filename'
65
+
66
+ Where:
67
+ * :rails_root - is the root directory of your Rails application
68
+ * :environment - can be "production", "test" or "development"
69
+ * :class - the name of the class ('foo' the example above)
70
+ * :attachment - is the name of the column's collection ('photo' the example above)
71
+ * :id - the "id" of the record on MongoDB
72
+ * :style - if you specified the styles
73
+ * :filename - the name of the file
74
+ * :extension - the extension of the file
75
+
76
+ === View
77
+
78
+ Check the model below
79
+
80
+ class Foo
81
+ include MongoMapper::Document
82
+ plugin AttachIt
83
+
84
+ # on file system
85
+ has_attachment :photo, {
86
+ :style => { :thumb => '100x100>' },
87
+ :url => '/assets/groups/:id/:style/:filename',
88
+ :path => '/:rails_root/public/image/foos/:id/:style/:filename', :storage => 'gridfs',
89
+ }
90
+
91
+ # on GridFS
92
+ has_attachment :avatar, {
93
+ :style => { :thumb => '100x100>' },
94
+ :storage => 'gridfs',
95
+ }
96
+ end
97
+
98
+ On form you must set the "multipart" option and the field as "file_field":
99
+
100
+ <%= form_for(@foo, :html => { :multipart => true }) do |f| %>
101
+
102
+ <p>
103
+ <%= f.label :photo %>
104
+ <%= f.file_field :photo %>
105
+ </p>
106
+
107
+ <p>
108
+ <%= f.label :avatar %>
109
+ <%= f.file_field :avatar %>
110
+ </p>
111
+
112
+ <div class="actions">
113
+ <%= f.submit %>
114
+ </div>
115
+ <% end %>
116
+
117
+
118
+ The safest way to show the images is using Base64, doesn't matter if you are using the file system or GridFS.
119
+
120
+ <img src="<%= foo.photo.base64 %>">
121
+ <img src="<%= foo.photo.base64('thumb') %>">
122
+
123
+ <img src="<%= foo.avatar.base64 %>">
124
+ <img src="<%= foo.avatar.base64('thumb') %>">
125
+
126
+ Also, only for file system, if you specify the 'url' option you can do:
127
+
128
+ <%= image_tag foo.photo.url %>
129
+ <%= image_tag foo.photo.url('thumb') %>
130
+
131
+ === Controller
132
+
133
+ If you are using the GridFS to store your files and youn don't want to use Base64 data to show the images you've got to create a action on a controller.
134
+
135
+ But first create a new route on the route's file:
136
+
137
+ match '/foos/avatar/:id(/:style)', :to => 'foos#avatar'
138
+
139
+ Now the controller:
140
+
141
+ class FoosController < ApplicationController
142
+
143
+ ...
144
+
145
+ def avatar
146
+ foo = Foo.where('_id' => BSON::ObjectId(params[:id])).first
147
+ grid_io_data = (params[:style].nil?) ? foo.avatar.get_from_gridfs : foo.avatar.get_from_gridfs(params[:style])
148
+ bytes = grid_io_data.read
149
+ send_data(bytes, :type => foo.avatar_content_type, :disposition => 'inline')
150
+ end
151
+
152
+ ...
153
+
154
+ end
155
+
156
+ And the view do that:
157
+
158
+ <%= image_tag "/foos/avatar/#{foo.id.to_s}/small" %>
159
+
160
+ == Note on Patches/Pull Requests
161
+
162
+ * Fork the project.
163
+ * Make your feature addition or bug fix.
164
+ * Add tests for it. This is important so I don't break it in a
165
+ future version unintentionally.
166
+ * Commit, do not mess with rakefile, version, or history.
167
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
168
+ * Send me a pull request. Bonus points for topic branches.
169
+
170
+ == Copyright
171
+
172
+ Copyright (c) 2010 Adilson Chacon. See LICENSE for details.
data/README.rdoc ADDED
@@ -0,0 +1,172 @@
1
+ = Mm-attach-it
2
+
3
+ Attach files (images, videos, pdfs, txts, zips and etc) to a MongoMapper record. You can choose if you to store it on file system or GridFS.
4
+
5
+ == Install
6
+
7
+ sudo gem install mm-attach-it
8
+
9
+ == Usage
10
+
11
+ === Model
12
+
13
+ Declare the plugin and use the attachment method to make attachments.
14
+
15
+ class Foo
16
+ include MongoMapper::Document
17
+ plugin AttachIt
18
+
19
+ has_attachment :photo
20
+ end
21
+
22
+ The default storage is the file system, if you want to change for GridFS you should do:
23
+
24
+ class Bar
25
+ include MongoMapper::Document
26
+ plugin AttachIt
27
+
28
+ has_attachment :photo, { :storage => 'gridfs' }
29
+ end
30
+
31
+ If you want to resize the images (you can store resized images on both: file system or GridFS)
32
+
33
+ class Foo
34
+ include MongoMapper::Document
35
+ plugin AttachIt
36
+
37
+ has_attachment :photo, { :styles => { :small => '100x100>', :medium => '200x200>' } }
38
+ end
39
+
40
+ If you want to validate the attached file (again you can validate attaches on both: file system or GridFS)
41
+
42
+ class Foo
43
+ include MongoMapper::Document
44
+ plugin AttachIt
45
+
46
+ has_attch :photo
47
+
48
+ validates_attachment_presence :photo
49
+ validates_attachment_content_type: photo, :content_type => ['image/jpeg', 'image/gif', 'image/png']
50
+ validates_attachment_size :photo, { :less_than => 1.megabyte, :greater_than => 500.kilobytes }
51
+ end
52
+
53
+ OBS: But remember, you can attach whatever kind of file!
54
+
55
+ If you are using the file system to store the files you can specify the storage directory
56
+
57
+ class Foo
58
+ include MongoMapper::Document
59
+ plugin AttachIt
60
+
61
+ has_attachment :photo, { :path => '/:rails_root/public/images/foos/:id/:style/:filename' }
62
+ end
63
+
64
+ OBS: The default directory is '/:rails_root/public/system/:attachment/:id/:style/:filename'
65
+
66
+ Where:
67
+ * :rails_root - is the root directory of your Rails application
68
+ * :environment - can be "production", "test" or "development"
69
+ * :class - the name of the class ('foo' the example above)
70
+ * :attachment - is the name of the column's collection ('photo' the example above)
71
+ * :id - the "id" of the record on MongoDB
72
+ * :style - if you specified the styles
73
+ * :filename - the name of the file
74
+ * :extension - the extension of the file
75
+
76
+ === View
77
+
78
+ Check the model below
79
+
80
+ class Foo
81
+ include MongoMapper::Document
82
+ plugin AttachIt
83
+
84
+ # on file system
85
+ has_attachment :photo, {
86
+ :style => { :thumb => '100x100>' },
87
+ :url => '/assets/groups/:id/:style/:filename',
88
+ :path => '/:rails_root/public/image/foos/:id/:style/:filename', :storage => 'gridfs',
89
+ }
90
+
91
+ # on GridFS
92
+ has_attachment :avatar, {
93
+ :style => { :thumb => '100x100>' },
94
+ :storage => 'gridfs',
95
+ }
96
+ end
97
+
98
+ On form you must set the "multipart" option and the field as "file_field":
99
+
100
+ <%= form_for(@foo, :html => { :multipart => true }) do |f| %>
101
+
102
+ <p>
103
+ <%= f.label :photo %>
104
+ <%= f.file_field :photo %>
105
+ </p>
106
+
107
+ <p>
108
+ <%= f.label :avatar %>
109
+ <%= f.file_field :avatar %>
110
+ </p>
111
+
112
+ <div class="actions">
113
+ <%= f.submit %>
114
+ </div>
115
+ <% end %>
116
+
117
+
118
+ The safest way to show the images is using Base64, doesn't matter if you are using the file system or GridFS.
119
+
120
+ <img src="<%= foo.photo.base64 %>">
121
+ <img src="<%= foo.photo.base64('thumb') %>">
122
+
123
+ <img src="<%= foo.avatar.base64 %>">
124
+ <img src="<%= foo.avatar.base64('thumb') %>">
125
+
126
+ Also, only for file system, if you specify the 'url' option you can do:
127
+
128
+ <%= image_tag foo.photo.url %>
129
+ <%= image_tag foo.photo.url('thumb') %>
130
+
131
+ === Controller
132
+
133
+ If you are using the GridFS to store your files and youn don't want to use Base64 data to show the images you've got to create a action on a controller.
134
+
135
+ But first create a new route on the route's file:
136
+
137
+ match '/foos/avatar/:id(/:style)', :to => 'foos#avatar'
138
+
139
+ Now the controller:
140
+
141
+ class FoosController < ApplicationController
142
+
143
+ ...
144
+
145
+ def avatar
146
+ foo = Foo.where('_id' => BSON::ObjectId(params[:id])).first
147
+ grid_io_data = (params[:style].nil?) ? foo.avatar.get_from_gridfs : foo.avatar.get_from_gridfs(params[:style])
148
+ bytes = grid_io_data.read
149
+ send_data(bytes, :type => foo.avatar_content_type, :disposition => 'inline')
150
+ end
151
+
152
+ ...
153
+
154
+ end
155
+
156
+ And the view do that:
157
+
158
+ <%= image_tag "/foos/avatar/#{foo.id.to_s}/small" %>
159
+
160
+ == Note on Patches/Pull Requests
161
+
162
+ * Fork the project.
163
+ * Make your feature addition or bug fix.
164
+ * Add tests for it. This is important so I don't break it in a
165
+ future version unintentionally.
166
+ * Commit, do not mess with rakefile, version, or history.
167
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
168
+ * Send me a pull request. Bonus points for topic branches.
169
+
170
+ == Copyright
171
+
172
+ Copyright (c) 2010 Adilson Chacon. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
@@ -0,0 +1,83 @@
1
+ module AttachIt
2
+
3
+ def self.configure(model)
4
+ model.class_eval do
5
+ end
6
+ end
7
+
8
+ module ClassMethods
9
+ def has_attachment(name, options = {})
10
+ options.symbolize_keys!
11
+ name = name.to_sym
12
+
13
+ after_save :save_attachments
14
+ before_destroy :destroy_attachments
15
+
16
+ key :"#{name}_file_name", String
17
+ key :"#{name}_file_size", Integer
18
+ key :"#{name}_content_type", String
19
+ key :"#{name}_updated_at", Date
20
+
21
+ define_method("#{name}=") do |file|
22
+ information_for(name, options).assign(file)
23
+ end
24
+
25
+ define_method("#{name}") do
26
+ information_for(name, options)
27
+ end
28
+
29
+ validates_each name, :logic => lambda { information_for(name, options).send(:flush_errors) }
30
+ end
31
+
32
+ def validates_attachment_size(name = nil, options = {})
33
+ min = options[:greater_than] || (options[:in] && options[:in].first) || 0
34
+ max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
35
+ range = (min..max)
36
+ message = options[:message] || "file size must be between :min and :max bytes"
37
+ message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
38
+
39
+ validates_inclusion_of :"#{name}_file_size",
40
+ :within => range,
41
+ :message => message,
42
+ :allow_nil => true
43
+ end
44
+
45
+ def validates_attachment_presence(name = nil, options = {})
46
+ message = options[:message] || "must be set"
47
+ validates_presence_of :"#{name}_file_name",
48
+ :message => message,
49
+ :if => options[:if]
50
+ end
51
+
52
+ def validates_attachment_content_type(name = nil, options = {})
53
+ validation_options = options.dup
54
+ allowed_types = [validation_options[:content_type]].flatten
55
+ message = options[:message] || "is not one of #{allowed_types.join(", ")}"
56
+
57
+ validates_inclusion_of :"#{name}_content_type",
58
+ :within => allowed_types,
59
+ :message => message,
60
+ :allow_nil => true
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+ def information_for(name = nil, options = nil)
66
+ @attachment_options ||= {}
67
+ @attachment_options[name] ||= AttachmentOptions.new(self, name, options)
68
+ end
69
+
70
+ def save_attachments
71
+ @attachment_options.keys.each do |name|
72
+ @attachment_options[name].save
73
+ end
74
+ end
75
+
76
+ def destroy_attachments
77
+ @attachment_options.keys.each do |name|
78
+ @attachment_options[name].delete
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,169 @@
1
+ require 'wand'
2
+
3
+ class AttachmentOptions
4
+
5
+ attr_accessor :styles, :assigned_file, :object_id, :name
6
+
7
+ def initialize(model = nil, name = nil, options = {})
8
+ @model = model
9
+ @class_name = model.class.name.downcase
10
+ @object_id = model.id.to_s
11
+
12
+ @name = name
13
+ @attachment = name.to_s.downcase.pluralize
14
+
15
+ @url = set_url(options[:url], options[:default_url])
16
+ @path = set_path(options[:path])
17
+ @styles = set_styles(options[:styles])
18
+ @storage = set_storage(options[:storage])
19
+
20
+ if !@model.send("#{@name.to_s}_file_name").nil?
21
+ @filename = @model.send("#{@name.to_s}_file_name")
22
+ @extension = set_extension(@model.send("#{@name.to_s}_file_name"))
23
+ end
24
+
25
+ @queued_for_delete = set_queued_for_delete
26
+
27
+ @errors = Hash.new
28
+ end
29
+
30
+ def assign(file = nil)
31
+ @errors = {}
32
+
33
+ @filename = file_name(file)
34
+ @extension = set_extension(@filename)
35
+ file = file.tempfile if file.respond_to?(:tempfile)
36
+
37
+ @model.send("#{@name.to_s}_file_name=", @filename)
38
+ @model.send("#{@name.to_s}_file_size=", File.size(file))
39
+ @model.send("#{@name.to_s}_content_type=", Wand.wave(file.path))
40
+ @model.send("#{@name.to_s}_updated_at=", Time.now)
41
+
42
+ add_error('Could not resize file') unless file_is_resizale?
43
+ end
44
+
45
+ def url(style_name = 'original')
46
+ return nil unless @storage.is_a?(Filesystem)
47
+ interpolate(@url, style_name)
48
+ end
49
+
50
+ def path(style_name = 'original')
51
+ return nil unless @storage.is_a?(Filesystem)
52
+ interpolate(@path, style_name)
53
+ end
54
+
55
+ def save
56
+ unless @assigned_file.nil?
57
+ @storage.flush_delete(@queued_for_delete)
58
+ @storage.flush_write(self)
59
+ @queued_for_delete = set_queued_for_delete
60
+ end
61
+ end
62
+
63
+ def delete
64
+ @storage.flush_delete(@queued_for_delete)
65
+ end
66
+
67
+ def add_error(description = nil)
68
+ (@errors[:processing] ||= []) << description
69
+ end
70
+
71
+ def flush_errors
72
+ @errors.each do |error, message|
73
+ [message].flatten.each { |m| @model.errors.add(@name, m) }
74
+ end
75
+ end
76
+
77
+ def file_name(file = nil)
78
+ @assigned_file ||= file
79
+ @assigned_file.respond_to?(:original_filename) ? @assigned_file.original_filename : File.basename(@assigned_file.path)
80
+ end
81
+
82
+ def get_from_gridfs(style = 'original')
83
+ if @storage.is_a?(Gridfs)
84
+ @storage.read("#{@object_id}_#{@name}_#{style}")
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ def base64(style = 'original')
91
+ begin
92
+ bytes = nil
93
+
94
+ if @storage.is_a?(Gridfs)
95
+ bytes = get_from_gridfs(style).read
96
+ elsif @storage.is_a?(Filesystem)
97
+ bytes = File.open(path(style), 'rb').read
98
+ end
99
+
100
+ 'data:' + @model.send("#{@name.to_s}_content_type") + ';base64,' + Base64.encode64(bytes)
101
+ rescue Exception => exception
102
+ nil
103
+ end
104
+ end
105
+
106
+ private
107
+ def interpolate(source = nil, style_name = nil)
108
+ result = source.gsub(/\:rails_root/, Rails.root)
109
+ result.gsub!(/\:environment/, Rails.env)
110
+ result.gsub!(/\:filename/, @filename.nil? ? '' : @filename)
111
+ result.gsub!(/\:extension/, @extension.nil? ? '' : @extension)
112
+ result.gsub!(/\:style/, style_name.to_s)
113
+ result.gsub!(/\:id/, @object_id)
114
+ result.gsub!(/\:class/, @class_name)
115
+ result.gsub!(/\:attachment/, @attachment)
116
+ result.gsub!(/(\/){2,}/, '/')
117
+ result
118
+ end
119
+
120
+ def set_storage(storage_option = nil)
121
+ storage_option = 'filesystem' if storage_option.nil?
122
+ (storage_option == 'filesystem') ? Filesystem.new : Gridfs.new
123
+ end
124
+
125
+ def set_styles(style_option = nil)
126
+ style_option.nil? ? {} : style_option
127
+ end
128
+
129
+ def set_url(url_option = nil, default_url = nil)
130
+ @default_url = default_url
131
+ if url_option.nil?
132
+ (@default_url.nil? ? '/system/:attachment/:id/:style/:filename' : @default_url)
133
+ else
134
+ url_option
135
+ end
136
+ end
137
+
138
+ def set_path(path_option = nil)
139
+ path_option.nil? ? ':rails_root/public/system/:attachment/:id/:style/:filename' : path_option
140
+ end
141
+
142
+ def set_extension(source_path_option = nil)
143
+ source_path_option.nil? ? nil : File.extname(source_path_option)
144
+ end
145
+
146
+ def set_queued_for_delete
147
+ if @storage.is_a?(Filesystem)
148
+ [@styles.keys, :original].flatten.map do |style_name|
149
+ path(style_name) if File.exists?(path(style_name))
150
+ end.compact
151
+ else
152
+ [@styles.keys, :original].flatten.map do |style_name|
153
+ "#{@object_id}_#{@name}_#{style_name}"
154
+ end.compact
155
+ end
156
+ end
157
+
158
+ def file_is_resizale?
159
+ if @styles.keys.size > 0
160
+ begin
161
+ system("identify -format %wx%h #{@assigned_file.path}")
162
+ rescue Exception => exception
163
+ false
164
+ end
165
+ else
166
+ true
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,30 @@
1
+ class Filesystem < Storage
2
+
3
+ def flush_write(image_options = nil)
4
+ image_options.styles.each do |style_name, style_value|
5
+ begin
6
+ FileUtils.mkdir_p(File.dirname(image_options.path(style_name)))
7
+ resize(style_value, image_options.assigned_file.path).write(image_options.path(style_name))
8
+ FileUtils.chmod(0644, image_options.path(style_name))
9
+ rescue Exception => exception
10
+ image_options.add_error(exception.to_s)
11
+ end
12
+ end
13
+
14
+ unless image_options.styles.has_key?(:original)
15
+ begin
16
+ FileUtils.mkdir_p(File.dirname(image_options.path(:original)))
17
+ FileUtils.cp(image_options.assigned_file.path, image_options.path(:original))
18
+ rescue Exception => exception
19
+ image_options.add_error(exception.to_s)
20
+ end
21
+ end
22
+ end
23
+
24
+ def flush_delete(queued_for_delete = nil)
25
+ queued_for_delete.each do |file|
26
+ FileUtils.rm(file) if File.exist?(file)
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,34 @@
1
+ class Gridfs < Storage
2
+
3
+ def initialize
4
+ @grid ||= Mongo::Grid.new(MongoMapper.database)
5
+ end
6
+
7
+ def flush_write(image_options = nil)
8
+ image_options.styles.each do |style_name, style_value|
9
+ begin
10
+ gridfs_id = @grid.put(resize(style_value, image_options.assigned_file.path).to_blob, :filename => style_name.to_s + '_' + image_options.file_name, :_id => "#{image_options.object_id}_#{image_options.name}_#{style_name}")
11
+ rescue Exception => exception
12
+ image_options.add_error(exception.to_s)
13
+ end
14
+ end
15
+
16
+ begin
17
+ gridfs_id = @grid.put(image_options.assigned_file, :filename => 'original_' + image_options.file_name, :_id => "#{image_options.object_id}_#{image_options.name}_original")
18
+ rescue Exception => exception
19
+ image_options.add_error(exception.to_s)
20
+ end
21
+
22
+ end
23
+
24
+ def flush_delete(queued_for_delete = nil)
25
+ queued_for_delete.each do |id|
26
+ @grid.delete(id)
27
+ end
28
+ end
29
+
30
+ def read(id = nil)
31
+ @grid.get(id) unless id.nil?
32
+ end
33
+
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'RMagick'
2
+
3
+ class Storage
4
+
5
+ def resize(style_value = nil, filename = nil)
6
+ new_image = Magick::Image.read(filename).first
7
+ new_image.change_geometry!(style_value) { |cols, rows, img| img.resize!(cols, rows) }
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ require File.join(File.dirname(__FILE__), 'storage/storage')
2
+ require File.join(File.dirname(__FILE__), 'storage/filesystem')
3
+ require File.join(File.dirname(__FILE__), 'storage/gridfs')
@@ -0,0 +1,3 @@
1
+ module AttachIt
2
+ Version = '0.1.2'
3
+ end
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'attach_it', 'version')
2
+ require File.join(File.dirname(__FILE__), 'attach_it', 'attach_it')
3
+ require File.join(File.dirname(__FILE__), 'attach_it', 'attachment_options')
4
+ require File.join(File.dirname(__FILE__), 'attach_it', 'storage')
5
+
@@ -0,0 +1,134 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'mongo_mapper'
4
+ require 'shoulda'
5
+ require 'mocha'
6
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/mm_attach_it')
7
+
8
+ MongoMapper.database = "testing_mm_attach_it"
9
+
10
+ FakeRailsRoot = File.join(File.dirname(__FILE__) + '/tmp/')
11
+
12
+ class Test::Unit::TestCase
13
+ def setup
14
+ public_dir = FakeRailsRoot + 'public/'
15
+ FileUtils.rm_rf(public_dir) if File.exist?(public_dir)
16
+ FileUtils.mkdir_p(public_dir)
17
+ MongoMapper.database.collections.each(&:remove)
18
+ end
19
+ end
20
+
21
+ class UserOne
22
+ include MongoMapper::Document
23
+ plugin AttachIt
24
+ key :name, String
25
+ has_attachment :avatar
26
+ end
27
+
28
+ class UserTwo
29
+ include MongoMapper::Document
30
+ plugin AttachIt
31
+ key :name, String
32
+ has_attachment :avatar, { :url => '/assets/users/:id/:filename', :path => ':rails_root/public/assets/users/:id/:filename' }
33
+ end
34
+
35
+ class UserThree
36
+ include MongoMapper::Document
37
+ plugin AttachIt
38
+ key :name, String
39
+ has_attachment :avatar, { :styles => { :small => '150x150>', :medium => '300x300>' } }
40
+ end
41
+
42
+ class UserFour
43
+ include MongoMapper::Document
44
+ plugin AttachIt
45
+ key :name, String
46
+ has_attachment :avatar, { :default_url => '/images/default/avatar.jpg' }
47
+ end
48
+
49
+ class UserFive
50
+ include MongoMapper::Document
51
+ plugin AttachIt
52
+ key :name, String
53
+ has_attachment :avatar
54
+
55
+ validates_attachment_size :avatar, :less_than => 1.megabyte
56
+ end
57
+
58
+ class UserSix
59
+ include MongoMapper::Document
60
+ plugin AttachIt
61
+ key :name, String
62
+ has_attachment :avatar
63
+
64
+ validates_attachment_size :avatar, :less_than => 90.kilobytes
65
+ end
66
+
67
+ class UserSeven
68
+ include MongoMapper::Document
69
+ plugin AttachIt
70
+ key :name, String
71
+ has_attachment :avatar
72
+
73
+ validates_attachment_size :avatar, :greater_than => 90.kilobytes
74
+ end
75
+
76
+ class UserEight
77
+ include MongoMapper::Document
78
+ plugin AttachIt
79
+ key :name, String
80
+ has_attachment :avatar
81
+
82
+ validates_attachment_size :avatar, :greater_than => 1.megabyte
83
+ end
84
+
85
+ class UserNine
86
+ include MongoMapper::Document
87
+ plugin AttachIt
88
+ key :name, String
89
+ has_attachment :avatar
90
+
91
+ validates_attachment_presence :avatar
92
+ end
93
+
94
+ class UserTen
95
+ include MongoMapper::Document
96
+ plugin AttachIt
97
+ key :name, String
98
+ has_attachment :avatar
99
+
100
+ validates_attachment_content_type :avatar, :content_type => ['image/gif', 'image/png']
101
+ end
102
+
103
+ class UserEleven
104
+ include MongoMapper::Document
105
+ plugin AttachIt
106
+ key :name, String
107
+ has_attachment :avatar
108
+
109
+ validates_attachment_content_type :avatar, :content_type => ['image/jpg', 'image/jpeg']
110
+ end
111
+
112
+ class UserTwelve
113
+ include MongoMapper::Document
114
+ plugin AttachIt
115
+ key :name, String
116
+ has_attachment :avatar, { :styles => { :small => '150x150>', :medium => '300x300>' }, :storage => 'gridfs' }
117
+ end
118
+
119
+ class DocumentOne
120
+ include MongoMapper::Document
121
+ plugin AttachIt
122
+ key :name, String
123
+ has_attachment :document
124
+ end
125
+
126
+ class Rails
127
+ def self.env
128
+ 'test'
129
+ end
130
+
131
+ def self.root
132
+ File.join(File.dirname(__FILE__) + '/tmp/')
133
+ end
134
+ end
@@ -0,0 +1,289 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ JpgImageFile = File.join(File.dirname(__FILE__) + '/../fixtures/lake_moraine.jpg')
4
+ PdfFile = File.join(File.dirname(__FILE__) + '/../fixtures/example.pdf')
5
+ FakeImageFile = File.join(File.dirname(__FILE__) + '/../fixtures/fakeimage.jpg')
6
+
7
+ class TestAttachIt < Test::Unit::TestCase
8
+ context "#initialize" do
9
+ setup do
10
+ @user = UserOne.new
11
+ @user.name = 'Myname'
12
+ end
13
+
14
+ should "set instance of image_options" do
15
+ assert_instance_of AttachmentOptions, @user.avatar
16
+ end
17
+ end
18
+
19
+ context "Attaching an image on file system" do
20
+ setup do
21
+ @user = UserOne.new
22
+ @user.name = 'Myname'
23
+ @user.avatar = File.open(JpgImageFile, 'rb')
24
+ end
25
+
26
+ should "respond with path and url methods with default value" do
27
+ destiny_file = Rails.root + 'public/system/avatars/' + @user.id.to_s + '/original/lake_moraine.jpg'
28
+ assert_equal(destiny_file, @user.avatar.path)
29
+
30
+ destiny_url = '/system/avatars/' + @user.id.to_s + '/original/lake_moraine.jpg'
31
+ assert_equal(destiny_url, @user.avatar.url)
32
+ end
33
+
34
+ should "create file and informations columns after save" do
35
+ destiny_file = Rails.root + 'public/system/avatars/' + @user.id.to_s + '/original/lake_moraine.jpg'
36
+ @user.save
37
+ assert(File.exist?(destiny_file))
38
+ assert_equal('lake_moraine.jpg', @user.avatar_file_name)
39
+ assert_equal('image/jpeg', @user.avatar_content_type)
40
+ assert_equal(102520, @user.avatar_file_size)
41
+ assert_instance_of(Date, @user.avatar_updated_at)
42
+ end
43
+
44
+ should "retreive correct informations" do
45
+ destiny_file = Rails.root + 'public/system/avatars/' + @user.id.to_s + '/original/lake_moraine.jpg'
46
+ destiny_url = '/system/avatars/' + @user.id.to_s + '/original/lake_moraine.jpg'
47
+
48
+ @user.save
49
+
50
+ saved_user = UserOne.find(@user.id.to_s)
51
+ assert_equal(destiny_file, saved_user.avatar.path)
52
+ assert_equal(destiny_url, saved_user.avatar.url)
53
+ assert_equal('lake_moraine.jpg', saved_user.avatar_file_name)
54
+ assert_equal(102520, saved_user.avatar_file_size)
55
+ assert_equal('image/jpeg', saved_user.avatar_content_type)
56
+ assert_instance_of(Date, saved_user.avatar_updated_at)
57
+ assert_equal(@user.avatar_updated_at, saved_user.avatar_updated_at)
58
+ end
59
+
60
+ end
61
+
62
+
63
+ context "Attaching not an image on file system" do
64
+
65
+ end
66
+ setup do
67
+ @doc = DocumentOne.new
68
+ @doc.name = 'Mydocument'
69
+ @doc.document = File.open(PdfFile, 'rb')
70
+ end
71
+
72
+ should "respond with path and url methods with default value" do
73
+ destiny_file = Rails.root + 'public/system/documents/' + @doc.id.to_s + '/original/example.pdf'
74
+ assert_equal(destiny_file, @doc.document.path)
75
+
76
+ destiny_url = '/system/documents/' + @doc.id.to_s + '/original/example.pdf'
77
+ assert_equal(destiny_url, @doc.document.url)
78
+ end
79
+
80
+ should "create file and informations columns after save" do
81
+ destiny_file = Rails.root + 'public/system/documents/' + @doc.id.to_s + '/original/example.pdf'
82
+ @doc.save
83
+ assert(File.exist?(destiny_file))
84
+ assert_equal('example.pdf', @doc.document_file_name)
85
+ assert_equal('application/pdf', @doc.document_content_type)
86
+ assert_equal(9785, @doc.document_file_size)
87
+ assert_instance_of(Date, @doc.document_updated_at)
88
+ end
89
+
90
+ should "retreive correct informations from db" do
91
+ destiny_file = Rails.root + 'public/system/documents/' + @doc.id.to_s + '/original/example.pdf'
92
+ destiny_url = '/system/documents/' + @doc.id.to_s + '/original/example.pdf'
93
+
94
+ @doc.save
95
+
96
+ saved_doc = DocumentOne.find(@doc.id.to_s)
97
+ assert_equal(destiny_file, saved_doc.document.path)
98
+ assert_equal(destiny_url, saved_doc.document.url)
99
+ assert_equal('example.pdf', saved_doc.document_file_name)
100
+ assert_equal('application/pdf', saved_doc.document_content_type)
101
+ assert_equal(9785, saved_doc.document_file_size)
102
+ assert_instance_of(Date, saved_doc.document_updated_at)
103
+ assert_equal(@doc.document_updated_at, saved_doc.document_updated_at)
104
+ end
105
+ end
106
+
107
+ context "Creating differents sizes from an image" do
108
+ setup do
109
+ @user = UserThree.new
110
+ @user.name = 'Myname'
111
+ @user.avatar = File.open(JpgImageFile, 'rb')
112
+ end
113
+
114
+ should "respond with path and url methods by styles" do
115
+ [:small, :medium, :original].each do |style|
116
+ destiny_file = Rails.root + 'public/system/avatars/' + @user.id.to_s + '/' + style.to_s + '/lake_moraine.jpg'
117
+ assert_equal(destiny_file, @user.avatar.path(style))
118
+
119
+ destiny_url = '/system/avatars/' + @user.id.to_s + '/' + style.to_s + '/lake_moraine.jpg'
120
+ assert_equal(destiny_url, @user.avatar.url(style))
121
+ end
122
+ end
123
+
124
+ should "create file after save by styles" do
125
+ @user.save
126
+ [:small, :medium, :original].each do |style|
127
+ destiny_file = Rails.root + 'public/system/avatars/' + @user.id.to_s + '/' + style.to_s + '/lake_moraine.jpg'
128
+ assert(File.exist?(destiny_file))
129
+ end
130
+ end
131
+
132
+ should "return the base64 from file" do
133
+ @user.save
134
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64)
135
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('small'))
136
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('medium'))
137
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('original'))
138
+ end
139
+ end
140
+
141
+ context "Set the url and path" do
142
+ setup do
143
+ @user = UserTwo.new
144
+ @user.name = 'Myname'
145
+ @user.avatar = File.open(JpgImageFile, 'rb')
146
+ end
147
+
148
+ should "respond with path and url" do
149
+ destiny_file = Rails.root + 'public/assets/users/' + @user.id.to_s + '/lake_moraine.jpg'
150
+ assert_equal(destiny_file, @user.avatar.path)
151
+
152
+ destiny_url = '/assets/users/' + @user.id.to_s + '/lake_moraine.jpg'
153
+ assert_equal(destiny_url, @user.avatar.url)
154
+ end
155
+
156
+ should "retreive correct informations from db" do
157
+ destiny_file = Rails.root + 'public/assets/users/' + @user.id.to_s + '/lake_moraine.jpg'
158
+ destiny_url = '/assets/users/' + @user.id.to_s + '/lake_moraine.jpg'
159
+
160
+ @user.save
161
+
162
+ saved_user = UserTwo.find(@user.id.to_s)
163
+ assert_equal(destiny_file, saved_user.avatar.path)
164
+ assert_equal(destiny_url, saved_user.avatar.url)
165
+ end
166
+ end
167
+
168
+ context "Use default URL" do
169
+ setup do
170
+ @user = UserFour.new
171
+ @user.name = 'Myname'
172
+ end
173
+
174
+ should "retreive correct informations from db" do
175
+ destiny_url = '/images/default/avatar.jpg'
176
+
177
+ @user.save
178
+
179
+ saved_user = UserFour.find(@user.id.to_s)
180
+ assert_equal(destiny_url, saved_user.avatar.url)
181
+ end
182
+ end
183
+
184
+ context "Handle errors" do
185
+ setup do
186
+ end
187
+
188
+ should "have an error if file can't be resized" do
189
+ user = UserThree.new
190
+ user.name = 'Myname'
191
+ user.avatar = File.open(FakeImageFile)
192
+ user.save
193
+
194
+ assert_equal(user.errors.size, 1)
195
+ assert_equal(user.errors[:avatar].first, 'Could not resize file')
196
+ end
197
+
198
+ should "haven't an error if file size is less than a specific value" do
199
+ user = UserFive.new
200
+ user.name = 'Myname'
201
+ user.avatar = File.open(JpgImageFile, 'rb')
202
+ user.save
203
+
204
+ assert_equal(user.errors.size, 0)
205
+ end
206
+
207
+ should "have an error if file size is not less than a specific value" do
208
+ user = UserSix.new
209
+ user.name = 'Myname'
210
+ user.avatar = File.open(JpgImageFile, 'rb')
211
+ user.save
212
+
213
+ assert_equal(user.errors.size, 1)
214
+ assert_equal(user.errors[:avatar_file_size].first, 'file size must be between 0 and 92160 bytes')
215
+ end
216
+
217
+ should "haven't error if file size is greater than a specific value" do
218
+ user = UserSeven.new
219
+ user.name = 'Myname'
220
+ user.avatar = File.open(JpgImageFile, 'rb')
221
+ user.save
222
+
223
+ assert_equal(user.errors.size, 0)
224
+ end
225
+
226
+ should "have an error if file size is not greater than a specific value" do
227
+ user = UserEight.new
228
+ user.name = 'Myname'
229
+ user.avatar = File.open(JpgImageFile, 'rb')
230
+ user.save
231
+
232
+ assert_equal(user.errors.size, 1)
233
+ assert_equal(user.errors[:avatar_file_size].first, 'file size must be between 1048576 and Infinity bytes')
234
+ end
235
+
236
+ should "have an error if file size is not greater than a specific value" do
237
+ user = UserNine.new
238
+ user.name = 'Myname'
239
+ user.save
240
+
241
+ assert_equal(user.errors.size, 1)
242
+ assert_equal(user.errors[:avatar_file_name].first, 'must be set')
243
+ end
244
+
245
+ should "have an error if file content type is not one of the specifieds" do
246
+ user = UserTen.new
247
+ user.name = 'Myname'
248
+ user.avatar = File.open(JpgImageFile, 'rb')
249
+ user.save
250
+
251
+ assert_equal(user.errors.size, 1)
252
+ assert_equal(user.errors[:avatar_content_type].first, 'is not one of image/gif, image/png')
253
+ end
254
+
255
+ should "haven't error if file content type is one of the specifieds" do
256
+ user = UserEleven.new
257
+ user.name = 'Myname'
258
+ user.avatar = File.open(JpgImageFile, 'rb')
259
+ user.save
260
+
261
+ assert_equal(user.errors.size, 0)
262
+ end
263
+ end
264
+
265
+ context "Gridfs behavior" do
266
+ setup do
267
+ @user = UserTwelve.new
268
+ @user.name = 'Myname'
269
+ @user.avatar = File.open(JpgImageFile, 'rb')
270
+ @user.save
271
+ end
272
+
273
+ should "save the images resizeds and the original" do
274
+ assert_equal(@user.avatar.get_from_gridfs.class, Mongo::GridIO)
275
+ assert_equal(@user.avatar.get_from_gridfs('small').class, Mongo::GridIO)
276
+ assert_equal(@user.avatar.get_from_gridfs('medium').class, Mongo::GridIO)
277
+ assert_equal(@user.avatar.get_from_gridfs('original').class, Mongo::GridIO)
278
+ end
279
+
280
+ should "return the base64 from file" do
281
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64)
282
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('small'))
283
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('medium'))
284
+ assert_match(/^data:image\/jpeg;base64,/, @user.avatar.base64('original'))
285
+ end
286
+
287
+ end
288
+
289
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mm-attach-it
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
11
+ platform: ruby
12
+ authors:
13
+ - Adilson Chacon
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-30 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: wand
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: mongo_mapper
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: rmagick
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: mime-types
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: shoulda
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: mocha
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ type: :development
103
+ version_requirements: *id006
104
+ description: Attach files (images, videos, pdfs, txts, zips and etc) to a MongoMapper record. You can choose if you to store it on file system or GridFS.
105
+ email: adilsonchacon@gmail.com
106
+ executables: []
107
+
108
+ extensions: []
109
+
110
+ extra_rdoc_files:
111
+ - README
112
+ - README.rdoc
113
+ files:
114
+ - README
115
+ - README.rdoc
116
+ - LICENSE
117
+ - Rakefile
118
+ - Gemfile
119
+ - lib/mm-attach-it.rb
120
+ - lib/attach_it/version.rb
121
+ - lib/attach_it/storage/gridfs.rb
122
+ - lib/attach_it/storage/filesystem.rb
123
+ - lib/attach_it/storage/storage.rb
124
+ - lib/attach_it/attachment_options.rb
125
+ - lib/attach_it/attach_it.rb
126
+ - lib/attach_it/storage.rb
127
+ - test/test_helper.rb
128
+ - test/unit/test_attach_it.rb
129
+ homepage: https://github.com/adilsonchacon/mm-attach-it
130
+ licenses: []
131
+
132
+ post_install_message:
133
+ rdoc_options: []
134
+
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ hash: 3
143
+ segments:
144
+ - 0
145
+ version: "0"
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ hash: 3
152
+ segments:
153
+ - 0
154
+ version: "0"
155
+ requirements:
156
+ - ImageMagick
157
+ rubyforge_project: mm-attach_it
158
+ rubygems_version: 1.8.4
159
+ signing_key:
160
+ specification_version: 3
161
+ summary: MongoMapper Plugin File Attacher.
162
+ test_files:
163
+ - test/unit/test_attach_it.rb
164
+ - test/test_helper.rb