![]() |
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.
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':
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
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:
You may send your feedback to us or report typos.
|
|