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

The Pragmatic Hanami

Avatar for kbaba1001 kbaba1001
November 30, 2017

The Pragmatic Hanami

Avatar for kbaba1001

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