The Pragmatic Hanami

B49aa473d5cd7f08cdce3d56ef837f29?s=47 kbaba1001
November 30, 2017

The Pragmatic Hanami

B49aa473d5cd7f08cdce3d56ef837f29?s=128

kbaba1001

November 30, 2017
Tweet

Transcript

  1. 2.

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

    nomad • Rubyist Magazine ◦ HanamiはRubyの救世主(メシア)となるか、愚かな星と散る のか
  2. 5.

    What is Hanami ? • Full-stack Ruby web framework •

    DDD (Domain Driven Design) • April 06, 2017, release v1.0.0
  3. 9.
  4. 10.

    Hanami project directories project_name/ ├── apps/ │ ├── admin/ │

    └── web/ ├── config/ ├── db/ ├── lib/ │ ├── project_name/ │ └── project_name.rb ├── spec/ ├── public/
  5. 11.

    “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
  6. 14.

    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)
  7. 18.
  8. 23.
  9. 24.

    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
  10. 25.

    Entity class Diary < Hanami::Entity end diary = Diary.new(title: 'learn

    ruby') diary.title #=> "learn ruby" diary.title = 'learn english' #=> NoMethodError: undefined method `title='
  11. 26.

    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
  12. 27.

    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
  13. 30.

    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
  14. 31.

    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
  15. 32.

    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
  16. 33.

    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
  17. 36.

    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
  18. 37.

    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
  19. 38.
  20. 40.
  21. 41.

    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'
  22. 42.

    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
  23. 43.

    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
  24. 44.
  25. 45.

    webpack • Output builded files under public directory • Use

    webpack-manifest-plugin ◦ output manifest.json • Load output files to view template
  26. 47.

    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