has_image 0.1.0
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/CHANGELOG +3 -0
- data/FAQ +25 -0
- data/MIT-LICENSE +20 -0
- data/README +139 -0
- data/Rakefile +47 -0
- data/init.rb +1 -0
- data/lib/has_image/processor.rb +76 -0
- data/lib/has_image/storage.rb +167 -0
- data/lib/has_image/view_helpers.rb +39 -0
- data/lib/has_image.rb +242 -0
- data/test/processor_test.rb +49 -0
- data/test/storage_test.rb +98 -0
- data/test_rails/database.yml +3 -0
- data/test_rails/fixtures/bad_image.jpg +0 -0
- data/test_rails/fixtures/image.jpg +0 -0
- data/test_rails/fixtures/image.png +0 -0
- data/test_rails/pic.rb +3 -0
- data/test_rails/pic_test.rb +79 -0
- data/test_rails/schema.rb +9 -0
- data/test_rails/test_helper.rb +54 -0
- metadata +76 -0
data/CHANGELOG
ADDED
data/FAQ
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
= Frequently Asked Questions
|
2
|
+
|
3
|
+
HasImage is too new to have many FAQ items yet. {Ask
|
4
|
+
me}[mailto:norman@randomba.org] and they will be included; this is a work in
|
5
|
+
progress.
|
6
|
+
|
7
|
+
= How do I validate the mime type of my uploaded images?
|
8
|
+
|
9
|
+
You don't. Rather than examine the mime type, HasImage runs the "identify"
|
10
|
+
command on the file to determine if it is processable by ImageMagick, and if
|
11
|
+
it is, converts it to the format you specify, which defaults to JPEG.
|
12
|
+
|
13
|
+
This is better than checking for mime types, because your users may upload
|
14
|
+
exotic image types that you didn't even realize would work, such as Truevision
|
15
|
+
Targa images, or Seattle Film Works files.
|
16
|
+
|
17
|
+
If you wish to give users a list of file types they can upload, a good start
|
18
|
+
would be jpeg, png, bmp, and maybe gif and ttf if your installation of
|
19
|
+
ImageMagick understands them. You can find out what image types your
|
20
|
+
ImageMagick understands by running:
|
21
|
+
|
22
|
+
identify -list format
|
23
|
+
|
24
|
+
Ideally, if your users just upload files that "look like" images on their
|
25
|
+
computers, it HasImage should "just work."
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [name of plugin creator]
|
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/README
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
= HasImage[http://github.com/norman/has_image] -- Image attachment gem/plugin for Ruby on Rails
|
2
|
+
|
3
|
+
HasImage[http://github.com/norman/has_image] was created as a smaller,
|
4
|
+
simpler, lighter alternative to
|
5
|
+
attachment_fu[http://github.com/technoweenie/attachment_fu] for applications
|
6
|
+
that need to handle uploaded images.
|
7
|
+
|
8
|
+
It creates only one database record per image, requires only one column in
|
9
|
+
your model, and creates great-looking fixed-dimension thumbnails by using
|
10
|
+
{ImageMagick's}[http://www.imagemagick.org/]
|
11
|
+
resize[http://www.imagemagick.org/script/command-line-options.php#resize],
|
12
|
+
crop[http://www.imagemagick.org/script/command-line-options.php#crop] and
|
13
|
+
gravity[http://www.imagemagick.org/script/command-line-options.php#gravity]
|
14
|
+
functions.
|
15
|
+
|
16
|
+
Some typical use cases are: websites that want to create photo galleries with
|
17
|
+
fixed-dimension thumbnails, or that want to store user profile pictures
|
18
|
+
without creating a separate model for the images.
|
19
|
+
|
20
|
+
It supports only filesystem storage, and uses only MiniMagick[http://github.com/probablycorey/mini_magick] to process
|
21
|
+
images. However, the codebase is very small, simple, readable, and hackable.
|
22
|
+
So it should be easy to modify or enhance its functionality with different
|
23
|
+
storage or processor options.
|
24
|
+
|
25
|
+
== Another image attachment library? Why?
|
26
|
+
|
27
|
+
<em>The three chief virtues of a programmer are: Laziness, Impatience and Hubris.</em> - {Larry Wall}[http://en.wikipedia.org/wiki/Larry_Wall]
|
28
|
+
|
29
|
+
Attachment_fu is too large and general for some of the places I want to use
|
30
|
+
images. I sometimes found myself writing more code to hack attachment_fu than
|
31
|
+
it took to create this gem. In fact, most of the code here has been plucked
|
32
|
+
from my various projects that use attachment_fu.
|
33
|
+
|
34
|
+
The other image attachment libraries I found fell short of my needs for
|
35
|
+
various other reasons, so I decided to roll my own.
|
36
|
+
|
37
|
+
== Examples
|
38
|
+
|
39
|
+
Point-and-drool use case. It's probably not what you want, but it may be
|
40
|
+
useful for bootstrapping.
|
41
|
+
|
42
|
+
class Member < ActiveRecord::Base
|
43
|
+
has_image
|
44
|
+
end
|
45
|
+
|
46
|
+
Single image, no thumbnails, with some size limits:
|
47
|
+
|
48
|
+
class Picture < ActiveRecord::Base
|
49
|
+
has_image :resize_to => "200x200",
|
50
|
+
:max_size => 3.megabytes,
|
51
|
+
:min_size => 4.kilobytes
|
52
|
+
end
|
53
|
+
|
54
|
+
Image with some thumbnails:
|
55
|
+
|
56
|
+
class Photo < ActiveRecord::Base
|
57
|
+
has_image :resize_to => "640x480",
|
58
|
+
:thumbnails => {
|
59
|
+
:square => "200x200",
|
60
|
+
:medium => "320x240"
|
61
|
+
},
|
62
|
+
:max_size => 3.megabytes,
|
63
|
+
:min_size => 4.kilobytes
|
64
|
+
end
|
65
|
+
|
66
|
+
It also provides a view helper to make displaying the images extremely simple:
|
67
|
+
|
68
|
+
<%= image_tag_for(@photo, :thumb => :square) %>
|
69
|
+
|
70
|
+
== Getting it
|
71
|
+
|
72
|
+
Has image can be installed as a gem, or as a Rails plugin. Gem installation
|
73
|
+
is easiest, and recommended:
|
74
|
+
|
75
|
+
gem install norman-has_image --source http://gems.github.com
|
76
|
+
|
77
|
+
and add
|
78
|
+
|
79
|
+
require 'has_image'
|
80
|
+
|
81
|
+
to your environment.rb file.
|
82
|
+
|
83
|
+
Alternatively, you can install it as a Rails plugin:
|
84
|
+
|
85
|
+
./script plugin install git://github.com/norman/has_image.git
|
86
|
+
|
87
|
+
Rails versions before 2.1 do not support plugin installation using Git, so if
|
88
|
+
you're on 2.0 (or earlier), then please install the gem rather than the
|
89
|
+
plugin.
|
90
|
+
|
91
|
+
Then, make sure the model has a column named "has_image_file."
|
92
|
+
|
93
|
+
{Git repository}[http://github.com/norman/has_image]:
|
94
|
+
|
95
|
+
git://github.com/norman/has_image.git
|
96
|
+
|
97
|
+
|
98
|
+
== Hacking it
|
99
|
+
|
100
|
+
Don't like the way it makes images? Want to pipe the images through some
|
101
|
+
{crazy fast seam carving library written in
|
102
|
+
OCaml}[http://eigenclass.org/hiki/seam-carving-in-ocaml], or watermark them
|
103
|
+
with your corporate logo? Happiness is just a monkey-patch[http://en.wikipedia.org/wiki/Monkey_patch] away:
|
104
|
+
|
105
|
+
module HasImage
|
106
|
+
class Processor
|
107
|
+
def resize_image(size)
|
108
|
+
# your new-and-improved thumbnailer code goes here.
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
HasImage[http://github.com/norman/has_image] follows a philosophy of "{skinny
|
114
|
+
model, fat plugin}[http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model]."
|
115
|
+
This means that it tries to pollute your ActiveRecord model with as little
|
116
|
+
functionality as possible, so that in a sense, the model is acts like a
|
117
|
+
"controller" and the plugin like a "model" as regards the image handling
|
118
|
+
functionality. This makes it easier to test, hack, and reuse, because the
|
119
|
+
storage and processing functionality is largely independent of your model, and
|
120
|
+
of Rails.
|
121
|
+
|
122
|
+
My goal for HasImage[http://github.com/norman/has_image] is to keep it very
|
123
|
+
small. If you need <strong>a lot</strong> of functionality that's not here, instead of patching
|
124
|
+
this code, you will likely be better off using
|
125
|
+
attachment_fu[http://github.com/technoweenie/attachment_fu], which is much
|
126
|
+
more powerful, but also more complex.
|
127
|
+
|
128
|
+
== Bugs
|
129
|
+
|
130
|
+
Please report them on Lighthouse[http://randomba.lighthouseapp.com/projects/14674-has_image].
|
131
|
+
|
132
|
+
At the time of writing (July 2008),
|
133
|
+
HasImage[http://github.com/norman/has_image] is in its infancy. Your patches,
|
134
|
+
bug reports and withering criticism are more than welcome.
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
Copyright (c) 2008 {Norman Clarke}[mailto:norman@randomba.org], released under
|
139
|
+
the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the non-Rails part of has_image.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Test the Rails part of has_image.'
|
17
|
+
Rake::TestTask.new(:test_rails) do |t|
|
18
|
+
t.libs << 'lib'
|
19
|
+
t.libs << 'test_rails'
|
20
|
+
t.pattern = 'test_rails/**/*_test.rb'
|
21
|
+
t.verbose = true
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Run rcov"
|
25
|
+
task :rcov do
|
26
|
+
rm_f "coverage"
|
27
|
+
rm_f "coverage.data"
|
28
|
+
if PLATFORM =~ /darwin/
|
29
|
+
exclude = '--exclude "gems"'
|
30
|
+
else
|
31
|
+
exclude = '--exclude "rubygems"'
|
32
|
+
end
|
33
|
+
rcov = "rcov --rails -Ilib:test --sort coverage --text-report #{exclude} --no-validator-links"
|
34
|
+
cmd = "#{rcov} #{Dir["test/**/*.rb"].join(" ")}"
|
35
|
+
sh cmd
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Generate documentation for has_image.'
|
39
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = 'HasImage'
|
42
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '-c UTF-8'
|
43
|
+
rdoc.rdoc_files.include('README')
|
44
|
+
rdoc.rdoc_files.include('FAQ')
|
45
|
+
rdoc.rdoc_files.include('CHANGELOG')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'has_image'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
|
3
|
+
module HasImage
|
4
|
+
|
5
|
+
# Image processing functionality for the HasImage gem.
|
6
|
+
class Processor
|
7
|
+
|
8
|
+
attr_accessor :options
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Arg should be either a file, or a path. This runs ImageMagick's
|
12
|
+
# "identify" command and looks for an exit status indicating an error. If
|
13
|
+
# there is no error, then ImageMagick has identified the file as something
|
14
|
+
# it can work with and it will be converted to the desired output format.
|
15
|
+
def valid?(arg)
|
16
|
+
arg.close if arg.respond_to?(:close) && !arg.closed?
|
17
|
+
silence_stderr do
|
18
|
+
`identify #{arg.respond_to?(:path) ? arg.path : arg.to_s}`
|
19
|
+
$? == 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The constuctor should be invoked with the options set by has_image.
|
25
|
+
def initialize(options) # :nodoc:
|
26
|
+
@options = options
|
27
|
+
end
|
28
|
+
|
29
|
+
# Create the resized image, and transforms it to the desired output
|
30
|
+
# format if necessary. The size should be a valid ImageMagick {geometry
|
31
|
+
# string}[http://www.imagemagick.org/script/command-line-options.php#resize].
|
32
|
+
def resize(file, size)
|
33
|
+
silence_stderr do
|
34
|
+
path = file.respond_to?(:path) ? file.path : file
|
35
|
+
file.close if file.respond_to?(:close) && !file.closed?
|
36
|
+
@image = MiniMagick::Image.from_file(path)
|
37
|
+
convert_image
|
38
|
+
resize_image(size)
|
39
|
+
return @image
|
40
|
+
end
|
41
|
+
rescue MiniMagick::MiniMagickError
|
42
|
+
raise ProcessorError.new("That doesn't look like an image file.")
|
43
|
+
end
|
44
|
+
|
45
|
+
# Image resizing is placed in a separate method for easy monkey-patching.
|
46
|
+
# This is intended to be invoked from resize, rather than directly.
|
47
|
+
# By default, the following ImageMagick functionality is invoked:
|
48
|
+
# * auto-orient[http://www.imagemagick.org/script/command-line-options.php#auto-orient]
|
49
|
+
# * strip[http://www.imagemagick.org/script/command-line-options.php#strip]
|
50
|
+
# * resize[http://www.imagemagick.org/script/command-line-options.php#resize]
|
51
|
+
# * gravity[http://www.imagemagick.org/script/command-line-options.php#gravity]
|
52
|
+
# * extent[http://www.imagemagick.org/script/command-line-options.php#extent]
|
53
|
+
# * quality[http://www.imagemagick.org/script/command-line-options.php#quality]
|
54
|
+
def resize_image(size)
|
55
|
+
@image.combine_options do |commands|
|
56
|
+
commands.send("auto-orient".to_sym)
|
57
|
+
commands.strip
|
58
|
+
commands.resize "#{size}^"
|
59
|
+
commands.gravity "center"
|
60
|
+
commands.extent size
|
61
|
+
commands.quality options[:output_quality]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# This was placed in a separate method largely to facilitate debugging
|
68
|
+
# and profiling.
|
69
|
+
def convert_image
|
70
|
+
return if @image[:format] == options[:convert_to]
|
71
|
+
@image.format(options[:convert_to])
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'stringio'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'zlib'
|
5
|
+
|
6
|
+
module HasImage
|
7
|
+
|
8
|
+
# Filesystem storage for the HasImage gem. The methods that HasImage inserts
|
9
|
+
# into ActiveRecord models only depend on the public methods in this class, so
|
10
|
+
# it should be reasonably straightforward to implement a different storage
|
11
|
+
# mechanism for Amazon AWS, Photobucket, DBFile, SFTP, or whatever you want.
|
12
|
+
class Storage
|
13
|
+
|
14
|
+
attr_accessor :image_data, :options, :temp_file
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Stolen from {Jamis Buck}[http://www.37signals.com/svn/archives2/id_partitioning.php].
|
19
|
+
def partitioned_path(id, *args)
|
20
|
+
("%08d" % id).scan(/..../) + args
|
21
|
+
end
|
22
|
+
|
23
|
+
# Generates a 4-6 character random file name to use for the image and its
|
24
|
+
# thumbnails. This is done to avoid having files with unfortunate names.
|
25
|
+
# On one of my sites users frequently upload images with Arabic names, and
|
26
|
+
# they end up being hard to manipulate on the command line. This also
|
27
|
+
# helps prevent a possibly undesirable sitation where the uploaded images
|
28
|
+
# have offensive names.
|
29
|
+
def random_file_name
|
30
|
+
Zlib.crc32(Time.now.to_s + rand(10e10).to_s).to_s(36)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
# The constuctor should be invoked with the options set by has_image.
|
36
|
+
def initialize(options) # :nodoc:
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
# The image data can be anything that inherits from IO. If you pass in an
|
41
|
+
# instance of Tempfile, it will be used directly without being copied to
|
42
|
+
# a new temp file.
|
43
|
+
def image_data=(image_data)
|
44
|
+
raise StorageError.new if image_data.blank?
|
45
|
+
if image_data.is_a?(Tempfile)
|
46
|
+
@temp_file = image_data
|
47
|
+
else
|
48
|
+
image_data.rewind
|
49
|
+
@temp_file = Tempfile.new 'has_image_data_%s' % Storage.random_file_name
|
50
|
+
@temp_file.write(image_data.read)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Is uploaded file smaller than the allowed minimum?
|
55
|
+
def image_too_small?
|
56
|
+
@temp_file.open if @temp_file.closed?
|
57
|
+
@temp_file.size < options[:min_size]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Is uploaded file larger than the allowed maximum?
|
61
|
+
def image_too_big?
|
62
|
+
@temp_file.open if @temp_file.closed?
|
63
|
+
@temp_file.size > options[:max_size]
|
64
|
+
end
|
65
|
+
|
66
|
+
# A tip of the hat to attachment_fu.
|
67
|
+
alias uploaded_data= image_data=
|
68
|
+
|
69
|
+
# A tip of the hat to attachment_fu.
|
70
|
+
alias uploaded_data image_data
|
71
|
+
|
72
|
+
# Invokes the processor to resize the image(s) and the installs them to
|
73
|
+
# the appropriate directory.
|
74
|
+
def install_images(id)
|
75
|
+
random_name = Storage.random_file_name
|
76
|
+
install_main_image(id, random_name)
|
77
|
+
install_thumbnails(id, random_name) if !options[:thumbnails].empty?
|
78
|
+
return random_name
|
79
|
+
ensure
|
80
|
+
@temp_file.close! if !@temp_file.closed?
|
81
|
+
@temp_file = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Gets the "web" path for an image. For example:
|
85
|
+
#
|
86
|
+
# /photos/0000/0001/3er0zs.jpg
|
87
|
+
def public_path_for(object, thumbnail = nil)
|
88
|
+
filesystem_path_for(object, thumbnail).gsub(options[:base_path], '')
|
89
|
+
end
|
90
|
+
|
91
|
+
# Deletes the images and directory that contains them.
|
92
|
+
def remove_images(id)
|
93
|
+
FileUtils.rm_r path_for(id)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is the uploaded file within the min and max allowed sizes?
|
97
|
+
def valid?
|
98
|
+
!(image_too_small? || image_too_big?)
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
# Gets the extension to append to the image. Transforms "jpeg" to "jpg."
|
104
|
+
def extension
|
105
|
+
options[:convert_to].to_s.downcase.gsub("jpeg", "jpg")
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# File name, plus thumbnail suffix, plus extension. For example:
|
111
|
+
#
|
112
|
+
# file_name_for("abc123", :thumb)
|
113
|
+
#
|
114
|
+
# gives you:
|
115
|
+
#
|
116
|
+
# "abc123_thumb.jpg"
|
117
|
+
#
|
118
|
+
#
|
119
|
+
def file_name_for(*args)
|
120
|
+
"%s.%s" % [args.compact.join("_"), extension]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Gets the full local filesystem path for an image. For example:
|
124
|
+
#
|
125
|
+
# /var/sites/example.com/production/public/photos/0000/0001/3er0zs.jpg
|
126
|
+
def filesystem_path_for(object, thumbnail = nil)
|
127
|
+
File.join(path_for(object.id), file_name_for(object.has_image_file, thumbnail))
|
128
|
+
end
|
129
|
+
|
130
|
+
# Write the main image to the install directory - probably somewhere under
|
131
|
+
# RAILS_ROOT/public.
|
132
|
+
def install_main_image(id, name)
|
133
|
+
FileUtils.mkdir_p path_for(id)
|
134
|
+
main = processor.resize(@temp_file, @options[:resize_to])
|
135
|
+
main.write(File.join(path_for(id), file_name_for(name)))
|
136
|
+
main.tempfile.close!
|
137
|
+
end
|
138
|
+
|
139
|
+
# Write the thumbnails to the install directory - probably somewhere under
|
140
|
+
# RAILS_ROOT/public.
|
141
|
+
def install_thumbnails(id, name)
|
142
|
+
FileUtils.mkdir_p path_for(id)
|
143
|
+
path = File.join(path_for(id), file_name_for(name))
|
144
|
+
options[:thumbnails].each do |thumb_name, size|
|
145
|
+
thumb = processor.resize(path, size)
|
146
|
+
thumb.write(File.join(path_for(id), file_name_for(name, thumb_name)))
|
147
|
+
thumb.tempfile.close!
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Get the full path for the id. For example:
|
152
|
+
#
|
153
|
+
# /var/sites/example.org/production/public/photos/0000/0001
|
154
|
+
def path_for(id)
|
155
|
+
File.join(options[:base_path], options[:path_prefix], Storage.partitioned_path(id))
|
156
|
+
end
|
157
|
+
|
158
|
+
# Instantiates the processor using the options set in my contructor (if
|
159
|
+
# not already instantiated), stores it in an instance variable, and
|
160
|
+
# returns it.
|
161
|
+
def processor
|
162
|
+
@processor ||= Processor.new(options)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module HasImage
|
2
|
+
|
3
|
+
# Some helpers to make working with HasImage models in views a little
|
4
|
+
# easier.
|
5
|
+
module ViewHelpers
|
6
|
+
|
7
|
+
# Wraps the image_tag helper from Rails. Instead of passing the path to
|
8
|
+
# an image, you can pass any object that uses HasImage. The options can
|
9
|
+
# include the name of one of your thumbnails, for example:
|
10
|
+
#
|
11
|
+
# image_tag_for(@photo)
|
12
|
+
# image_tag_for(@photo, :thumb => :square)
|
13
|
+
#
|
14
|
+
# If your object uses fixed dimensions (i.e., "200x200" as opposed to
|
15
|
+
# "200x200>"), then the height and width properties will automatically be
|
16
|
+
# added to the resulting img tag unless you explicitly specify the size in
|
17
|
+
# the options.
|
18
|
+
#
|
19
|
+
# All arguments other than :thumb will simply be passed along to the Rails
|
20
|
+
# image_tag helper without modification.
|
21
|
+
#
|
22
|
+
# See also: HasImage::ModelInstanceMethods#public_path
|
23
|
+
def image_tag_for(object, options = {})
|
24
|
+
thumb = options.delete(:thumb)
|
25
|
+
if !options[:size]
|
26
|
+
if thumb
|
27
|
+
size = object.class.thumbnails[thumb.to_sym]
|
28
|
+
options[:size] = size if size =~ /\A[\d]*x[\d]*\Z/
|
29
|
+
else
|
30
|
+
size = object.class.resize_to
|
31
|
+
options[:size] = size if size =~ /\A[\d]*x[\d]*\Z/
|
32
|
+
end
|
33
|
+
end
|
34
|
+
image_tag(object.public_path(thumb), options)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/has_image.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'has_image/processor'
|
2
|
+
require 'has_image/storage'
|
3
|
+
require 'has_image/view_helpers'
|
4
|
+
|
5
|
+
# = HasImage
|
6
|
+
#
|
7
|
+
# HasImage allows Ruby on Rails applications to have attached images. It is very
|
8
|
+
# small and lightweight: it only requires one column ("has_image_file") in your
|
9
|
+
# model to store the uploaded image's file name.
|
10
|
+
#
|
11
|
+
# HasImage is, by design, very simplistic: It only supports using a filesystem
|
12
|
+
# for storage, and only supports
|
13
|
+
# MiniMagick[http://github.com/probablycorey/mini_magick] as an image processor.
|
14
|
+
# However, its code is very small, clean and hackable, so adding support for
|
15
|
+
# other backends or processors should be fairly easy.
|
16
|
+
#
|
17
|
+
# HasImage works best for sites that want to show image galleries with
|
18
|
+
# fixed-size thumbnails. It uses ImageMagick's
|
19
|
+
# crop[http://www.imagemagick.org/script/command-line-options.php#crop] and
|
20
|
+
# {center
|
21
|
+
# gravity}[http://www.imagemagick.org/script/command-line-options.php#gravity]
|
22
|
+
# functions to produce thumbnails that generally look acceptable, unless the
|
23
|
+
# image is a panorama, or the subject matter is close to one of the margins,
|
24
|
+
# etc. For most sites where people upload pictures of themselves or their pets
|
25
|
+
# the generated thumbnails will look good almost all the time.
|
26
|
+
#
|
27
|
+
# It's pretty easy to change the image processing / resizing code; you can just
|
28
|
+
# override HasImage::Processor#resize_image to do what you wish:
|
29
|
+
#
|
30
|
+
# module HasImage::
|
31
|
+
# class Processor
|
32
|
+
# def resize_image(size)
|
33
|
+
# @image.combine_options do |commands|
|
34
|
+
# commands.my_custom_resizing_goes_here
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Compared to attachment_fu, HasImage has advantages and disadvantages.
|
41
|
+
#
|
42
|
+
# = Advantages:
|
43
|
+
#
|
44
|
+
# * Simpler, smaller, more easily hackable codebase - and specialized for
|
45
|
+
# images only.
|
46
|
+
# * Installable via Ruby Gems. This makes version dependencies easy when using
|
47
|
+
# Rails 2.1.
|
48
|
+
# * Creates only one database record per image.
|
49
|
+
# * Has built-in facilities for making distortion-free, fixed-size thumbnails.
|
50
|
+
# * Doesn't regenerate the thumbnails every time you save your model. This means
|
51
|
+
# you can easily use it, for example, inside a Member model to store member
|
52
|
+
# avatars.
|
53
|
+
#
|
54
|
+
# = Disadvantages:
|
55
|
+
#
|
56
|
+
# * Doesn't save image dimensions. However, if you're using fixed-sized images,
|
57
|
+
# this is not a problem because you can just read the size from MyModel.thumbnails[:my_size]
|
58
|
+
# * No support for AWS or DBFile storage, only filesystem.
|
59
|
+
# * Only supports MiniMagick[http://github.com/probablycorey/mini_magick/tree] as an image processor, no RMagick, GD, CoreImage,
|
60
|
+
# etc.
|
61
|
+
# * No support for anything other than image attachments.
|
62
|
+
# * Not as popular as attachment_fu, which means fewer bug reports, and
|
63
|
+
# probably more bugs. Use at your own risk!
|
64
|
+
module HasImage
|
65
|
+
|
66
|
+
class ProcessorError < StandardError ; end
|
67
|
+
class StorageError < StandardError ; end
|
68
|
+
class FileTooBigError < StorageError ; end
|
69
|
+
class FileTooSmallError < StorageError ; end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
|
73
|
+
def included(base) # :nodoc:
|
74
|
+
base.extend(ClassMethods)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Enables has_image functionality. You probably don't need to ever invoke
|
78
|
+
# this.
|
79
|
+
def enable # :nodoc:
|
80
|
+
return if ActiveRecord::Base.respond_to? :has_image
|
81
|
+
ActiveRecord::Base.send(:include, HasImage)
|
82
|
+
return if ActionView::Base.respond_to? :image_tag_for
|
83
|
+
ActionView::Base.send(:include, ViewHelpers)
|
84
|
+
end
|
85
|
+
|
86
|
+
# If you're invoking this method, you need to pass in the class for which
|
87
|
+
# you want to get default options; this is used to determine the path where
|
88
|
+
# the images will be stored in the file system. Take a look at
|
89
|
+
# HasImage::ClassMethods#has_image to see examples of how to set the options
|
90
|
+
# in your model.
|
91
|
+
#
|
92
|
+
# This method is called by your model when you call has_image. It's
|
93
|
+
# placed here rather than in the model's class methods to make it easier
|
94
|
+
# to access for testing. Unless you're working on the code, it's unlikely
|
95
|
+
# you'll ever need to invoke this method.
|
96
|
+
#
|
97
|
+
# * :resize_to => "200x200",
|
98
|
+
# * :thumbnails => {},
|
99
|
+
# * :max_size => 12.megabytes,
|
100
|
+
# * :min_size => 4.kilobytes,
|
101
|
+
# * :path_prefix => klass.to_s.tableize,
|
102
|
+
# * :base_path => File.join(RAILS_ROOT, 'public'),
|
103
|
+
# * :convert_to => "JPEG",
|
104
|
+
# * :output_quality => "85",
|
105
|
+
# * :invalid_image_message => "Can't process the image.",
|
106
|
+
# * :image_too_small_message => "The image is too small.",
|
107
|
+
# * :image_too_big_message => "The image is too big.",
|
108
|
+
def default_options_for(klass)
|
109
|
+
{
|
110
|
+
:resize_to => "200x200",
|
111
|
+
:thumbnails => {},
|
112
|
+
:max_size => 12.megabytes,
|
113
|
+
:min_size => 4.kilobytes,
|
114
|
+
:path_prefix => klass.to_s.tableize,
|
115
|
+
:base_path => File.join(RAILS_ROOT, 'public'),
|
116
|
+
:convert_to => "JPEG",
|
117
|
+
:output_quality => "85",
|
118
|
+
:invalid_image_message => "Can't process the image.",
|
119
|
+
:image_too_small_message => "The image is too small.",
|
120
|
+
:image_too_big_message => "The image is too big."
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
module ClassMethods
|
127
|
+
# To use HasImage with a Rails model, all you have to do is add a column
|
128
|
+
# named "has_image_file." For configuration defaults, you might want to take
|
129
|
+
# a look at the default options specified in HasImage#default_options_for.
|
130
|
+
# The different setting options are described below.
|
131
|
+
#
|
132
|
+
# Options:
|
133
|
+
# * <tt>:resize_to</tt> - Dimensions to resize to. This should be an ImageMagick {geometry string}[http://www.imagemagick.org/script/command-line-options.php#resize]. Fixed sizes are recommended.
|
134
|
+
# * <tt>:thumbnails</tt> - A hash of thumbnail names and dimensions. The dimensions should be ImageMagick {geometry strings}[http://www.imagemagick.org/script/command-line-options.php#resize]. Fixed sized are recommended.
|
135
|
+
# * <tt>:min_size</tt> - Minimum file size allowed. It's recommended that you set this size in kilobytes.
|
136
|
+
# * <tt>:max_size</tt> - Maximum file size allowed. It's recommended that you set this size in megabytes.
|
137
|
+
# * <tt>:base_path</tt> - Where to install the images. You should probably leave this alone, except for tests.
|
138
|
+
# * <tt>:path_prefix</tt> - Where to install the images, relative to basepath. You should probably leave this alone.
|
139
|
+
# * <tt>:convert_to</tt> - An ImageMagick format to convert images to. Recommended formats: JPEG, PNG, GIF.
|
140
|
+
# * <tt>:output_quality</tt> - Image output quality passed to ImageMagick.
|
141
|
+
# * <tt>:invalid_image_message</tt> - The message that will be shown when the image data can't be processed.
|
142
|
+
# * <tt>:image_too_small_message</tt> - The message that will be shown when the image file is too small. You should ideally set this to something that tells the user what the minimum is.
|
143
|
+
# * <tt>:image_too_big_message</tt> - The message that will be shown when the image file is too big. You should ideally set this to something that tells the user what the maximum is.
|
144
|
+
#
|
145
|
+
# Examples:
|
146
|
+
# has_image # uses all default options
|
147
|
+
# has_image :resize_to "800x800", :thumbnails => {:square => "150x150"}
|
148
|
+
# has_image :resize_to "100x150", :max_size => 500.kilobytes
|
149
|
+
# has_image :invalid_image_message => "No se puede procesar la imagen."
|
150
|
+
def has_image(options = {})
|
151
|
+
options.assert_valid_keys(:resize_to, :thumbnails, :max_size, :min_size,
|
152
|
+
:path_prefix, :base_path, :convert_to, :output_quality,
|
153
|
+
:invalid_image_message, :image_too_big_message, :image_too_small_message)
|
154
|
+
options = HasImage.default_options_for(self).merge(options)
|
155
|
+
class_inheritable_accessor :has_image_options
|
156
|
+
write_inheritable_attribute(:has_image_options, options)
|
157
|
+
|
158
|
+
after_create :install_images
|
159
|
+
after_save :update_images
|
160
|
+
after_destroy :remove_images
|
161
|
+
|
162
|
+
validate_on_create :image_data_valid?
|
163
|
+
|
164
|
+
include ModelInstanceMethods
|
165
|
+
extend ModelClassMethods
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
module ModelInstanceMethods
|
172
|
+
|
173
|
+
# Sets the uploaded image data. Image data can be an instance of Tempfile,
|
174
|
+
# or an instance of any class than inherits from IO.
|
175
|
+
def image_data=(image_data)
|
176
|
+
return if image_data.blank?
|
177
|
+
storage.image_data = image_data
|
178
|
+
end
|
179
|
+
|
180
|
+
# Is the image data a file that ImageMagick can process, and is it within
|
181
|
+
# the allowed minimum and maximum sizes?
|
182
|
+
def image_data_valid?
|
183
|
+
return if !storage.temp_file
|
184
|
+
if storage.image_too_big?
|
185
|
+
errors.add_to_base(self.class.has_image_options[:image_too_big_message])
|
186
|
+
elsif storage.image_too_small?
|
187
|
+
errors.add_to_base(self.class.has_image_options[:image_too_small_message])
|
188
|
+
elsif !HasImage::Processor.valid?(storage.temp_file)
|
189
|
+
errors.add_to_base(self.class.has_image_options[:invalid_image_message])
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Gets the "web path" for the image, or optionally, its thumbnail.
|
194
|
+
def public_path(thumbnail = nil)
|
195
|
+
storage.public_path_for(self, thumbnail)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Deletes the image from the storage.
|
199
|
+
def remove_images
|
200
|
+
return if has_image_file.blank?
|
201
|
+
storage.remove_images(self.id)
|
202
|
+
rescue Errno::ENOENT
|
203
|
+
logger.warn("Could not delete files for #{self.class.to_s} #{to_param}")
|
204
|
+
end
|
205
|
+
|
206
|
+
# Creates new images and removes the old ones when image_data has been
|
207
|
+
# set.
|
208
|
+
def update_images
|
209
|
+
return if storage.temp_file.blank?
|
210
|
+
storage.remove_images(self.id)
|
211
|
+
update_attribute(:has_image_file, storage.install_images(self.id))
|
212
|
+
end
|
213
|
+
|
214
|
+
# Processes and installs the image and its thumbnails.
|
215
|
+
def install_images
|
216
|
+
return if !storage.temp_file
|
217
|
+
update_attribute(:has_image_file, storage.install_images(self.id))
|
218
|
+
end
|
219
|
+
|
220
|
+
# Gets an instance of the underlying storage functionality. See
|
221
|
+
# HasImage::Storage.
|
222
|
+
def storage
|
223
|
+
@storage ||= HasImage::Storage.new(has_image_options)
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
module ModelClassMethods
|
229
|
+
|
230
|
+
# Get the hash of thumbnails set by the options specified when invoking
|
231
|
+
# HasImage::ClassMethods#has_image.
|
232
|
+
def thumbnails
|
233
|
+
has_image_options[:thumbnails]
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
if defined?(Rails) and defined?(ActiveRecord) and defined?(ActionController)
|
241
|
+
HasImage.enable
|
242
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
class StorageTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def teardown
|
6
|
+
@temp_file.close if @temp_file
|
7
|
+
FileUtils.rm_rf(File.dirname(__FILE__) + '/../tmp')
|
8
|
+
end
|
9
|
+
|
10
|
+
def temp_file(fixture)
|
11
|
+
@temp_file = Tempfile.new('test')
|
12
|
+
@temp_file.write(File.new(File.dirname(__FILE__) + "/../test_rails/fixtures/#{fixture}", "r").read)
|
13
|
+
return @temp_file
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_detect_valid_image
|
17
|
+
assert HasImage::Processor.valid?(File.dirname(__FILE__) + "/../test_rails/fixtures/image.jpg")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_detect_valid_image_from_tmp_file
|
21
|
+
assert HasImage::Processor.valid?(temp_file("image.jpg"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_detect_invalid_image
|
25
|
+
assert !HasImage::Processor.valid?(File.dirname(__FILE__) + "/../test_rails/fixtures/bad_image.jpg")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_detect_invalid_image_from_tmp_file
|
29
|
+
assert !HasImage::Processor.valid?(temp_file("bad_image.jpg"))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_resize
|
33
|
+
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
34
|
+
assert @processor.resize(temp_file("image.jpg"), "100x100")
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_resize_and_convert
|
38
|
+
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
39
|
+
assert @processor.resize(temp_file("image.png"), "100x100")
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_resize_should_fail_with_bad_image
|
43
|
+
@processor = HasImage::Processor.new({:convert_to => "JPEG", :output_quality => "85"})
|
44
|
+
assert_raises HasImage::ProcessorError do
|
45
|
+
@processor.resize(temp_file("bad_image.jpg"), "100x100")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
class StorageTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
FileUtils.rm_rf(File.dirname(__FILE__) + '/../tmp')
|
10
|
+
@temp_file.close! if @temp_file && !@temp_file.closed?
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_options
|
14
|
+
HasImage.default_options_for("tests").merge(
|
15
|
+
:base_path => File.join(File.dirname(__FILE__), '..', 'tmp')
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_partitioned_path
|
20
|
+
assert_equal(["0001", "2345"], HasImage::Storage.partitioned_path("12345"))
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_random_file_name
|
24
|
+
assert_match(/[a-z0-9]{4,6}/i, HasImage::Storage.random_file_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_path_for
|
28
|
+
@storage = HasImage::Storage.new(default_options)
|
29
|
+
assert_match(/\/tmp\/tests\/0000\/0001/, @storage.send(:path_for, 1))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_public_path_for
|
33
|
+
@storage = HasImage::Storage.new(default_options)
|
34
|
+
pic = stub(:has_image_file => "mypic", :id => 1)
|
35
|
+
assert_equal "/tests/0000/0001/mypic_square.jpg", @storage.public_path_for(pic, :square)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_filename_for
|
39
|
+
@storage = HasImage::Storage.new(default_options)
|
40
|
+
assert_equal "test.jpg", @storage.send(:file_name_for, "test")
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_set_data_from_file
|
44
|
+
@storage = HasImage::Storage.new(default_options)
|
45
|
+
@file = File.new(File.dirname(__FILE__) + "/../test_rails/fixtures/image.jpg", "r")
|
46
|
+
@storage.image_data = @file
|
47
|
+
assert @storage.temp_file.size > 0
|
48
|
+
assert_equal Zlib.crc32(@file.read), Zlib.crc32(@storage.temp_file.read)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_set_data_from_tempfile
|
52
|
+
@storage = HasImage::Storage.new(default_options)
|
53
|
+
@storage.image_data = temp_file("image.jpg")
|
54
|
+
assert @storage.temp_file.size > 0
|
55
|
+
assert_equal Zlib.crc32(@storage.temp_file.read), Zlib.crc32(@temp_file.read)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_install_and_remove_images
|
59
|
+
@storage = HasImage::Storage.new(default_options)
|
60
|
+
@storage.image_data = temp_file("image.jpg")
|
61
|
+
assert @storage.install_images(1)
|
62
|
+
assert @storage.remove_images(1)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_image_not_too_small
|
66
|
+
@storage = HasImage::Storage.new(default_options.merge(:min_size => 1.kilobyte))
|
67
|
+
@storage.image_data = temp_file("image.jpg")
|
68
|
+
assert !@storage.image_too_small?
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_image_too_small
|
72
|
+
@storage = HasImage::Storage.new(default_options.merge(:min_size => 1.gigabyte))
|
73
|
+
@storage.image_data = temp_file("image.jpg")
|
74
|
+
assert @storage.image_too_small?
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_image_too_big
|
78
|
+
@storage = HasImage::Storage.new(default_options.merge(:max_size => 1.kilobyte))
|
79
|
+
@storage.image_data = temp_file("image.jpg")
|
80
|
+
assert @storage.image_too_big?
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_image_not_too_big
|
84
|
+
@storage = HasImage::Storage.new(default_options.merge(:max_size => 1.gigabyte))
|
85
|
+
@storage.image_data = temp_file("image.jpg")
|
86
|
+
assert !@storage.image_too_big?
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def temp_file(fixture)
|
92
|
+
file = File.new(File.dirname(__FILE__) + "/../test_rails/fixtures/#{fixture}", "r")
|
93
|
+
@temp_file = Tempfile.new("test")
|
94
|
+
@temp_file.write(file.read)
|
95
|
+
return @temp_file
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
data/test_rails/pic.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PicTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Pic.has_image_options = HasImage.default_options_for(Pic)
|
7
|
+
Pic.has_image_options[:base_path] = File.join(RAILS_ROOT, '/tmp')
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
FileUtils.rm_rf(File.join(RAILS_ROOT, 'tmp', 'pics'))
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_be_valid
|
15
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
16
|
+
assert @pic.valid? , "#{@pic.errors.full_messages.to_sentence}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_be_too_big
|
20
|
+
Pic.has_image_options[:max_size] = 1.kilobyte
|
21
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
22
|
+
assert !@pic.valid?
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_be_too_small
|
26
|
+
Pic.has_image_options[:min_size] = 1.gigabyte
|
27
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
28
|
+
assert !@pic.valid?
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_invalid_image_detected
|
32
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/bad_image.jpg", "image/jpeg"))
|
33
|
+
assert !@pic.valid?
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_create
|
37
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
38
|
+
assert @pic.save!
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_update
|
42
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.jpg", "image/jpeg"))
|
43
|
+
@pic.save!
|
44
|
+
@pic.image_data = fixture_file_upload("/image.png", "image/png")
|
45
|
+
assert @pic.save!
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_create_model_without_setting_image_data
|
49
|
+
assert Pic.new.save!
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_destroy_model_without_no_images
|
53
|
+
@pic = Pic.new
|
54
|
+
@pic.save!
|
55
|
+
assert @pic.destroy
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_destroy_model_with_images_already_deleted_from_filesystem
|
59
|
+
@pic = Pic.new
|
60
|
+
@pic.save!
|
61
|
+
@pic.update_attribute(:has_image_file, "test")
|
62
|
+
assert @pic.destroy
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_create_with_png
|
66
|
+
Pic.has_image_options[:min_size] = 1
|
67
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.png", "image/png"))
|
68
|
+
assert @pic.save!
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_multiple_calls_to_valid_doesnt_blow_away_temp_image
|
72
|
+
Pic.has_image_options[:min_size] = 1
|
73
|
+
@pic = Pic.new(:image_data => fixture_file_upload("/image.png", "image/png"))
|
74
|
+
@pic.valid?
|
75
|
+
assert @pic.valid?
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/../../../../config/environment.rb'))
|
7
|
+
require 'active_record/fixtures'
|
8
|
+
require 'action_controller/test_process'
|
9
|
+
|
10
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
11
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
12
|
+
|
13
|
+
db_adapter = ENV['DB']
|
14
|
+
|
15
|
+
# no db passed, try one of these fine config-free DBs before bombing.
|
16
|
+
db_adapter ||=
|
17
|
+
begin
|
18
|
+
require 'rubygems'
|
19
|
+
require 'sqlite3'
|
20
|
+
'sqlite3'
|
21
|
+
rescue MissingSourceFile
|
22
|
+
begin
|
23
|
+
require 'sqlite'
|
24
|
+
'sqlite'
|
25
|
+
rescue MissingSourceFile
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if db_adapter.nil?
|
30
|
+
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite3 or Sqlite."
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveRecord::Base.establish_connection(config[db_adapter])
|
34
|
+
|
35
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
36
|
+
|
37
|
+
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures"
|
38
|
+
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
|
39
|
+
|
40
|
+
class Test::Unit::TestCase #:nodoc:
|
41
|
+
include ActionController::TestProcess
|
42
|
+
def create_fixtures(*table_names)
|
43
|
+
if block_given?
|
44
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
45
|
+
else
|
46
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
self.use_transactional_fixtures = true
|
51
|
+
self.use_instantiated_fixtures = false
|
52
|
+
|
53
|
+
end
|
54
|
+
require 'pic'
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_image
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Norman Clarke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-23 00:00:00 -03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: HasImage is a Ruby on Rails gem/plugin that allows you to attach images to ActiveRecord models.
|
17
|
+
email: norman@randomba.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- CHANGELOG
|
25
|
+
- FAQ
|
26
|
+
files:
|
27
|
+
- CHANGELOG
|
28
|
+
- FAQ
|
29
|
+
- MIT-LICENSE
|
30
|
+
- README
|
31
|
+
- init.rb
|
32
|
+
- lib/has_image.rb
|
33
|
+
- lib/has_image/processor.rb
|
34
|
+
- lib/has_image/storage.rb
|
35
|
+
- lib/has_image/view_helpers.rb
|
36
|
+
- Rakefile
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://randomba.org
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options:
|
41
|
+
- --main
|
42
|
+
- README
|
43
|
+
- --inline-source
|
44
|
+
- --line-numbers
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.2.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: Lets you attach images with thumbnails to active record models.
|
66
|
+
test_files:
|
67
|
+
- test_rails/database.yml
|
68
|
+
- test_rails/fixtures/bad_image.jpg
|
69
|
+
- test_rails/fixtures/image.jpg
|
70
|
+
- test_rails/fixtures/image.png
|
71
|
+
- test_rails/pic.rb
|
72
|
+
- test_rails/pic_test.rb
|
73
|
+
- test_rails/schema.rb
|
74
|
+
- test_rails/test_helper.rb
|
75
|
+
- test/processor_test.rb
|
76
|
+
- test/storage_test.rb
|