Help us understand the problem. What is going on with this article?

Rubyの新しいフレームワーク、Hanamiで遊んでみた

More than 1 year has passed since last update.

はじめに

RubyKaigi2017 に、新米エンジニアながら参戦してきました。東京から広島の観光も兼ねて行ったところ、台風18号に巻き込まれ傘を一本失い、広島東洋カープのセ・リーグ優勝のハイタッチに巻き込まれ、優勝祝いでハイボールを33円で飲むなど非常に稀有な体験をし非常に満足しました。広島人のカープ愛を見ていれば、カープが優勝するのも当たり前と思えるというものです。広島の皆さん、おめでとうございます。

閑話休題。

さて初日の発表の中で”Hanami”というRubyのWebフレームワークの話がありましたので、せっかくですし実際にHanami | Guides - Getting Startedに触ってみました。

Hello, hanami!

公式サイトはこちら
また、日本語ではるびまで記事が書かれております。hanamiの思想などの部分はこちらにありますので、そこはお譲りして今回は簡単に触りだけやってみようと思います。

Rubyのバージョンは、僕の環境では以下のとおり。

% ruby -v                                                                                                                                                                                   (git)-[master]
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]

gemがありますので、いつものごとく。

gem install hanami

以上!

プロジェクトを作ります。

hanami new bookshelf

デフォルトではSQliteを使っているとのことですが、以下のように変更もできます。

hanami new bookshelf --database=postgres

そして生成されたbookshelfの中身がこちら。

% ls                                                                                                                                                                                        
Gemfile       Gemfile.lock  Rakefile      apps/         config/       config.ru     db/           lib/          public/       spec/

bundle install しましょう。その後、以下のコマンドでサーバーを立ててみると

% bundle exec hanami server
[2017-09-20 18:33:12] INFO  WEBrick 1.3.1
[2017-09-20 18:33:12] INFO  ruby 2.4.1 (2017-03-22) [x86_64-darwin16]
[2017-09-20 18:33:12] INFO  WEBrick::HTTPServer#start: pid=12598 port=2300

Screenshot 2017-09-20 18.35.14.png

こんな画面が見えたらオーケーです!

Development on hanami

hanamiはTDD, もといBDD(Behavior Driven Development)に依って開発を進めていくことが推奨されているようです。
これは、hanamiの思想として長期的なメンテナンス性などを維持できることが望まれているから、ということでGetting Startedにもそれは現れています。
が、今回はあえてそこをスキップして実装だけやっていきましょう。まずはアクションを生成します。

Action

僕はZshを使っているので、以下のコマンドを入力します。

hanami generate action web books/index

Zsh以外の方は以下です!

bundle exec hanami generate action web books#index

こんな結果になります

% hanami generate action web books/index
      create  spec/web/controllers/books/index_spec.rb
      create  apps/web/controllers/books/index.rb
      create  apps/web/views/books/index.rb
      create  apps/web/templates/books/index.html.erb
      create  spec/web/views/books/index_spec.rb
      insert  apps/web/config/routes.rb

今生成された、apps/web/templates/books/index.html.erbを以下のように編集しましょう。

<h1>Bookshelf</h1>
<h2>All books</h2>

<div id="books">
  <div class="book">
    <h3>Patterns of Enterprise Application Architecture</h3>
    <p>by <strong>Martin Fowler</strong></p>
  </div>

  <div class="book">
    <h3>Test Driven Development</h3>
    <p>by <strong>Kent Beck</strong></p>
  </div>
</div>

同様にapps/web/templates/application.html.erbについて、以下のように編集してください。

<!DOCTYPE html>
<html>
  <head>
    <title>Bookshelf</title>
    <%= favicon %>
  </head>
  <body>
    <h1>Bookshelf</h1>
    <%= yield %>
  </body>
</html>

テンプレートを変更したことで、他のファイルの重複部分を取り除くことができるようになりました。

apps/web/templates/books/index.html.erbにおける重複部分を取り除くと、以下のようになります。

Screenshot 2017-09-20 20.12.53.png

Model

次に、モデルを生成してみましょう。

% bundle exec hanami generate model book
      create  lib/bookshelf/entities/book.rb
      create  lib/bookshelf/repositories/book_repository.rb
      create  db/migrations/20170921023533_create_books.rb
      create  spec/bookshelf/entities/book_spec.rb
      create  spec/bookshelf/repositories/book_repository_spec.rb

エンティティ、レポジトリ、マイグレーション、そしてテストファイルを生成してくれました。
今生成されたマイグレーションファイルdb/migrations/20170921023533_create_books.rbを、タイトルと筆者情報を含むように変更してみます。

Hanami::Model.migration do
  change do
    create_table :books do
      primary_key :id

      column :title,  String, null: false
      column :author, String, null: false

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false
    end
  end
end

新しいテーブルを準備します!

% bundle exec hanami db prepare
% HANAMI_ENV=test bundle exec hanami db prepare

Entity

エンティティの操作をします。エンティティはプレーンなRubyオブジェクトに極めて似ているものとのこと。
ここでは極めて単純なエンティティのクラスを作ります。lib/bookshelf/entities/book.rbこちらのファイルです。

