85
Ruby, Rails и BDD Владимир Мельник #kranonit S15 2013

kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Ruby, Rails и BDD

Владимир Мельник#kranonit S15 2013

Page 2: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Who am I?

Page 3: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Мельник Владимирaka egoholic | egotraumatic | rubydev

Веб разработчик, автор блога RubyDev.ru

2 года опыта коммерческой разработки на Ruby и Rails

Работал в:

[email protected]

http://rubydev.ru

@egotraumatic

Page 4: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Что я сегодня расскажу?

● Расскажу что такое Ruby и почему Ruby’истам больше дают.

● Расскажу что такое Rails и почему все его хотят.

● Расскажу что такое BDD, как BDD связан с BDSM.

Page 5: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Что такое Ruby?

Page 6: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

● Объектная-ориентированность

● Выразительность и минимализм

● Динамичность и Динамическая типизация

● Широкие возможности метапрограммирования

● Развитая инфраструктура

Page 7: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Бла-бла-бла...

Page 8: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Немного истории

Page 9: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Юкихиро Мацумото(Yukihiro Matsumoto) aka Matz

Page 10: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Юкихиро Мацумото(Yukihiro Matsumoto) aka Matz

Page 11: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Основной принцип

Концентрация не на машине, но на программисте, его продуктивности и

удовольствии от работы.

Page 12: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Объектная ориентированность

● Практически все есть объект.

● Нет примитивов.

1.class #=> Fixnum

3.14.class #=> Float

'string'.class #=> String

/\Aregexp\Z/.class #=> Regexp

:symbol.class #=> Symbol

[1, 2, 3].class #=> Array

{'a' => 1, 2 => 'ololo'}.class #=> Hash

{a: 1, b: 2, c: 3}.class #=> Hash

Page 13: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

class Integer

def leap_year?

(self % 4 == 0 && self % 100 != 0) || self % 400 == 0

end

def year

days = 0

current_year = Time.now.year

self.times do |n|

days += (current_year + n + 1).leap_year? ? 366 : 365

end

return 3600 * 24 * days

end

alias years year

end

Page 14: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

2013.leap_year? #=> false5.years #=> 157766400Time.now #=> 2013-10-08 16:03:20 +0300Time.now.class #=> TimeTime.now + 10.years #=> 2023-10-08 16:03:29 +0300

Page 15: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

class User

def initialize(name, last_name, email)

@name = name

@last_name = last_name

@email = email

end

end

user = User.new('Bill', 'Clinton', '[email protected]')

user.class #=> User

user.name # raises NoMethodError

Page 16: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

class President < User

attr_reader :name, :last_name, :email, :elected_at, :on_period

def initialize(name, last_name, email, elected_at, on_period)

super(name, last_name, email)

@elected_at = elected_at

@on_period = on_period

end

end

President.ancestors

#=> [President, User, Object, Kernel, BasicObject]

president = President.new('Bill', 'Clinton', '[email protected]', Time.new(1993, 1, 20), 4.years)

president.name #=> 'Bill'

Page 17: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
Page 18: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Множественное наследование

Page 19: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

● Его нет

● … но оно есть

● mixins (примеси) реализуются через модули

Page 20: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

module Singleton

def self.extended(klass)

klass.class_eval do

extend ClassMethods

include InstanceMethods

end

end

module ClassMethods

def new(*args)

@instance = super(*args) unless @instance

return @instance

end

end

module InstanceMethods

def singleton?; true end

end

end

Page 21: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

class User

def initialize(name, last_name)

@name = name

@last_name = last_name

end

end

User.new('Bill', 'Clinton').object_id #=> 7939460

User.new('Bill', 'Clinton').object_id #=> 7874780

User.extend Singleton

User.new('Bill', 'Clinton').object_id #=> 7767300

User.new('Bill', 'Clinton').object_id #=> 7767300

Page 22: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Выразительность и минимализм

● блоки кода,

● каждое выражение возвращает значение,

● итераторы, итераторы, итераторы

● удобные условные операторы,

● интерполяция строк,

● много сахара.

Page 23: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Блоки кода

def do_something; yield 100 end

def do_something(&block); block.call 100 end

def do_somethind

# Проверка наличия блока yield 100 if block_given?

end

do_something { |n| n.to_s(2) } #=> "1100100"

do_something do |n|

n ** 2

end #=> 10000

Page 24: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Итераторы

[1, 2, 3, 4, 5].inject(0) { |sum, n| sum += n } #=> 15

[1, 2, 3, 4, 5].select { |n| n % 2 == 0 } #=> [2, 4]

{a: 1, b: 2}.each { |k, v| puts "#{k} -> #{v}" }

# a -> 1

# b -> 2

3.times { |t| puts t }

# 0

# 1

# 2

[2, 4, 6].all? { |n| n.even? } #=> true

Page 25: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Динамичность и метапрограммирование

● Возможность определять и переопределять методы и классы во время исполнения.

● Возможность перехватывать и обрабатывать NoMethodError.

