[ANN] SuperImage plugin

Greetings all,

This is the first release of the SuperImage plugin. The idea is you
upload images to the database, and then pull them out at any size you
want. Combine this with caching and it will stay light and fast.

More info and instructions are here:
http://beautifulpixel.textdriven.com/articles/2006/04/27/superimage-plugin-making-resizeable-uploaded-images-easy

svn: http://beautifulpixel.textdriven.com/svn/plugins/super_image/

That’s all well and good and useable, but there are 2 things I would
like to improve. I am stuck on them, however.

First, the included migration is an ugly hack

def self.up
create_table :super_images, :force => true do |t|
t.column :data, :binary, :size => 10000000, :null => false
t.column :created_at, :datetime
end
execute “ALTER TABLE super_images MODIFY data MEDIUMBLOB”
end

I just could not get the ruby definition for :data to create a
MEDIUMBLOB on mysql. The size parameter seems to have been completely
disregarded. The clomun sets up as BLOB with a size limit of 16K, which
is far to small for most image uploading needs.

Second, the “show_image” action is currently included in via “include
SuperImagePlugin::Show”

This works, but it means the action name that performs the image
retrieval is locked in to be “show_image”. I would like to have it
works this way:

class MyImagesController < …
image_action :foo
end

Then you can access the images via “/my_images/foo/12”. Sadly though,
metaprogramming like this still makes my head spin.

If anyone has ideas about alleviating these 2 points, it sure would be
appreciated.

Hey Ezra, you tha man !

Peter wrote:

Hey Ezra, you tha man !

How rude of me.

Thank you Alex W…

Now published at http://agilewebdevelopment.com/plugins/show/102. :slight_smile:


Benjamin C.
http://www.tesly.com/ – Collaborative test case management
http://www.agilewebdevelopment.com/ – Resources for the Rails community

this is nice, but isn’t it quite dangerous in regards on generating
a huge load, while automatically GET /show_image?size=$autoincrement
-robert

2006/4/28, Benjamin C. [email protected]:

Hi!

On Apr 26, 2006, at 6:33 PM, Alex W. wrote:

svn: textdriven.com - textdriven Resources and Information.

Nice idea.

  t.column :created_at, :datetime

Not sure there is a prettier way to handle this part.

end

Then you can access the images via “/my_images/foo/12”. Sadly though,
metaprogramming like this still makes my head spin.

If anyone has ideas about alleviating these 2 points, it sure would be
appreciated.

I have taken the liberty of implementing image_action :foo for you.
And I also made the data= method on ActiveRecord::Base in a different
way. I commented the way this stuff works so you can learn how to do it.

This code is untested so it may need some slight adjustment, but
hopefully not :wink:

----init.rb—
require ‘super_image’
require ‘RMagick’

ActionController::Base.send :include, SuperImagePlugin::Show
ActiveRecord::Base.send :include, SuperImagePlugin::SetImage

–super_image.rb----

module SuperImagePlugin
module Show
# when we get included into the ApplicationController in init.rb
# we extend that class with the methods in our ClassMethods module
def self.included(base)
base.extend(ClassMethods)
end

 module ClassMethods
   # The image_action method will become a class method of
   # ApplicationController. This allows it to be used as a
   # macro like before_filter & friends. It takes one symbol
   # or string and uses define_method to create an instance
   # method that is in the same scope as controller actions.
   # You can use it in any controller class to define and

image_method:
# image_action :show
def image_action(meth)
define_method(meth) {
@headers[‘Cache-Control’] = ‘public’
img = SuperImage.find(params[:id])
if img
if params[:size]
data = Magick::Image.from_blob(img.data)[0]
data.change_geometry!(“#{params[:size]}x#{(params
[:size]).to_i}”) do
|cols, rows, img|
img.resize!(cols, rows)
end
else
data = Magick::Image.from_blob(img.data)[0]
end

         send_data data.to_blob,
                   :type => 'image/jpeg',
                   :disposition => 'inline'
       else
         render :text => 'Image Disabled!'
       end
       GC.start
     }
   end

 end # ClassMethods

end # Show

module SetImage
# when we get included into ActiveRecord::Base, a data= method
# is defined.
def self.included(base)
base.include(ModelMethods)
end

 module ModelMethods
   def data=(file)
     if file.size > 0
       img = Magick::Image.from_blob(file.read)[0]
       img.format = 'JPG'
       self[:data] = img.to_blob
     end
     GC.start
   end
 end # InstanceMethods

end # SetImage
end # SuperImagePlugin

I removed your comments just to make it clear what I did. Just add
them back if this works for you. I haven’t looked at the rest of it
yet, but this should get you started.

Cheers-
-Ezra

On 4/27/06, Alex W. [email protected] wrote:

Greetings all,

This is the first release of the SuperImage plugin. The idea is you
upload images to the database, and then pull them out at any size you
want. Combine this with caching and it will stay light and fast.

Why use the database as image store and not the file system? I rather
agree with these arguments:
http://mysqldump.azundris.com/archives/36-Serving-Images-From-A-Database.html
linked from the lighttpd blog
(http://blog.lighttpd.net/articles/2006/03/10/serving-images-from-a-database)

Raph

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi Alex,

I just could not get the ruby definition for :data to create a
MEDIUMBLOB on mysql. The size parameter seems to have been completely
disregarded. The clomun sets up as BLOB with a size limit of 16K,
which
is far to small for most image uploading needs.

I’ve been thinking about this for a while, and I don’t understand
why Rails can’t be made to handle this sort of thing automatically.

Right now with Migrations Rails takes what you say literally, if
you specify :binary its mapped onto a BLOB. If you sat :integer
its mapped onto INT.

What I think should happen is to use :binary as a reference
to a class of columns (BLOB, MEDIUMBLOB, etc), and the :limit
parameter should be used to select the best column from the
class – ideally the smallest column that will still fit whatever
size of data :limit specifies.

The biggest place this would help is with integers. If I say I
want :integer, with a :limit of 2, it should use TINYINT rather
than INT. Its a waste to use something like an INT when a
TINYINT will do.

This would require the connection adapters to know how to
“negotiate” the correct column size, but I think it is doable.
I’d be willing to write the MySQL logic if others were willing
to do patches for other databases… drop me a line if you’re
interested in collaborating on this.

We’d get two benefits with this approach: Space would be conserved
and when it is needed (as in this case) and larger columns would be
selected automatically based on the size needs specified.


Thanks,

Dan


Dan K.
Autopilot Marketing Inc.

Email: [email protected]
Phone: 1 (604) 820-0212
Web: http://autopilotmarketing.com/
vCard: http://autopilotmarketing.com/~dan.kubb/vcard


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (Darwin)

iD8DBQFEU4PE4DfZD7OEWk0RAv+SAJ9tUAHS5OFrk0JYdIztEZ1JVuDJrQCgrnXh
VrIXKTIBhBGVFAxfol4vJkI=
=t2xo
-----END PGP SIGNATURE-----