Статьи

Rails Deep Dive: локации, аутентификация

В последнем посте мы закончили нашу первую историю пользователя. Эта пользовательская история была довольно простой, но она вычеркнула дизайн нашей домашней страницы. Следующая история пользователя, как администратора, я хочу пригласить пользователей в Loccasions , не так проста.

Значения этой пользовательской истории велики: во-первых, у нас есть новая роль администратор, которая позволяет разделить наши роли на две (незарегистрированный пользователь и администратор). Во-вторых, роль администратора выявляет идею авторизации , когда функциональность сайта ограничена в зависимости от роли пользователя. Конечно, это указывает нам на аутентификацию , потому что мы должны знать, кто пользователь, прежде чем мы сможем выяснить, что пользователь может сделать. К концу этой пользовательской истории мы должны настроить аутентификацию и авторизацию и быть готовы к работе.

Создать филиал

В последнем посте я упустил возможность создать ветку git для нашей новой истории. Это держит наш код изолированным от ветви и позволяет нам вносить несвязанные изменения в master, если это необходимо. Вот хорошая статья о рабочем процессе Git. Git облегчает ветвление

git checkout -b inviting_users 

и мы в нашей новой ветке, готовы к работе.

Написать тест

Продолжая наш подход, основанный на тестировании, давайте напишем тест для входа в приложение. Здесь мы начнем разбивать нашу пользовательскую историю на более мелкие истории, чтобы облегчить тестирование. Разбиение проблемы или части функциональности на более мелкие части облегчает решение более крупной проблемы. Вот наша первая история с субпользователями : Как администратор, я хочу войти в Loccasions .

Поскольку мы пишем спецификации с точки зрения пользователя, каждая история (и дополнительная история) имеет значение. В этой истории «вход в систему» ​​может означать много вещей, поэтому как мы можем измерить его, чтобы соответствовать тому, что мы / наш клиент хотим? В этом случае я посоветовался с клиентом, который хочет, чтобы крутой выпадающий список (а-ля Twitter) имел форму входа. Хотя я согласился, что это круто, я убедил клиента в прогрессивном улучшении , что позволило нам на данный момент разработать отдельную страницу для формы входа и вернуться, чтобы сделать ее более сексуальной позже.

Учитывая ожидания наших клиентов, мы можем провести тестирование формы входа. Сначала мы просто убедимся, что страница входа имеет форму и заголовок.

 require 'spec_helper' feature 'Sign In', %q{ As an administrator I want to sign in to Loccasions } do background do visit "/" end scenario "Click Sign In" do click_link "Sign In" page.should have_selector("title", :text => "Loccasions: Sign In") page.should have_selector('form') end end 

(Не забудьте запустить Mongodb, прежде чем запускать ваши спецификации)
Эта спецификация не работает, жалуется на несоответствие названия. Кроме того, я заметил, что получаю следующее сообщение, когда запускаю свои спецификации:

 NOTE: Gem.available? is deprecated, use Specification::find_by_name. It will be removed on or after 2011-11-01. Gem.available? called from /Users/ggoodrich/.rvm/gems/ruby-1.9.2-p290@loccasions/gems/jasmine-1.0.2.1/lib/jasmine/base.rb:64. 

Ммм … Мне это не нравится. Я быстро отключил репозиторий jasmine-gem github и показал, что они на версии 1.1.0.rc3. Сейчас я добавлю версию в свой Gemfile и надеюсь, что она сработает, когда мы перейдем к тестированию на стороне клиента. Повышение версии исправляет это предупреждение, поэтому насущные потребности удовлетворяются.

Здесь я полагаюсь на опыт, чтобы сделать свой следующий шаг. Я заинтересован в использовании Devise для аутентификации, и я знаю, что у него есть свои собственные представления для регистрации, входа и т. Д. Другими словами, пришло время настроить Devise.

Настройка Devise

Снова положив голову на удивительные RailsApps , давайте подготовим RSpec для Devise. Создайте файл spec / support / devise.rb с помощью:

 RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller end 

Теперь перейдем к типичной настройке Devise.

 rails g devise:install 

