Size: a a a

2017 June 24
2pegramming
Только что прошла сингапурская конференция http://reddotrubyconf.com, которую я посетил два раза: в прошлом году как участник, а в этом – как спикер. Так как впечатления и эмоции еще свежи, было бы логично поделиться мнением, связанным с конференциями, митапами и другими похожими ивентами.

Есть люди, уверенные в том, что подобные ивенты — трата времени. Я так не считаю, и вот почему:
источник
2pegramming
## Тусовка

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

Два дня назад я впервые встретился вживую с коллегой по Hanami, Марион. За эти 2 дня, попутно решив насущные проблемы, связанные с проектом, я узнал от неё, что вегетарианство в Швейцарии намного выгоднее. Причина в том, что в Японии мало блюд, где нет мяса или рыбы.

С Ником, создателем фреймворка Trailblazer, мы познакомились на прошлогодней конференции Euruko. Я спросил, как у него дела с гемом для Hanami, и мы разговорились. За последние 3 дня, успев обсудить варианты использования Trailblazer и Hanami, мы выпили пива с дурианом, чего, по словам местных, делать ни в коем случае нельзя.

Иногда конференции — единственное место, где встречаются удаленные разработчики. Например, Railsclub объединяет кор-разработчиков Toptal, Evil Martians и других компаний. Разработчики Gitlab собираются на Euruko. На прошлой работе я увидел вживую коллегу из Таиланда только один раз – на RedDotRubyConf 2016.
источник
2pegramming
## Обмен опытом и / или идеями

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

Найдите автора технологии и спросите, как правильно использовать его проект. Каждый раз так делает Ник: допрашивает Матца и других разработчиков, как решить очередную проблему в Ruby коде.

