together since 2001) • 6 years with PHP + 4 years with Ruby • among them 3 years with Trailblazer • You may know me by ◦ “Ruby Web Dev: The Other Way” book http://rwdtow.stdout.in ◦ http://stdout.in/en/post/ruby-ecosystem-bittersweet-or-we-like-to-hate-php • I am in love with Elixir • “In complicated relationships” with RoR and Blockchain • but last 1+½ years I am a Solution Architect of one cool web app...
• fat controllers • fat models • fat views • fat strong parameters • the whole app is your business logic • Rails-deformation: try to fit any Business Entity in ActiveRecord model if can’t fit - it’s a wrong… Business Entity • going against the Rails-way hurts so much
split) • structure for common and specific code • separate class per action, flexible setting per each action • Interactors aka Service Objects • Entity - Repository instead of ActiveRecord • middleware oriented, even actions are middlewares • dry-gems • being free from ActiveSupport “pollution”
become productive, when we started “from scratch” • need to educate each new team member (but they enjoy it :) ) • no known ways to integrate most of existing Ruby code/gems • Rails-ish ecosystems is a bit depressing without Rails • lack of docs, examples, learning resources...
are ActiveModel oriented, need to write own for Hanami • Validation via pure dry-validation • Hanami’s Interactor is too simplistic • TRB is not as useful as with Rails • Still, perhaps, most powerful Service Objects solution • But docs sometimes not clear and functionality tend to be overcomplicated
started with naive JWT token auth • Now switching to gem 'jwt_sessions' by @tuwukee • JWTSessions gives secure and solid foundation • For Cookie-based (non API) auth solution is still to come
parser • View helper for `javascript_pack_tag` • Config switcher for WebPack Dev Server usage What you get: • Happy frontend devs • Completely custom WebPack setup • No coupling with WebPack, because it is not needed!
how it works” • constantly trying different options and methods “while it works” • supports only one DB connection • nested Entities not working, return Hashes • custom mapping not working, always maps result to Entity “corresponding” to current Repo • so we did an unexpected move...
don’t panic! :) • ActiveRecord is not bad when used solely for Data Manipulation • Write simplistic own Repository class • Use Sequel queries ONLY inside the Repository • Flexible usage of Sequel::Model or Dataset • Easy and clear huge complicated raw SQL queries • Don’t forget to before_fork { DB.disconnect }
PostgreSQL • Redis (persistent) • InfuxDB • Cassandra • JanusGraph • Algolia search To be added soon: • Clickhouse • Algolia >>> Elastic Make a Repository class (not inherited from Hanami::Repository) and use any DB client / SDK calls, return structured data
single instance • register most Repositories • Dry-Systems could work, but way too complicated # ./config/dry-auto-inject.rb class Container extend Dry::Container::Mixin register('influx', memoize: true) do db_params = {...} InfluxDB::Client.new(ENV['INFLUXDB_DB'], db_params) end … Container['influx'] # include ::Inject['influx']
compatible match • Basic cache methods + custom Redis commands support class PostCacheRepository < CacheRepository def find(uuid) cache_get(cache_id(uuid), expires_in: 3600) do PostRepository.new.find(uuid) end end ...
Gemfile group :production • HANAMI_ENV=production • SERVE_STATIC_ASSETS=false • hanami server --no-code-reloading # detects Puma • ./config/puma.rb # almost like for Rails, but with... on_worker_boot do Hanami.boot end
love and hate) • but would migrate to RSpec sometime (as it is by default in 1.3+) • all your favorite things would work: ◦ Capybara ◦ Database Cleaner ◦ VCR ◦ Webmock ◦ etc. • only Factory Bot requires “ActiveSupport” and replaced by Fabricator • test mostly TRB Operations • do not use “Hanami’s advertised” Action tests via “.call”
• the less ActionController code/helpers, the better • gem 'action_policy' by Evil Martians common usage with Trailblazer Operation: step :check_policy, Output(:failure) => End(:forbidden) def check_policy(ctx, **) ::PostPolicy.new(user: ctx[:user], post: ctx[:model]).apply(:create?) end
mention that Evil Martians ? • requires additional configuration and separate process • LiteCable gem integrates well with Hanami • really fluent and fast operation, very low RAM consumption • clearer Channels code and messages broadcasting logic (that ActionCable) • uses ActionCable JS on frontend
to Sidekiq, but not limited to Ruby clients • sneakers work --require config/sneakers_boot.rb class MakeAKeynote include Sneakers::Worker from_queue 'rubyc.keynotes' def work(msg) # do smth with `msg` ack! end end
• https://discourse.hanamirb.org/t/roadmap-for-v2-0-0-alpha1/475 • https://www.youtube.com/watch?v=LqGBhTSOmTI • Less internal state • More functional Containers • More flexible Configuration • Fresh Dry Gems
can not take place without mentioning Elixir/Phoenix • composed from loosely coupled libs (greeting from PHP!) • full request path transparency from app server, through router to action • middlewares on all layers, router middleware pipelines • flexible data mapper (maybe even not an ORM) • basic DDD-ish structure for files/folders/modules • not being Resources/CRUD oriented • framework should teach me to write a better apps, to do better architectures
apps • No need even to try • Clear “complex domain” app niche positioning needed for Hanami • Not trying to pretend “you can do the same even better and faster” than Rails
and big apps • “batteries not included” • a few compatible out-of-the-box Ruby libs, but they are growing! • you would need to write some code :) • it is you are adjusting the framework, not framework adjusts you • good choice for: ◦ non-CRUD long running projects with complex domain (it is our case) ◦ API ◦ (micro) service oriented projects ◦ non WebUI apps