Это создает config / initializer / devise.rb и config / locales / devise.en.yml . Если мы посмотрим в файле инициализатора, то увидим, что require 'devise/orm/mongoid' , поэтому мы знаем, что Devise знает о нашем выборе использовать Mongoid. Вывод устройства devise:install дает несколько инструкций:

  • Настройка параметров по умолчанию для ActionMailer
  • Настройте маршрут по умолчанию (мы уже сделали это)
  • Убедитесь, что мы обрабатываем наши сообщения flash / уведомления в нашем макете.

Давайте сделаем это, пока оно свежее. я добавил

 config.action_mailer.default_url_options = { :host => 'localhost:3000' } 

в config / environment / development.rb Также я добавил:

 %p.notice= notice %p.alert= alert 

в app / views / layouts / application.html.haml чуть выше вызова yield . Это может не остаться там, но мы не беспокоимся об этом прямо сейчас.
Devise создаст для нас модель User:

 rails g devise User 

Вывод этой команды показывает, что мы получаем модель (User), user_spec и новый маршрут ( devise_for :users ). Если мы делаем быстрые rake routes в командной строке, мы видим:

 new_user_session GET /users/sign_in(.:format) {:action=>"new", :controller=>"devise/sessions"} 

именно туда мы хотим перейти по ссылке «Войти». Давайте изменим это в макете приложения.

 #sign_in.sixteen.columns %a(href= new_user_session_path ) Sign In 

Перезапускаем нашу спецификацию, и у нас все та же ошибка. На данный момент, давайте запустим сервер и посмотрим, что происходит.

Страница регистрации

Вау … это выглядит довольно хорошо. Однако название не то, что мы хотим, и у него есть ссылка «Зарегистрироваться», которую мы можем захотеть позже, но пока нет. Нам нужно настроить представления Devise, и, к счастью, Devise предоставляет нам простой способ сделать это:

 rails g devise:views 

Вывод разработанных представлений

Это создает довольно много взглядов, и все они ERB, а не Haml … UGH. Погуглив, я нашел это в вики Devise, где подробно рассказывалось, как получить вид Haml для Devise. Итак, rm -rf app/views/devise , добавьте gem 'hpricot', '~> 0.8.4'
и gem 'ruby_parser', '~> 2.2.0' для группы разработчиков в вашем Gemfile, bundle install и следуйте инструкциям в вики. Blech. Не конец света, но не единороги и радуги.
Во-первых, давайте изменим название. Поскольку представления Devise будут использовать один и тот же макет приложения, нам нужен способ изменить заголовок для каждой страницы. Введите ApplicationHelper . Добавьте этот метод в app / helpers / application_helper.rb

 def title(page_title) content_for(:title) { page_title } end 

Теперь замените тег title в макете приложения на:

 %title= "Loccasions: #{content_for?(:title) ? content_for(:title) : 'Home' }" 

Наконец, добавьте это в начало app / views / devise / session / new.html.haml :

 - title('Sign In') 

Теперь спецификации все проходят.

Точка принятия решения: имена пользователей

После некоторого обсуждения с клиентом мы собираемся добавить атрибут name для пользователей. Давайте добавим тест для нашего нового атрибута. Devise был достаточно любезен, чтобы создать user_spec, поэтому давайте создадим тест для name . spec / models / user_spec.rb )

 describe User do it "should have a 'name' attribute" do user = User.new user.should respond_to(:name) user.should respond_to(:name=) end end 

Эта спецификация не работает, как и ожидалось. Мы можем сделать это, добавив это в app / models / user.rb

 field :name attr_accessible :name 

Я хочу, чтобы имя было уникальным и обязательным. Тесты (с небольшим рефакторингом):

 describe User do describe "the 'Name' attribute" do before(:each) do @user = Factory.build(:user) end it "should exist on the User model" do @user.should respond_to(:name) @user.should respond_to(:name=) end it "should be unique" do @user.save user2 = Factory.build(:user, :email=>'[email protected]') user2.valid?.should be_false user2.errors[:name].should include("is already taken") end it "should be required" do @user.name=nil @user.valid?.should be_false @user.errors[:name].should include("can't be blank") end end end 

Считыватель уведомлений заметил вызовы Factory в переработанной спецификации. Нам нужен пользователь для этого теста, и мы обратимся к Factory Girl, чтобы получить его. Добавьте файл spec / factories.rb с помощью:

 require 'factory_girl' FactoryGirl.define do factory :user do name 'Testy' email '[email protected]' password 'password' end end 

