Upload
krivoy-rog-it-community
View
2.390
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Citation preview
Ruby, Rails и BDD
Владимир Мельник#kranonit S15 2013
Who am I?
Мельник Владимирaka egoholic | egotraumatic | rubydev
Веб разработчик, автор блога RubyDev.ru
2 года опыта коммерческой разработки на Ruby и Rails
Работал в:
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', '[email protected]')
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', '[email protected]', 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
● Имэил: [email protected]
● Twitter: @egotraumatic
● Skype: rubydev.ru
Читайте, пишите, спрашивайтеИменно в таком порядке!