Free, tested & ready to use examples!
AnyExample.com
 
Web anyexample.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 
  • Mac OS X :: Ruby On Rails 1.2.3
  • Ubuntu 7.04 :: Ruby On Rails 1.2.3
  •  


     
    © AnyExample 2010-2013
    License | Privacy | Contact