20190126のRailsに関する記事は9件です。

Bootstrap4 hoverした時のリンクの背景色を変えたい

はじめに

RailsでBootstrap4.1.1を使用中に遭遇

スクリーンショット 2019-01-26 20.15.41.png

リンクにカーソルを合わせると(hoverすると)背景色が黒くなっててジーザスクライス


app/assets/stylesheets/application.scss
a:hover {
  background-color: transparent;
}

aタグにhoverさせると背景色が透明になるように上書き
(Railsだとlink_toメソッドはaタグに変換される)

スクリーンショット 2019-01-26 20.35.44.png

これで解決!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cloud9 と GitHub を使ってRailsアプリを開発する際の下準備

内容

cloud9 と GitHub を使ってRailsアプリを開発する際の下準備

なぜcloud9上でRailsアプリを開発するのか?

A. 従来はローカル環境(Mac)とGitHubを利用してアプリ開発をしていた。
しかし、ある時からnokogiriがインストールできないためローカルでの開発が困難に。

「Rubyの開発をするにはcloud9でやったほうがいい」とのアドバイスをいただいたので、cloud9とGitHubで開発することにした。

具体的な下準備

cloud9 に git をインストール

command-on-cloud9
gem intall git

バージョンを指定してRailsのインストール

command-on-cloud9
gem install rails -v 5.1.6

cloud9からGitHubにUP

command-on-cloud9
git init
git add .
git commit -m "Write your comment here."
git remote add origin [your GitHub repository]
git push -u origin master
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FactoryBot で汎用的な skip Validation, save(validate: false)

パッと見どこにも書いてなかったので覚書

Validation を無視して FactoryBot を使った Model を作成するとき、よくある方法

spec/factories/users.rb
FactoryGirl.define do
  factory :user do
    name 'HAZI'
    age 31

    to_create { |instance| instance.save(validate: false) }
  end
end

この方法だと、該当のモデル(この場合 User)を作成するとき、全てにおいて Validation がスキップされるのでよろしくない。

なので、下記のように trait を使って create(:user, :skip_validate) と指定した時のみ validation を無効にする。

spec/factories/users.rb
FactoryGirl.define do
  factory :user do
    name 'HAZI'
    age 31

    trait :skip_validate do
      to_create { |instance| instance.save(validate: false) }
    end
  end
end

が、そもそも traitfactory ネームスペース外で定義すれば共有できるので、spec/factories/traits.rb を作成しそこで定義をすることで全てのモデルで利用できるようになる。

spec/factories/traits.rb
FactoryBot.define do
  trait :skip_validate do
    to_create { |instance| instance.save(validate: false) }
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RSpec] CapybaraでCSSの●番目の要素を指定したい!(同じ要素が複数あるとき)

例えば

class: "fa-pen"が3個あるとき

find(".fa-pen").click

→Rails「何個目の".fa-pen"だよ??」

こうする

first(".fa-pen").click

→1個目を取り出す

2個目以降は??

