Ненавязчивая система входа в систему — это та, которая мешает пользователю. Это сделает ваше приложение более приятным и изысканным. Эта статья проведет вас через процесс настройки пользовательских логинов, а затем упростит процесс, переместив форму в модальное окно, которое обменивается данными с сервером. Кроме того, эта статья покажет вам, как создать установку, используя jQuery и Prototype, чтобы вы могли выбрать свою любимую библиотеку. В этой статье предполагается, что у вас есть опыт работы с Rails и jQuery или Prototype.
Вы можете использовать Adman65 / nettuts для успешного входа в систему. Обязательно используйте неверные учетные данные, чтобы вы могли видеть, как все работает.
Что мы делаем
Начиная
Мы начнем с создания фиктивного приложения, имеющего открытую и закрытую страницу. Корневой URL является общедоступной страницей. На общедоступной странице есть ссылка для входа. Если пользователь успешно входит в систему, он перенаправляется на личную страницу. Если нет, они перенаправляются обратно в форму входа. Личная страница показывает имя пользователя. Мы будем использовать это как отправную точку для адаптации сайта.
Первый шаг — использование команды rails для создания нового приложения, затем установка и настройка authlogic.
1
2
|
$ cd into-a-directory
$ rails unobtrusive-login
|
Добавьте authlogic.
1
2
|
# /config/environment.rb
config.gem ‘authlogic’
|
Теперь установите драгоценные камни.
1
2
3
|
$ sudo gem install gemcutter
$ sudo gem tumble
$ sudo rake gems:install
|
Затем создайте модель пользователя и добавьте необходимые столбцы аутентификации в миграцию.
1
2
3
4
5
6
7
8
9
|
$ ./script/generate model User
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate
create db/migrate/20100102082657_create_users.rb
|
Теперь добавьте столбцы к новой миграции.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :login, :null => false
t.string :crypted_password, :null => false
t.string :password_salt, :null => false
t.string :persistence_token, :null => false
t.timestamps
end
end
def self.down
drop_table :users
end
end
|
Перенос базы данных.
1
|
$ rake db:migrate
|
Включите authlogic в пользовательскую модель.
1
2
3
4
|
# /app/models/user.rb
class User < ActiveRecord::Base
acts_as_authentic
end
|
Теперь мы можем создать пользователя. Поскольку это демонстрационное приложение, веб-функциональность для регистрации не требуется. Итак, откройте консоль и создайте пользователя:
1
2
|
$ ./script/console
>> me = User.create(:login => ‘Adman65’, :password => ‘nettuts’, :password_confirmation => ‘nettuts’)
|
Теперь у нас есть пользователь в системе, но у нас нет возможности войти или выйти. Для этого нам нужно создать модели, контроллеры и представления. Authlogic имеет свой собственный класс для отслеживания логинов. Мы можем использовать генератор для этого:
1
2
|
# create the user session
$ ./script/generate UserSession
|
Далее нам нужно сгенерировать контроллер, который будет входить / выходить из системы пользователей. Вы можете создавать сессии, как и любой другой ресурс в Rails.
1
2
|
# create the session controller
$ ./script/generate controller UserSessions
|
Теперь установите его содержимое:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
# /app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
flash[:notice] = «Login successful!»
redirect_back_or_default user_path
else
render :action => :new
end
end
end
|
Он выглядит точно так же, как контроллер, созданный с помощью скаффолдинга. Теперь создайте контроллер пользователя, который имеет публичный и приватный контент. Создайте пользовательский контроллер. Внутри контроллера мы будем использовать фильтр before для ограничения доступа к закрытым областям. Действие индекса является общедоступным, а показ — закрытым.
1
2
|
# create the users controller
$ ./script/generate controller users
|
Обновите его содержимое:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
# /app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :login_required, :only => :show
def index
end
def show
@user = current_user
end
private
def login_required
unless current_user
flash[:error] = ‘You must be logged in to view this page.’
redirect_to new_user_session_path
end
end
end
|
Вы должны заметить, что current_user — неопределенный метод в этой точке. Определите эти методы в ApplicationController. Откройте application_controller.rb и обновите его содержимое:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
# application controller
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
# From authlogic
filter_parameter_logging :password, :password_confirmation
helper_method :current_user_session, :current_user
private
def current_user_session
@current_user_session ||= UserSession.find
end
def current_user
@current_user ||= current_user_session && current_user_session.user
end
end
|
На данный момент модели и контроллеры завершены, но представления нет. Нам нужно создать представления для формы входа в систему и публичного и частного контента. Мы будем использовать гем nifty-generators, чтобы создать базовый макет.
1
2
|
$ sudo gem install nifty-generators
$ ./script/generate nifty_layout
|
Время создать форму входа. Мы собираемся использовать частичное для этого, потому что в будущем мы будем использовать частичное для визуализации только формы входа в модальное поле. Вот код для создания формы входа. Это точно так же, как если бы вы создавали запись в блоге или любую другую модель.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
# create the login views
# /app/views/user_sessions/_form.html.erb
<% form_for(@user_session, :url => user_session_path) do |form|
<%= form.error_messages %>
<p>
<%= form.label :login %>
<%= form.text_field :login %>
</p>
<p>
<%= form.label :password %>
<%= form.password_field :password %>
</p>
<%= form.submit ‘Login’ %>
<% end %>
|
Отобразите частичное в новом представлении:
1
2
3
|
# /app/views/user_sessions/new.html.erb
<% title ‘Login Please’ %>
<%= render :partial => ‘form’ %>
|
Создайте несколько основных страниц для публичного и частного контента. Действие index показывает общедоступный контент, а show показывает частный контент.
1
|
# create the dummy public page # /app/views/users/index.html.erb <% title ‘Unobtrusive Login’ %> <p>Public Facing Content</p> <%= link_to ‘Login’, new_user_session_path %>
|
И для личной страницы:
1
2
3
4
5
6
|
# create the dummy private page
# /app/views/users/show.html.erb
<% title ‘Welcome’ %>
<h2>Hello <%=h @user.login %></h2>
<%= link_to ‘Logout’, user_session_path, :method => :delete %>
|
Удалите файл /public/index.html и запустите сервер. Теперь вы можете войти и выйти из приложения.
1
|
$ ./script/server
|
Вот несколько скриншотов демонстрационного приложения. Первая — это общедоступная страница.
Теперь форма входа
И личная страница
И, наконец, доступ запрещен при попытке зайти на http: // localhost: 3000 / user
Процесс входа в AJAX
Прежде чем продолжить, нам нужно понять, как сервер и браузер будут работать вместе, чтобы завершить этот процесс. Мы знаем, что нам нужно будет использовать JavaScript для модального поля и сервера для проверки логинов. Давайте выясним, как это будет работать. Пользователь нажимает на ссылку для входа, затем появляется модальное поле с формой для входа. Пользователь заполняет форму и либо перенаправляется на личную страницу, либо модальное поле обновляется новой формой входа. Следующий вопрос: как обновить модальное окно или сказать браузеру, что делать после того, как пользователь отправит форму? В Rails есть блоки response_to. С помощью response_to вы можете указать контроллеру визуализировать различное содержимое, если пользователь запросил XML, HTML, JavaScript, YAML и т. Д. Поэтому, когда пользователь отправляет форму, сервер может возвратить некоторый JavaScript для выполнения в браузере. Мы будем использовать эту визуализацию для новой формы или перенаправления. Прежде чем углубляться, давайте пройдемся по порядку.
- Пользователь заходит на публичную страницу
- Пользователь нажимает на ссылку для входа
- Появляется модальное окно
- Пользователь заполняет форму
- Форма отправлена на сервер
- Сервер возвращает JavaScript для выполнения
- Браузер выполняет JavaScript, который либо перенаправляет, либо обновляет модальное окно.
Это высокий уровень. Вот реализация низкого уровня.
- Пользователь заходит на публичную страницу
- На общедоступной странице есть некоторый JavaScript, который запускается, когда DOM готов, который присоединяет JavaScript к ссылке для входа. Этот javscript выполняет XMLHTTPRequest (с этого момента XHR) на сервере для некоторого JavaScript. JavaScript устанавливает содержимое модального поля в форму HTML. JavaScript также делает что-то очень важное. Он связывает действие отправки формы с XHR с данными POST и действием формы. Это позволяет пользователю продолжать заполнять форму авторизации внутри модального поля.
- Модальное поле теперь имеет форму и требуется JavaScript
- Пользователь нажимает «Войти»
- Вызывается функция submit (), которая выполняет POST XHR для действия формы с ее данными.
- Сервер генерирует JavaScript для формы или перенаправления
- Браузер получает JavaScript и выполняет его. Браузер либо обновит модальное окно, либо перенаправит пользователя через window.location.
Пик на контроллере AJAX Ready
Давайте посмотрим на новую структуру для контроллера UserSessions.
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
|
class UserSessionsController < ApplicationController
layout :choose_layout
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
respond_to do |wants|
wants.html { redirect_to user_path(@user_session.user) }
wants.js { render :action => :redirect } # JavaScript to do the redirect
end
else
respond_to do |wants|
wants.html { render :new }
wants.js # defaults to create.js.erb
end
end
end
private
def choose_layout
(request.xhr?) ?
end
end
|
Как видите, структура другая. Внутри условия if save, иначе условного, response_to используется для визуализации правильного содержимого. want.xx где xx — тип содержимого По умолчанию Prototype и jQuery запрашивают текст / JavaScript. Это соответствует want.js. Мы готовы начать работу с AJAX. Мы не будем использовать какие-либо плагины, за исключением модальных блоков. Мы будем использовать Facebox для jQuery и ModalBox для Prototype.
Опытный образец
Rails имеет встроенную поддержку Prototype. JavaScript-помощники Rail — это функции Ruby, которые генерируют JavaScript, использующий Prototype. Этот метод известен как RJS (Ruby JavaScript). Одним из примеров является remote_form_for, который работает как стандартный for_for и добавляет JS, связанный с onsubmit, который отправляет в форму свое действие, используя метод с данными. Я не буду использовать RJS в этой статье, так как хочу продемонстрировать ванильный JS. Я думаю, что используя чистый JS и исключая JS-помощников, эта статья станет более доступной для менее опытных разработчиков. При этом вы можете легко выполнить эти шаги, используя помощники RJS / Prototype, если вы выберете.
Добавить Prototype в приложение очень просто. Когда вы используете команду rails, она создает файлы Prototype и scriptaculous в / public / JavaScripts. Включать их легко. Откройте /app/views/layouts/application.erb и добавьте эту строку в тег head:
1
|
<%= JavaScript_include_tag :defaults %>
|
JavaScript_include_tag создает теги сценариев для файлов по умолчанию в / public / JavaScripts, наиболее важно prototype.js, effect.js и application.js. Effects.js является сценарием. application.js — это файл, который вы можете использовать для хранения JS для конкретного приложения. Теперь нам нужен модальный плагин. Мы собираемся использовать это . Это очень хороший модальный плагин, вдохновленный OSX. Исходный код размещен на GitHub, поэтому вам придется клонировать и перемещать файлы в каталоге вашего проекта. Например:
1
2
3
4
5
6
7
|
$ cd code
$ git clone git://github.com/okonet/modalbox.git
$ cd modalbox
# move the files in the correct directories.
# move modalbox.css into /public/stylesheets
# move modalbox.js into /public/JavaScripts
# move spinner.gif into /public/images
|
Теперь включите таблицы стилей и JavaScript в ваше приложение.
1
2
3
4
|
<%= stylesheet_link_tag ‘application’ %>
<%= stylesheet_link_tag ‘modalbox’ %>
<%= JavaScript_include_tag :defaults %>
<%= JavaScript_include_tag ‘modalbox’%>
|
Теперь давайте получим ссылку для входа в систему, чтобы открыть модалбокс. Для этого нам нужно добавить JavaScript, который запускается, когда DOM готов, который присоединяет modalbox к нашей ссылке. Когда пользователь нажимает ссылку для входа в систему, браузер выполняет GET для / user_sessions / new, которая содержит форму для входа. Ссылка для входа в систему использует селектор # login-link. Обновите ссылку для входа в систему, чтобы использовать новый идентификатор в /app/views/users/index.html.erb. Измените функцию link_to следующим образом:
1
|
<%= link_to ‘Login’, new_user_session_path, :id => ‘login-link’ %>
|
Это дает нам # логин-ссылку. Теперь для JavaScript, чтобы прикрепить модалбокс. Добавьте этот JS в /public/JavaScripts/application.js
1
2
3
4
5
6
7
8
9
|
document.observe(‘dom:loaded’, function() {
$(‘login-link’).observe(‘click’, function(event) {
event.stop();
Modalbox.show(this.href,
{title: ‘Login’,
width: 500}
);
});
})
|
Существует несколько простых JS для того, когда пользователь нажимает на ссылку, открывается модальное окно с ссылкой на ссылку. Обратитесь к документации modalbox, если вы хотите больше настроек. Вот скриншот:
Обратите внимание, что внутри модальной рамки выглядит очень похоже на нашу стандартную страницу. Rails использует макет нашего приложения для всех ответов HTML. Поскольку наши XHR хотят фрагменты HTML, имеет смысл отображать их без макетов. Вернитесь к примеру контроллера. Я ввел метод определения макета. Добавьте это в UserSessionsController, чтобы отключить макет для XHR.
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
|
class UserSessionsController < ApplicationController
layout :choose_layout
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
flash[:notice] = «Login successful!»
redirect_to user_path
else
render :action => :new
end
end
def destroy
current_user_session.destroy
flash[:notice] = «Logout successful!»
redirect_to root_path
end
private
def choose_layout
(request.xhr?) ?
end
end
|
Обновите страницу и нажмите на ссылку, вы должны получить что-то вроде этого:
Заполните форму и посмотрите, что получится. Если вы заполняете поле с плохой информацией, вы будете перенаправлены за пределы модального поля. Если вы залогинены правильно, вы будете перенаправлены в обычном режиме. В соответствии с требованиями пользователь должен иметь возможность снова и снова заполнять форму внутри модального поля, пока он не войдет в систему правильно. Как мы можем сделать это? Как описано выше, нам нужно использовать AJAX для отправки данных на сервер, а затем использовать JavaScript, чтобы обновить модальное поле с помощью формы или выполнить перенаправление. Мы знаем, что modalbox выполняет GET для HTML. После отображения исходного modalbox нам нужно написать JS, который заставляет форму представлять себя в стиле AJAX. Это позволяет форме отправлять себя внутри модального поля. Простое добавление этого кода после вызова модального окна не сработает, потому что XHR может не завершиться. Нам нужно использовать обратный вызов Modalbox afterLoad. Вот новый код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
document.observe(‘dom:loaded’, function() {
$(‘login-link’).observe(‘click’, function(event) {
event.stop();
Modalbox.show(this.href,
{title: ‘Login’,
width: 500,
afterLoad: function() {
$(‘new_user_session’).observe(‘submit’, function(event) {
event.stop();
this.request();
})
}}
);
});
})
|
Запрос формы # — это удобный метод для сериализации и отправки формы с помощью Ajax.Request на URL-адрес атрибута действия формы — это именно то, что нам нужно. Теперь вы можете заполнить форму внутри модала без ее закрытия. Клиентская часть теперь завершена. Как насчет серверной части? Клиент отправляет POST, желая вернуть JS. Сервер должен принять решение либо вернуть JavaScript для обновления формы, либо выполнить перенаправление. В UserSessionsController мы будем использовать response_to для обработки запроса JS и условное выражение для возврата правильного JS. Давайте начнем с обработки неудачного входа в систему. Сервер должен вернуть JS, который обновляет форму и сообщает новой форме о передаче через ajax. Мы разместим этот шаблон в /app/views/users_sessions/create.js.erb. Вот структура для нового действия создания:
01
02
03
04
05
06
07
08
09
10
11
12
|
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
flash[:notice] = «Login successful!»
redirect_to user_path
else
respond_to do |wants|
wants.html { render :new }
wants.js # create.js.erb
end
end
end
|
Теперь давайте заполним create.js.erb:
1
2
3
4
5
6
|
$(‘MB_content’).update(«<%= escape_JavaScript(render :partial => ‘form’) %>»);
Modalbox.resizeToContent();
$(‘new_user_session’).observe(‘submit’, function(event) {
event.stop();
this.request();
});
|
Сначала мы обновляем содержимое, чтобы включить новую форму. Затем мы изменяем размер модальной рамки. Затем мы как-то изменили форму. Вуаля, вы можете заполнить форму столько раз, сколько захотите.
Далее нам нужно обработать случай перенаправления. Создайте новый файл в /app/views/users_sessions/redirect.js.erb:
1
|
window.location=”<%= user_path %>”;
|
Теперь обновите действие create для обработки процесса перенаправления:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
respond_to do |wants|
wants.html do
flash[:notice] = «Login successful!»
redirect_to user_path
end
wants.js { render :redirect }
end
else
respond_to do |wants|
wants.html { render :new }
wants.js # create.js.erb
end
end
end
|
Вот и все! Теперь попробуйте войти с правильными учетными данными, и вы будете перенаправлены на личную страницу. Для дальнейшего изучения, попробуйте добавить счетчик и уведомление о том, что форма отправляется или они перенаправлены. Приложение все еще работает, если у пользователя отключен JavaScript.
JQuery
Поскольку я уже рассмотрел процесс создания прототипа, я не буду вдаваться в те же детали, что и раньше. Вместо этого я быстро перейду к описанию альтернативного JavaScript для добавления в приложение. Версия jQuery будет иметь ту же структуру, что и версия Prototype. Все, что нам нужно изменить — это то, что находится в application.js, create.js.erb и JavaScript / css.
Первое, что нам нужно сделать, это загрузить jQuery и Facebox . Переместите jQuery в / public / JavaScripts как jquery.js. Для facebox переместите изображения в / public / images /, таблицы стилей в / public / stylesheets и, наконец, JS в / public / JavaScripts. Теперь обновите /app/views/layouts/application.html.erb, чтобы отразить изменения:
1
2
3
4
5
6
7
8
|
<head>
<title><%= h(yield(:title) || «Untitled») %></title>
<%= stylesheet_link_tag ‘facebox’ %>
<%= stylesheet_link_tag ‘application’ %>
<%= JavaScript_include_tag ‘jquery’ %>
<%= JavaScript_include_tag ‘facebox’ %>
<%= JavaScript_include_tag ‘application’ %>
</head>
|
Facebox поставляется с таблицей стилей по умолчанию, которая предполагает, что у вас есть ваши изображения в / facebox. Вам нужно обновить эти селекторы в facebox.css следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
#facebox .b {
background:url(/images/b.png);
}
#facebox .tl {
background:url(/images/tl.png);
}
#facebox .tr {
background:url(/images/tr.png);
}
#facebox .bl {
background:url(/images/bl.png);
}
#facebox .br {
background:url(/images/br.png);
}
|
Теперь мы прикрепляем фейсбокс к ссылке для входа. Откройте /public/JavaScripts/application.js и используйте это:
1
2
3
4
5
6
|
$(document).ready(function() {
$(‘#login-link’).facebox({
loadingImage : ‘/images/loading.gif’,
closeImage : ‘/images/closelabel.gif’,
});
});
|
Я переопределяю настройки по умолчанию для изображений, чтобы отразить новый путь изображения. Запустите сервер и перейдите на страницу индекса. Вы должны увидеть симпатичный фейсбокс с формой входа в систему:
Следующее, что нам нужно сделать, это настроить форму для отправки через AJAX. Как и раньше, мы должны будем использовать обратные вызовы для выполнения кода после того, как модальное окно будет готово. Мы будем использовать метод post jQuery для запроса XHR. Facebox имеет крючок после открытия, который мы можем использовать. application.js:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$(document).ready(function() {
$(‘#login-link’).facebox({
loadingImage : ‘/images/loading.gif’,
closeImage : ‘/images/closelabel.gif’,
});
$(document).bind(‘reveal.facebox’, function() {
$(‘#new_user_session’).submit(function() {
$.post(this.action, $(this).serialize(), null, «script»);
return false;
});
});
});
|
Обновление create.js.erb должно быть достаточно простым. Мы должны обновить содержимое фейсбокса и повторно изменить форму. Вот код:
1
2
3
4
5
|
$(‘#facebox .content’).html(«<%= escape_JavaScript(render :partial => ‘form’) %>»);
$(‘#new_user_session’).submit(function() {
$.post(this.action, $(this).serialize(), null, «script»);
return false;
});
|
Вот и все! Вот конечный продукт:
Загрузка кода
Вы можете получить код здесь . Есть ветви для каждой библиотеки, так что вы можете проверить версии Prototype или jQuery. Есть вопросы, комментарии, проблемы? Еще раз спасибо за чтение!
- Подпишитесь на нас в Твиттере или подпишитесь на ленту Nettuts + RSS для получения лучших учебных материалов по веб-разработке.