● eval, class_eval, instance_eval, module_eval

Page 26: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Развитая архитектура

● Огромное количество библиотек (Gem’ов), фреймворков и приложений.

● Централизованное хранилище gem’ов (RubyGems.org)

● Удубный менеджер гемов (RubyGems)

● Удобный менеджер зависимостей (Bundler)

Page 27: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
Page 28: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
Page 29: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Недостатки Ruby● Отсутствие параллельного выполнения кода в MRI

(из-за GIL).

● Сложность разработки многопоточных приложений.

● Относительно низкая производительность.

● Нет проверки типов аргументов методов и возвращаемого значение.

● Динамичностью и метапрограмированием можно отстрелить сразу обе ноги.

Page 30: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Где использутся?

● Везде

● Серьезно, везде. (NASA, Google, Github, Twitter, LivingSocial, Basecamp, Groupon, ...)

● В основном для web

● И еще в отличных инструментах для разработчиков и не только (Vagrant, Chef, Metasploit, Capistrano, ...)

Page 31: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Веб фреймворк Rails

Page 32: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Немного истории

Фреймворк впервые увидел свет в 2004 году.

Изначально разрабатывался Девидом Хейнемеером Хенсоном (David Heinemeier Hansson) aka DHH.

Текущая версия - 4.0.

Page 33: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Задачи веб фреймворков

● Избавить разработчика от рутины,

● навязать архитектурные решения и соглашения, которые авторам фреймворка кажутся верными,

● обеспечить безопасности веб приложения.

● Что-то еще?

Page 34: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Что есть у Rails?● Один из самых развитых и прагматичных веб

фреймворков.

● Архитектурный паттерн MVC (Model - View - Controller).

● Принцип CoC (Convention over Configuration)

● Принцип DRY (Don’t repeat yourself!)

● Один из самых безопасных веб фреймворков.

● RESTfull архитектура

Page 35: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

MVC

Model - работа с данными, бизнес логика.

View - логика представления и HTML код.

Controller - обработка запроса.

Page 36: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

MVC - это не архитектура проекта!

● MVC - это частный случай применения принципа SRP на высоком уровне.

● MVC позволяет сделать код проще в поддержке.

● MVC - это одно из соглашений в Rails.

Page 37: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Структура приложения

$ rails new my_app создает приложение MyApp.

my_app/

app/

config/

db/

lib/

log/

public/

vendor/

Page 38: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

app/ - основное место работы

app/

assets/ - stylesheets, javascripts, images

controllers/ - контроллеры

helpers/ - вспомогательный код

mailers/ - генераторы писем

models/ - модели

views/ - макеты, шаблоны, паршиалы

Page 39: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Пример моделей

# app/models/user.rb

class User < ActiveRecord::Base

has_many :articles, foreign_key: :author_id

has_many :comments, foreign_key: :commenter_id

end

# app/models/article.rb

class Article < ActiveRecord::Base

belongs_to :author, class_name: 'User'

has_many :comments, as: :commentable, dependent: :destroy

end

# app/models/comment.rb

class Comment < ActiveRecord::Base

belongs_to :commenter, class_name: 'User'

belongs_to :commentable, polymorphic: true

end

Page 40: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Пример контроллера

class ApplicationController < ActionController::Base

protect_from_forgery with: :exception

end

class ArticlesController < ApplicationController

# http://localhost:3000/articles

def index

@articles = Article.order('published_at DESC')

.includes(:author)

end

# http://localhost:3000/articles/:id

def show

@article = Article.find(params[:id])

@comments = @article.comments

end

end

Page 41: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Пример вьюшки (HAML)

#articles

- @articles.each do |article|

.article{id: "article_#{article.id}"}

.a-title= article.title

.a-published_at= article.published_at

.a-content= article.content

.a-author

authored by:

link_to article.author.name, article.author

Page 42: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Сгенерированный HTML

<div id="articles">

<div class="article" id="article_2">

<div class="a-title">Article 2 Title</div>

<div class="a-published-at">2013-09-28</div>

<div class="a-content">Article 2 Content</div>

<div class="a-author">

authored by: <a href="/users/1">Bill Clinton</a>

</div>

</div>

<div class="article", id="article_1">

<!-- ... -->

</div>

</div>

Page 43: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Мягкое введение в TDD/BDD

Behavior-Driven Development kinkier than Test-Driven Development

Page 44: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

RED, GREEN, REFACTOR

Page 45: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

RED, GREEN, REFACTOR

Page 46: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

“BDD - как BDSM, либо ты подчиняешь, либо подчиняешься.”

(c) Владимир Мельник

Page 47: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Breaking News: Единственный достоверный тест на определение

сексуальных предпочтений!

Page 48: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Вы используете TDD или BDD?

а. Да

б. Нет

Page 49: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

● TDD/BDD позволяют подчинить код,

● Их отсутствие подчиняет разработчика коду.

Page 50: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Test-Driven vs Behavior-Driven

TDD BDD

Page 51: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

TDD