class Book < Hanami::Entity
end

Console

コンソールでも操作することができます。

% bundle exec hanami console

こんな感じで操作できます。

% bundle exec hanami console
>> repository = BookRepository.new
=> => #<BookRepository relations=[:books]>
>> repository.all
=> []
>> book = repository.create(title: 'TDD', author: 'Kent Beck')
=> #<Book:0x007f9ab61c23b8 @attributes={:id=>1, :title=>"TDD", :author=>"Kent Beck", :created_at=>2016-11-15 11:11:38 UTC, :updated_at=>2016-11-15 11:11:38 UTC}>
>> repository.find(book.id)
=> #<Book:0x007f9ab6181610 @attributes={:id=>1, :title=>"TDD", :author=>"Kent Beck", :created_at=>2016-11-15 11:11:38 UTC, :updated_at=>2016-11-15 11:11:38 UTC}>

表示の変更

テンプレートファイルapps/web/templates/books/index.html.erbを変更しましょう。

<h2>All books</h2>

<% if books.any? %>
  <div id="books">
    <% books.each do |book| %>
      <div class="book">
        <h2><%= book.title %></h2>
        <p><%= book.author %></p>
      </div>
    <% end %>
  </div>
<% else %>
  <p class="placeholder">There are no books yet.</p>
<% end %>

[image:37362FAF-2F22-46F9-AFF2-40BF0F4464AC-12422-000033C6D1B09365/Screenshot 2017-09-21 12.28.56.png]

先ほどコンソールで挿入したデータが表示されているかと思います!

フォームの作成

次に、データを挿入するためのフォームを作成してみましょう。

bundle exec hanami generate action web books/newを実行しましょう!

これによって、routeが追加されます。apps/web/config/routes.rbを確認してみましょう。

get '/books/new', to: 'books#new'
get '/books', to: 'books#index'

このようになっているはずです。
それでは、hanamiのフォームビルダーを使ってフォームを作成します。apps/web/templates/books/new.html.erbを編集してみてください。

<h2>Add book</h2>

<%=
  form_for :book, '/books' do
    div class: 'input' do
      label      :title
      text_field :title
    end

    div class: 'input' do
      label      :author
      text_field :author
    end

    div class: 'controls' do
      submit 'Create Book'
    end
  end
%>

また、アクションを追加しましょう。

bundle exec hanami generate action web books/createを実行してください。またrouteに新たな行が追加されたはずです!

post '/books', to: 'books#create'

Controllerを編集します。apps/web/controllers/books/create.rbを編集しましょう。

module Web::Controllers::Books
  class Create
    include Web::Action

    expose :book

    params do
      required(:book).schema do
        required(:title).filled(:str?)
        required(:author).filled(:str?)
      end
    end

    def call(params)
      if params.valid?
        @book = BookRepository.new.create(params[:book])

        redirect_to '/books'
      else
        self.status = 422
      end
    end
  end
end

また、apps/web/views/books/create.rbも合わせて変更しましょう。

module Web::Views::Books
  class Create
    include Web::View
    template 'books/new'
  end
end

localhost:2300/books/newにアクセスしてみてください。
Screenshot 2017-09-21 12.55.34.png

Screenshot 2017-09-21 12.55.25.png

うまくいきました!

バリデーションエラーの表示

次にバリデーションエラーを表示できるようにしましょう。

コントローラーapps/web/controllers/books/create.rbを以下のように編集してください。

module Web::Controllers::Books
  class Create
    include Web::Action

    expose :book

    params do
      required(:book).schema do
        required(:title).filled(:str?)
        required(:author).filled(:str?)
      end
    end

    def call(params)
      if params.valid?
        @book = BookRepository.new.create(params[:book])

        redirect_to '/books'
      else
        self.status = 422
      end
    end
  end
end

同様に、apps/web/views/books/create.rbを以下のように編集してください。

module Web::Views::Books
  class Create
    include Web::View
    template 'books/new'
  end
end

また、apps/web/templates/books/new.html.erbについて編集してください。

<% unless params.valid? %>
  <div class="errors">
    <h3>There was a problem with your submission</h3>
    <ul>
      <% params.error_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

<h2>Add book</h2>

<%=
  form_for :book, '/books' do
    div class: 'input' do
      label      :title
      text_field :title
    end

    div class: 'input' do
      label      :author
      text_field :author
    end

    div class: 'controls' do
      submit 'Create Book'
    end
  end
%>

これで、バリデーションが効くようになりました!!!

最後に、ルーティングapps/web/config/routes.rbを整理しましょう。

root to: 'books#index'
resources :books, only: [:index, :new, :create]

Getting Startedではroot to: 'home#index'になっていますが、今回はあえてこうさせてもらっています。
これで終了です!

おわりに

今回はhanamiにとりあえず触ってみる、ということでGetting Startedをテストの部分をスキップしてやってみました。hanami自体が4年程度しかまだ歴史がない中、新しいフレームワークとしてどの程度活躍するのかはわかりませんが、いちRubyist見習いとして楽しみにしておきたいところです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした