railsmaniac

Railsmaniac.com rss

Notes by web-addicted

Нужно ли разработчику знание высшей математики?

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

Я хочу рассказать, почему мне после трех с половиной лет работы не хочется потерять возможность получить высшее образование.

Read the rest of this entry »

Remarkable goodness?

Коллеги, Shoulda и Remarkable — это такие очень хорошие наборы макросов для покрытия тестами тривиальных вещей — валидации в моделях, правильности ответов ваших контроллеров и так далее.

Но как аргументировать необходимость покрытия тестами таких тривиальных вещей? Мы попали в ситуацию, когда выполнение всех спеков не гарантирует целостности и полной работоспособности этой версии кода, казалось бы, необходимость полного покрытия кода спецификациями в такой момент должна быть просто очевидна! Хочу объяснить ребятам, зачем мне 100% spec coverage, но не получается хоть убей.

UPD: На самом деле, спецификации нужны только для того, чтобы гарантировать работоспособность проекта после внесения изменений или после разработки нового функционала по новым спецификациям, это очевидно :) Средства де-факто не важны.

На вопрос о том, какие ошибки могут возникнуть в валидации объектов или в ассоциациях AR классов — у меня нет честного и точного ответа. Да, покрытие спецификацией таких вещей — в чем-то нарушает конвенцию DRY, однако, без таких спецификаций не гарантируется работоспособность объектов данного класса, а значит и всего приложения.

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

Лучше у меня будет болеть голова о том, как «распрямить» объект, чем о том, как понять, почему он не работает после очередного изменения в кореллирующем классе.

Большое спасибо всем откликнувшимся :)


Rails API

Вот штука, которая наверняка пригодится как новичкам, так и уже опытным Ruby On Rails разработчикам: http://railsapi.com.
Мне ее совсем недавно подсказал коллега, когда rubyonrails.org был недоступен.

Очень-очень удобно, если использовать в Fluid приложении локальную копию, хороший локальный api к ruby и rails и упаковке плагинов (среди них: rspec, rspec-rails, authlogic, даже sinatra и rack в качестве отдельных подарков судьбы :) ), но он быстро устареет. Если не хочется устанавливать rdoc server в passenger локально — не плохой вариант :) Если все-таки решите делать FluidApp — на railslogo есть хорошая иконка.

За все это великолепие нужно сказать отдельное спасибо @voloko.
Хорошего дня :)

Мини-конференция по Ruby и Rails

Привет!
Я уже писал об этом на хабре, а в ror2ru обсуждение идет уже почти две недели!

19-го апреля в три часа дня, в Москве, в клубе Гоголь состоится мини-конференция по ruby и rails.
Будет несколько интересных презентаций, я расскажу про pivotal tracker и то, как его красиво подружить с github, будет очень много интересных людей, будет достаточно времени со всеми пообщаться!

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

%Юзернейм%, мы тебя ждем!

Огромное спасибо Ивану Евтуховичу за организацию этой конференции :) и огромное спасибо Дину за красивую картинку :Р

Литература про Ruby и Rails

Уж очень часто задают этот простой вопрос:

— А что мне прочитать про Ruby On Rails?

Про ruby (с этого надо начинать) очень хорошие вот эти:

И про Rails:

Аккуратнее с «Гибкой разработкой на Ruby On Rails», ее русские версии сильно устарели, лучше всего купить англоязычную третью редакцию.

И не забывайте о подкастах!

FAQ

Несколько дней назад я обещал своему старому знакомому ответить на пару вопросов о веб-разработке, веб-фреймворках и вообще ©
Выполняю обещание в немного измененном формате :)
Read the rest of this entry »

Обновление задач Pivoral Tracker из коммитов в Github

Эти выходные пролетели как-то слишком быстро, незаметно, и я вообще почти ничего не успел — но это только с одной стороны! С другой — я купил себе MacBook Pro. И почти сутки пытаюсь привыкнуть к этому зверьку :) Он с характером, но замечательный :)

Собравшись с мыслями, решил расквитаться с одной старой проблемой — заставить все-таки GitHub обновлять истории в Pivotal Tracker.

Если вы еще по какой-то нелепой случайности не знаете, что это — обязательно посмотрите! Я уже как-то писал, что им пользуюсь и вполне доволен, но до восторгов как раз этой мелочи и не хватало.

Как это работает?

Обычно, если вы, к примеру, пользуетесь LightHouse, когда вы отправляете изменения в репозиторий на github, он заботливо отправляет обновления всех затронутых задач в LightHouse в виде комментариев. Поддерживается далеко не только LightHouse, но и много других приложений, но для тех, кто не попал в список, предлагается альтернативный вариант. Github post-recieve hook позволяет отправить post-запрос со всеми коммит-логами по указанному адресу, а вот его обработка ложится на наши плечи. Подсунуть в виде URL просто адрес API трекера, конечно, нельзя, так что нужен посредник: небольшое веб-приложение, которое бы получало запрос от Github и отправляло уведомляющие запросы к API pivotal tracker’а.