second(".fa-pen").click

 Failure/Error: second(".fa-pen").click

     NoMethodError:
       undefined method `second' for #<RSpec::ExampleGroups〜〜〜



ダメじゃん。。

2個目以降は、【配列から取り出す】

page.all(".fa-pen")[1].click

→配列のindexが1なので、2個目を取り出す
(1個目を取り出す場合は、[0]とする)

お世話になりました

参考:Capybara で同名の CSS セレクタを持つ複数の HTML 要素から任意の要素を見つける
https://easyramble.com/find-html-element-with-capybara.html



おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RSpec] (feature) Capybaraでfill_in使って失敗するとき

こんなコードがありました

<%= provide(:title, "Create New List") %>
<div class="listsnewPage">
  <div class="container">
    <%= form_with model: @list, url: lists_path, class: "new_list" do |f| %>
      <%= f.label :title, "List Title" %> <span>(1〜20 characters)</span>
      <%= f.text_field :title, autofocus: true, autocomplete: "off", class: "form-control listName" %>
      <div class="text-center">
        <%= f.submit "Create", class: "submitBtn" %>
      </div>
    <% end %>
  </div>
</div>

こんなテストを書きました(feature spec, js: true)

fill_in "List Title", with: "created_now"

→ダメ

fill_in ".form-control", with: "created_now"

→ダメ!!!!

fill_in使ってるのにformに入力できない問題発生

いきなりですが

参考:Rspec Capybaraで実際テストを書いて困ったシチュエーションの解消法
https://qiita.com/kon_yu/items/52a0f5f0016564486061

fill_in

form(input)にidまたはnameが指定されていないと使えない

class指定の場合は???

こうする

find(".form-control").set("created")

通ったーーーーー!!!!!!!

けど

なんでf.labelに"List Title"って設定してるのに通らないんだろ?
わからん。。

とりあえず

fill_inでダメなときはfind使ってみるといいことあるかも
(根本的な解決には至っていない可能性)



おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RSpec] (feature) js: trueのテストが通ったり通らなかったり(成功・失敗を繰り返す)

こんなfeature specを書いたのだ

spec/features/lists_operation_spec.rb
RSpec.feature "ListsOperation", type: :feature do
  feature "create and destroy lists", js: true do
    scenario "create a list" do
      visit root_path
      expect(current_path).to eq root_path
      click_on "Create List"
      expect(current_path).to eq new_list_path
      # failed with invalid parameters
      find(".form-control").set("")
      expect do
        find(".submitBtn").click
      end.not_to change(List, :count)
      expect(current_path).to eq new_list_path
      # succeeded with valid params
      find(".form-control").set("created")

      expect do
        find(".submitBtn").click
      end.to change(List, :count).by(1)

      expect(current_path).to eq root_path
      expect(page).to have_selector "h2", text: "created"
      expect(page).to have_selector "div.lists", count: lists.count + 1
      save_screenshot
    end
  end
end



すると

ListsOperation
  create and destroy lists
Capybara starting Puma...
* Version 3.12.0 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:51883
    shows lists
    create a list
    edit a list
    delete a list

Finished in 3.49 seconds (files took 0.29178 seconds to load)
4 examples, 0 failures



落ちなかったり(これが正常)

Capybara starting Puma...
* Version 3.12.0 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:51973
    shows lists
    create a list (FAILED - 1)
    edit a list
    delete a list

Failures:

  1) ListsOperation create and destroy lists create a list
     Failure/Error:
       expect do
         find(".submitBtn").click
       end.to change(List, :count).by(1)

       expected `List.count` to have changed by 1, but was changed by 0
     # ./spec/features/lists_operation_spec.rb:35:in `block (3 levels) in <main>'
     # -e:1:in `<main>'

Finished in 3.34 seconds (files took 0.30711 seconds to load)
4 examples, 1 failure



落ちたり。。。

調べたゾイ

参考:Rspecのフィーチャーテストが失敗したりしなかったり(StackOverFlow)
https://ja.stackoverflow.com/questions/463/rspec%E3%81%AE%E3%83%95%E3%82%A3%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E5%A4%B1%E6%95%97%E3%81%97%E3%81%9F%E3%82%8A%E3%81%97%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%82%8A


js: trueを付けるとJSの処理が終わる前にRSpec側が先に進んでしまい、
本来通るはずのテストがパスしない、もしくはタイミングによってパスするときと失敗するときがある、
ということは僕も良く体験しています。

へ〜〜〜〜〜〜〜〜!!!!!!!!!!

そうなんだ。。
というわけで、該当部の下にsleep 3をつけて試してみる。

spec/features/lists_operation_spec.rb
      find(".form-control").set("created")

      expect do
        find(".submitBtn").click
      end.to change(List, :count).by(1)

      sleep 3  # ←これ

      expect(current_path).to eq root_path



