社内勉強会で感動した話(DDD ユビキタス言語再考)

社内勉強会で先輩がお話していた内容に感動しました。

内容の解説ではなく、自分が思ったことを書くので他の人の役には立たないかもしれないです。

また、勉強会は社内クローズドなイベントでしたが、
先輩のスライドはSpeaker Deckというサービスで公開されており、
ブログに書いても良いという許可もご本人にいただいております。

先輩は下記のスライドをもとに発表していました。

speakerdeck.com

どこに感動したのか

ユビキタス言語はチーム全員で合意し創るもの
ユーザーの言語 = ユビキタス言語 というわけではない

という部分に感動した。
感動というか「今ならわかる!」と思った。

なぜ感動したのか(なぜ今ならわかるのか)

多分、似たような話を半年ほど前にも社内勉強会でお話してもらったと思う。
でも、そのときの私は「ユビキタス言語??言葉そんなに大事なの????」という感じだった。

ここ3ヶ月、私は社内向けのシステムを開発している。
(そのシステムは経理担当者や営業担当者がExcelを用いて手動で行っていることをシステム化&一部自動化を目指している。)
ヒアリングを行い、システムの構造を考えていく中で、社内でAと呼んでいた書類が実は一般的にはBという書類とCという書類があってそれを組み合わせたものだということがわかる場面があった。(業務としてもB、Cの書類を発行すべきだった)
担当者から言われた書類の名前をそのまま使うのではなく、どうあるべきかということも担当者やその他関係するメンバーと考えながら認識をすりあわせて行くことができた。
同じ認識になった言葉をつかうと、資料つくりも楽で、認識の齟齬も減った気がする。
その実体験があったから「今ならわかる!」と思えたのだろう。

昔はわからなかったことでも、自分が体験すると理解しやすくなることもあるのかもしれない。


疑問(内容には関係ない可能性あり)

今回の発表内容には関係ないかもしれないが、下記のような疑問がある。

システム化するにあたり、社内オリジナルの名前で英語に直訳できないモデルの名前はどうすればいいのだろう?
(今は無理やり英訳した長い名前をつけている)

railsについて、先輩にもらったアドバイスまとめ(その1)

最近はRuby on Railsで社内システムを作っています。 その中で先輩からもらったアドバイスやレビューで受けた指摘をまとめます。

new()はいらない

hoge = Hoge.new()

hoge = Hoge.new

でよい。
(Rubyは短く楽に書けるときは楽をするのじゃ!)

is_available?のisはいらない

有効かを判定しtrue/falseを返却するようなメソッドの名前は

def is_available?
    # 何らかの処理
end

ではなく

def available?
    # 何らかの処理
end

でよい。
(present?やblank?というメソッドがありますよねー)

※私はこの「?」を疑問形の「?」だと思っていました…(Is this a pen?的な)

破壊的でないメソッドに「!」はいらない

def hoge!
    # 破壊的な処理
end

であり、破壊的でないメソッドに「!」はいらない。
(破壊的な処理を行う場合はprivateメソッドであっても「!」をつけるようにしています)

ある機能追加のために複数のテーブルにカラム追加を行う場合、migrateファイルは分ける

分けたのですが理由が思い出せず…。
※これはもしかしたら1つの機能追加で複数のテーブルに手が入ったことをわかりやすくするために、migrateファイルはわけないほうがよかったのかも?と思いました。

できるだけ短く、意味のある処理ごとにメソッドにする

def calc_hoge
    hoge = 0
    hoge = hoge + (a * b)
    hoge = hoge + (c * d)
end

def calc_hoge
    hoge = 0
    hoge += calc_a_b
    hoge += calc_c_d
end

def calc_a_b
    a * b
end

def calc_c_d
    c * d
end

のようにしたほうがコードが説明的になってわかりやすい。


2016/4/19(火)追記

ステータスコード404でリソースの存在を隠すこともできる

現在作成しているシステムはユーザーにロールを設定しており、ロールによっては閲覧できないリソースがある。
そのリソースを表示するようなページに権限のないユーザーがアクセスした場合、
403が返るとリソースが存在していることが権限のないユーザーにも分かってしまう。
存在自体を隠したいときは404を返す。

詳しくは下記の記事を参照
HTTPステータスコードを適切に選ぶためのフローチャート : 難しく考えるのをやめよう | インフラ・ミドルウェア | POSTD

railsで404のときに表示されるページは public/404.html である。 任意のデザインにしたい場合は404.htmlを編集すればよい。

rspecのテストでカバレッジが計測できるgem、SimpleCovを導入する

テストを書くならカバレッジ計測と行単位で通った・通ってないの確認ができたらいいな、と思っていたら
@tmtms さんより「SimpleCov」を教えていただきました。

GitHub - colszowka/simplecov: Code coverage for Ruby 1.9+ with a powerful configuration library and automatic merging of coverage across test suites

導入手順

