Загрузка файлов, как правило, сложная область в веб-разработке. В этом уроке мы узнаем, как использовать Dragonfly , мощный гем Ruby, который позволяет легко и эффективно добавлять любые функции загрузки в проект Rails.
Что мы собираемся построить
В нашем примере приложения будет отображен список пользователей, и для каждого из них мы сможем загрузить аватар и сохранить его. Кроме того, Dragonfly позволит нам:
- Динамически манипулировать изображениями без сохранения дополнительных копий
- Использование HTTP-кэширования для оптимизации загрузки нашего приложения
В этом уроке мы будем следовать подходу BDD [Behavior Driven Development], используя Cucumber и RSpec.
Предпосылки
Вам нужно будет установить Imagemagick : вы можете обратиться к этой странице для установки двоичных файлов. Поскольку я базируюсь на платформе Mac, использую Homebrew , я могу просто набрать brew install imagemagick
.
Вам также нужно будет клонировать базовое приложение Rails, которое мы будем использовать в качестве отправной точки.
Настроить
Мы начнем с клонирования исходного репозитория и установки наших зависимостей:
1
2
|
git clone http://[email protected]/cloud8421/tutorial_dragonfly_template.git
cd tutorial_dragonfly_template
|
Это приложение требует как минимум Ruby 1.9.2 для запуска, однако я рекомендую вам использовать 1.9.3. Версия Rails — 3.2.1. Проект не включает .rvmrc
или .rbenv
.
Далее мы запускаем:
1
2
|
bundle install
bundle exec rake db:setup db:test:prepare db:seed
|
Это позаботится о зависимостях гемов и настройке базы данных (мы будем использовать sqlite, поэтому не нужно беспокоиться о конфигурации базы данных).
Чтобы проверить, что все работает должным образом, мы можем запустить:
1
2
|
bundle exec rspec
bundle exec cucumber
|
Вы должны обнаружить, что все тесты пройдены. Давайте рассмотрим вывод огурца:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
Feature: managing user profile
As a user
In order to manage my data
I want to access my user profile page
Background:
Given a user exists with email «[email protected]»
Scenario: viewing my profile
Given I am on the home page
When I follow «Profile» for «[email protected]»
Then I should be on the profile page for «[email protected]»
Scenario: editing my profile
Given I am on the profile page for «[email protected]»
When I follow «Edit»
And I change my email with «[email protected]»
And I click «Save»
Then I should be on the profile page for «[email protected]»
And I should see «User updated»
2 scenarios (2 passed)
11 steps (11 passed)
0m0.710s
|
Как видите, эти функции описывают типичный рабочий процесс пользователя: мы открываем страницу пользователя из списка, нажимаем «Изменить», чтобы редактировать данные пользователя, изменять электронную почту и сохранять.
Теперь попробуйте запустить приложение:
1
|
rails s
|
Если вы откроете http:://localhost:3000
в браузере, вы найдете список пользователей (мы предварительно заполнили базу 40 случайными записями благодаря жемчужине Faker ).
На данный момент каждый из пользователей будет иметь небольшой аватар размером 16×16 пикселей и большой аватар-заполнитель на странице своего профиля. Если вы отредактируете пользователя, вы сможете изменить его данные (имя, фамилию и пароль), но если вы попытаетесь загрузить аватар, он не будет сохранен.
Не стесняйтесь просматривать кодовую базу: приложение использует Простую форму для генерации представлений формы и Twitter Bootstrap для CSS и макета, поскольку они прекрасно интегрируются и очень помогают в ускорении процесса прототипирования.
Особенности загрузки аватара
Мы начнем с добавления нового сценария в features/managing_profile.feature
:
1
2
3
4
5
6
7
8
|
…
Scenario: adding an avatar
Given I am on the profile page for «[email protected]»
When I follow «Edit»
And I upload the mustache avatar
And I click «Save»
Then I should be on the profile page for «[email protected]»
And the profile should show «the mustache avatar»
|
Эта функция довольно features/step_definitions/user_steps.rb
, но для добавления в features/step_definitions/user_steps.rb
требуется несколько дополнительных шагов:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
…
When /^I upload the mustache avatar$/ do
attach_file ‘user[avatar_image]’, Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
end
Then /^the profile should show «([^»]*)»$/ do |image|
pattern = case image
when ‘the mustache avatar’
/mustache_avatar/
end
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ pattern
end
|
Этот шаг предполагает, что у вас есть изображение mustache_avatar.jpg
внутри spec/fixtures
. Как вы можете догадаться, это всего лишь пример; это может быть что угодно.
Первый шаг использует Capybara, чтобы найти поле файла user[avatar_image]
и загрузить файл. Обратите внимание, что мы предполагаем, что у нас будет атрибут avatar_image
в модели User
.
На втором этапе используется Nokogiri (мощная библиотека для разбора HTML / XML) и XPath, чтобы проанализировать содержимое страницы профиля и найти первый тег img
с помощью класса thumbnail
и проверить, что атрибут src
содержит mustache_avatar
.
Если вы сейчас запустите cucumber
, этот сценарий вызовет ошибку, так как отсутствует поле файла с указанным нами именем. Пришло время сосредоточиться на модели User
.
Добавление поддержки Dragonfly в пользовательскую модель
Прежде чем интегрировать Dragonfly с моделью User
, давайте добавим пару спецификаций в user_spec.rb
.
Мы можем добавить новый блок сразу после контекста attributes
:
1
2
3
4
5
6
7
|
context «avatar attributes» do
it { should respond_to(:avatar_image) }
it { should allow_mass_assignment_of(:avatar_image) }
end
|
Мы проверяем, что у пользователя есть атрибут avatar_image
и, поскольку мы будем обновлять этот атрибут через форму, он должен быть доступен (вторая спецификация).
Теперь мы можем установить Dragonfly: сделав это, мы получим эти характеристики зелеными.
Давайте добавим следующие строки в Gemfile:
1
2
|
gem ‘rack-cache’, require: ‘rack/cache’
gem ‘dragonfly’, ‘~>0.9.10’
|
Далее мы можем запустить bundle install
. Rack-cache необходим при разработке, так как это самый простой вариант для HTTP-кеширования. Он также может быть использован в производстве, даже если более надежные решения (такие как Varnish или Squid ) будут лучше.
Нам также нужно добавить инициализатор Dragonfly. Давайте создадим файл config/initializers/dragonfly.rb
и добавим следующее:
1
2
3
4
5
6
7
|
require ‘dragonfly’
app = Dragonfly[:images]
app.configure_with(:imagemagick)
app.configure_with(:rails)
app.define_macro(ActiveRecord::Base, :image_accessor)
|
Это ванильная конфигурация Dragonfly: она устанавливает приложение Dragonfly и настраивает его с помощью необходимого модуля. Он также добавляет новый макрос в ActiveRecord
который мы сможем использовать для расширения нашей модели User
.
Нам нужно обновить config/application.rb
и добавить новую директиву в конфигурацию (прямо перед блоком config.generators
):
1
2
3
4
5
6
7
|
config.middleware.insert 0, ‘Rack::Cache’, {
verbose: true,
metastore: URI.encode(«file:#{Rails.root}/tmp/dragonfly/cache/meta»),
entitystore: URI.encode(«file:#{Rails.root}/tmp/dragonfly/cache/body»)
} unless Rails.env.production?
config.middleware.insert_after ‘Rack::Cache’, ‘Dragonfly::Middleware’, :images
|
Не вдаваясь в подробности, мы настраиваем Rack::Cache
(за исключением рабочего, где он включен по умолчанию) и настраиваем Dragonfly для его использования.
Мы будем хранить наши изображения на диске, однако нам нужен способ отслеживать связь с пользователем. Самый простой вариант — добавить два столбца в пользовательскую таблицу с помощью миграции:
1
2
|
rails g migration add_avatar_to_users avatar_image_uid:string avatar_image_name:string
bundle exec rake db:migrate db:test:prepare
|
Еще раз, это прямо из документации Dragonfly : нам нужен столбец avatar_image_uid
для уникальной идентификации файла аватара и имя avatar_image_name
для хранения его исходного имени файла (последний столбец не нужен строго, но он позволяет генерировать URL-адреса изображений, которые заканчиваются с оригинальным именем файла).
Наконец, мы можем обновить модель User
:
1
2
3
4
5
6
|
class User < ActiveRecord::Base
image_accessor :avatar_image
attr_accessible :email, :first_name, :last_name, :avatar_image
…
|
Метод image_accessor
доступен инициализатору Dragonfly и требует только имени атрибута. Мы также делаем этот атрибут доступным в строке ниже.
Запуск rspec
теперь должен показать все спецификации зелеными.
Загрузка и отображение аватара
Чтобы протестировать функцию загрузки, мы можем добавить контекст к users_controller_spec.rb
в блоке PUT update
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
context «avatar image» do
let!(:image_file) { fixture_file_upload(‘/mustache_avatar.jpg’, ‘image/jpg’) }
context «uploading an avatar» do
before do
put :update, id: user.id, user: { avatar_image: image_file }
end
it «should effectively save the image record on the user» do
user.reload
user.avatar_image_name.should =~ /mustache_avatar/
end
end
end
|
Мы повторно используем тот же прибор и создадим макет для загрузки с помощью fixture_file_upload
.
Так как эта функциональность используется в Dragonfly, нам не нужно писать код для ее прохождения.
Теперь мы должны обновить наши представления, чтобы показать аватар. Давайте начнем со страницы показа пользователя и откроем app/views/users/show.html.erb
и обновим ее следующим содержанием:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<div class=»row»>
<div class=»span4″>
<% if @user.avatar_image.present?
<%= image_tag @user.avatar_image.url, class: ‘thumbnail’ %>
<% else %>
<img src=»http://placehold.it/400×400&text=Super+cool+avatar» alt=»Super cool avatar» class=»thumbnail»>
<% end %>
</div>
<div class=»span8″>
<hr /> <h2><%= @user.name %></h2>
<h4><%= @user.email %></h4>
<hr />
<%= link_to ‘Edit’, edit_user_path(@user), class: «btn» %>
</div>
</div>
|
Далее мы можем обновить app/views/users/edit.html.erb
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<%= simple_form_for @user, multipart: true do |f|
<div class=»row»>
<div class=»span4″>
<div class=»thumbnail-wrapper»>
<% if @user.avatar_image.present?
<%= image_tag @user.avatar_image.url, class: ‘thumbnail’ %>
<% else %>
<img src=»http://placehold.it/400×400&text=Super+cool+avatar» alt=»Super cool avatar» class=»thumbnail»>
<% end %>
<div class=»caption form-inline»>
<%= f.input :avatar_image, as: :file %>
</div>
</div>
</div>
<div class=»span8″>
<div class=»well»>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
</div>
<div class=»form-actions»>
<%= f.submit ‘Save’, class: «btn btn-primary» %>
</div>
</div>
</div>
<% end %>
|
Мы можем показать аватар пользователя простым вызовом @user.avatar_image.url
. Это вернет URL-адрес неизмененной версии аватара, загруженной пользователем.
Если вы запустите cucumber
сейчас, вы увидите зеленую функцию. Не стесняйтесь попробовать это в браузере!
Мы неявно полагаемся на CSS для изменения размера изображения, если оно слишком велико для его контейнера. Это шаткий подход: наш пользователь может загружать неквадратные аватары или очень маленькое изображение. Кроме того, мы всегда предоставляем одно и то же изображение, не слишком заботясь о размере страницы или пропускной способности.
Нам нужно работать в двух разных областях: добавление некоторых правил проверки к загрузке аватара и указание размера и соотношения изображений с помощью Dragonfly.
Загрузить валидации
Начнем с открытия файла user_spec.rb
и добавления нового блока спецификации:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
context «avatar attributes» do
%w(avatar_image retained_avatar_image remove_avatar_image).each do |attr|
it { should respond_to(attr.to_sym) }
end
%w(avatar_image retained_avatar_image remove_avatar_image).each do |attr|
it { should allow_mass_assignment_of(attr.to_sym) }
end
it «should validate the file size of the avatar» do
user.avatar_image = Rails.root + ‘spec/fixtures/huge_size_avatar.jpg’
user.should_not be_valid # size is > 100 KB
end
it «should validate the format of the avatar» do
user.avatar_image = Rails.root + ‘spec/fixtures/dummy.txt’
user.should_not be_valid
end
end
|
Мы проверяем наличие и разрешаем «массовое назначение» для дополнительных атрибутов, которые мы будем использовать для улучшения пользовательской формы ( :retained_avatar_image
и :remove_avatar_image
).
Кроме того, мы также проверяем, что наша пользовательская модель не будет принимать большие загрузки (более 200 КБ) и файлы, которые не являются изображениями. Для обоих случаев нам нужно добавить два файла фикстуры (изображение с указанным именем и размером более 200 КБ и текстовый файл с любым содержимым).
Как обычно, запуск этих спецификаций не приведет нас к зеленому цвету. Давайте обновим модель пользователя, чтобы добавить эти правила проверки:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
…
attr_accessible :email, :first_name, :last_name, :avatar_image, :retained_avatar_image, :remove_avatar_image
…
validates_size_of :avatar_image, maximum: 100.kilobytes
validates_property :format, of: :avatar_image, in: [:jpeg, :png, :gif, :jpg]
validates_property :mime_type, of: :avatar_image,
in: [‘image/jpg’, ‘image/jpeg’, ‘image/png’, ‘image/gif’],
case_sensitive: false
|
Эти правила довольно эффективны: обратите внимание, что в дополнение к проверке формата мы также проверяем тип mime для большей безопасности. Будучи изображением, мы допускаем файлы jpg, png и gif.
Наши спецификации должны пройти сейчас, поэтому пришло время обновить представления для оптимизации загрузки изображения.
Динамическая обработка изображений
По умолчанию Dragonfly использует ImageMagick для динамической обработки изображений по запросу. Предполагая, что у нас есть экземпляр user
в одном из наших представлений, мы могли бы тогда:
1
2
|
user.avatar_image.thumb(‘100×100’).url
user.avatar_image.process(:greyscale).url
|
Эти методы создадут обработанную версию этого изображения с уникальным хешем, и благодаря нашему уровню кэширования ImageMagick будет вызываться только один раз для каждого изображения. После этого изображение будет обслуживаться прямо из кеша.
Вы можете использовать множество встроенных методов или просто создать свой собственный, в документации Dragonfly есть много примеров .
Давайте вернемся к нашей странице edit
пользователя и обновим код представления:
1
2
3
4
5
|
…
<% if @user.avatar_image.present?
<%= image_tag @user.avatar_image.thumb(‘400×400#’).url, class: ‘thumbnail’ %>
<% else %>
…
|
Мы сделаем то же самое для страницы show
пользователя:
1
2
3
4
5
|
…
<% if @user.avatar_image.present?
<%= image_tag @user.avatar_image.thumb(‘400×400#’).url, class: ‘thumbnail’ %>
<% else %>
…
|
Мы увеличиваем размер изображения до 400 x 400 пикселей. Параметр #
также указывает ImageMagick обрезать изображение, сохраняя центральное положение. Вы можете видеть, что у нас один и тот же код в двух местах, поэтому давайте сменим его на частичный views/users/_avatar_image.html.erb
1
2
3
4
5
|
<% if @user.avatar_image.present?
<%= image_tag @user.avatar_image.thumb(‘400×400#’).url, class: ‘thumbnail’ %>
<% else %>
<img src=»http://placehold.it/400×400&text=Super+cool+avatar» alt=»Super cool avatar» class=»thumbnail»>
<% end %>
|
Затем мы можем заменить содержимое контейнера .thumbnail
простым вызовом:
1
2
3
|
<div class=»thumbnail»>
<%= render ‘avatar_image’ %>
</div>
|
Мы можем сделать еще лучше, переместив аргумент thumb
из частичного. Давайте обновим _avatar_image.html.erb
:
1
2
3
4
5
|
<% if user.avatar_image.present?
<%= image_tag user.avatar_image.thumb(args).url %>
<% else %>
<img src=»http://placehold.it/<%= args.gsub(/\W/, ») %>&text=Super+cool+avatar» alt=»Super cool avatar»>
<% end %>
|
Теперь мы можем вызвать наше частичное с двумя аргументами: один для желаемого аспекта и один для пользователя:
1
|
<%= render ‘avatar_image’, args: ‘400×400#’, user: @user %>
|
Мы можем использовать приведенный выше фрагмент при edit
и show
представлений, в то время как мы можем вызывать его следующим образом в views/users/_user_table.html.erb
, где мы показываем небольшие эскизы.
1
2
3
4
5
6
7
|
…
<td><%= link_to ‘Profile’, user_path(user) %></td>
<td>
<%= render ‘avatar_image’, args: ’16×16#’, user: user %>
</td>
<td><%= user.first_name %></td>
…
|
В обоих случаях мы также выполняем регулярное выражение для аспекта, чтобы извлечь строку, совместимую со службой placehold.it (т.е. удалить не алфавитно-цифровые символы).
Удаление Аватара
Dragonfly создает два дополнительных атрибута, которые мы можем использовать в форме:
-
retained_avatar_image
: хранит загруженное изображение между перезагрузками. Если проверки для другого поля формы (скажем, по электронной почте) не пройдены и страница перезагружена, загруженное изображение все еще доступно без необходимости его повторной загрузки. Мы будем использовать его непосредственно в форме. -
remove_avatar_image
: когда это правда, текущее изображение аватара будет удалено как из пользовательской записи, так и с диска.
Мы можем проверить удаление аватара, добавив дополнительную спецификацию к users_controller_spec.rb
в блоке avatar image
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
…
context «removing an avatar» do
before do
user.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
user.save
end
it «should remove the avatar from the user» do
put :update, id: user.id, user: { remove_avatar_image: «1» }
user.reload
user.avatar_image_name.should be_nil
end
end
…
|
Еще раз, Dragonfly получит эту спецификацию для прохождения автоматически, поскольку у нас уже есть атрибут remove_avatar_image
доступный для пользовательского экземпляра.
Давайте тогда добавим еще одну функцию в managing_profile.feature
:
1
2
3
4
5
6
7
8
|
Scenario: removing an avatar
Given the user with email «[email protected]» has the mustache avatar
And I am on the profile page for «[email protected]»
When I follow «Edit»
And I check «Remove avatar image»
And I click «Save»
Then I should be on the profile page for «[email protected]»
And the profile should show «the placeholder avatar»
|
Как обычно, нам нужно добавить несколько шагов в user_steps.rb
и обновить один, чтобы добавить user_steps.rb
для аватара заполнителя:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
Given /^the user with email «([^»]*)» has the mustache avatar$/ do |email|
u = User.find_by_email(email)
u.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
u.save
end
When /^I check «([^»]*)»$/ do |checkbox|
check checkbox
end
Then /^the profile should show «([^»]*)»$/ do |image|
pattern = case image
when ‘the placeholder avatar’
/placehold.it/
when ‘the mustache avatar’
/mustache_avatar/
end
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ pattern
end
|
Нам также необходимо добавить два дополнительных поля в форму edit
:
1
2
3
4
5
6
7
|
…
<div class=»caption form-inline»>
<%= f.input :retained_avatar_image, as: :hidden %>
<%= f.input :avatar_image, as: :file, label: false %>
<%= f.input :remove_avatar_image, as: :boolean %>
</div>
…
|
Это сделает нашу функцию успешной.
Особенности рефакторинга
Чтобы не иметь больших и слишком подробных функций, мы можем протестировать те же функции в спецификации запроса.
Давайте создадим новый файл с именем spec/requests/user_flow_spec.rb
и добавим в него этот контент:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
require ‘spec_helper’
describe «User flow» do
let!(:user) { Factory(:user, email: «[email protected]») }
describe «viewing the profile» do
it «should show the profile for the user» do
visit ‘/’
page.find(‘tr’, text: user.email).click_link(«Profile»)
current_path = URI.parse(current_url).path
current_path.should == user_path(user)
end
end
describe «updating profile data» do
it «should save the changes» do
visit ‘/’
page.find(‘tr’, text: user.email).click_link(«Profile»)
click_link ‘Edit’
fill_in :email, with: «[email protected]»
click_button ‘Save’
current_path.should == user_path(user)
page.should have_content «User updated»
end
end
describe «managing the avatar» do
it «should save the uploaded avatar» do
user.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
user.save
visit user_path(user)
click_link ‘Edit’
attach_file ‘user[avatar_image]’, Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
click_button ‘Save’
current_path.should == user_path(user)
page.should have_content «User updated»
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ /mustache_avatar/
end
it «should remove the avatar if requested» do
user.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
user.save
visit user_path(user)
click_link ‘Edit’
check «Remove avatar image»
click_button ‘Save’
current_path.should == user_path(user)
page.should have_content «User updated»
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ /placehold.it/
end
end
end
|
Спецификация включает в себя все шаги, которые мы использовали для определения нашей основной функции. Он тщательно проверяет разметку и поток, поэтому мы можем убедиться, что все работает должным образом на уровне детализации.
Теперь мы можем сократить managing_profile.feature
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
Feature: managing user profile
As a user
In order to manage my data
I want to access my user profile page
Background:
Given a user exists with email «[email protected]»
Scenario: editing my profile
Given I change the email with «[email protected]» for «[email protected]»
Then I should see «User updated»
Scenario: adding an avatar
Given I upload the mustache avatar for «[email protected]»
Then the profile should show «the mustache avatar»
Scenario: removing an avatar
Given the user «[email protected]» has the mustache avatar and I remove it
Then the user «[email protected]» should have «the placeholder avatar»
|
Обновлен user_steps.rb
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
Given /^a user exists with email «([^»]*)»$/ do |email|
Factory(:user, email: email)
end
Given /^the user with email «([^»]*)» has the mustache avatar$/ do |email|
u = User.find_by_email(email)
u.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
u.save
end
Then /^I should see «([^»]*)»$/ do |content|
page.should have_content(content)
end
Then /^the profile should show «([^»]*)»$/ do |image|
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ pattern_for(image)
end
Given /^I change the email with «([^»]*)» for «([^»]*)»$/ do |new_email, old_email|
u = User.find_by_email(old_email)
visit edit_user_path(u)
fill_in :email, with: new_email
click_button ‘Save’
end
Given /^I upload the mustache avatar for «([^»]*)»$/ do |email|
u = User.find_by_email(email)
visit edit_user_path(u)
attach_file ‘user[avatar_image]’, Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
click_button ‘Save’
end
Given /^the user «([^»]*)» has the mustache avatar and I remove it$/ do |email|
u = User.find_by_email(email)
u.avatar_image = Rails.root + ‘spec/fixtures/mustache_avatar.jpg’
u.save
visit edit_user_path(u)
check «Remove avatar image»
click_button ‘Save’
end
Then /^the user «([^»]*)» should have «([^»]*)»$/ do |email, image|
u = User.find_by_email(email)
visit user_path(u)
n = Nokogiri::HTML(page.body)
n.xpath(«.//img[@class=’thumbnail’]»).first[‘src’].should =~ pattern_for(image)
end
def pattern_for(image_name)
case image_name
when ‘the placeholder avatar’
/placehold.it/
when ‘the mustache avatar’
/mustache_avatar/
end
end
|
Добавление поддержки S3
В качестве последнего шага мы можем легко добавить поддержку S3 для хранения файлов аватаров. Давайте снова откроем config/initializers/dragonfly.rb
и обновим блок конфигурации:
01
02
03
04
05
06
07
08
09
10
11
|
Dragonfly::App[:images].configure do |c|
c.datastore = Dragonfly::DataStorage::S3DataStore.new
c.datastore.configure do |d|
d.bucket_name = ‘dragonfly_tutorial’
d.access_key_id = ‘some_access_key_id’
d.secret_access_key = ‘some_secret_access_key’
end
end unless %(development test cucumber).include?
|
Это будет работать «из коробки» и повлияет только на производство (или любую другую среду, которая не указана в файле). Dragonfly по умолчанию будет хранилищем файловой системы для всех остальных случаев.
Вывод
Я надеюсь, что вы нашли этот урок интересным, и вам удалось найти несколько интересных деталей.
Я рекомендую вам обратиться к странице Dragonfly GitHub для получения обширной документации и других примеров использования — даже вне приложения Rails.