すると、何回繰り返しても

ListsOperation
  create and destroy lists
Capybara starting Puma...
* Version 3.12.0 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:51883
    shows lists
    create a list
    edit a list
    delete a list

Finished in 3.49 seconds (files took 0.29178 seconds to load)
4 examples, 0 failures

Oh my goodness

素晴らしいですね。
ですが、sleep 3だとテストに時間がかかってしまうので、sleep 0.5にしました。

spec/features/lists_operation_spec.rb
      find(".form-control").set("created")

      expect do
        find(".submitBtn").click
      end.to change(List, :count).by(1)

      sleep 0.5  # ←3から0.5にしたゾイ

      expect(current_path).to eq root_path

というわけで

無事解決!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

小さく薄くrails newする(ViewやJSが必要ない場合)

HerokuでRubyのスクリプトのような何かを動かす時に、activerecord等は利用したいのでRuby単体ではなくてRailsとしての方が都合がいいのだけどViewやJS関係は特に必要ない。そんな時があります。
そういうわけで、上記のような用途向けのView関連、JS関連のファイルの作成の全てをスキップした rails new のオプション構成を調べました。

確認環境は

  • Rails 5.2.2 (Ruby 2.6.0)

です。

結論

結論は下記です。

rails new . --database=postgresql --skip-yarn --skip-git --skip-action-mailer --skip-active-storage --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle

各オプションの解説

各オプションの概要は下記の通りです。

オプション 効果
--database=DATABASE 使用するdatabaseを指定
--skip-yarn Yarnを利用しない
--skip-git .gitignore を作成しない
--skip-action-mailer Action Mailer関連のファイルを作成しない
--skip-active-storage Active Storage関連のファイルを作成しない
--skip-action-cable Action Cable関連のファイルを作成しない
--skip-sprockets Sprockets関連のファイルを作成しない
--skip-javascript JavaScript関連のファイルを作成しない
--skip-turbolinks turbolinks gemを利用しない
--skip-test test関連ファイルを作成しない
--api APIとして利用するapp向けの小さい構成
--skip-bundle bundle installを実行しない

詳しくは

rails new --help

でどうぞ。

補足しておくと、

  • 利用DBはPostgreSQL
  • 予め .gitignore は用意しているので デフォルトの .gitignore は作成しない ( -skip-git )
  • どうせあとでRspecをインストールするので標準のtest関連ファイルは必要ない ( --skip-test )
  • どうせあと後でGemfileに必要なgemを追加して bunde install するので bundle install しない ( --skip-bundle )

です。

参考: 上記オプション付与時の差分等

removeされるファイル一覧

参考までに、上記オプション付与時にremoveされるファイルは下記の通りです。

ちゃんとどのファイルが削除されるのか表示されて便利だ……。

rails new . --database=postgresql --skip-yarn --skip-git --skip-action-mailer --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle
  (中略)
      remove  app/assets
      remove  lib/assets
      remove  tmp/cache/assets
      remove  app/helpers
      remove  test/helpers
      remove  app/views
      remove  public/404.html
      remove  public/422.html
      remove  public/500.html
      remove  public/apple-touch-icon-precomposed.png
      remove  public/apple-touch-icon.png
      remove  public/favicon.ico
      remove  app/assets/javascripts
      remove  config/initializers/assets.rb
      remove  app/views/layouts/mailer.html.erb
      remove  app/views/layouts/mailer.text.erb
      remove  app/mailers
      remove  test/mailers
      remove  app/assets/javascripts/cable.js
      remove  app/channels
      remove  config/initializers/cookies_serializer.rb
      remove  config/initializers/content_security_policy.rb
      remove  config/initializers/new_framework_defaults_5_2.rb
      remove  bin/yarn

Gemfile差分

また、作成されるGemfileとデフォルトのGemfile( Gemfile.default )の差分は下記となります。