Вообще-то, такое приложение уже было написано и работало. Но работало оно с первой версией API трекера, которую обещали в будущем прикрыть. Когда я попытался его использовать, у меня оно не сработало, причину я опишу ниже, и я решил подкорректировать его для работы со второй версией API, заодно доделав дополнительный функционал, а не только добавление комментариев. Сейчас оно лежит в репозитории на github.

Установка

Чтобы увидеть всю эту магию, нужно клонировать репозиторий на ваш сервер, создать на нем конфигурационный файл, который будет выглядеть примерно так:

tracker_github_hook:
  github_url: 'http://github.com/railsmaniac/deliveryservice'
  tracker_api_token: __TOKEN__
  tracker_project_id: 123456

И запустить приложение, как любое другое sinatra веб-приложение. После этого необходимо зайти в настройки вашего проекта на GitHub и указать post-recieve hook url.

Нагрузка на эту штуку будет небольшой, если пользуетесь ей только вы — достаточно будет связки thin + sinatra. Для этого понадобится rackup файл:

require 'sinatra'

Sinatra::Application.default_options.merge!(
  :run => false,
  :env => :production
)

require 'tracker_github_hook'
run Sinatra.application

Теперь можно запустить thin:

thin start -d -R YOUR_RACKUP_FILE

Резюме

Я сам использую эту штуку уже для нескольких своих проектов, она здорово работает :) Сейчас она умеет добавлять комментарий к истории и изменять статус истории. С кириллицей тоже все в порядке, специально проверил.

Есть ряд минусов: пока невозможно изменять другие параметры истории и установка и настройка достаточно сложны. Если найдутся те, кому понадобится подобный скрипт, я вполне мог бы сделать очень простое приложение с человеческим интерфейсом, позволяющее использовать такой сервис без установки на сервер.
Они же есть, правда?

Toggl!


А помните, я недавно рассуждал на тему GTD-приложений? И тогда и сейчас я использую remember the milk и, в целом, доволен :)

Вчера я нашел одну очень интересную штуку: toggl. Это сервис, который считает за вас, сколько времени вы потратили на ту или иную задачу.

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

Плюс toggl в том, что он предоставляет маленькое такое приложение, написанное на Adobe AIR, которое живет у вас в таскбаре. Итого для остановки / переключения таймера на другую задачу нужно два клика: один по иконке в таскбаре, второй на кнопку таймера в окне приложения.

Остается только одна проблема: мои задачи живут в rtm и переводить их в toggl не хочется — toggl не умеет многое из того, что может rtm.

Вот если бы написать mash-up…

Аутентификация в Rails #2

Я уже давно обещал показать, как использовать решения для аутентификации в Ruby On Rails. И вот, вторая часть статьи про управление пользователями и Rails перед вашими глазами.

Подготовка

В этой статье мы не коснемся всех аспектов работы с пользователями. Мы не разработаем систему администрирования, наши пользователи не смогут получать оповещения и подтверждения регистрации по почте, аутентификация через openID тоже останется за бортом.

Мы будем использовать Shoulda для тестирования и authlogic для аутентификации пользователей.

Недавно в google группе ror2ru было обсуждение библиотек для аутентификации. Еще о них можно прочитать в одной из предыдущих статей. Authlogic – не самое простое решение, как правильно написал Макс Лапшин, не мало кода придется дописать, чтобы довести его до полноценной системы аутентификации и авторизации. Однако, authlogic развивается, уже скоро будут доступны дополнения к нему, позволяющие работать с openId, с ldap аутентификацией. За ними последуют дополнения и для всего остального, разве что, кофе он варить все-таки не сможет %)

Давайте сразу установим нужные библиотеки. Shoulda и Authlogic распространяются в виде gem-пакетов, для их установки достаточно подредактировать файлы config/environment.rb и config/environment/test.rb:

# config/environments/test.rb
config.gem 'mocha',
           :version => '>= 0.9.5'
config.gem 'thoughtbot-factory_girl',
           :lib => 'factory_girl',
           :source => 'http://gems.github.com',
           :version => '>= 1.2.0'
config.gem 'thoughtbot-shoulda',
           :lib => 'shoulda',
           :source => 'http://gems.github.com',
           :version => '>= 2.10.1'
# config/environment.rb
config.gem 'authlogic'

Теперь достаточно выполнить пару команд, и нужные gem-пакеты будут установлены:

rake gems:install
rake gems:install RAILS_ENV=test

