Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Pragmatic Hanami

kbaba1001
November 30, 2017

The Pragmatic Hanami

kbaba1001

November 30, 2017
Tweet

More Decks by kbaba1001

Other Decks in Technology

Transcript

  1. BIO • kbaba1001(Kazuki Baba) • Ruby 6 years • Digital

    nomad • Rubyist Magazine ◦ HanamiはRubyの救世主(メシア)となるか、愚かな星と散る のか
  2. What is Hanami ? • Full-stack Ruby web framework •

    DDD (Domain Driven Design) • April 06, 2017, release v1.0.0
  3. Hanami project directories project_name/ ├── apps/ │ ├── admin/ │

    └── web/ ├── config/ ├── db/ ├── lib/ │ ├── project_name/ │ └── project_name.rb ├── spec/ ├── public/
  4. “apps” and “lib” directories apps/web/ ├── application.rb ├── assets/ ├──

    config/ ├── controllers/ ├── templates/ └── views/ lib/ ├── project_name/ │ ├── entities/ │ ├── repositories/ │ └── interactors/ └── project_name.rb Application Layer Domain Layer
  5. Routes get '/proc', to: ->(env) { [200, {}, ['Hello from

    Hanami!']] } get '/action', to: "home#index" get '/middleware', to: Middleware get '/rack-app', to: RackApp.new get '/rails', to: ActionControllerSubclass.action(:new)
  6. Migration # db/migrations/20170908025424_create_diaries.rb Hanami::Model.migration do change do create_table :diaries do

    primary_key :id column :body, String, null: false column :title, String column :created_at, DateTime, null: false column :updated_at, DateTime, null: false end end end
  7. Entity class Diary < Hanami::Entity end diary = Diary.new(title: 'learn

    ruby') diary.title #=> "learn ruby" diary.title = 'learn english' #=> NoMethodError: undefined method `title='
  8. Repository class DiaryRepository < Hanami::Repository associations do has_many :comments end

    def find_with_comment(id) aggregate(:comments).where(diaries_id: id).map_to(Diary).one end end
  9. Service(Interactor) module DiaryInteractor class Create include Hanami::Interactor expose :params, :diary

    def initialize(params) @params = params end def call @diary = DiaryRepository.new.create(params) end end end
  10. Validation (in action) module Web::Controllers::Users class Create include Web::Action params

    do required(:user).schema do required(:email) { filled? } end end # … end end
  11. Issue params do # do not work predicate :email?, message:

    'invalid email format' do |value| # ... end required(:user).schema do required(:email) { filled? & email? } end end
  12. Issue params Class.new(Hanami::Action::Params) do predicate :email?, message: 'invalid email format'

    do |value| # ... end validations do required(:user).schema do required(:email) { filled? & email? } end end end
  13. Issue params Class.new(Hanami::Action::Params) do predicate :email?, message: 'invalid email format'

    do |value| # ... end validations do required(:user).schema do required(:email) { filled? & email? } end end end
  14. Independent validation class class DiaryInteractor::Create::Validation include Hanami::Validations predicate :email?, message:

    'invalid email format' do |value| # ... end validations do required(:email) { filled? & email? } end end
  15. Service Layer class DiaryInteractor::Create include Hanami::Interactor expose :params, :diary def

    initialize(params) @params = params end def call @diary = DiaryRepository.new.create(params) end def valid? DiaryInteractor::Create::Validation.new(@params).validate.success? end
  16. initializer # config/initializers/i18n.rb require 'i18n' require 'i18n/debug' if ENV['I18N_DEBUG'] ==

    'true' I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) I18n.load_path = Dir[ Hanami.root.join('config/locales/*.yml').to_s, Hanami.root.join('config/locales/**/*.yml').to_s ] I18n.backend.load_translations I18n.enforce_available_locales = false I18n.config.default_locale = 'ja'
  17. view helper module LocaleHelper def t(key, options = {}) ::I18n.t(key,

    default_options(key).merge(options)) end def default_options(key) if key.start_with?('.') app, _, controller, action = self.class.name.split('::').map {|class_name| Hanami::Utils::String.new(class_name).underscore } {scope: "#{app}.#{controller}.#{action}"} else {} end
  18. Validation class module AbstractValidation def self.included(klass) klass.class_eval do include Hanami::Validations

    messages :i18n end end end class DiaryInteractor::Create::Validation include AbstractValidation end
  19. webpack • Output builded files under public directory • Use

    webpack-manifest-plugin ◦ output manifest.json • Load output files to view template
  20. view helper module ViewHelper def webpack_asset_path(filepath) manifest = JSON.parse(manifest_filepath.read) bundled_filename

    = manifest[filename.to_s] raw(Hanami.public_directory.join(bundled_filename)) end end