デフォルトのGemfileと比べると、sass-rails , coffee-rails から始まって web-console , またtestの capybara , selenium-webdrive に至るまでViewに関するgem一式が削除されていることがわかりますね。

git diff --no-index -U10 Gemfile.default Gemfile
diff --git a/Gemfile.default b/Gemfile
index e45e610..c5441b8 100644
--- a/Gemfile.default
+++ b/Gemfile
@@ -1,62 +1,40 @@
 source 'https://rubygems.org'
 git_source(:github) { |repo| "https://github.com/#{repo}.git" }

 ruby '2.6.0'

 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
 gem 'rails', '~> 5.2.2'
 # Use postgresql as the database for Active Record
 gem 'pg', '>= 0.18', '< 2.0'
 # Use Puma as the app server
 gem 'puma', '~> 3.11'
-# Use SCSS for stylesheets
-gem 'sass-rails', '~> 5.0'
-# Use Uglifier as compressor for JavaScript assets
-gem 'uglifier', '>= 1.3.0'
-# See https://github.com/rails/execjs#readme for more supported runtimes
-# gem 'mini_racer', platforms: :ruby
-
-# Use CoffeeScript for .coffee assets and views
-gem 'coffee-rails', '~> 4.2'
-# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
-gem 'turbolinks', '~> 5'
 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
-gem 'jbuilder', '~> 2.5'
-# Use Redis adapter to run Action Cable in production
-# gem 'redis', '~> 4.0'
+# gem 'jbuilder', '~> 2.5'
 # Use ActiveModel has_secure_password
 # gem 'bcrypt', '~> 3.1.7'

-# Use ActiveStorage variant
-# gem 'mini_magick', '~> 4.8'
-
 # Use Capistrano for deployment
 # gem 'capistrano-rails', group: :development

 # Reduces boot times through caching; required in config/boot.rb
 gem 'bootsnap', '>= 1.1.0', require: false

+# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
+# gem 'rack-cors'
+
 group :development, :test do
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
 end

 group :development do
-  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
-  gem 'web-console', '>= 3.3.0'
   gem 'listen', '>= 3.0.5', '< 3.2'
   # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
   gem 'spring'
   gem 'spring-watcher-listen', '~> 2.0.0'
 end

-group :test do
-  # Adds support for Capybara system testing and selenium driver
-  gem 'capybara', '>= 2.15'
-  gem 'selenium-webdriver'
-  # Easy installation and use of chromedriver to run system tests with Chrome
-  gem 'chromedriver-helper'
-end

 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
 gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsのキャッシュの速度を計測してみた

初めに

cacheについて調べて実際に計測してみた

参考記事
Railsのフラグメントキャッシュについて調べてみた
https://qiita.com/suketa/items/eeae7e2196520323f694

環境

userが1000件あってそれを読み込む

rails dev:cache

でcacheを有効にする。

users_controllerに

def index
  @users = User.all
end 

viewを

index.html.erb
    <h1>Users#index</h1>
    <p>Find me in app/views/users/index.html.erb</p>
    <% @users.each do |user| %>
            <%= user.first_name %>
            <%= user.last_name %>
            <%= user.phone %>
            <%= user.address %>
    <% end %>

これがキャッシュを使っていない普通の状態

実行してみると

Completed 200 OK in 198ms (Views: 173.6ms | ActiveRecord: 11.2ms)
Completed 200 OK in 48ms (Views: 41.7ms | ActiveRecord: 2.4ms)
Completed 200 OK in 51ms (Views: 45.0ms | ActiveRecord: 2.6ms)

最初は200ms位でそのあとは50ms位です

次にcacheを使ってみる

まずこんな感じのコードを書いてみた

index.html.erb
    <h1>Users#index</h1>
    <p>Find me in app/views/users/index.html.erb</p>
    <% @users.each do |user| %>
        <% cache user do %>
            <%= user.first_name %>
            <%= user.last_name %>
            <%= user.phone %>
            <%= user.address %>
        <% end %>
    <% end %>