Заметили лишний gem? Мы не говорили о factory_girl — отличной библиотеке для создания динамических тестовых объектов от ThoughtBot. Если вы еще с ней не знакомы — прочитайте ее описание, мы будем использовать ее в наших тестах, но об этом чуть позже.

Многие ruby on rails разрботчики используют mocking / stubbing фреймворки в разработке тестов, но если вы не принадлежите к их числу — вам совершенно необходимо прочитать и о них.

К сожалению, в одной статье не описать всех этих технологий, но если в них что-то осталось непонятным — спросите об этом в комментариях!

Authlogic

Одним из основных преимущество authlogic называлось то, что authlogic не является генератором, но первое же наше действие будет вызовом генератора:
script/generate session user_session

На самом деле все не так страшно, этот генератор лишь создает модель сессии, в которой подключает модуль authlogic. Вся логика поведения сессий скрыта:

# app/models/user_session.rb
class UserSession < Authlogic::Session::Base
  # Класс уже унаследован от Authlogic::Session::Base.
  # Тут можно добавить необходимые правки конфигурации. Подробнее: AuthLogic::Session::Config
end

С моделью разобрались, теперь создадим контроллер:
script/generate controller user_sessions

В контроллере сессий вам понадобится ну как минимум три метода — один для формы входа, второй непосредственно для аутентификации, а третий — для выхода из системы. Вот какой драфт получился у меня в одном из проектов:

class UserSessionsController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => [:destroy, :not_authorized ] 

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = I18n.translate "flashes.user_session.created"
      redirect_to account_url
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = I18n.translate "flashes.user_session.destroyed"
    redirect_back_or_default signin_url
  end
end

Сразу добавим маршруты для RESTful сессий:

# config/routes.rb
map.resource :user_session

Чего не хватает в этом примере? Ну, во первых, я удалил метод not_authorized, о нем я расскажу в следующей статье. Во-вторых, мои методы всегда возвращают ответ в html формате, не предусматривая никакого API, но для примера, этого достаточно. А самое главное — я использовал несколько методов, о которых еще не рассказал: require_user, require_no_user, current_user, current_user_session. Их названия говорят сами за себя, не так ли? Забегая вперед, скажу, что они все будут дописаны в ApplicationController.

Теперь вплотную займемся моделью пользователя:

script/generate model user \
login:string \
email:string \
crypted_password:string \
password_salt:string \
persistence_token:string \
login_count:integer \
last_request_at:datetime \
last_login_at:datetime \
current_login_at:datetime \
last_login_ip:string \
current_login_ip:string

rake db:migrate

Тем самым мы создали в базе данных таблицу пользователей, подняли версию базы с учетом этой таблицы. Authlogic самостоятельно управляет моделью, от нас потребуется лишь предоставить ему простой интерфейс для аутентификации пользователей.

# app/models/user.rb
class User < ActiveRecord::Base
  # даем authlogic управлять моделью!
  acts_as_authentic
end

Acts_as_authentic добавит проверку полей (имя пользователя, электропочта, пароль, проверки на уникальность почты и имени пользователя), специфичные для модели пользователя методы для работы с паролем и многое другое.

Теперь пора перейти к контроллеру пользователей. Разберемся с маршрутизацией:

# config/routes.rb
map.resource :account, :controller => "users"
map.resources :users

Тут есть небольшая хитрость — при таком подходе account_path будет обрабатываться тем же контроллером, что и user_path(id). Сейчас нам не нужно реализовывать и то и другое в одном контроллере, давайте попробуем сделать набросок:

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => [:show, :edit, :update]
  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])
    if @user.save
      flash[:notice] = "Account registered!"
      redirect_back_or_default account_url
    else
      render :action => :new
    end
  end

  def show
    @user = @current_user
  end

  def edit
    @user = @current_user
  end

  def update
    @user = @current_user # makes our views "cleaner" and more consistent
    if @user.update_attributes(params[:user])
      flash[:notice] = "Account updated!"
      redirect_to account_url
    else
      render :action => :edit
    end
  end
end

Ну вот, должно работать :) Осталось только дописано функционал в ApplicationController и сверстать шаблоны. Не буду приводить тут код шаблонов, он слишком прост и очевиден, а хороший пример есть на github.

Помните, мы говорили про методы, которые надо дописать в ApplicationController? Это один из ключевых моментов доработки authlogic до полноценного решения:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # не писать в логи пароли.
  filter_parameter_logging :password, :password_confirmation
  # А вот эти методы сделать видимыми из шаблонов.
  helper_method :current_user_session, :current_user

  private
    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session &amp;amp;&amp;amp; current_user_session.user
    end

    def require_user
      unless current_user
        store_location
        flash[:notice] = "You must be logged in to access this page"
        redirect_to new_user_session_url
        return false
      end
    end

    def require_no_user
      if current_user
        store_location
        flash[:notice] = "You must be logged out to access this page"
        redirect_to account_url
        return false
      end
    end

    def store_location
      session[:return_to] = request.request_uri
    end

    def redirect_back_or_default(default)
      redirect_to(session[:return_to] || default)
      session[:return_to] = nil
    end
