роки у PHP • 4 роки у Ruby • з них 3 роки по коліно у Trailblazer • люблю Elixir • не люблю RoR і Blockchain • останні півроку моя команда пише великий проект на Hanami...
• структуру для специфічного і загальновживаного коду • окремий клас на кожен екшн (гнучка настройка на кожен) • інтерактори aka service objects • Entity - Repository замість ActiveRecord • middleware орієнтованість, навіть екшн - middleware • dry-gems • свобода від ActiveSupport
ліб, але завжди працюють просто “rack middleware” орієнтовані • автентифікація - немає Device-подібного рішення, потребує кастомне • кеш - кастомна реалізація з Redis, readthis + hiredis gems • Sidekiq - працює, але треба трошки загуглити • Sidekiq::Web - працює тільки як окремий процесс (Docker рятує) • RSpec/Minitest є з короби, а от Capybara (Hanami <=1.2) / DBCleaner / WebDriver / WebMock доведеться дороблювати методом проб та помилок
ActiveSupport, на щастя є Fabricator • pagination - через плагін ROM.rb • soft delete - через monkey patching Hanami::Repository • enum у моделях - через ruby-enum • WebPack(er) - через WebPack и свій хелпер • бізнес логіка - через ваш улюблений сервіс об’єкт • Trailblazer йде окремо, але працює без “танців з бубном” • структура файлів і папок - майже яку забажаєте!
valid params' do it 'redirects the user to the books listing' do response = action.call(params) expect(response[0]).to eq(302) expect(response[1]['Location']).to eq('/books') end end context 'with invalid params' do it 're-renders the books#new view' do response = action.call(params) expect(response[0]).to eq(422) end ...
AddBook include Hanami::Interactor expose :book def initialize(repository: BookRepository.new) @repository = repository end def call(book_attributes) @book = @repository.create(book_attributes) end end
а документації на макроси немає :( def validate(ctx, params:, **) sch = Dry::Validation.Form(PostSchema) do required(:type).filled(included_in?: ::Post::Types.values) optional(:body).value(:length_permitted?) optional(:tags).maybe end ctx[:validation_result] = sch.with(type: params[:embed_type]).call(params) ctx[:params] = ctx[:validation_result].to_h handle_validation_errors(ctx) end
воно працює • постійно перебираєш варіанти • не підтримується більше одного БД коннекта • вкладені Entity не працюють, все через Hash • не працює кастомний мапінг, завжди “мапить” до однойменного з репозиторієм Entity • як результат - у нашому плані “перейти на Sequel + dry-struct mapping”
(firstname ILIKE :query OR lastname ILIKE :query OR nickname ILIKE :query) AND id != :requester_id SQL users.where( Sequel.lit(sql, query: "#{query}%", requester_id: user.id) ).limit(count).as(User) end
без реклами Elixir та Phoenix! • складається з незалежних бібліотек (вітання зі світу PHP!) • повна прозорість ходу запиту від проксі, до роутінгу і екшна • middleware на всіх рівнях, middleware pipelines на роутинг • гнучкий data mapper • базовий DDD структуру тек / файлів / модулів
маленький застосунок • доведеться “покопати” • дуже мало готових рішень, але вони з’являються • ви знову згадаєте як це - “програмувати” :) • все можна підлаштувати під себе, а не гнутися під фреймворк • добрий вибір для: ◦ довгограючих проектів ◦ API ◦ мікро-сервісних проектів ◦ застосунків без “людського” інтерфейсу