結果はこんな感じ

Completed 200 OK in 993ms (Views: 976.4ms | ActiveRecord: 4.3ms)
Completed 200 OK in 364ms (Views: 343.8ms | ActiveRecord: 15.7ms)
Completed 200 OK in 393ms (Views: 386.7ms | ActiveRecord: 2.7ms)

時間はめっちゃ長くなった....

このキャッシュのやり方は調べたら結構出てきたやり方だけど使いどころは考えないといけないのかも…

とくに内容が特に無く数が多くなるものに関してはあんま良くないのかな?わかんないけど…

次にこのコード

users_controllerに

def index 
   @user_cache = User.last

   @users = User.all
end 

viewは

index.html.erb
<% cache(@user_cache) do  %>

    <h1>Users#index</h1>
    <p>Find me in app/views/users/index.html.erb</p>
    <% @users.each do |user| %>
            <%= user.first_name %>
            <%= user.last_name %>
            <%= user.phone %>
            <%= user.address %>
    <% end %>

<% end %>

これで結果は

Completed 200 OK in 214ms (Views: 197.8ms | ActiveRecord: 3.5ms)
Completed 200 OK in 32ms (Views: 28.2ms | ActiveRecord: 0.2ms)
Completed 200 OK in 34ms (Views: 29.5ms | ActiveRecord: 0.3ms)

初回は197msと遅いがそのあとは30ms以下と早くなった。

確かに二回目からの読み込みが少し早くなるけど正直cache使わないのとたいして変わらなかったのでまだあんまりcacheの良さがわからない…

多分使い方なんだろうけどとりあえずcache使えば良いってわけではなさそう

おわり:sunny:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

知識0から始めるRails on Docker

はじめに

Railsの勉強を始めようと思ったが、どうせならDockerを使って仮想環境で開発したい。
しかしどちらも実務で使ったことがないためよくわからない(というかRubyすら触ったことがない)。

ということで、一から調べてみた。RailsとDockerの欲張りセット。
筆者はMacBookProを使っているため、MacOS向けの記事となります。

tl;td

  • Rubyにまつわる各用語の確認、ローカル環境でRailsの導入・起動方法の確認
  • 手動でDocker上でのRailsの導入・起動方法の確認
  • dockerfileにRailsの導入・起動情報を文書化して格納し、docker-composeから呼び出して起動

対象読者

  • わたし
  • これからRailsを勉強しようという方
  • これからDockerを勉強しようという方

ロードマップ(超簡易版)

最終目標:Dockerによる仮想環境上でRails serverを起動し、開発できるようにする
※RailsのデフォルトサーバーはPumaです。この記事ではデフォルトのままなのでPumaを使いますが、便宜上「Rails server」と呼称します。

  • Railsをローカル環境で起動する
    • ローカル環境にRubyを導入する
    • ローカル環境にRailsを導入する
    • Railsプロジェクトを作成し、Rails serverを起動
  • Docker環境を構築して仮想環境でRails serverを起動する
    • ローカル環境にDockerを導入する
    • DockerでRubyを使える仮想環境を構築する
    • 仮想環境でRails serverを起動する
  • dockerfiledocker-composeを使ってコマンド一つでRails serverを起動する
    • dockerfileを作成する
    • docker-compose.ymlを作成し、起動する

Railsをローカル環境で起動する

これについては各所でがっつり解説されているので、ここでは流れ・用語の説明と参考資料の紹介にとどめます。
なお、この章の目的はRubyにまつわる各用語とRails serverの起動までの流れについて確認することが目的なので、既にバッチリの方は飛ばしてください。
参考資料:Ruby初学者のRuby On Rails 環境構築【Mac】

ローカル環境にRubyを導入する

流れ
Homebrewの導入(更新)-> rbenvの導入 -> Rubyの導入

ざっくりHomebrew解説

ざっくりrbenv解説

ローカル環境にRailsを導入する

流れ
Bundlerの導入 -> BundlerでGemfileの作成 -> Gemfileの編集 -> Gemの取得(Railsの導入)

ざっくりGem解説

ざっくりBundler解説

Railsプロジェクトを作成し、Rails serverを起動

流れ
Railsからプロジェクトを作成 -> Rails serverの起動 -> 接続確認

Docker環境を構築して仮想環境でRails serverを起動する

ローカル環境にDockerを導入する

Docker公式から最新版を取得しましょう。
アカウントの登録が必要です。

なお、Dockerの基礎的な仕組みやコマンドなどについては、以下の参考資料がおすすめです。

参考資料:Dockerでプログラマが最低限知るべきことが、最速でわかるチュートリアル
docker

参考資料:【図解】Dockerの全体像を理解する -前編-

ざっくりDocker解説

  • 仮想環境の構築・管理を行うツール
  • 擬似的に様々な環境を構築することができ、ローカル環境と本番環境との差異を減らすことができる
  • 「本番環境で急に動かなくなった問題」や「環境構築面倒すぎる問題」の救世主……らしい

DockerでRubyを使える仮想環境を構築する

参考資料:RailsアプリをDockerで開発するための手順

ここからが本題です。
まずは利用するイメージを決めます。Docker Hubにrubyという便利なイメージが用意されているので、ありがたく使わせていただきましょう。

rubyイメージの取得
$ docker pull ruby

イメージが取得できたら早速run……する前に、このイメージについて調べましょう。

イメージの情報を出力
$ docker inspect ruby

コマンドを実行すると大量の文字が吐き出されます。
目眩がするかもしれませんが、頑張って大事な記述を確認しましょう。注目するのは以下の場所です。

