Free, tested & ready to use examples : Ruby on Rails SQL MySQL Gallery image photo RMagick ImageMagick identity
AnyExample.com
 
Webanyexample.com
 

Rails Photo Gallery

abstract 
This article contains step-by-step tutorial for creating photo gallery in Ruby On Rails -- basically a list of jpeg images with uploading/editing support. Command-line ImageMagick tools are used to generate thumbnails and determine image width and height. No Rails plugins or Ruby libraries (like RMagick) required.
compatible 
  • Ruby On Rails 1.0 or higher
  • Any sane ImageMagick versions ('identity' and 'convert' command-line utilities)

First, you'll need to create empty Rails project.

You will also need to configure Rails project to use your database — edit config/database.yml file. This tutorial does not contain any database-specific code, so you can use any Rails-supported database.

Our gallery will use only one Model class (Image). To generate it, type in Rails console:

$ script/generate model image
            

You should expect following output:

    exists  app/models/
    exists  test/unit/
    exists  test/fixtures/
    create  app/models/image.rb
    create  test/unit/image_test.rb
    create  test/fixtures/images.yml
    create  db/migrate
    create  db/migrate/001_create_images.rb

Edit file db/migrate/001_create_images.rb to match following code (you'll need to add several t.column lines):

source code: Ruby on Rails
 
class CreateImages < ActiveRecord::Migration
  def self.up
    create_table :images do |t|
      t.column "name", :string
      t.column "width", :integer
      t.column "height", :integer
    end
  end
 
  def self.down
    drop_table :images
  end
end	
 

As you can see our Image objects will have name, width and height. Pretty much enough for basic gallery.

Run 'rake db:migrate' from Rails console to create 'images' SQL table.

Now, let's generate scaffolding code for this mode:

$ script/generate scaffold image
    exists  app/controllers/
    exists  app/helpers/
    create  app/views/images
    exists  app/views/layouts/
    exists  test/functional/
dependency  model
    exists    app/models/
    exists    test/unit/
    exists    test/fixtures/
 identical    app/models/image.rb
 identical    test/unit/image_test.rb
 identical    test/fixtures/images.yml
    create  app/views/images/_form.rhtml
    create  app/views/images/list.rhtml
    create  app/views/images/show.rhtml
    create  app/views/images/new.rhtml
    create  app/views/images/edit.rhtml
    create  app/controllers/images_controller.rb
    create  test/functional/images_controller_test.rb
    create  app/helpers/images_helper.rb
    create  app/views/layouts/images.rhtml
    create  public/stylesheets/scaffold.css

Most of application should work now. If you run this Rails application and point your browser to 'http://127.0.0.1:3000/images/', you will see standard Rails-scaffold table editing interface. We need to add support for uploading and displaying images. Let's begin with edit/new form, which is in partial template 'app/views/images/_form.rhtml'.

Since we are going to upload image file, text fields for width and height are not required: image width and height will be detected by ImageMagick utility 'identity' later.

So, here is the new app/views/images/_form.rhtml:

source code: RHTML
<%= error_messages_for 'image' %>

<!--[form:image]-->
<p><label for="image_name">Name</label><br/>
<%= text_field 'image', 'name'  %></p>

<p><label for="image_file">File</label><br/>
<input type="file" name="image[file]" /></p>
<!--[eoform:image]-->

There is new <input type="file"...> field, which has name 'image[file]'. When processing new parameters, rails will know, that this field refers to field 'file'.

Note, that <input type="file"...> requires attribute enctype="multipart/form-data" in <form> tag. So, you should edit files 'app/views/images/new.rhtml' and 'app/views/images/edit.rhtml' and change line with form_tag helper. In 'app/views/images/new.rhtml':

source code: Ruby on Rails
 
<% form_tag({:action => 'create'}, :multipart => true)  do %>
 

And in 'app/views/images/edit.rhtml':

source code: Ruby on Rails
 
<% form_tag({:action => 'update', :id => @image}, :multipart=>true ) do %>
 

Let's return to our model 'Image'. First, we have to create writable attribute 'file'. This attribute will be written by Rails while processing create/new form.

source code: Ruby on Rails
 
# this function should be put to 'app/models/image.rb'
def file= (f)
  @file = f 
end
 

Function 'file=' creates writable attribute 'file' for model Image. This function simply saves it's argument to instance variable @file.

After image file has been assigned for object 'Image', it is necessary to validate object: check whether uploaded file is correct image, check it's size and dimensions. This is done in method 'validate':

source code: Ruby on Rails
 
# this function should be put to 'app/models/image.rb'
 
def validate 
  # Empty 'name' field is not allowed
  errors.add_on_empty 'name'
 
  # @file should contain file uploaded by HTTP 
  # it is either StringIO object (if file was smaller than 10Kb)
  # or Tempfile (otherwise)
  if not (@file.kind_of? StringIO or @file.kind_of? Tempfile)
    errors.add_to_base("No file selected")
    return
  end
 
  # We can't use StringIO data to call external programs
  # So, if object is StringIO -- we have to create Tempfile
  # Unfortunatly, in Rails it is impossible to force all uploads to be Tempfiles
  if @file.kind_of? StringIO
    # Yes, if @file is StringIO, create new Tempfile and copy everything to it 
    @real_file = Tempfile.new("AEGALLERY")
    while not @file.eof? 
      @real_file.write @file.read
    end
  else
    # Most uploads will be Tempfiles
    @real_file = @file
  end
 
  # Here is a call to ImageMagick tool identity 
  # it prints to standard output 
  # type, width and height of images 
  # (this is specified by -format %m,%w,%h)
  identify = `#{IMAGE_MAGICK_PATH}/identify -format %m,%w,%h #{@real_file.path} 2>&1`
  # Now identity is a string like "JPEG,640,480"
 
  # We split this string to array 
  @jpeg_info = identify.split(',', 3)
  # convert width and height to integer and assign it to object fields 
  self.width = @jpeg_info[1].to_i
  self.height = @jpeg_info[2].to_i      
 
  # Finally, cheking if everything was fine with this image 
  # if file was not valid JPEG -- something will fail here
  if @jpeg_info == nil or @jpeg_info[0] != 'JPEG' or self.width <= 0 or self.height <= 0
     errors.add_to_base("Wrong image format (use only JPEG) or broken data")
     return
  end
 
end
 

Read comments inside this method for details. Please note, that this method uses constant 'IMAGE_MAGICK_PATH'. Edit file 'config/environment.rb' and add to the end of it (after "# Include your application configuration below") line IMAGE_MAGICK_PATH = "/opt/local/bin". You must write real path of ImageMagick binaries on your system.

We'll have to write 3 more functions for model 'Image':

  • after_save -- which will save image and generate thumbnail
  • after_destroy -- which will delete images files when record is destroyed
  • img_tag -- which will return <img> tag for full-size image
  • img_tag_thumbnail -- which will return <img> tag for image thumbnail

First two functions is overloaded methods of ActiveRecord. 'img_tag' and 'img_tag_thumbnail' -- is our own functions which will generate HTML code.

source code: Ruby on Rails
 
# these functions should be put to 'app/models/image.rb'
 
def after_save
  dest_photo = "#{RAILS_ROOT}/public/photo/f/#{self.id}.jpeg"
  dest_photo_t = "#{RAILS_ROOT}/public/photo/t/#{self.id}.jpeg"
 
  # Copying Tempfile to our storage, 
  # which is a subdirectory 'photo/f' in 'public' 
  # directory of Rails project
  FileUtils.cp(@real_file.path, dest_photo);
 
  # Setting right permissions 
  FileUtils.chmod 0644, dest_photo
 
  # call image magick 'convert' utility to 
  # generate 200x200 thumbnail 
  `#{IMAGE_MAGICK_PATH}/convert -size 200x200 #{dest_photo} \
  -resize 200x200 -quality 90 +profile \"*\" #{dest_photo_t} 2>&1`
end
 
def after_destroy 
  # Deleting image files
  FileUtils.safe_unlink("#{RAILS_ROOT}/public/photo/f/#{self.id}.jpeg")
  FileUtils.safe_unlink("#{RAILS_ROOT}/public/photo/t/#{self.id}.jpeg")
end 
 
def img_tag 
  "<img src='/photo/f/#{self.id}.jpeg' width='#{self.width}' height='#{self.height}' alt='#{self.name}'>"
end
 
def img_tag_thumbnail
  kf = 200.0 / ( width > height ? width : height ) 
  tw = (width.to_f * kf).to_i
  th = (height.to_f * kf).to_i
  "<img src='/photo/t/#{self.id}.jpeg' width='#{tw}' height='#{th}' alt='#{self.name}'>"
end
 
 

Our gallery stores photos and thumbnails in filesystem. So, you need to create following directories

  • photos
    • f
    • t
in your application's 'public' folder, and insure that they are writable by Rails.

Finally, let's add image displaying capabilities. Add to the beginning of file 'app/views/images/show.rhtml' line:

source code: Ruby on Rails
 
<%= @image.img_tag %>
 

And line <td><%= image.img_tag_thumbnail %></td> to 'app/views/images/show.rhtml':

source code: Ruby on Rails
 
<% for image in @images %>
  <tr>
  <% for column in Image.content_columns %>
    <td><%=h image.send(column.name) %></td>
  <% end %>
    <%# following line outputs THUMBNAIL: %>
    <td><%= image.img_tag_thumbnail %></td>
    <td><%= link_to 'Show', :action => 'show', :id => image %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => image %></td>
    <td><%= link_to 'Destroy', { :action => 'destroy', :id => image }, :confirm => 'Are you sure?', :method => :post %></td>
  </tr>
<% end %>
 

Congratulations, image gallery is complete. You should be able to upload photos, view them (thumbnail and full-size) and reupload them by editing.

 

You may download source code as a Rails project

Don't forget to:

  • Change your database settings in 'config/database.yml'
  • Run 'rake db:migrate'
  • Change path to ImageMagick utilities in config/environment.rb
    (download ImageMagick here if you don't have it)
  • Rails application should be accessible by opening 'images' controller

 

You may send your feedback to us or report typos.

warning 
  • This example does not contain any password-protection or uploading limitations
  • Web browser caching may prevent loading new versions of edited photos. Don't forget to press reload, or configure your web server caching.
tested by AnyExample.com on 2007-09-21
  • Mac OS X :: Ruby On Rails 1.2.3
  • Ubuntu 7.04 :: Ruby On Rails 1.2.3
 


 
© AnyExample 2007
License | Privacy | Contact