hanami - Base repository, CLI
router - Rack compatible HTTP router for Ruby
controller - Full featured and fast actions for Rack
utils - Ruby core extensions and class utilities
model - Persistence with entities and repositories
Slide 55
Slide 55 text
validations - Validations mixin for Ruby objects
helpers - View helpers for Ruby applications
view - Presentation with a separation
assets - Assets management for Ruby
mailer - Mail for Ruby applications
Slide 56
Slide 56 text
events - PubSub framework for Ruby
cli - CLI framework for Ruby
webconsole - Web console for development
ujs - Assets management for Ruby
Slide 57
Slide 57 text
Differences
Slide 58
Slide 58 text
# rack
class HelloApp
def call(env)
[200, { **env }, ['Hello!']]
end
end
Slide 59
Slide 59 text
# hanami-router
class HelloApp
def call(env)
[200, { **env }, ['Hello!']]
end
end
router = Hanami::Router.new
router.get '/', to: 'hello_app'
Slide 60
Slide 60 text
# sinatra
class Hello < Sinatra
get '/' do
'Hello!'
end
end
Slide 61
Slide 61 text
# hanami
Hanami::Router.new do
get '/' do
[200, { **env }, ['Hello!']]
end
end
Slide 62
Slide 62 text
Controllers
Slide 63
Slide 63 text
class UsersController < AC
def new
end
def send_sms
end
private
def user_params
end
end
Controllers: Rails
Slide 64
Slide 64 text
Controllers: hanami action
module Web::Controllers::Board
class Index
include Web::Action
params do
required(:email).filled
end
def call(params)
end
end
end
Slide 65
Slide 65 text
Controllers: hanami action
module Web::Controllers::Board
class Index
include Web::Action
params do
required(:email).filled
end
def call(params)
end
end
end
Slide 66
Slide 66 text
Controllers: hanami action
module Web::Controllers::Board
class Index
include Web::Action
params do
required(:email).filled
end
def call(params)
end
end
end
Slide 67
Slide 67 text
Model
Slide 68
Slide 68 text
class User < ActiveRecord::Base
include Gravtastic
before_destroy :yank_gems
has_many :rubygems, through: :ownerships
validates :name, presence: true
# ...
end
Model: Rails
Slide 69
Slide 69 text
class User < ActiveRecord::Base
include Gravtastic
before_destroy :yank_gems
has_many :rubygems, through: :ownerships
validates :name, presence: true
# ...
end
Model: Rails
Slide 70
Slide 70 text
class User < ActiveRecord::Base
include Gravtastic
before_destroy :yank_gems
has_many :rubygems, through: :ownerships
validates :name, presence: true
# ...
end
Model: Rails
Slide 71
Slide 71 text
class User < ActiveRecord::Base
include Gravtastic
before_destroy :yank_gems
has_many :rubygems, through: :ownerships
validates :name, presence: true
# ...
end
Model: Rails
Slide 72
Slide 72 text
class User < ActiveRecord::Base
include Gravtastic
before_destroy :yank_gems
has_many :rubygems, through: :ownerships
validates :name, presence: true
# ...
end
Model: Rails
Slide 73
Slide 73 text
Model: hanami
hanami + ROM = ❤
rom-rb.org
Slide 74
Slide 74 text
Model: hanami entity
class User < Hanami::Entity
# ...
end
Slide 75
Slide 75 text
Model: hanami entity
>> user = User.new(id: 1)
=> #1}>
>> user.id
=> 1
>> user.id = 1
NoMethodError: undefined method `id=' for
#1}>
Did you mean? id
Slide 76
Slide 76 text
Model: hanami entity
>> user = User.new(id: 1)
=> #1}>
>> user.id
=> 1
>> user.id = 1
NoMethodError: undefined method `id=' for
#1}>
Did you mean? id
Slide 77
Slide 77 text
Model: hanami entity
>> user = User.new(id: 1)
=> #1}>
>> user.id
=> 1
>> user.id = 1
NoMethodError: undefined method `id=' for
#1}>
Did you mean? id
Slide 78
Slide 78 text
class UserRepository < Hanami::Repository
associations do
has_many :books
end
def find_by_name(name)
users # => ROM relation
users.where(name: name).limit(1).order { id }.one
end
end
Model: hanami repository
class Service
include Container[redis: :redis]
def call(payload)
user = redis.get(payload[:id])
# …
end
end
Slide 154
Slide 154 text
class Service
include Container[redis: :redis]
def call(payload)
user = redis.get(payload[:id])
# …
end
end
Slide 155
Slide 155 text
class Service
include Container[redis: :redis]
def call(payload)
user = redis.get(payload[:id])
# …
end
end
Slide 156
Slide 156 text
class Service
include Container[redis: :redis]
def call(payload)
user = redis.get(payload[:id])
# …
end
end
Slide 157
Slide 157 text
More here
Slide 158
Slide 158 text
No content
Slide 159
Slide 159 text
And here
Slide 160
Slide 160 text
dry-rb.org/gems/dry-system/
Slide 161
Slide 161 text
Why?
Slide 162
Slide 162 text
Container Architecture
+
Dependency management
Slide 163
Slide 163 text
DDD
Slide 164
Slide 164 text
No content
Slide 165
Slide 165 text
No content
Slide 166
Slide 166 text
Separate domains
for different business
values
Slide 167
Slide 167 text
Each domain
isolated from others
Slide 168
Slide 168 text
pros
easy to create separated
services: just replace code
domains + containers = ❤
Slide 169
Slide 169 text
cons
hard to start
so much files™
Slide 170
Slide 170 text
No content
Slide 171
Slide 171 text
http is a small part
of system
Slide 172
Slide 172 text
No content
Slide 173
Slide 173 text
websockets
rack
background processing
ETL
…
Slide 174
Slide 174 text
They all use business
logic
Slide 175
Slide 175 text
No content
Slide 176
Slide 176 text
No content
Slide 177
Slide 177 text
Hanami
Slide 178
Slide 178 text
Pros and Cons
Slide 179
Slide 179 text
No magic
Slide 180
Slide 180 text
module Web::Controllers::Board
class Index
include Web::Action
def call(params)
end
end
end
Slide 181
Slide 181 text
Rails action test
describe BoardController do
let(:response){ get :index }
it 'is successful' do
expect(response).to eq(200)
end
end
Slide 182
Slide 182 text
Action test
describe Web::Controllers::Board::Index do
let(:action){ Board::Index.new }
let(:params){ Hash[] }
it 'is successful' do
response = action.call(params)
expect(response[0]).to eq(200)
end
end
Slide 183
Slide 183 text
Action test
describe Web::Controllers::Board::Index do
let(:action){ Board::Index.new }
let(:params){ Hash[] }
it 'is successful' do
response = action.call(params)
expect(response[0]).to eq(200)
end
end
Slide 184
Slide 184 text
Action test
describe Web::Controllers::Board::Index do
let(:action){ Board::Index.new }
let(:params){ Hash[] }
it 'is successful' do
response = action.call(params)
expect(response[0]).to eq(200)
end
end
Slide 185
Slide 185 text
No monkey-patching
Slide 186
Slide 186 text
Best practices
Slide 187
Slide 187 text
The logic separation
Slide 188
Slide 188 text
transport logic =/= business logic
Slide 189
Slide 189 text
TDD
Slide 190
Slide 190 text
No content
Slide 191
Slide 191 text
No content
Slide 192
Slide 192 text
Cons
Slide 193
Slide 193 text
Architecture
Implementation
Ecosystem
Slide 194
Slide 194 text
Architecture
Slide 195
Slide 195 text
Validations in actions
Slide 196
Slide 196 text
No content
Slide 197
Slide 197 text
No content
Slide 198
Slide 198 text
No content
Slide 199
Slide 199 text
No content
Slide 200
Slide 200 text
No content
Slide 201
Slide 201 text
No content
Slide 202
Slide 202 text
No content
Slide 203
Slide 203 text
No content
Slide 204
Slide 204 text
No content
Slide 205
Slide 205 text
No content
Slide 206
Slide 206 text
How to fix it?
Slide 207
Slide 207 text
Have no ideas
Slide 208
Slide 208 text
Data transfer objects
(DTO)
Slide 209
Slide 209 text
Validate payload and create
an object
Slide 210
Slide 210 text
No content
Slide 211
Slide 211 text
No content
Slide 212
Slide 212 text
No content
Slide 213
Slide 213 text
No content
Slide 214
Slide 214 text
Only HTTP transport
Slide 215
Slide 215 text
No content
Slide 216
Slide 216 text
No content
Slide 217
Slide 217 text
No content
Slide 218
Slide 218 text
No content
Slide 219
Slide 219 text
No content
Slide 220
Slide 220 text
Will fixed soon
Slide 221
Slide 221 text
Implementation
Slide 222
Slide 222 text
Hanami Model
Slide 223
Slide 223 text
Wrapped on rom 3.0
Slide 224
Slide 224 text
Associations
Slide 225
Slide 225 text
How we solve it?
Slide 226
Slide 226 text
Use rom instead
hanami-model
Slide 227
Slide 227 text
Ecosystem
Slide 228
Slide 228 text
It’s grow but we need
gems
Slide 229
Slide 229 text
Example: auth libs
like devise
Slide 230
Slide 230 text
Tip: good idea to
start OSS career
Slide 231
Slide 231 text
Also, we have some
opensourse project
for learning
Slide 232
Slide 232 text
octostar.herokuapp.com
Slide 233
Slide 233 text
contributors.hanamirb.org
Slide 234
Slide 234 text
rubyjobs.dev
Slide 235
Slide 235 text
More on
awesome-hanami.org
Slide 236
Slide 236 text
Conclusion
Slide 237
Slide 237 text
Ruby lives outside rails
Slide 238
Slide 238 text
Rails - fast prototyping or legacy
Hanami - small or well maintained projects