"ContainerConfig": {
……
  "Env": [
                "PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "RUBY_MAJOR=2.6",
                "RUBY_VERSION=2.6.0",
                "RUBY_DOWNLOAD_SHA256=acb00f04374899ba8ee74bbbcb9b35c5c6b1fd229f1876554ee76f0f1710ff5f",
                "GEM_HOME=/usr/local/bundle",
                "BUNDLE_PATH=/usr/local/bundle",
                "BUNDLE_SILENCE_ROOT_WARNING=1",
                "BUNDLE_APP_CONFIG=/usr/local/bundle"
            ],
……
RUBY_VERSION=2.6.0

Rubyのバージョンを示しています。Docker Hubでも確認できますが、イメージ本体にも情報がしっかり記されていますね。
このバージョンは重要なので覚えておきましょう。あるいはイメージをpullする段階で指定してあげてもいいでしょう。

BUNDLE_PATH=/usr/local/bundle

Bundlerのパスが指定されています。BundlerによるGemfileを使ったGem管理を行えることがわかりますね。

ではこのイメージからコンテナを起動しましょう。
適当な名前のディレクトリを作成し、ターミナルの作業フォルダとします。
以降はプロジェクト名をproject_nameとします。
project_name以外の名前でも構いません。本記事ではこの名前で統一するということです。

rubyイメージからコンテナを起動
$ docker run -i -t --name TEST -p 3000:3000 -v "$PWD":/usr/src/project_name ruby /bin/bash

まずは各オプションについて。

-i       :コンテナのSTDIN(標準入力)にアタッチ。標準入力に入力できる状態にするということ。
-t       :疑似ターミナル (pseudo-TTY) を割り当て。ターミナル画面で操作できるようにするということ。
--name   :コンテナの名前。今回は'TEST'を指定。
-p       :ポートの指定。今回は3000ポートを解放しています。
-v       :ボリュームの指定。[ホストPCのディレクトリ指定]:[コンテナ内のディレクトリ指定]。
/bin/bash:コンテナ起動後に実行するコマンド。今回はシェルを指定。

上記の説明は簡略したもので語弊を含むので、気になった項目は別途調べることをお勧めします。
-i-tの二つはセットで使う(-it)ことが多いので是非覚えておきましょう。
ボリュームについては概念が難しいですが、とりあえず[ホストPCで指定したディレクトリ]を[コンテナ内で指定したディレクトリ]として扱えるようにする、程度の理解でいいと思います。

参考資料:Dockerリファレンス

うまく起動できたら下のような状態になるはずです。

root@03eb04059b1f:/#

コンテナの中のターミナルを操作しているようなイメージですね。
コンテナの中から一旦抜ける時はcontrol + pq、再接続するときは$ docker attach [コンテナの指定]です。
exitでも抜けられますが、コンテナが停止してしまうため注意。停止した場合、ポートを閉じてしまうようです。
$ docker execでもコンテナに接続できます。ただし、プロセス接続するごとにプロセスが増えます。

仮想環境でRails serverを起動する

それでは今起動したコンテナでRails serverの準備をしましょう。
先ほどコンテナを起動した際に、コンテナ内のproject_nameディレクトリをボリュームとして指定しました。
指定したディレクトリが作成されているはずなので、そこまで移動しましょう。

Railsプロジェクトの確認
$ cd /usr/src/project_name

このディレクトリにRailsプロジェクトを作成していきます。
まずは何をするにもGemfileが必要なので、Bundlerを使って作成しましょう。

Gemfileの作成
$ bundle init

次にRailsを導入していきます。
Gemfileを編集する必要がありますが、これはコンテナ内で行う必要はありません。Gemifileを作成したディレクトリはコンテナ起動時にホストPC側のディレクトリとセットでボリューム指定したため、ホストPC側の指定したディレクトリにもGemfileが作成されています。このGemfileを編集するだけでOKです。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

- # gem "rails"
+ gem "rails"
コンテナ内のGemfile確認に反映されているか確認
$ cat Gemfile

これでGemfileの準備ができたので、GemのインストールとRailsプロジェクトの作成を行いましょう。

Gemのインストール
$ bundle install
$ bundle exec rails new .

rails exec .は作業ディレクトリに対してRailsプロジェクトを作成します。
実行するとGemfileの上書き確認をされるので許可しましょう。

ローカル環境ではこの時点でRails serverを起動できましたが、rubyイメージから作成したコンテナではまだRails serverは起動できません。
ですが、なぜ起動できないか確認するためにも一度試してみましょう。

起動確認(失敗する).
$ bundle exec rails server
エラーメッセージ(最後のみ抜粋)
/usr/src/project_name/vendor/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

ExecJS::RuntimeUnavailableというエラーについて調べてみると、どうやらJavaScriptランタイムというものが必要のようです。
今回はnode.jsを導入することにしましょう。
参考資料:rails sコマンド実行時に「Could not find a JavaScript runtime.」とエラーが出る場合の対処法

rubyイメージではyumコマンドは対応していないようなので、apt-getを使ってインストールしましょう。
参考資料:apt-get - パッケージの操作・管理 - Linuxコマンド

node.jsのインストール
$ apt-get update
$ apt-get install nodejs

完了したら今度こそRails serverが起動できるはずです。

Rails_server起動
$ bundle exec rails server

http://localhost:3000/に接続して起動できているか確認しましょう。

dockerfile と docker-compose を使ってコマンド一つでRails serverを起動する

ここからは今まで行ってきたことを設定として文書化していきます。
先ほどまで使用していたコンテナはもう使わないので、$ exitで停止しておきましょう。
ただし、作成したRailsプロジェクトはそのままにしておいてください。

参考資料:Dockerfile リファレンス
参考資料:Compose ファイル・リファレンス
参考資料:RailsアプリをDockerで開発するための手順

dockerfile を作成する

まず、前の章で行ったことを整理しておきましょう。

  • rubyイメージからコンテナを作成(本記事ではバージョン2.6.0)
  • ポート3000:3000を開放し、ボリュームを指定してコンテナを起動
  • コンテナ内でRailsプロジェクトを作成
  • コンテナにnode.jsをインストール
  • Rails serverを実行

dockerfileを書くということは、これらの作業を文書化していくということです。

それではdockerfileを書いていきます。Railsプロジェクトを作成したディレクトリにdockerfileという名前でファイルを作成し、編集していきます。
まずは使用したイメージの情報です。

dockerfile
FROM ruby:2.6.0

:2.6.0はバージョン情報です。指定しなければ最新版が自動で選択されますが、固定させた方がいいでしょう。
これはRubyのバージョンと同じなので、もし違うバージョンからRailsプロジェクトを作成したならそのバージョンに合わせてください。

次にポートの開放とボリューム指定ですが、dockerfileではホストPCのディレクトリを指定することはできません(後でdocker-compose.ymlで指定します)。よって、ここではポートの開放だけを行います。

dockerfile
FROM ruby:2.6.0

EXPOSE 3000

ここでポートの開放を行なっても、runコマンドやdocker-compose.ymlで指定しなければ直接接続できないので注意してください。

次はコンテナ内でRailsプロジェクト作成ですが、これは後でボリューム指定により共有する予定なので必要ありません。
ですが、Gemfileが無くてはGemのインストールができず、railsコマンドが実行できません。
そこで、Gemfileだけをコンテナ内にコピーしてインストールすることにします。

dockerfile
FROM ruby:2.6.0

ENV APP_ROOT /usr/src/project_name 

WORKDIR ${APP_ROOT}

COPY Gemfile ${APP_ROOT}
COPY Gemfile.lock ${APP_ROOT}

RUN bundle install

EXPOSE 3000

各コマンドは以下の通りです。

ENV [key] [value] …… 環境変数の定義。
WORKDIR [ディレクトリ指定] …… 作業ディレクトリの指定
COPY [ソース指定] [保存先指定] …… ファイルのコピー。
RUN [コマンド] …… コマンドの実行。

これでコンテナ内でもrailsコマンドが利用できるようになります。

最後にnode.jsのインストールとRails serverの起動です。

dockerfile
FROM ruby:2.6.0

ENV APP_ROOT /usr/src/project_name 

WORKDIR ${APP_ROOT}

RUN apt-get update && \
    apt-get install -y nodejs

COPY Gemfile ${APP_ROOT}
COPY Gemfile.lock ${APP_ROOT}

RUN bundle install

EXPOSE 3000

CMD bundle exec rails server

Railse sever の起動がRUNではなくCMDなのは、コンテナの立ち上げが完了してから実行してほしいからです。
もしRUNで書いてしまうとそこで動作が止まってしまい、いつまでもコンテナが立ち上がらないということになってしまいます。

では、これでうまく動作するかどうか確認してみましょう。

dockerfileからイメージの作成と起動
$ docker build .

エラーが出ずにRails serverの起動までできればOKです。

docker-compose.yml を作成し、起動する

では、先ほど作成したdockerfileを使ってdocker-compose.ymlを作っていきましょう。
先ほどと同じく、Railsプロジェクトのディレクトリに作成し、以下の通りに編集してください。

docker-compose.yml
app:
  build: .
  ports: 
    - '3000:3000'
  volumes: 
    - .:/usr/src/project_name
app: …… アプリケーションの名前。今回はappとした。
build: …… docker-composeのパス指定。今回は同一ディレクトリにある。
ports: …… 開放するポートの指定。
volumes: …… ボリュームの指定。

これで完了です。では起動してみましょう。

docker-composeからの起動
$ docker-compose up -d

http://localhost:3000/に接続して、いつものアレが表示されたらOKです!
ちなみに、ホストPCのディレクトリを参照しているため、ファイルを編集すると即座に反映されます。Gemfileと同じですね。

おわりに

というわけで、起動できました。とりあえず動いたので満足。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む