end

Готово!

Shoulda

Мы только что разработали очень простое приложение с системой аутентификации пользователей, но не написали ни строчки тестов. Я не стал нагружать самые основные моменты установки authlogic тестами, они в ней не нужны вовсе. Но вот контроллеры пользователей и сессий тестами покрыть нужно, иначе, когда они разрастутся, будет непонятно, что там на самом деле происходит.

Для примера, попробуем написать тесты для users_controller:

require 'test_helper'

class UserSessionsControllerTest < ActionController::TestCase
  # Тестируем форму аутентификации
  context "get new" do
    # Которая должна работать только для неаутентифицированных пользователей.
    context "being anonymous" do
      setup do
        logout
        get :new
      end

      should_render_template :new
      should_respond_with :success
      should_have_form :user_session
    end
    # А если пользователь уже аутентифицирован - перебрасываем его на страницу его аккаунта.
    # Иначе говоря, не разрешаем повторную регистрацию уже вошедшим в систему пользователям.
    context "being logged in" do
      setup do
        login
        get :new
      end
      should_redirect_to ":account"
      should_set_the_flash_to "You must be logged out to access this page"
    end
  end

  # LOGIN
  # Аналогичное поведение для самой аутентификации с тем исключением,
  # что контроллер должен сохранять сессию (производить аутентификацию)
  context "logging in" do
    context "being logged in" do
      setup do
        login
        post :create
      end
      should_redirect_to ":account"
      should_set_the_flash_to "You must be logged out to access this page"
    end

    context "being anonymous" do
      context "with valid credentials" do
        setup do
          logout
          # Требуем вызова сохранения сессии.
          UserSession.any_instance.expects(:save).returns(true)
          post :create
        end
        # после успешной аутентификации нас перекинут на наш аккаунт
        should_redirect_to ":account"
      end
      # Или, если пользователь облажался, опять показываем ему форму.
      context "supplying invalid credentials" do
        setup do
          logout
          UserSession.any_instance.stubs(:save).returns(false)
          post :create
        end

        should_respond_with :success
        should_render_template :new
      end
    end
  end
  # ...
end

В этом коде некоторые вещи могут показаться странными: во-первых, это не традиционый Text::Unit::TestCase, этот тест использует Shoulda. Если context и should методы вызывают недоумение — стоит прочитать о них в документации к shoulda. Второе — методы, о которых мы еще не говорили: login и logout.

Эмуляция аутентификации — один из основных камней преткновения в разработке тестов. Методы login и logout — моя попытка решить эту проблему. Они далеки от идеала и не подойдут для cucumber и сложных приемочных тестов, прыгающих по нескольким контроллерам, но для простых функциональных тестов — этого хватает:

  def current_user
    # Создаем нового тестового пользователя
    @current_user ||= Factory(:user)
  end

  def user_session
    @user_session = mock
    # заставляем сессию возвращать тестового пользователя
    @user_session.stubs(:user).returns(current_user)
    # authlogic временами вызывает current_user_session.record %)
    @user_session.stubs(:record)
    @user_session
  end

  def login
    # Делаем вид, что у нас существует сессия :)
    UserSession.stubs(:find).returns( user_session )
  end

  def logout
    @user_session = nil
  end

Для начала подойдет :) Если у вас есть желание как-то это доработать, или уже есть реализованные методы лучше — буду очень рад, если вы ими поделитесь в комментариях!

Резюме

Я уже давно работал над этой статьей. В одной статье очень сложно рассказать и про тестирование, и про аутентификацию и про авторизацию. Мы коснулись лишь очень малой части всего этого, но прочитав эту статью вы должны быть готовы написать систему аутентификации на authlogic и разрабатывать свое приложение дальше, используя shoulda.

Очень многое еще будет написано в следующих статьях:

  • Разработка системы аутентификации по openID.
  • Статусы пользователей: почтовые подтверждения регистрации пользователей, восстановление паролей.
  • Авторизация и роли пользователей.

Если что-то осталось непонятным, или я пропустил что-то важное, то, пожалуйста, напишите мне об этом в комментариях! Я буду рад вашему мнению и постараюсь сделать следующие статьи более содержательными.

Временно недоступен?

Приветствую!
Вчера ночью блог переехал с (dv) на (gs) от media temple, всвязи с чем мог какое-то время быть недоступен, хотя переезд DNS должен был быть безболезненным %)
И вообще спасибо всем большое, что решили меня читать. А кто еще не решил — фид тут >.<