● Пишем тесты, которые “фейлятся”.

● Пишем код, который делает тесты “зелеными”.

● Рефакторим код.

BDD

● Пишем тесты, которые “фейлятся”.

● Пишем код, который делает тесты “зелеными”.

● Рефакторим код.

Page 52: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

И в чем же разница?

Page 53: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

TDD

● Пишем тесты, который “фейлятся”.

● Пишем код, который делает тесты “зелеными”.

● Рефакторим код.

BDD

● Описываем поведение системы.

● Пишем код реализующий описанное поведение.

● Рефакторим код.

Page 54: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

BDD - это правильный TDD

● Немного другая философия.

● Другой язык.

● Больше информации.

Page 55: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Философия BDD

● Важно то, как система ведет себя, а не то, как реализована функциональность.

● Проектирование в момент написания спецификаций.

● Спецификации служат документацией по коду проекта.

● Тестирование предоставляет максимум информации о “красных” тестах.

Page 56: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

“Правильный программный код - легко тестируемый код.”

(с) Владимир Мельник

Page 57: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Off topic:

“Отличный способ показаться высокомерным и настроить против себя

людей - цитировать самого себя.”

(c) Владимир Мельник

Page 58: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Инструментарий TDD/BDDдля Ruby / Rails

Page 59: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

● Test::Unit - TDD (поставляется с Ruby StdLib)

● MiniTest - TDD/BDD (поставляется с Ruby 1.9 StdLib)

● RSpec - BDD

● Cucumber - BDD

… over 9000 прочих библиотек (gem’ов)

Инструментарий TDD/BDD для Ruby / Rails

Page 60: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Давайте напишем программу на Ruby которая считает сумму

выплат по кредиту!

Page 61: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Ууу! На Ruby?

Page 62: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

RED

Page 63: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Пишем спецификации используя RSpec

describe CreditCalc do

describe '.calc' do

context 'when amount = 0' do

let :args do

{amount: 0, percent: 22, period: 10}

end

it ‘returns 0’ do

expect(described_class.calc(args)).to be_zero

end

end

#...

#...

end

Page 64: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

CreditCalc

.calc

when amount of credit = 0

returns 0

example а не testОписываем сценарий использования.

“Когда сумма кредита равна 0, калькулятор должен вернуть 0.”

Page 65: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
Page 66: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

GREEN

Page 67: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Когда “фичи” реализованы и “баги” исправлены.

Page 68: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
Page 69: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

REFACTOR

Page 70: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

А необходим ли рефакторинг?

● Деньги платят не за рефакторинг, но за решение задач.

● Без рефакторинга растет технический долг.

Page 71: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Как правильно рефакторить?

● Мелкими шагами.

● Во время решения задачи.

● Только тот код, который касается задачи.

● Рефакторить только покрытый тестами код.

Page 72: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Плюсы такого подхода

● Никаких специальных задач и затраты времени на переключение.

● Заказчик видит в списке задач только ценные тикеты.

● Технический долг постепенно минимизируется.

● Никаких “Потом”

● Никаких авралов**почти правда

Page 73: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

На самом деле я вас немного обманул!

Дело здесь в “двустороннем” тестировании.

Page 74: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

TDD / BDD в Ruby on Rails

Page 75: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Зоопарк технологий

Page 76: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

● guard - обработчик событий FS

● spork / spring - прелодеры Rails приложения.

● rspec-rails - TDD/BDD фреймворк

● factory_girl_rails - фабрики объектов на замену фикстурам

● database_cleaner - очистка тестовой БД

● shoulda_matchers - специальные матчеры для Rails

● timecop - mock для даты и времени

● capybara - симуляция поведения пользователя

● cucumber - BDD фреймворк (не использую)

Page 77: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Немного о Cucumber (огурце)

● Много од

● Мало кто использует

● Очень медленный

● Много писанины

Page 78: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Никакой “силы земли” в огурце нет!

Page 79: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Разработка приложения на Rails с нуля

● Настраиваем тестовое окружение.

● Описываем поведение используя RSpec.

● Пишем код.

● Повторяем N раз.

Page 80: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

configure_test_environment!

while food.present? do

write_specs!

write_code! refactoring: true, bugs: false

end

Page 81: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Советы

● Переходите на BDD.

● Красивые спеки - красивый код.

● Быстрые тесты - хорошие тесты.

● Mocking для внешних сервисов.

● Mocking / Stubbing хороши, но в меру.

● Важно не количество спеков / тестов, а покрытие.

Page 82: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Спасибо за внимание.

Page 83: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Вопросы?

Page 84: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Мои контакты

● Блог: http://rubydev.ru

● Тусовка Ruby’стов: http://vk.com/rubydevclub

● Я во Вконтакте: http://vk.com/rubydev_ru

● Имэил: [email protected]

● Twitter: @egotraumatic

● Skype: rubydev.ru

Page 85: kranonit S15 Vladimir Melnik - Ruby on Rails, BDD

Читайте, пишите, спрашивайтеИменно в таком порядке!