Запуск спецификации дает нам 2 ошибки:

Имя пользователя Spec Fails

Добавьте быструю проверку в наше поле name ,

 validates :name, :presence => true, :uniqueness => true 

и все наши спецификации проходят. Теперь мы можем перейти к тестированию входа.

Тестовый вход

Первый пункт, который нужно определить для нашего теста входа, — что происходит, когда пользователь успешно входит в систему?
Клиент считает, что пользователь должен быть перенаправлен на свою индивидуальную «домашнюю» страницу. Что же находится на домашней странице пользователя? Мы знаем, что нашими основными бизнес-объектами являются Событие и Случай, и что Случаи живут внутри Событий. Домашняя страница пользователя, следовательно, должна, вероятно, перечислить события пользователя, чтобы начать. Затем спецификация должна заполнить и отправить форму, а затем перенаправить на домашнюю страницу пользователя.

Прежде чем мы напишем эту спецификацию, я хочу сделать spec задачей Rake по умолчанию (я устал набирать rake spec ), поэтому добавьте ее в конец вашего Rakefile

 Rake::Task[:default].prerequisites.clear task :default => [:spec] по Rake::Task[:default].prerequisites.clear task :default => [:spec] 

Теперь мы можем просто набрать rake и наши спецификации будут запущены. Аааааа, так лучше

Вот наша спецификация для входа:

 scenario "Successful Sign In" do click_sign_in fill_in 'Email', :with => '[email protected]' fill_in 'Password', :with => 'password' click_on('Sign in') current_path.should == user_root_path # this path is used by Devise end 

Обратите внимание на метод click_sign_in ? Я сделал быстрый помощник ( spec / support / request_helpers.rb ), поэтому мне не нужно было продолжать вводить строки, чтобы щелкнуть на странице входа.

 module RequestHelpers module Helpers def click_sign_in visit "/" click_link "Sign In" end end end RSpec.configure.include RequestHelpers::Helpers, :type => :acceptance, :example_group => { :file_path => config.escaped_path(%w[spec acceptance]) } 

Это будет включать только нашего помощника в приемочные тесты, то есть любые спецификации в спецификации / принятии (Примечание: RSpec определяет набор «типов» спецификаций, таких как запрос, контроллер, модели и т. Д. Здесь мы просто добавляем принятие )

Запустив rake (да! Разве это не лучше?), И мы получим ожидаемую ошибку о неопределенности user_root_path . Просто чтобы пройти тестирование, добавьте это в config / rout.rb

 match 'events' => 'home#index', :as => :user_root 

Мы назовем маршрут /events , так как мы знаем, что события будут основным курсом домашней страницы пользователя. Спецификация теперь не работает, потому что URL не совпадают. После отправки формы URL-адрес не изменяется. Это потому, что у нас нет пользователей в базе данных. Добавьте это в сценарий «Успешный click_sign_in сразу после click_sign_in

 FactoryGirl.create(:user) 

Ура! Спецификация сейчас проходит. user_root маршрут user_root — это соглашение Devise о переопределении, когда пользователь перенаправляется после успешного входа. Мы не полностью проверили аутентификацию, но она работает. Для полноты картины, давайте удостоверимся, что неудачный вход в систему не удается Добавьте это в сценарии «Успешный вход»:

 scenario "Unsuccessful Sign In" do click_sign_in fill_in 'Email', :with => '[email protected]' fill_in 'Password', :with => 'badpassword' click_on 'Sign in' current_path.should == user_session_path page.should have_content("Invalid email or password") end 

Да, новый сценарий проходит, как и ожидалось. Давайте пойдем дальше и отправим это в github.

 git add . git commit -m "Basic authentication" git checkout master git merge inviting_users 

Запустите ваши спецификации здесь, просто чтобы убедиться, что все в порядке, затем

 git push origin master git branch -d inviting_users 

Ну, это заняло больше времени, чем я ожидал

Эта статья становится слишком длинной, поэтому мы остановимся на ней и перейдем к нашей странице пользовательских событий в следующей статье. Как всегда, ваши комментарии о прогрессе Loccasions приветствуются.