erb を haml に変換 : 小林研 Rails Tips (48)

はじめに

Rails Tips の 48 回目です。昨日は RSpec と Guard をインストールしました。次に進みたいところですが、まだ view が erb のままでした。小林研では記述を簡単にするために、erb ではなく haml を使っています。今回は、既存の erb を haml に変更したいと思います。

Rails をはじめよう - Railsガイド

erb から haml に変換

haml-rails が入っていると rails のコマンドで erb ファイルを haml に変換できます。rails のコマンドはいつものように help で確認します。

$ rails --help
Usage:
  bin/rails COMMAND [options]

You must specify a command. The most common commands are:

  generate     Generate new code (short-cut alias: "g")
  console      Start the Rails console (short-cut alias: "c")
  server       Start the Rails server (short-cut alias: "s")
  test         Run tests except system tests (short-cut alias: "t")
  test:system  Run system tests
  dbconsole    Start a console for the database specified in config/database.yml
               (short-cut alias: "db")
  plugin new   Create a new Rails railtie or engine

All commands can be run with -h (or --help) for more information.

In addition to those commands, there are:

about                              List versions of all Rails frameworks and the environment
action_mailbox:ingress:exim        Relay an inbound email from Exim to Action Mailbox (URL and INGRESS_PASSWORD required)
action_mailbox:ingress:postfix     Relay an inbound email from Postfix to Action Mailbox (URL and INGRESS_PASSWORD required)
action_mailbox:ingress:qmail       Relay an inbound email from Qmail to Action Mailbox (URL and INGRESS_PASSWORD required)
action_mailbox:install             Install Action Mailbox and its dependencies
action_mailbox:install:migrations  Copy migrations from action_mailbox to application
action_text:install                Copy over the migration, stylesheet, and JavaScript files
action_text:install:migrations     Copy migrations from action_text to application
active_storage:install             Copy over the migration needed to the application
app:template                       Apply the template supplied by LOCATION=(/path/to/template) or URL
app:update                         Update configs and some other initially generated files (or use just update:configs or update:bin)
assets:clean                       Removes old files in config.assets.output_path
assets:clobber                     Remove config.assets.output_path
assets:precompile                  Compile all the assets from config.assets.paths
assets:reveal                      Print all the assets available in config.assets.paths
assets:reveal:full                 Print the full path of assets available in config.assets.paths
cache_digests:dependencies         Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)
cache_digests:nested_dependencies  Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
credentials:diff                   Enroll/disenroll in decrypted diffs of credentials using git
credentials:edit                   Open the decrypted credentials in `$VISUAL` or `$EDITOR` for editing
credentials:show                   Show the decrypted credentials
db:create                          Create the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). With...
db:drop                            Drop the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RA...
db:encryption:init                 Generate a set of keys for configuring Active Record encryption in a given environment
db:environment:set                 Set the environment value for the database
db:fixtures:load                   Load fixtures into the current environment's database
db:migrate                         Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
db:migrate:down                    Run the "down" for a given migration VERSION
db:migrate:redo                    Roll back the database one migration and re-migrate up (options: STEP=x, VERSION=x)
db:migrate:status                  Display status of migrations
db:migrate:up                      Run the "up" for a given migration VERSION
db:prepare                         Run setup if database does not exist, or run migrations if it does
db:reset                           Drop and recreate all databases from their schema for the current environment and load the seeds
db:rollback                        Roll the schema back to the previous version (specify steps w/ STEP=n)
db:schema:cache:clear              Clear a db/schema_cache.yml file
db:schema:cache:dump               Create a db/schema_cache.yml file
db:schema:dump                     Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)
db:schema:load                     Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) int...
db:seed                            Load the seed data from db/seeds.rb
db:seed:replant                    Truncate tables of each database for current environment and load the seeds
db:setup                           Create all databases, load all schemas, and initialize with the seed data (use db:reset to also drop all databases first)
db:system:change                   Change `config/database.yml` and your database gem to the target database
db:version                         Retrieve the current schema version number
destroy                            Remove code generated by `bin/rails generate`
dev:cache                          Toggle development mode caching on/off
encrypted:edit                     Open the decrypted file in `$VISUAL` or `$EDITOR` for editing
encrypted:show                     Show the decrypted contents of the file
haml:erb2haml                      Convert html.erb to html.haml each file in app/views
importmap:install                  Setup Importmap for the app
initializers                       Print out all defined initializers in the order they are invoked by Rails.
log:clear                          Truncate all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
middleware                         Print out your Rack middleware stack
notes                              Show comments in your code annotated with FIXME, OPTIMIZE, and TODO
restart                            Restart app by touching tmp/restart.txt
routes                             List all the defined routes
runner                             Run Ruby code in the context of your application
secret                             Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions).
secrets:edit                       **deprecated** Open the secrets in `$VISUAL` or `$EDITOR` for editing
secrets:show                       **deprecated** Show the decrypted secrets
spec                               Run all specs in spec directory (excluding plugin specs)
stats                              Report code statistics (KLOCs, etc) from the application or engine
stimulus:install                   Install Stimulus into the app
stimulus:install:bun               Install Stimulus on an app running bun
stimulus:install:importmap         Install Stimulus on an app running importmap-rails
stimulus:install:node              Install Stimulus on an app running node
stimulus:manifest:display          Show the current Stimulus manifest (all installed controllers)
stimulus:manifest:update           Update the Stimulus manifest (will overwrite controllers/index.js)
test:all                           Run all tests, including system tests
test:channels                      Run tests in test/channels
test:controllers                   Run tests in test/controllers
test:functionals                   Run tests in test/controllers, test/mailers, and test/functional
test:generators                    Run tests in test/lib/generators
test:helpers                       Run tests in test/helpers
test:integration                   Run tests in test/integration
test:jobs                          Run tests in test/jobs
test:mailboxes                     Run tests in test/mailboxes
test:mailers                       Run tests in test/mailers
test:models                        Run tests in test/models
test:units                         Run tests in test/models, test/helpers, and test/unit
time:zones[country_or_offset]      List all time zones, list by two-letter country code (`bin/rails time:zones[US]`), or list by UTC offset (`bin/rails time:zones[-8]`)
tmp:clear                          Clear cache, socket and screenshot files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear, tmp:screenshots:clear)
tmp:create                         Create tmp directories for cache, sockets, and pids
turbo:install                      Install Turbo into the app
turbo:install:bun                  Install Turbo into the app with bun
turbo:install:importmap            Install Turbo into the app with asset pipeline
turbo:install:node                 Install Turbo into the app with webpacker
turbo:install:redis                Switch on Redis and use it in development
version                            Show the Rails version
yarn:install                       Install all JavaScript dependencies as specified via Yarn
zeitwerk:check                     Check project structure for Zeitwerk compatibility

