Hanami 
          
          
        
 Hanami 
          Originalmente chamado Lotus
Seu framework não é sua aplicação.
The web is just a delivery mechanism!
Vale assistir a talk: Architecture: The Lost Years
      
            $ hanami new awesome_all_the_things
            
          
          
        
        São providas por um modulo que pode ser incluido em qualquer objeto.
# Rails
validates :some_field, presence: true, length: { in: 3..8 }
# Hanami
required(:some_field) { filled? & str? & size?(3..8)  }
# Agora com Macros!
required(:some_field).filled(:str?, size?: (3..8))
          
        
class SomeRandomCoolThing
  include Hanami::Validations
  EMAIL_REGEX = /.+@.+/ #totally works!
  predicate :email?, message: 'not an email' do |actual|
    actual =~ EMAIL_REGEX
  end
  validations do
    required(:nullable).maybe(:int?)
    required(:something_or_the_other) { str? | int? }
    required(:an_email) { email? }
    optional(:an_array).each(:int?)
  end
end
          
          
        
validations do
  required(:reason).maybe(:int?)
  optional(:reason_text).maybe(:str?)
  required(:address).schema do
    required(:zip_code) { filled? & format?(/\d{5}-\d{3}/) }
    required(:country).filled(:str?)
  end
  rule(:others, [:reason, :reason_text]) do |reason, text|
    reason.none?.then(text.filled?) & reason.filled?.then(text.none?)
  end
end
          
        
class AddressValidator
  include Hanami::Validations
  validations do
    required(:zip_code) { format? /^\d{5}-?\d{3}$/ }
  end
end
class UserValidator
  include Hanami::Validations
  validations do
    required(:name) { filled? & str? }
    required(:address).schema(AddressValidator)
  end
end
          
        
      
        São apenas um modulo, um namespace.
module Web::Controllers::SemanticallyRelevantName
  # here be dragons
end
          
        São objetos que respondem a #call
module Web::Controllers::SemanticallyRelevantName
  class Index
    include Web::Action
    def call(params)
      # here be dragons
    end
  end
end
          
          Mas qual a vantagem disso?
RSpec.describe Web::Controllers::SemanticallyRelevantName::Index
  let(:action) { Web::Controllers::AwesomeClassName::Index.new }
  let(:params) { Hash[] }
  subject(:response) { action.call(params) }
  it 'just works' do
    expect(response[0]).to eq(200)
  end
end
          
        
module Web::Controllers::SemanticallyRelevantName
  class Index
    include Web::Action
    expose :instance_variable #algo que a view precise ver
  end
end
          
        
module Web::Controllers::SemanticallyRelevantName
  class Create
    include Web::Action
    params do
      required(:title) { filled? & str? & size?(3..254) }
    end
    def call(params)
      params.get('um.valor.aninhado.que.nao.existe') #=> nil
    end
  end
end
          
          
        
module Web
  module MyReusableBehaviour
  def self.included(action)
    action.class_eval do
      before :behaviour
      expose :some_object
    end
  end
  private def behaviour; @some_object = "Nothing to see here"; end
end
module Web::Controllers::SemanticallyRelevantName
  class Index
    include Web::Action
    include Web::MyReusableBehaviour
  end
end
          
        
        
module Web::Views::SemanticallyRelevantName
  class Index
    include Web::View
    def title
      'Hanami & Ruby'
    end
    def sidebar
      html.aside(id: sidebar) { div 'Content' }
    end
  end
end
          
          
        
RSpec.describe Web::Views::SemanticallyRelevantName::Index
  let(:exposures) { Hash[stuff: %w(cool awesome nifty)] }
  let(:template) do
    Hanami::View::Template.new('path/to/template.html.erb')
  end
  let(:view) do
    Web::Views::SemanticallyRelevantName::Index.new(template, exposures)
  end
  let(:rendered) { view.render }
  it 'autoescape all strings' do
    expect(view.title).to eq('Hanami & Ruby')
  end
end
          
        Esses vão parecer familiares...
<h1><%= title %></h1>
<%= yield %>
<%= render partial: 'home/index' %>
          
        
module Web::Views::SemanticallyRelevantName
  class Index
    include Web::Action
    def render
      { some: 'object' }.to_json
    end
  end
end
          
        
module Web::Controllers::AwesomeClassName
  class Index
    include Web::Action
    def call(params)
      self.body = 'something important '
    end
  end
end
            
        
      
        
class AwesomeThing
  include Hanami::Entity
end
          
          Neste momento são imutáveis
thing1 = AwesomeThing.new(id: 3, value: 'cool')
thing2 = AwesomeThing.new(id: 3, value: 'awesome')
thing1 == thing2 #=> true (╯°□°)╯︵ ┻━┻
          
        São a interface entre o BD e a Entity.
class AwesomeThingRepository < Hanami::Repository
end
          
        Os métodos de query são privados
# AR::Base
AwesomeThing.order(:created_at).limit(10)
# Hanami::Repository
AwesomeThingRepository.new.most_recent
class AwesomeThingRepository < Hanami::Repository
  def most_recent(amount = 10)
    order(:created_at).limit(amount).all
  end
end
          
        
class LegacyDatabaseRepository < Hanami::Repository
  self.relation = :tbl_dumb_names
  mapping do
    attribute :id, from: :int_identity_id
    attribute :date_of_birth, from: date_birth_date
  end
end
          
          
        
class UserRepository < Hanami::Repository
  associations do
    has_many :posts
  end
  def find_with_posts(id)
    aggregate(:posts).where(user_id: id).as(User).one
  end
  def posts_for(user)
    assoc(:posts, user)
  end
end
          
          
        O ideal é isolar sua aplicação também do mecanismo de entrega
        
class AwesomeInteractor
  include Hanami::Interactor
  expose :awesome_sauce
  def initialize(value)
    @inst_var = value
  end
  def valid?
    fail! unless @inst_var
  end
  def call
    @awesome_sauce = AwesomeThingRepository.most_awesome(value)
  end
end
            
        #call retorna um Hanami::Result
        
      Se tiver dúvidas, perguntas, vontade de trabalhar conosco ou só quiser trocar uma ideia: marcello.rocha@vagas.com.br | @mereghost
Participem no gitter do projeto Hanami: https://gitter.im/hanami/chat