Gemfileに「simplecov」を追加

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'
  gem 'rspec-rails'
  # Test format nyan cat
  gem 'nyan-cat-formatter'
  # テストを自動実行
  gem 'spring-commands-rspec'
  gem 'guard-rspec', require: false
  # カバレッジ取得
  gem "simplecov"
  # モデルにテーブルのカラムをコメントで出力
  gem 'annotate'
end

gemをインストール

$ bundle install --path vendor/bundle --jobs=4
$ # .bundle/configに設定されている場合はbundleでOK

spec/spec_helper.rbにsimplecovのstartを記述

require 'simplecov'

SimpleCov.start 'rails'

RSpec.configure do |config|
  # rspec-expectations config goes here. You can use an alternate
#・・・略・・・

これで準備は完了

使い方

rspecでテストを実行する。

$ bundle exec rspec spec/

するとプロジェクトフォルダ直下に「coverage」というフォルダが作成される。

f:id:maetoo11:20160411194209p:plain

coverageフォルダ配下の「index.html」をブラウザで開く。

f:id:maetoo11:20160411194648p:plain

するとファイルごとのカバレッジ一覧が表示される。

f:id:maetoo11:20160411195350p:plain

ファイル名をクリックすれば、行単位で通った・通っていないが確認できる。

f:id:maetoo11:20160411195827p:plain


導入にあたり参考にしたサイト

rspecでのテストがちょっと楽しくなるgem、nyan-cat-formatterを導入する

テストの待ち時間&結果確認がちょっと楽しくなるgemです。

GitHub - mattsears/nyan-cat-formatter: Nyan Cat inspired RSpec formatter!

導入手順

Gemfileに「nyan-cat-formatter」を追加

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'
  gem 'rspec-rails'
  # Test format nyan cat
  gem 'nyan-cat-formatter'
end

gemをインストール

$ bundle install --path vendor/bundle --jobs=4
$ # .bundle/configに設定されている場合はbundleでOK

使い方

オプションをつけてrspecを実行!!

$ bundle exec rspec --format NyanCatFormatter spec/

すると… f:id:maetoo11:20160407155537p:plain

※テスト実行中はNyan Catが走ります

テストが落ちると… f:id:maetoo11:20160407155851p:plain

とりあえずユーザー管理と認証を実装したいのでdeviseを使う

自力で実装する心の余裕が消えたのでdeviseを使うことにしました。
もうすでにあるものはガンガン使おう!そうしよう。

インストールや設定手順は下記リンクを参照しました。

手順

Gemfileにdeviseを追加する。

gem 'devise'

bundle installを行う。
(ここではbundlerの設定をそのまま使用したいのでbundleコマンドを実行している)

$ bundle
Fetching gem metadata from https://rubygems.org/...........
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.5.0
・・・略・・・
Using rack-test 0.6.3
Installing orm_adapter 0.5.0
Installing warden 1.2.6
Installing bcrypt 3.1.11 with native extensions
Using mail 2.6.3
・・・略・・・
Using web-console 2.3.0
Installing responders 2.1.1
Installing devise 3.5.6
Bundle complete! 16 Gemfile dependencies, 72 gems now installed.
Bundled gems are installed into ./vendor/bundle.

devise関連ファイルのをrailsコマンドで生成する。

$ bundle exec rails generate devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:

       config.assets.initialize_on_precompile = false

     On config/application.rb forcing your application to not access the DB
     or load models when precompiling your assets.

  5. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

環境を設定する。
config/environments/development.rb に下記を追記する。

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

devise用のUserモデルを作成する。

$ bundle exec rails g devise User
      invoke  active_record
      create    db/migrate/20160321071935_devise_create_users.rb
      create    app/models/user.rb
      invoke    rspec
      create      spec/models/user_spec.rb
      insert    app/models/user.rb
       route  devise_for :users

Userテーブルを追加するのでmigrateする。

$ bundle exec rake db:migrate:reset
== 20160321071935 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0166s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0333s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0259s
== 20160321071935 DeviseCreateUsers: migrated (0.0760s) =======================

deviseが出力するメッセージの表示場所を設定する。
(ここでは app/views/layouts/application.html.erb に設定)
ついでにナビゲーションバーにログインしていない場合はログイン、ログイン済みの場合はログアウトというリンクを表示する。

<body>
  <header>
    <div class="">
      <% if user_signed_in? %>
        <div>
          Logged in as <strong><%= current_user.email %></strong>.
          <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
        </div>
      <% else %>
        <div>
          <%= link_to "ログイン", new_user_session_path %>
        </div>
      <% end %>

    </div>
  </header>

  <div>
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <%= yield %>
  </div>

</body>

undefined method `devise_for'というエラーが出た。
f:id:maetoo11:20160321175102p:plain
→一度rails serverを再起動しないといけないらしい。
undefined method `devise_for' in rails - Stack Overflow

正しく動けば以下のようなログイン画面が表示されるはず。
f:id:maetoo11:20160321175236p:plain

その他

