dm-fasten-the-seat-belt 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +0 -0
- data/LICENSE +20 -0
- data/Manifest.txt +22 -0
- data/README.txt +2 -0
- data/Rakefile +53 -0
- data/TODO +3 -0
- data/lib/dm-fasten-the-seat-belt.rb +199 -0
- data/lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/compression.rb +35 -0
- data/lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/version.rb +5 -0
- data/spec/classes.rb +2 -0
- data/spec/documents/classes.rb +10 -0
- data/spec/documents/files/document.pdf +0 -0
- data/spec/documents/files/word.doc +0 -0
- data/spec/documents/integration_spec.rb +53 -0
- data/spec/pictures/classes.rb +14 -0
- data/spec/pictures/files/children.jpg +0 -0
- data/spec/pictures/files/children.png +0 -0
- data/spec/pictures/files/sunshine.jpg +0 -0
- data/spec/pictures/integration_spec.rb +194 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +37 -0
- data/tasks/hoe.rb +39 -0
- metadata +107 -0
data/History.txt
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Maxime Guilbot for Ekohe Limited
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
lib/dm-fasten-the-seat-belt.rb
|
8
|
+
lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/compression.rb
|
9
|
+
lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/version.rb
|
10
|
+
spec/classes.rb
|
11
|
+
spec/documents/classes.rb
|
12
|
+
spec/documents/files/document.pdf
|
13
|
+
spec/documents/files/word.doc
|
14
|
+
spec/documents/integration_spec.rb
|
15
|
+
spec/pictures/classes.rb
|
16
|
+
spec/pictures/files/children.jpg
|
17
|
+
spec/pictures/files/children.png
|
18
|
+
spec/pictures/files/sunshine.jpg
|
19
|
+
spec/pictures/integration_spec.rb
|
20
|
+
spec/spec.opts
|
21
|
+
spec/spec_helper.rb
|
22
|
+
tasks/hoe.rb
|
data/README.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'spec'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
ROOT = Pathname(__FILE__).dirname.expand_path
|
8
|
+
require ROOT + 'lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/version'
|
9
|
+
|
10
|
+
PLUGIN = "dm-fasten-the-seat-belt"
|
11
|
+
PROJECT_NAME = NAME = "dm-fasten-the-s"
|
12
|
+
AUTHOR = "Maxime Guilbot"
|
13
|
+
EMAIL = "maxime [a] ekohe [d] com"
|
14
|
+
HOMEPAGE = PROJECT_URL = "http://github.com/maxime/dm-fasten-the-seat-belt"
|
15
|
+
SUMMARY = PROJECT_DESCRIPTION = PROJECT_SUMMARY = "A new merb plugin for adding image and file upload storage capabilities"
|
16
|
+
GEM_VERSION = DataMapper::FastenTheSeatBelt::VERSION
|
17
|
+
GEM_DEPENDENCIES = [["dm-core", GEM_VERSION], ["mini_magick", ">=1.2.3"]]
|
18
|
+
GEM_CLEAN = ["log", "pkg"]
|
19
|
+
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO ] }
|
20
|
+
|
21
|
+
GEM_NAME = "dm-fasten-the-seat-belt"
|
22
|
+
|
23
|
+
require 'tasks/hoe'
|
24
|
+
|
25
|
+
task :default => [ :spec ]
|
26
|
+
|
27
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
28
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
29
|
+
|
30
|
+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
31
|
+
task :install => [ :package ] do
|
32
|
+
sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
|
36
|
+
task :uninstall => [ :clobber ] do
|
37
|
+
sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Run specifications'
|
41
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
42
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
43
|
+
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
|
44
|
+
|
45
|
+
begin
|
46
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
47
|
+
t.rcov_opts << "--exclude 'config,spec,#{Gem::path.join(',')}'"
|
48
|
+
t.rcov_opts << '--text-summary'
|
49
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
50
|
+
rescue Exception
|
51
|
+
# rcov not installed
|
52
|
+
end
|
53
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'mini_magick'
|
5
|
+
|
6
|
+
gem 'dm-core', '~>0.9.8'
|
7
|
+
require 'dm-core'
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), 'dm-fasten-the-seat-belt', 'fasten-the-seat-belt','compression')
|
10
|
+
|
11
|
+
module DataMapper
|
12
|
+
module FastenTheSeatBelt
|
13
|
+
def self.included(base)
|
14
|
+
base.send(:extend, ClassMethods)
|
15
|
+
base.send(:include, InstanceMethods)
|
16
|
+
base.send(:include, Compression)
|
17
|
+
base.send(:include, MiniMagick)
|
18
|
+
base.class_eval do
|
19
|
+
attr_accessor :file
|
20
|
+
|
21
|
+
class << self; attr_accessor :fasten_the_seat_belt_options end
|
22
|
+
|
23
|
+
@fasten_the_seat_belt_options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
|
29
|
+
def fasten_the_seat_belt(options={})
|
30
|
+
# Properties
|
31
|
+
self.property :filename, String
|
32
|
+
self.property :size, Integer, :lazy => true
|
33
|
+
self.property :content_type, String, :lazy => true
|
34
|
+
self.property :created_at, DateTime, :lazy => true
|
35
|
+
self.property :updated_at, DateTime, :lazy => true
|
36
|
+
|
37
|
+
self.property :images_are_compressed, DataMapper::Types::Boolean, :lazy => true
|
38
|
+
|
39
|
+
# Callbacks to manage the file
|
40
|
+
before :save, :save_attributes
|
41
|
+
after :save, :save_file
|
42
|
+
after :destroy, :delete_directory
|
43
|
+
|
44
|
+
# Options
|
45
|
+
if !defined?(Merb) && !options[:file_system_path]
|
46
|
+
raise "If you're running dm-fasten-the-seat-belt outside of Merb, you must specifiy :file_system_path in the options"
|
47
|
+
end
|
48
|
+
|
49
|
+
options[:file_system_path] ||= File.join((defined?(Merb) ? Merb.root : ''), 'public', merb_environment, self.storage_name)
|
50
|
+
options[:thumbnails] ||= {}
|
51
|
+
|
52
|
+
self.fasten_the_seat_belt_options = options
|
53
|
+
end
|
54
|
+
|
55
|
+
def recreate_thumbnails!
|
56
|
+
all.each {|object| object.generate_thumbnails! }
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def merb_environment
|
61
|
+
(defined?(Merb) ? Merb.env : '')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module InstanceMethods
|
66
|
+
|
67
|
+
# Get file path
|
68
|
+
#
|
69
|
+
def path(thumb=nil)
|
70
|
+
File.join(web_directory_name, filename_for_thumbnail(thumb))
|
71
|
+
end
|
72
|
+
|
73
|
+
def absolute_path(thumb=nil)
|
74
|
+
File.join(complete_directory_name, filename_for_thumbnail(thumb))
|
75
|
+
end
|
76
|
+
|
77
|
+
def filename_for_thumbnail(thumb=nil)
|
78
|
+
if thumb != nil
|
79
|
+
basename = self.filename.gsub(/\.(.*)$/, '')
|
80
|
+
extension = self.filename.gsub(/^(.*)\./, '')
|
81
|
+
return basename + '_' + thumb.to_s + '.' + extension
|
82
|
+
else
|
83
|
+
return self.filename
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def save_attributes
|
88
|
+
return unless @file
|
89
|
+
# The following line is solving an IE6 problem. This removes the C:\Documents and Settings\.. shit
|
90
|
+
@file[:filename] = File.basename(@file[:filename].gsub(/\\/, '/')) if @file[:filename]
|
91
|
+
|
92
|
+
# Setup attributes
|
93
|
+
[:content_type, :size, :filename].each do |attribute|
|
94
|
+
self.send("#{attribute}=", @file[attribute])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def save_file
|
99
|
+
return unless self.filename and @file
|
100
|
+
|
101
|
+
create_directory
|
102
|
+
|
103
|
+
FileUtils.mv @file[:tempfile].path, complete_file_path if File.exists?(@file[:tempfile].path)
|
104
|
+
|
105
|
+
generate_thumbnails!
|
106
|
+
|
107
|
+
@file = nil
|
108
|
+
|
109
|
+
# By default, images are supposed to be compressed
|
110
|
+
self.images_are_compressed ||= true
|
111
|
+
end
|
112
|
+
|
113
|
+
def directory_name
|
114
|
+
# you can thank Jamis Buck for this: http://www.37signals.com/svn/archives2/id_partitioning.php
|
115
|
+
dir = ("%08d" % self.id).scan(/..../)
|
116
|
+
File.join(dir[0], dir[1])
|
117
|
+
end
|
118
|
+
|
119
|
+
def web_directory_name
|
120
|
+
raise "Can't return web directory name if not running in Merb" unless defined?(Merb)
|
121
|
+
unless complete_directory_name.include?(Merb.root)
|
122
|
+
raise "Can't return web directory name, the images aren't stored under the Merb application public directory"
|
123
|
+
end
|
124
|
+
|
125
|
+
complete_directory_name.gsub(/^#{Merb.root}\/public/, '')
|
126
|
+
end
|
127
|
+
|
128
|
+
def complete_directory_name
|
129
|
+
File.join(self.class.fasten_the_seat_belt_options[:file_system_path], directory_name)
|
130
|
+
end
|
131
|
+
|
132
|
+
def complete_file_path
|
133
|
+
File.join(complete_directory_name, (self.filename || ""))
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_directory
|
137
|
+
FileUtils.mkdir_p(complete_directory_name) unless FileTest.exists?(complete_directory_name)
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete_directory
|
141
|
+
FileUtils.rm_rf(complete_directory_name) if FileTest.exists?(complete_directory_name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def generate_thumbnails!
|
145
|
+
self.class.fasten_the_seat_belt_options[:thumbnails].each_pair do |key, value|
|
146
|
+
resize_to = value[:size]
|
147
|
+
quality = value[:quality].to_i
|
148
|
+
|
149
|
+
image = MiniMagick::Image.from_file(complete_file_path)
|
150
|
+
if value[:crop]
|
151
|
+
# tw, th are target width and target height
|
152
|
+
|
153
|
+
tw = resize_to.gsub(/([0-9]*)x([0-9]*)/, '\1').to_i
|
154
|
+
th = resize_to.gsub(/([0-9]*)x([0-9]*)/, '\2').to_i
|
155
|
+
|
156
|
+
# ow and oh are origin width and origin height
|
157
|
+
ow = image[:width]
|
158
|
+
oh = image[:height]
|
159
|
+
|
160
|
+
# iw and ih and the dimensions of the cropped picture before resizing
|
161
|
+
# there are 2 cases, iw = ow or ih = oh
|
162
|
+
# using iw / ih = tw / th, we can determine the other values
|
163
|
+
# we use the minimal values to determine the good case
|
164
|
+
iw = [ow, ((oh.to_f*tw.to_f) / th.to_f)].min
|
165
|
+
ih = [oh, ((ow.to_f*th.to_f) / tw.to_f)].min
|
166
|
+
|
167
|
+
# we calculate how much image we must crop
|
168
|
+
shave_width = ((ow.to_f - iw.to_f) / 2.0).to_i
|
169
|
+
shave_height = ((oh.to_f - ih.to_f) / 2.0).to_i
|
170
|
+
|
171
|
+
|
172
|
+
# specify the width of the region to be removed from both sides of the image and the height of the regions to be removed from top and bottom.
|
173
|
+
image.shave "#{shave_width}x#{shave_height}"
|
174
|
+
|
175
|
+
# resize of the pic
|
176
|
+
image.resize resize_to.to_s + "!"
|
177
|
+
else
|
178
|
+
# no cropping
|
179
|
+
image.resize resize_to
|
180
|
+
end
|
181
|
+
basename = self.filename.gsub(/\.(.*)$/, '')
|
182
|
+
extension = self.filename.gsub(/^(.*)\./, '')
|
183
|
+
|
184
|
+
thumb_filename = File.join(complete_directory_name, (basename + '_' + key.to_s + '.' + extension))
|
185
|
+
|
186
|
+
# Delete thumbnail if exists
|
187
|
+
File.delete(thumb_filename) if File.exists?(thumb_filename)
|
188
|
+
image.write thumb_filename
|
189
|
+
|
190
|
+
next if self.images_are_compressed == false
|
191
|
+
|
192
|
+
if quality and quality!=0 and quality < 100
|
193
|
+
compress_jpeg(thumb_filename, quality)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end # FastenTheSeatBelt
|
199
|
+
end # DataMapper
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module FastenTheSeatBelt
|
3
|
+
module Compression
|
4
|
+
COMPRESSABLE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/pjpeg"]
|
5
|
+
|
6
|
+
def compressable?
|
7
|
+
COMPRESSABLE_MIME_TYPES.include?(self.content_type)
|
8
|
+
end
|
9
|
+
|
10
|
+
def dont_compress_now!
|
11
|
+
self.images_are_compressed = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def compress_jpeg(filename, quality)
|
15
|
+
if compressable?
|
16
|
+
system("jpegoptim -m#{quality} -q --strip-all \"#{filename}\" &> /dev/null")
|
17
|
+
else
|
18
|
+
puts "FastenTheSeatBelt says: Quality setting not supported for #{self.content_type} files"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def compress_now!
|
23
|
+
return false if self.images_are_compressed
|
24
|
+
self.class.fasten_the_seat_belt_options[:thumbnails].each_pair do |key, value|
|
25
|
+
thumb_filename = absolute_path(key)
|
26
|
+
|
27
|
+
compress_jpeg(thumb_filename, value[:quality].to_i) if value[:quality] and value[:quality].to_i < 100
|
28
|
+
end
|
29
|
+
|
30
|
+
self.update_attributes(:images_are_compressed => true)
|
31
|
+
end
|
32
|
+
|
33
|
+
end # Compression
|
34
|
+
end # FastenTheSeatBelt
|
35
|
+
end # DataMapper
|
data/spec/classes.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
class Document
|
2
|
+
include DataMapper::Resource
|
3
|
+
include DataMapper::FastenTheSeatBelt
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
|
7
|
+
fasten_the_seat_belt :file_system_path => File.dirname(__FILE__) + '/storage/documents'
|
8
|
+
|
9
|
+
validates_present :file, :if => Proc.new{|resource| resource.new_record?}
|
10
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
4
|
+
|
5
|
+
describe DataMapper::FastenTheSeatBelt, "outside of Merb" do
|
6
|
+
before :all do
|
7
|
+
@pdf_original_file_path = File.dirname(__FILE__) + '/files/document.pdf'
|
8
|
+
@pdf_tempfile = Tempfile.new('document.pdf')
|
9
|
+
FileUtils.copy(@pdf_original_file_path, @pdf_tempfile.path)
|
10
|
+
|
11
|
+
@pdf = Document.new(:file => {:filename => 'document.pdf',
|
12
|
+
:content_type => 'application/pdf',
|
13
|
+
:tempfile => @pdf_tempfile})
|
14
|
+
# Expected file paths
|
15
|
+
@main_file_path = File.join(File.dirname(__FILE__), 'storage', 'documents', '0000', '0001', 'document.pdf')
|
16
|
+
end
|
17
|
+
|
18
|
+
after :all do
|
19
|
+
@pdf_tempfile.close!
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be able to attach pdf documents" do
|
23
|
+
@pdf.should be_valid
|
24
|
+
@pdf.save.should == true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be able to attach a word document" do
|
28
|
+
@doc_original_file_path = File.dirname(__FILE__) + '/files/word.doc'
|
29
|
+
@doc_tempfile = Tempfile.new('word.doc')
|
30
|
+
FileUtils.copy(@doc_original_file_path, @doc_tempfile.path)
|
31
|
+
|
32
|
+
@doc = Document.new(:file => {:filename => 'word.doc',
|
33
|
+
:content_type => 'application/msword',
|
34
|
+
:tempfile => @doc_tempfile})
|
35
|
+
|
36
|
+
@doc.should be_valid
|
37
|
+
@doc.save.should == true
|
38
|
+
File.exists?(File.join(File.dirname(__FILE__), 'storage', 'documents', '0000', '0002', 'word.doc')).should == true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be able to save the document file in the right place" do
|
42
|
+
File.exists?(@main_file_path).should == true
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to return the path of a document" do
|
46
|
+
File.expand_path(@pdf.absolute_path).should == @main_file_path
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to delete the files if the object is destroyed" do
|
50
|
+
@pdf.destroy
|
51
|
+
File.exists?(@main_file_path).should == false
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Picture
|
2
|
+
include DataMapper::Resource
|
3
|
+
include DataMapper::FastenTheSeatBelt
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
|
7
|
+
fasten_the_seat_belt :thumbnails => {
|
8
|
+
:small => {:size => "232x232", :crop => true, :quality => 100},
|
9
|
+
:large => {:size => "800x600", :quality => 90},
|
10
|
+
},
|
11
|
+
:file_system_path => File.dirname(__FILE__) + '/storage/pictures'
|
12
|
+
|
13
|
+
validates_present :file, :if => Proc.new{|resource| resource.new_record?}
|
14
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'mini_magick'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
5
|
+
|
6
|
+
describe DataMapper::FastenTheSeatBelt, "outside of Merb" do
|
7
|
+
before :all do
|
8
|
+
@children_original_file_path = File.dirname(__FILE__) + '/files/children.jpg'
|
9
|
+
@children_tempfile = Tempfile.new('children.jpg')
|
10
|
+
FileUtils.copy(@children_original_file_path, @children_tempfile.path)
|
11
|
+
|
12
|
+
@children_picture = Picture.new(:file => {:filename => 'children.jpg',
|
13
|
+
:content_type => 'image/jpeg',
|
14
|
+
:tempfile => @children_tempfile})
|
15
|
+
# Expected file paths
|
16
|
+
@main_file_path = File.join(File.dirname(__FILE__), 'storage', 'pictures', '0000', '0001', 'children.jpg')
|
17
|
+
@large_thumbnail_file_path = File.join(File.dirname(__FILE__), 'storage', 'pictures', '0000', '0001', 'children_large.jpg')
|
18
|
+
@small_thumbnail_file_path = File.join(File.dirname(__FILE__), 'storage', 'pictures', '0000', '0001', 'children_small.jpg')
|
19
|
+
end
|
20
|
+
|
21
|
+
after :all do
|
22
|
+
@children_tempfile.close!
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be able to attach pictures" do
|
26
|
+
@children_picture.should be_valid
|
27
|
+
@children_picture.save.should == true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should create the pictures in the file system with the right dimensions and the right path" do
|
31
|
+
# original file
|
32
|
+
File.exists?(@main_file_path).should == true
|
33
|
+
File.size(@main_file_path).should == File.size(@children_original_file_path)
|
34
|
+
|
35
|
+
# thumbnails
|
36
|
+
|
37
|
+
# large thumbnail
|
38
|
+
File.exists?(@large_thumbnail_file_path).should == true
|
39
|
+
large_thumbnail = MiniMagick::Image.from_file(@large_thumbnail_file_path)
|
40
|
+
large_thumbnail[:width].should == 800
|
41
|
+
large_thumbnail[:height].should == 533
|
42
|
+
|
43
|
+
# small thumbnail
|
44
|
+
File.exists?(@small_thumbnail_file_path).should == true
|
45
|
+
small_thumbnail = MiniMagick::Image.from_file(@small_thumbnail_file_path)
|
46
|
+
small_thumbnail[:width].should == 232
|
47
|
+
small_thumbnail[:height].should == 232
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be able to recreate the thumbnails of the pictures" do
|
51
|
+
File.delete(@large_thumbnail_file_path)
|
52
|
+
File.delete(@small_thumbnail_file_path)
|
53
|
+
|
54
|
+
File.exists?(@large_thumbnail_file_path).should == false
|
55
|
+
File.exists?(@small_thumbnail_file_path).should == false
|
56
|
+
|
57
|
+
Picture.recreate_thumbnails!
|
58
|
+
|
59
|
+
File.exists?(@large_thumbnail_file_path).should == true
|
60
|
+
File.exists?(@small_thumbnail_file_path).should == true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be able to get the image and thumbnail absolute paths" do
|
64
|
+
File.expand_path(@children_picture.absolute_path).should == @main_file_path
|
65
|
+
File.expand_path(@children_picture.absolute_path(:small)).should == @small_thumbnail_file_path
|
66
|
+
File.expand_path(@children_picture.absolute_path(:large)).should == @large_thumbnail_file_path
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should support delayed compression" do
|
70
|
+
new_picture_tempfile = Tempfile.new('children.jpg')
|
71
|
+
FileUtils.copy(@children_original_file_path, new_picture_tempfile.path)
|
72
|
+
|
73
|
+
new_picture = Picture.new(:file => {:filename => 'children.jpg',
|
74
|
+
:content_type => 'image/jpeg',
|
75
|
+
:tempfile => new_picture_tempfile})
|
76
|
+
new_picture.dont_compress_now!
|
77
|
+
new_picture.save
|
78
|
+
|
79
|
+
new_picture.reload
|
80
|
+
|
81
|
+
# Image shouldn't be compressed yet
|
82
|
+
new_picture.images_are_compressed.should == false
|
83
|
+
|
84
|
+
size_before_compression = File.size(new_picture.absolute_path(:large))
|
85
|
+
|
86
|
+
# Compress now
|
87
|
+
new_picture.compress_now!
|
88
|
+
new_picture.reload
|
89
|
+
|
90
|
+
size_after_compression = File.size(new_picture.absolute_path(:large))
|
91
|
+
|
92
|
+
# Compressed image should be smaller in size
|
93
|
+
size_before_compression.should > size_after_compression
|
94
|
+
|
95
|
+
# Image should be marked as compressed
|
96
|
+
new_picture.images_are_compressed.should == true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should be able to delete the files if the object is destroyed" do
|
100
|
+
@children_picture.destroy
|
101
|
+
File.exists?(@main_file_path).should == false
|
102
|
+
|
103
|
+
File.exists?(@large_thumbnail_file_path).should == false
|
104
|
+
|
105
|
+
File.exists?(@small_thumbnail_file_path).should == false
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should raise an error if it's not running in Merb and if the file_system_path isn't present" do
|
109
|
+
lambda {
|
110
|
+
class BrokenPicture
|
111
|
+
include DataMapper::Resource
|
112
|
+
include DataMapper::FastenTheSeatBelt
|
113
|
+
|
114
|
+
property :id, Serial
|
115
|
+
fasten_the_seat_belt
|
116
|
+
end
|
117
|
+
}.should raise_error("If you're running dm-fasten-the-seat-belt outside of Merb, you must specifiy :file_system_path in the options")
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe DataMapper::FastenTheSeatBelt, "inside Merb" do
|
123
|
+
before :all do
|
124
|
+
module Merb
|
125
|
+
def self.root
|
126
|
+
File.join(File.dirname(__FILE__), 'merb')
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.env
|
130
|
+
"production"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Image
|
135
|
+
include DataMapper::Resource
|
136
|
+
include DataMapper::FastenTheSeatBelt
|
137
|
+
|
138
|
+
property :id, Serial
|
139
|
+
|
140
|
+
fasten_the_seat_belt :thumbnails => {:small => {:size => "320x240", :quality => 99}}
|
141
|
+
end
|
142
|
+
|
143
|
+
Image.auto_migrate!
|
144
|
+
|
145
|
+
@sunshine_original_file_path = File.dirname(__FILE__) + '/files/sunshine.jpg'
|
146
|
+
@sunshine_tempfile = Tempfile.new('sunshine.jpg')
|
147
|
+
FileUtils.copy(@sunshine_original_file_path, @sunshine_tempfile.path)
|
148
|
+
|
149
|
+
@sunshine_image = Image.new(:file => {:filename => 'sunshine.jpg',
|
150
|
+
:content_type => 'image/jpeg',
|
151
|
+
:tempfile => @sunshine_tempfile})
|
152
|
+
# Expected file paths
|
153
|
+
@main_file_path = File.join(File.dirname(__FILE__), 'merb', 'public', 'production', 'images', '0000', '0001', 'sunshine.jpg')
|
154
|
+
@small_thumbnail_file_path = File.join(File.dirname(__FILE__), 'merb', 'public', 'production', 'images', '0000', '0001', 'sunshine_small.jpg')
|
155
|
+
end
|
156
|
+
|
157
|
+
after :all do
|
158
|
+
@sunshine_tempfile.close!
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should be able to attach images" do
|
162
|
+
@sunshine_image.save.should == true
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should store the files under the public/production/images directory by default" do
|
166
|
+
File.expand_path(@sunshine_image.absolute_path).should == @main_file_path
|
167
|
+
File.expand_path(@sunshine_image.absolute_path(:small)).should == @small_thumbnail_file_path
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should be able to return the web path" do
|
171
|
+
@sunshine_image.path.should == '/production/images/0000/0001/sunshine.jpg'
|
172
|
+
@sunshine_image.path(:small).should == '/production/images/0000/0001/sunshine_small.jpg'
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should raise an error if we try to get a web path and if the pictures aren't in the public directory" do
|
176
|
+
class Image
|
177
|
+
fasten_the_seat_belt :thumbnails => {:small => {:size => "320x240", :quality => 99}}, :file_system_path => '/'
|
178
|
+
end
|
179
|
+
|
180
|
+
lambda { @sunshine_image.path }.should raise_error("Can't return web directory name, the images aren't stored under the Merb application public directory")
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should output 'Quality not supported' if we try to apply a quality setting on a non-jpeg image" do
|
184
|
+
@png_original_file_path = File.dirname(__FILE__) + '/files/children.png'
|
185
|
+
@png_tempfile = Tempfile.new('children.png')
|
186
|
+
FileUtils.copy(@png_original_file_path, @png_tempfile.path)
|
187
|
+
|
188
|
+
@png_image = Image.new(:file => {:filename => 'children.png',
|
189
|
+
:content_type => 'image/png',
|
190
|
+
:tempfile => @png_tempfile})
|
191
|
+
@png_image.should_receive(:puts).with("FastenTheSeatBelt says: Quality setting not supported for image/png files")
|
192
|
+
@png_image.save
|
193
|
+
end
|
194
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
$TESTING=true
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'rspec', '>=1.1.3'
|
4
|
+
require 'spec'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
gem 'dm-validations'
|
8
|
+
require 'dm-validations'
|
9
|
+
|
10
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-fasten-the-seat-belt'
|
11
|
+
|
12
|
+
def load_driver(name, default_uri)
|
13
|
+
return false if ENV['ADAPTER'] != name.to_s
|
14
|
+
|
15
|
+
lib = "do_#{name}"
|
16
|
+
|
17
|
+
begin
|
18
|
+
gem lib, '>=0.9.10'
|
19
|
+
require lib
|
20
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
21
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
22
|
+
true
|
23
|
+
rescue Gem::LoadError => e
|
24
|
+
warn "Could not load #{lib}: #{e}"
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
30
|
+
|
31
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
32
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_fasten_the_seat_belt_test')
|
33
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_fasten_the_seat_belt_test')
|
34
|
+
|
35
|
+
require File.dirname(__FILE__) + '/classes'
|
36
|
+
|
37
|
+
DataMapper.auto_migrate!
|
data/tasks/hoe.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
|
3
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
4
|
+
@config = nil
|
5
|
+
RUBYFORGE_USERNAME = "unknown"
|
6
|
+
def rubyforge_username
|
7
|
+
unless @config
|
8
|
+
begin
|
9
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
10
|
+
rescue
|
11
|
+
puts <<-EOS
|
12
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
13
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
14
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
15
|
+
EOS
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
20
|
+
end
|
21
|
+
|
22
|
+
hoe = Hoe.new(GEM_NAME, GEM_VERSION) do |p|
|
23
|
+
|
24
|
+
p.developer(AUTHOR, EMAIL)
|
25
|
+
|
26
|
+
p.description = PROJECT_DESCRIPTION
|
27
|
+
p.summary = PROJECT_SUMMARY
|
28
|
+
p.url = PROJECT_URL
|
29
|
+
|
30
|
+
p.rubyforge_name = PROJECT_NAME if PROJECT_NAME
|
31
|
+
|
32
|
+
p.clean_globs |= GEM_CLEAN
|
33
|
+
p.spec_extras = GEM_EXTRAS if GEM_EXTRAS
|
34
|
+
|
35
|
+
GEM_DEPENDENCIES.each do |dep|
|
36
|
+
p.extra_deps << dep
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-fasten-the-seat-belt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.9
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maxime Guilbot
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-08 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: dm-core
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.9
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mini_magick
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.3
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.2
|
44
|
+
version:
|
45
|
+
description: A new merb plugin for adding image and file upload storage capabilities
|
46
|
+
email:
|
47
|
+
- maxime [a] ekohe [d] com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- README.txt
|
54
|
+
- LICENSE
|
55
|
+
- TODO
|
56
|
+
files:
|
57
|
+
- History.txt
|
58
|
+
- LICENSE
|
59
|
+
- Manifest.txt
|
60
|
+
- README.txt
|
61
|
+
- Rakefile
|
62
|
+
- TODO
|
63
|
+
- lib/dm-fasten-the-seat-belt.rb
|
64
|
+
- lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/compression.rb
|
65
|
+
- lib/dm-fasten-the-seat-belt/fasten-the-seat-belt/version.rb
|
66
|
+
- spec/classes.rb
|
67
|
+
- spec/documents/classes.rb
|
68
|
+
- spec/documents/files/document.pdf
|
69
|
+
- spec/documents/files/word.doc
|
70
|
+
- spec/documents/integration_spec.rb
|
71
|
+
- spec/pictures/classes.rb
|
72
|
+
- spec/pictures/files/children.jpg
|
73
|
+
- spec/pictures/files/children.png
|
74
|
+
- spec/pictures/files/sunshine.jpg
|
75
|
+
- spec/pictures/integration_spec.rb
|
76
|
+
- spec/spec.opts
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
- tasks/hoe.rb
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: http://github.com/maxime/dm-fasten-the-seat-belt
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options:
|
83
|
+
- --main
|
84
|
+
- README.txt
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: "0"
|
92
|
+
version:
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: "0"
|
98
|
+
version:
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project: dm-fasten-the-s
|
102
|
+
rubygems_version: 1.3.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 2
|
105
|
+
summary: A new merb plugin for adding image and file upload storage capabilities
|
106
|
+
test_files: []
|
107
|
+
|