最初に RSpec をインストールします。たまにしか実行しないとすぐに忘れてしまうので、 --help で確認しましょう。 rspec:install というジェネレータがありますね。検索するよりまずはヘルプを見るのが一番です。

$ bin/rails g --help
Usage:
  bin/rails generate GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  application_record
  benchmark
  channel
  controller
  generator
  helper
  integration_test
  jbuilder
  job
  mailbox
  mailer
  migration
  model
  resource
  scaffold
  scaffold_controller
  system_test
  task

ActiveRecord:
  active_record:application_record
  active_record:multi_db

Erb:
  erb:controller
  erb:mailer
  erb:scaffold

Haml:
  haml:application_layout

Rspec:
  rspec:channel
  rspec:controller
  rspec:feature
  rspec:generator
  rspec:helper
  rspec:install
  rspec:integration
  rspec:job
  rspec:mailbox
  rspec:mailer
  rspec:model
  rspec:request
  rspec:scaffold
  rspec:system
  rspec:view

Stimulus:
  stimulus

TestUnit:
  test_unit:channel
  test_unit:controller
  test_unit:generator
  test_unit:helper
  test_unit:install
  test_unit:integration
  test_unit:job
  test_unit:mailbox
  test_unit:mailer
  test_unit:model
  test_unit:plugin
  test_unit:scaffold
  test_unit:system

ここから haml:erb2haml を rails に指示すれば erb から haml に変換できるようです。

> rails haml:erb2haml
--------------------------------------------------------------------------------
Generating HAML for app/views/layouts/application.html.erb...
rbenv: html2haml: command not found

The `html2haml' command exists in these Ruby versions:
  3.1.1

Generating HAML for app/views/layouts/mailer.html.erb...
rbenv: html2haml: command not found

The `html2haml' command exists in these Ruby versions:
  3.1.1

Generating HAML for app/views/layouts/mailer.text.erb...
rbenv: html2haml: command not found

The `html2haml' command exists in these Ruby versions:
  3.1.1

--------------------------------------------------------------------------------
HAML generated for the following files:
        app/views/layouts/application.html.erb
        app/views/layouts/mailer.html.erb
        app/views/layouts/mailer.text.erb
--------------------------------------------------------------------------------
Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)
y
Deleting original .erb files.
--------------------------------------------------------------------------------
Task complete!

実行しましたが、html2haml コマンドが存在しないとなりました。html2haml をインストールする必要があるようです。開発時にしか使わないので、Gemfile の development, test の一番下に入れました。

  gem "haml-lint"
  gem "html2haml"
end

bundle すると正しくインストールされました。

$ bundle
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Fetching erubis 2.7.0
Fetching sexp_processor 4.17.1
Installing erubis 2.7.0
Installing sexp_processor 4.17.1
Fetching ruby_parser 3.21.0
Installing ruby_parser 3.21.0
Fetching html2haml 2.3.0
Installing html2haml 2.3.0
Bundle complete! 21 Gemfile dependencies, 122 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

もう一度実行すると、以下のように haml が生成されました。

$ rails haml:erb2haml
--------------------------------------------------------------------------------
Generating HAML for app/views/layouts/application.html.erb...
Generating HAML for app/views/layouts/mailer.html.erb...
Generating HAML for app/views/layouts/mailer.text.erb...
--------------------------------------------------------------------------------
HAML generated for the following files:
        app/views/layouts/application.html.erb
        app/views/layouts/mailer.html.erb
        app/views/layouts/mailer.text.erb
--------------------------------------------------------------------------------
Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)
n
Please remember to delete your .erb files once you have ensured they were translated correctly.
--------------------------------------------------------------------------------
Task complete!

実際に application.html.haml は以下のようになりました。

!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title HkobBlog
    %meta{:content => "width=device-width,initial-scale=1", :name => "viewport"}/
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag "application", "data-turbo-track": "reload"
  %body
    = yield

このままだとハッシュの記述が古いので、以下のように書き換えます。RubyMine であれば Opt + Return で簡単に変更してくれます。

!!!
%html
  %head
    %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title HkobBlog
    %meta{content: "width=device-width,initial-scale=1", name: "viewport"}/
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag "application", "data-turbo-track": "reload"
  %body
    = yield

おわりに

erb を haml に変換することで、html の閉じるタグのことを気にする必要がなくなるので、view の記述が非常に簡単になりますね。