ログインしていないユーザーはログイン画面にリダイレクトするよう設定したかったので下記を参考に実装。
devise + omniauth-facebookでリクエストURLにリダイレクトする - yasuoza diary

初期ユーザーの追加をする。
Devise に初期ユーザを追加 - Ruby and Rails


Ruby on Rails 4 アプリケーションプログラミング

Ruby on Rails 4 アプリケーションプログラミング

N+1問題を検出してくれるgem、bulletを導入する

Railsで作ったプロジェクトを開発環境で動かしていくと、ある一覧ページを表示する度にすごい量のSELECTが発行されているように見え、先輩に相談!

「N+1問題」というものを教えていただきました。
N+1問題 / Eager Loading とは - Rails Webook

解決策

以下のページを参考にしました。
ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita

そもそもN+1問題はログを眺めていないと気づけないのでは…?と思ったところ、素敵なgemがあること先輩に教えていただきました!
それが「bullet」です。
GitHub - flyerhzm/bullet: help to kill N+1 queries and unused eager loading

導入手順

導入は下記ページを参考にしました。
» Railsライブラリ紹介: N+1問題を検出する「bullet」 TECHSCORE BLOG

Gemfileにbulletを追加
(開発環境でのみ使用する)

group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'
  gem 'thin'
  gem 'rails_best_practices'
  gem 'bullet'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

bundleコマンドを実行し、gemをインストールする

$ bundle
Fetching gem metadata from https://rubygems.org/...........
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.5.0
Using i18n 0.7.0
・・・略・・・
Using sass-rails 5.0.4
Using web-console 2.3.0
Installing uniform_notifier 1.9.0
Installing bullet 5.0.0
Bundle complete! 15 Gemfile dependencies, 67 gems now installed.
Bundled gems are installed into ./vendor/bundle.

開発環境のconfigにbullet用の設定を追加する
(config/environments/development.rbというファイルに下記の記述を追加する)

  config.after_initialize do
    Bullet.enable = true
    Bullet.alert = true
    Bullet.bullet_logger = true
    Bullet.console = true
    Bullet.rails_logger = true
  end

railsサーバーを再起動する
(再起動しないとbulletが使えない)

再起動してN+1問題のクエリが発行されるページへアクセスすると、下記のようなポップアップが表示されるようになる

f:id:maetoo11:20160314212116p:plain:w500

どこの処理で発生したかに加え対処方法をきちんと教えてくれるので、対処が簡単になりました(∩´∀`)∩ワーイ

素敵なgem、rails_best_practicesを導入する

先輩のアドバイスを受け、Railsのベストプラクティスに従っているかを確認できるgemを導入しました。

手順

Gemfileにrails_best_practicesを追加(今回は開発環境だけで使用する)

group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'
  gem 'thin'
  gem 'rails_best_practices'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

bundleコマンドを実行し、gemをインストールする

$ cat .bundle/config
---
BUNDLE_PATH: vendor/bundle
BUNDLE_JOBS: 4
BUNDLE_DISABLE_SHARED_GEMS: '1'
$
$
$ bundle
Fetching gem metadata from https://rubygems.org/...........
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies...
Using json 1.8.3
Using minitest 5.8.4
・・・略・・・
Installing code_analyzer 0.4.5
Installing rails_best_practices 1.16.0
Bundle complete! 13 Gemfile dependencies, 64 gems now installed.
Bundled gems are installed into ./vendor/bundle.
Post-install message from rails_best_practices:
********************************************************************************

  rails_best_practices is a code metric tool to check the quality of rails codes.

  I highly recommend you browse the Rails Best Practices website first.

      http://rails-bestpractices.com

  Please also try our online service

      http://railsbp.com

  Enjoy!

      Richard Huang (flyerhzm@gmail.com)

********************************************************************************
$

これでrails_best_practicesが使える状態になる。

早速実行してみる!
プロジェクトのディレクトリに入り、 rails_best_practices . を実行する。

$ bundle exec rails_best_practices .
Source Code: |==============================================================================================================================|
/Users/maetoo11/workspace/rails-app-name/db/schema.rb:34 - always add db index (model_name => [column_name])
/Users/maetoo11/workspace/rails-app-name/app/views/hoge/index.html.erb:53 - law of demeter
/Users/maetoo11/workspace/rails-app-name/app/helpers/humus_helper.rb:1 - remove empty helpers
/Users/maetoo11/workspace/rails-app-name/app/views/humus/_form.html.erb:1 - replace instance variable with local variable
/Users/maetoo11/workspace/rails-app-name/app/views/hoge/_form.html.erb:7 - replace instance variable with local variable

Please go to http://rails-bestpractices.com to see more useful Rails Best Practices.

Found 28 warnings.

各警告メッセージの意味(日本語)は下記の記事を参考にしました。
Rails Best Practices の警告をちゃんと考える - Qiita

Rails Best Practicesの本家ページ(?)です。
Rails Best Practices - Rails Best Practices
※どうやってプラクティスを検索していいかが不明…。

これでベストプラクティスをできるだけ守ったコードがかけるはず!!