Попробуйте выйти из зоны комфорта и узнать, как разработчики используют обыденную технологию в нестандартных ситуациях. Так, например, я узнал, что Ruby используется для инвалидных колясок (https://twitter.com/hanmd82/status/877735708798066688).
источник
2pegramming
## Карьера (и в open source тоже)

Конференция — скопление людей. Это выгодно компаниям: так проще заманивать к себе. Разработчикам это полезно тем, что можно неформально пообщаться с будущими коллегами. Например, мой знакомый устроился в Toptal только потому, что пришел на конференцию, где пересекся с разработчиками из этой компании.

А еще можно познакомиться с людьми, которые помогут в работе. Прочтите пост моей коллеги Марион в блоге Hanami (https://github.com/hanami/hanami.github.io/pull/267/files). Марион познакомилась с автором Hanami Лукой на конференции, и это знакомство привело к неожиданному результату. Вчера её презентацию оценили слушатели, а работа с опенсорсом помогает ей в решении ежедневных задач.
источник
2017 July 03
2pegramming
Немного советов о том, что делать до, во время и после конференции, чтобы получить максимум пользы и впечатлений.
источник
2pegramming
## До конференции

Прочтите программу выступления заранее и выберите наиболее интересные выступления.
Конференции – это высокая концентрация новой информации как для вас, так и для докладчиков и организаторов. Чтобы уменьшить стресс, определитесь заранее, на какой доклад пойдете, а какой доклад пропустите ради общения, сна или других дел.

Изучите вещи, о которых не слышали.
Если в программе встречаются термины или слова, которые вам неизвестны, советую бегло погуглить. Это поможет быть «в теме» и быстрее понять, что хочет рассказать спикер.

Узнайте, будет ли проводиться препати, и если да — будьте там
Часто на препати приходят спикеры, а вот других участников там мало. Это хороший способ поговорить с докладчиками в неформальной обстановке. Кроме того, на препати можно познакомиться с другими участниками: после этого вы будете чувствовать себя не так одиноко на конференции.

Приходите за полчаса до начала
Во-первых, так ваша регистрация пройдет без суеты — основная масса людей приходит за 5-15 минут до начала.
Во-вторых, это отличный шанс спокойно выпить кофе и поймать интересных людей первыми.
источник
2pegramming
## На конференции

Не бойтесь общаться
Вы здесь для того, чтобы общаться. Доклады останутся на ютубе, а вот возможность задать интересные и важные вопросы будет только здесь и сейчас. Поэтому первый и главный совет — общайтесь.

Общайтесь где угодно, как угодно, о чем угодно. Если сложно просто так заговорить, то придумайте себе задачу. Например, на railsclub в прошлом году раздавали купоны на бесплатное пиво. Я бросил себе вызов собрать 20 таких купонов https://twitter.com/anton_davydov/status/789848018107367424, а в итоге собрал больше 30. Благодаря этому я познакомился с интересными людьми и весело провел день.

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

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

Не конспектируйте доклады
Запись выступлений де-факто проводится на большинстве конференций и митапов. Это значит, что позже у вас будет шанс законспектировать интересные моменты выступления в спокойной обстановке и комфортном темпе. А также глубже изучить вещи, о которых говорит спикер. Не тратье время и силы на то, что можно сделать позже. Лучше вместо этого задайте вопрос, познакомьтесь с докладчиком или поделитесь личным опытом с другими.
источник
2pegramming
## После конференции

Дайте фидбэк
Поделитесь впечатлениями с организаторами, спикерами или просто друзьями. Важно быть активным и делиться знанием, ведь ради этого вы пришли на конференцю. Но не давайте необоснованного негативного фидбэка. Если что-то не понравилось, не оставляйте отзыв в духе «конференция ужасна, организаторы дураки». Вместо этого расскажите подробно о том, что не понравилось и какие вы видите пути решения этих проблем. Организация конференций — это тяжелый труд. Уважайте людей, которые сделали ивент для вас.

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

Доклады != Лекции в универе
Сложно успеть рассказать объемную и (или) хардкорную тему за 25 минут. Поэтому мой совет:  воспринимайте доклады не как лекции, а как мотивацию двигаться в этом направлении. Как пример — доклад Тима с прошлогоднего reddotrubyconf, где рассказывалось про микс ООП и функциональных объектов в вебе. Это сложная и объемная тема. Понятно, что непросто рассказать все и сразу. Но для меня это выступление стало стимулом глубже разобраться в теме и узнать много нового.
источник
2017 July 06
2pegramming
Получил предложение писать о том, что использую в ежедневной работе, и добавить больше юзкейсов.

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

Я фанат идеи функциональных объектов. В Rails я использовал сервис-объекты, в текущем dry-web проекте — оперейшены, а в ханами — интеракторы.

Текст будет касаться исключительно Hanami и hanami-interactor, но подход возможно использовать в других фреймворках.
источник
2pegramming
# Зачем это нужно
В первую очередь интерактор нужен, чтобы скрыть бизнес-логику в объекте с единственным публичным методом call. Пока это выглядит как типичный сервис-объект, но прелесть интерактора в том, что возвращаемый им объект (data object) содержит в себе статус (success или failed) и указанные геттеры.

# Какие проблемы решаются
Интерактор поможет решить три распространенных проблемы:

Изоляция бизнес-логики в отдельную сущность
Изолированные и имутабельные объекты легко тестируются. Объекты легко вызвать (в отличии от контроллеров и вью), объекты возвращают значение и при использовании dependency injection (DI) можно подменять логику в тестах вместо стабов. Я не фанат стабов и моков, но это отдельная тема для отдельного разговора.

С интеракторами легко шарить код без дублирования
Это плюс функциональных объектов, но так как разговор о интеракторах, то не будем говорить о другом. Как пример: в admin и в web апликейшене создается пост. Это один и тот же код в двух разных экшенах. С функциональными объектами, и интеракторами в частности, можно просто вызывать код без его дублирования.

Контроль флоу в зависимости от бизнес логики
Я писал, что интерактор возвращает data object со стейтом. Как можно догадаться, это используется. Например, надо сохранить объект, и в зависимости от того, сохранится успешно или нет, редиректнуть пользователя на указанный путь. Вот так это выглядит в приложении:


def call(params)
 result = Interactors::CreateDrug.new(params).call

 if result.successful?
   redirect_to routes.drug_path(result.drug.id)
 else
   redirect_to routes.drugs_path
 end
end
источник
2pegramming
## Практика

Я знаю о двух работающих библиотеках, в которых реализован этот паттерн:
1. Hanami-interactor
2. Гем interactor

Что из этого выбрать, решайте сами.

Лично мне нравится подход в Hanami, так как колбэки только усложняют код, а context объект добавляет еще одну штуку, о которой нужно постоянно думать. С hanami можно использовать просто класс с минимумом методов.

Как пример — рассмотрим экшен из open source проекта. И постараемся зарефакторить код с помощью interactor.
https://gist.github.com/davydovanton/5fc0f213b56da3baa3a1e2c7b378a6e

## Ссылки
1. How to Reduce Controller Bloat with Interactors in Ruby
2. A couple of words about interactors in Rails
3. Why using Interactor Gem is a Very Bad Idea
источник
2pegramming
Ссылка оказалась битой, вот работающая:
https://gist.github.com/davydovanton/5fc0f213b56da3baa3a1e2c7b378a6e9
источник
2017 July 10
2pegramming
#functional_objects

В прошлый раз мы смотрели, как изолировать часть логики. Сегодня же поговорим о ситуации, когда у изолированной логики есть сложные для тестирования или контроля части.
Возможно, вы слышали о таком подходе, и сегодняшняя тема вызывает ассоциацию с миром javaEE, но поверьте, это знание выручает меня каждый день. Начнем с примеров.
источник
2pegramming
1. Eсть класс, который возвращает данные для текущего времени:


class Foo
 def get_users_for_current_time
   User.where(created_at: Time.now)
   # ...
 end
end


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


describe Foo do
 before do
   @time = Time.now
   Time.stub(:now).and_return(@time)
 end
 ...
end



Код крутится, тесты мутятся. Но можно обнаружить пару минусов. Например: мок ведет себя себя как угодно, тесты становятся неконтролируемыми. Приходится использовать лишние абстракции в виде мока.

2. Eсть класс, который отображает логи:


class Logger
 def call(message)
   # ...

   print_message(message)
 end
end


Предположим, что в какой-то момент понадобилось отображать лог в JSON. Наследование спасает:


class JSONLogger < Logger
 def call(message)
   # ...

   to_json(message)
 end
end


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

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

Поэтому давайте попробуем изолировать эту логику и вынесем ее из объекта:


class Foo
 def get_users_for_current_time(time: Time.now)
   User.where(created_at: time)
   # ...
 end
end

describe Foo do
 let(:time) { Time.now }
 it { expect(Foo.get_users_for_current_time(time)).to eq ... }
 # ...
end


И для другого примера:


class Logger
 def call(message, formatter: BaseFormatter)
   # ...
   formatter.call(message)
 end
end

Logger.new.call(message, formatter: BaseFormatter)
Logger.new.call(message, formatter: JsonFormatter)
Logger.new.call(message, formatter: XmlFormatter)
источник
2pegramming
Этот подход называется dependency injection (DI) wiki и он «прокидывает» изолированную часть логики извне в объект или метод, что дает нам больше контроля.

Когда использовать
Если чувствуете, что появилась сложность со сколь угодно большим или маленьким участком кода.
Или чувствуете, что код мешает тестировать или расширить функционал.
В этих случаях DI — ваш выбор.

Плюсы
- Тестирование упрощается во много раз;
- Классы и методы становятся гибкими, их легко переиспользовать;
- Изоляция кода. Оборачиваете логику в абстракцию — в следующий раз не нужно думать, как она работает;
- DI превосходно работает с легаси кодом во время рефакторинга, так как не требует лишних библиотек и не меняет функционал;
- Работает в любом языке, фреймворке, библиотеке.

Минусы
- Бесконтрольное использование DI усложняет систему из-за количества сущностей и связей. Такой код сложнее поддерживать;
- Не всегда понятно, что делает объект, который инжектится;
- Как и все абстракции, может потечь.

Ссылки
- Отличное обсуждение о DI
- Пост Солника о DI в руби
- Пример замены наследования на DI
- Еще один пример рефакторинга код с использованием DI
- Обратная сторона, dhh рассказывает о том, почему DI это зло
источник
2017 July 13
2pegramming
#functional_objects

Сегодня поговорим о глобальной теме — функциональных объектах.
Функциональные объекты уже упоминались в предыдущем посте, посвященном интеракторам.

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

Итак, что такое функциональный объект и зачем он нужен?
Это объект, который выглядит как функция. Соответственно, он принимает и возвращает какие-то данные. Также в идеале он должен быть чистым (мы же за функциональное программирование!)
Простые примеры функциональных объектов выглядят следующим образом:

convert_to_string = :to_s.to_proc # => proc
convert_to_string.call(123)       # => "123"

pov2 = -> (x) { x ** 2 } # => proc
pov2.call(5)             # => 25


Плюсы таких объектов:
- Изолированность. Функциональный объект ничего не знает об окружающем мире, следовательно, его связанность с остальным приложением минимальна.
- Чистота. В идеале такой объект реализует чистую функцию. Это значит, что одни и те же входящие данные каждый раз возвращают один и тот же результат вне зависимости от количества повторений.
- Отсутствие мутаций. Если нет мутаций, тестировать и поддерживать код будет проще. Есть несколько исключений, но о них поговорим позже.


## Зачем нужны функциональные объекты?

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

Эти сервис-объекты будут выглядеть примерно так:

class ToString
 def call(object)
   String(object)
 end
end

to_string = ToString.new
to_string.call(:one) # => 'one'


А теперь сравните этот код с функциональным объектом :to_s.to_proc. Так, интеракторы, оперейшены и сервисы — это тоже функциональные объекты.

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

2. Если вы знаете функциональные языки, например, Haskell, вы знаете, что одни функции объединяются в другие со сложной логикой. Например:

plusOne = (+1)
square = (^2)
squareAndPlusOne = plusOne . square
squareAndPlusOne(4) # => (4 ^ 2) + 1 == 17


Есть энтузиасты, которые сделали библиотеку transproc. Библиотека дает возможность работать с функциональными объектами как в функциональных языках:

convert_to_json = Transproc(:load_json) # создаем первый функциональный объект
symbolize_keys = Transproc(:symbolize_keys) # второй
symbolize_json = convert_to_json >> symbolize_keys # объединяем их в один

symbolize_json.call('[{"name":"Jane"}]')
# => [{ :name => "Jane" }]


На этом все. Если хотите узнать больше, прочтите ссылки в конце статьи.


## Запомнить

* Объект, который ведет себя как функция, называется функциональным. Логика, которую реализует объект — не так важна.
* Такие объекты реализуют изолированную и чистую функцию, которая не мутирует данные.
* Сервис-объекты, интеракторы или оперейшены — функциональные объекты.
* Функциональные объекты — это просто, они используются каждый день.
* Соединяя функциональные объекты в цепочки, можно создавать сложную логику из небольших изолированных частей.


## Ссылки

* Пример использование функциональных объектов от icelab
* Пример использования функциональных объектов и чистых функций для json конвертера
* Примеры функциональных подходов в руби
* Пост солника о Transproc
источник
2017 July 16
2pegramming
#functional_objects

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

result = ValidateUser.new.call( ... )
result = result.success? ? CreateUser.new.call( ... ) : result

if result2.success?
redirect_to
else
 render
end

Или просто код, который валидирует две разные сущности:

validation_post_result = ValidatePost.new.call( ... )
validation_comments_result = ValidateComments.new.call( ... )

if validation_post_result.success? && validation_comments_result.success?
 ...
end

Или же код, который запускает цепочку сервис объектов:

UserCreator.call(...) && CommentCreator.call(...) && ...

В этих примерах много лишних условий, которые приводятся к общему правилу: выполни пачку действий и верни общий статус. Выглядит такое решение сложным для понимания и будущей отладки, поэтому давайте попробуем упростить код.
Telegram
Pepegramming
#functional_objects

Сегодня поговорим о глобальной теме — функциональных объектах.
Функциональные объекты уже упоминались в предыдущем посте, посвященном интеракторам.

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

Итак, что такое функциональный объект и зачем он нужен?
Это объект, который выглядит как функция. Соответственно, он принимает и возвращает какие-то данные. Также в идеале он должен быть чистым (мы же за функциональное программирование!)
Простые примеры функциональных объектов выглядят следующим образом:

convert_to_string = :to_s.to_proc # => proc
convert_to_string.call(123)       # => "123"

pov2 = -> (x) { x ** 2 } # => proc
pov2.call(5)             # => 25


Плюсы таких объектов:
- Изолированность. Функциональный объект ничего не знает об окружающем мире, следовательно, его связанность с остальным приложением минимальна.
- Чистота. В идеале такой объект реализует чистую…
источник
2pegramming
Вынести логику

Первое,что приходит в голову — создать еще одну абстракцию. Выносим туда условия и вызовы наших функциональных объектов. Например, вот так:

class UserOperation
 def call(payload)
   result = ValidateUser.new.call( ... )
   result = result.success? ? CreateUser.new.call( ... ) : result
   result
 end
end

if UserOperation.new.call({ ... }).success?
 ...
end

Плюсы:
 - процесс создания user находится в одном месте
 - легко переиспользовать объект

Минусы:
 - решение не решает проблему нескольких условий, а просто размазывает логику


Railway Oriented Programming

Не думайте, что это связано с Ruby on Rails, потому что это подход из мира функционального программирования, который исправляет ситуацию с условиями.

Создается цепочка функциональных объектов, в которые передаются данные. Дальше есть только 2 пути, по которому эти данные будут идти. Либо по success-пути, что будет значить, что объекты вернули положительное значение. Либо, зафейлившись на одном из объектов, возвращают значение failed. В конце можно просто обработать значение, как в интеракторе, и выполнить необходимую логику.

Схематично это выглядит следующим образом.

Допустим, есть объект, который принимает данные и возвращает два разных вида данных — success и failed:

success data → first function object → success data
failed data → first function object → failed data

success data → second function object → success data
failed data → second function object → failed data

В таком случае можно сделать цепочку объектов:

success data → first function object → second function object → success data
failed data → first function object → second function object → failed data
источник
2pegramming
Я знаю 2 способа применения Railway Oriented Programming в Ruby:

Waterfall

Гем, который чейнит функциональные объекты и работает с возвращаемым значением. Выглядит это так:


Wf.new
 .chain(user1: :user) { FetchUser.new(1) }
 .chain(user2: :user) { FetchUser.new(2) }
 .chain  {|outflow| puts(outflow.user1, outflow.user2)  } # report success
 .on_dam {|error|   puts(error)      }                    # report error


Использование бизнес-транзакций

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

Учтите, что придется использовать объект, который будет хранить в себе success/failed состояние и значение. Это нужно для передачи состояния между шагами транзакции. Выбирайте сами — подойдет все, что угодно, например, собственный класс, хеш или Either монада (о монадах в следующий раз).

В Ruby есть 3 библиотеки, которые реализуют транзакции:

* operation из trailblazer
* solid use case gem
* dry-transaction

Я фанат dry-transaction, так как библиотека позволяет:
* использовать матчер для возвращаемого значения
* работать с контейнерами (о контейнерах тоже поговорим позже);
* использовать DI в виде инжектинга операций
* использовать один из трех видов шагов (возвращать любой результат как успешный, возвращать failed при exception и передавать данные и не думать о возвращаемом результате)
* создавать кастомные адаптеры для шагов
* многое другое

Использование библиотеки выглядит следующим образом:


class CreateUser
include Dry::Transaction

 step :validate
 step :persist

 def validate(input)
   # ...
 end

 def persist(input)
   # ...
 end
end

CreateUser.new.call(name: "Jane", email: "jane@doe.com") do |m|
 m.success do |value|
   # ...
 end

 m.failure do |error|
   # ...
 end
end


Пример рефакторинга знакомого класса с использованием транзакций.

## Запомнить

* Проблема с чейнингом логики возникает чаще, чем кажется.
* Только лишь вынос логики не поможет справиться с хаосом условий.
* Railway Oriented Programming позволяет забыть о проблеме хаоса условий, а также использовать функциональный подход обработки данных в приложении.
* Места применения Railway Oriented Programming — экшены и любые другие места, где у вас есть «поток» данных.
* Забудьте о велосипедах, возьмите одну из готовых библиотек для работы с бизнес-транзакциями.
* dry-transactions — самая навороченная и гибкая из этих библиотек.

## Ссылки

* Главная страница railway programming где можно найти кучу ссылок и примеров
* Описание того, как чейнить объекты "правильно"
* Практическое руководство функционального программирования в rails
* Пример railway programming в elexir
источник
2pegramming
Тут подсказали, что про railway programming в elixir есть лучше статья:
http://www.scottmessinger.com/2016/03/25/railway-development-in-elixir-using-with/
источник