kranonit s15 vladimir melnik - ruby on rails, bdd

Post on 21-Dec-2014

2.407 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Ruby, Rails и BDD

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

Who am I?

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

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

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

Работал в:

egotraumatic@gmail.com

http://rubydev.ru

@egotraumatic

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

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

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

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

Что такое Ruby?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class User

def initialize(name, last_name, email)

@name = name

@last_name = last_name

@email = email

end

end

user = User.new('Bill', 'Clinton', 'bclinton@whitehouse.gov')

user.class #=> User

user.name # raises NoMethodError

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', 'bclinton@whitehouse.gov', Time.new(1993, 1, 20), 4.years)

president.name #=> 'Bill'

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

● Его нет

● … но оно есть

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

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

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

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

● блоки кода,

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

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

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

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

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

Блоки кода

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

Итераторы

[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

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

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

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

● eval, class_eval, instance_eval, module_eval

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

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

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

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

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

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

(из-за GIL).

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

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

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

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

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

● Везде

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

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

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

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

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

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

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

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

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

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

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

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

● Что-то еще?

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

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

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

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

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

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

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

MVC

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

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

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

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

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

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

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

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

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

my_app/

app/

config/

db/

lib/

log/

public/

vendor/

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

app/

assets/ - stylesheets, javascripts, images

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

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

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

models/ - модели

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

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

# 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

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

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

Пример вьюшки (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

Сгенерированный 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>

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

Behavior-Driven Development kinkier than Test-Driven Development

RED, GREEN, REFACTOR

RED, GREEN, REFACTOR

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

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

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

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

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

а. Да

б. Нет

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

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

Test-Driven vs Behavior-Driven

TDD BDD

TDD

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

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

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

BDD

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

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

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

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

TDD

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

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

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

BDD

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

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

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

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

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

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

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

Философия BDD

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

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

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

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

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

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

Off topic:

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

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

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

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

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

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

● RSpec - BDD

● Cucumber - BDD

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

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

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

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

Ууу! На Ruby?

RED

Пишем спецификации используя 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

CreditCalc

.calc

when amount of credit = 0

returns 0

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

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

GREEN

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

REFACTOR

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

TDD / BDD в Ruby on Rails

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

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

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

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

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

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

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

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

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

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

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

● Много од

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

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

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

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

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

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

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

● Пишем код.

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

configure_test_environment!

while food.present? do

write_specs!

write_code! refactoring: true, bugs: false

end

Советы

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

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

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

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

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

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

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

Вопросы?

Мои контакты

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

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

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

● Имэил: egotraumatic@gmail.com

● Twitter: @egotraumatic

● Skype: rubydev.ru

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

top related