В первой части статьи мы написали REST приложение и на 1/3 настроили Redactor.js. Сегодня мы закончим наше изобретение, написав интерфейс управления загруженными изображениями, и обеспечим загрузку файлов. При загрузке файлов мы не будем использовать CarrierWave, а пойдем обычным путем Ruby.

Начнем с загрузки файлов. Предполагаю, что сейчас у вас такой файл init.rb:
# coding: utf-8
require 'rubygems'
require 'sinatra'
require 'data_mapper'
require 'carrierwave'
require 'carrierwave/datamapper'
require 'rmagick'
require 'json'

set :public_directory, './public'

class ImageUploader < CarrierWave::Uploader::Base
  def store_dir
    'uploads/images'
  end
  def extension_white_list
    %w(jpg jpeg gif png bmp)
  end
  include CarrierWave::RMagick
  version :thumb do
    process :resize_to_fill => [100,74]
  end
  storage :file
end

class Post
  include DataMapper::Resource
  property :id,         Serial
  property :title,      String
  property :body,       Text
end

class UploadedImages
  include DataMapper::Resource
  property :id,    Serial
  property :image, String
  property :thumb, String

  mount_uploader :file, ImageUploader
end

class UploadedFiles
  include DataMapper::Resource
  property :id,    Serial
  property :name,  String
  property :path,  String
end

DataMapper.setup(:default, ENV['DATABASE_URL'] || 'sqlite:./db/base.db')
DataMapper.finalize
DataMapper.auto_upgrade!

get '/' do
  'REST приложение на Sinatra <a href="/posts">Перейти к страницам</a>'
end

#List posts
get '/posts' do
  @posts = Post.all
  erb :'index'
end

#Create new Post
get '/posts/new' do
  erb :'posts/new'
end

post '/posts/new' do
  params.delete 'submit'
  @post = Post.create(params)
  redirect '/posts'
end

#Edit post
get '/posts/:id/edit' do
  @post = Post.get(params[:id])
  erb :'posts/edit'
end

#Update post
put '/posts/:id/edit' do
  post = Post.get(params[:id])
  post.title = (params[:title])
  post.body = (params[:body])
  post.save
  redirect '/posts'
end

#Delete post
get '/posts/:id/delete' do
  Post.get(params[:id]).destroy
  redirect '/posts'
end

post '/upload/image' do
  params[:file]
  filename = params[:file][:filename]
  file = params[:file][:tempfile]
  upload = UploadedImages.new
  upload.file = params[:file]
  upload.image = params[:image] = '/uploads/images/' + File.join(filename)
  upload.thumb = params[:thumb] = '/uploads/images/thumb_' + File.join(filename)
  upload.save
  @images = UploadedImages.all
  File.open("public/uploads/images/imageslist.json","w") do |f|
    f.write JSON.pretty_generate(@images)
  end
  '<img src="/uploads/images/' + File.join(filename) + '" />'
end

Итак, создадим папку files в директории public/uploads, напишем новую модель UploadedFiles и добавим метод post для загрузки файлов
set :files,  File.join(settings.public_directory, 'uploads/files') #берем из настро��ки public директории (set :public_directory, './public' почти в самом начале init.rb) ее путь и прибавляем путь, куда будут загружаться файлы

class UploadedFiles
  include DataMapper::Resource
  property :id,    Serial #идентификатор
  property :name,  String #имя загруженного файла
  property :path,  String #путь к загруженному файлу
end

post '/upload/file' do
  params[:file]
  filename = params[:file][:filename]
  file = params[:file][:tempfile]
  ext = File.extname(filename) #устанавливаем переменную для проверки расширения файла, нам же не нужно, чтобы пытались загружать что угодно.
  if ext == ".doc" || ext == ".zip" || ext == ".dmg" #здесь я установил 3 допустимых расширения файла doc, zip, dmg, при желании список разрешенных файлов всегда можно увеличить.
    File.open(File.join(settings.files, filename), 'wb') #загрузили - сохранили
    upload_f = UploadedFiles.new #создаем новый UploadedFile в базе данных
    upload_f.name = params[:name] = File.join(filename) #в моделе UploadedFiles поле :name, записываем туда имя загруженного файла 
    upload_f.path = params[:path] = '/uploads/files/' + File.join(filename) #в моделе UploadedFiles поле :path, записываем туда путь к загруженному файлу
    upload_f.save #сохраняем
    '<a href="/uploads/files/' + File.join(filename) + '" />' + File.join(filename) + '</a>' #вставляем в редактор ссылку на файл
  end
end

Теперь осталось сказать редактору как именно загружать файлы, откроем layout.erb найдем строку

$('.redactor_1').redactor({toolbar: 'default', lang: 'ru', imageUpload: '/upload/image', imageGetJson: '/uploads/images/imageslist.json'});

Добавим настройку fileUpload: '/upload/file' чтобы выглядело следующим образом
$('.redactor_1').redactor({toolbar: 'default', lang: 'ru', imageUpload: '/upload/image', fileUpload: '/upload/file', imageGetJson: '/uploads/images/imageslist.json'});

Все необходимые настройки мы сделали: создали модель, настроили путь к загружаемым файлам, собствен��о реализовали саму загрузку файла, сделали проверку на разрешенные файлы. Вот что хотелось бы отметить, неплохо было бы реализовать сообщение об ошибке, если расширение файл не соответствует разрешенному, но тут есть 2 НО: во-первых, Redactor.js все делает во фрейме и, если сделать сообщение об ошибке, то оно будет писаться в textarea, что, на мой взгляд, не является хорошей идеей, если попытаться реализовать сообщение об ошибке через gem sinatra-flash, то опять не вариант, так как ошибка появится только после перегрузки страницы. Ну может более опытные люди подскажут в какую сторону копать.

Теперь приступим к управлению загруженными изображениями, максимум что мы сделаем, это список всех загруженных изображений и их удаление.
Добавим в init.rb
#init.rb
#List UploadedImages
get '/images' do
  @ uploadedimage = UploadedImages.all #задаем переменную для вывода всех загруженных изображений по адресу /images (убрать пробел между собакой и uploadedimage)
  erb :'images' #рендерим images.erb
end

#Delete UploadedImage
get '/images/:id/delete' do #обращаемся к изображению через его ID
  UploadedImages.get(params[:id]).destroy #удаляем (честно говоря, для меня какое-то волшебство, удаляет строки в бд и файлы!...)
  redirect '/images' #редеректимся на страницу со всеми оставшимися изображениями
end

Создадим в директории views файл images.erb
<strong><a href="/posts">Перейти ко всем post'ам</a></strong>
<h2>Список загруженных файлов</h2>	
<% @ uploadedimage.each do |uploadedimages| %>
<a href="<%= uploadedimages.image %>"><img src="<%= uploadedimages.thumb %>"></a><br> #ссылка на полное изображение при клике на его превью
<a href="/images/<%= uploadedimages.id %>/delete">удалить</a><br><br>
<% end %>

Ну вот мы и закончили наше приложение. По аналогии с управлением изображений можно сделать и управление файлами. Думаю получилось неплохо. И это еще не конец, я продолжаю изучать Ruby и Sinatra и будут еще статьи.
Исходный код на github
P.S.: Везде где попадается
<hh user=posts>
или
<hh user=images>
просто замените на @название_переменной