- 投稿日:2020-09-29T23:48:16+09:00
windowsでrubyが使えるようにするまで...
Windows10でRubyを使うために、
rbenv install 2.7.1
ができるようになるまでが長い道のりだったのでまとめました。環境
- Windows10
- Ubuntu
概要
実施することは以下になります。
- 初期化設定:
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
- 設定の保存:
source ~/.bash_profile
- rbenvのインストール:
brew install rbenv ruby-build
- バージョンを指定してインストール:
rbenv install 2.7.1
- 使用するrbenvのバージョンを設定:
rbenv global 2.7.1
- rubyのインストール:
sudo apt install ruby
これらのコマンドがエラーを出さずに全部通れば、以降の話は何も関係ありません。
初めてのセッティング故に環境が整っておらず、発生したエラーとその対処を記していきます。同じエラーがあれば参考にしていただければと思います。
上から順番にコマンドを入力していってます。目次
- .bash_profileの設定
- Homebrewをインストール
- rbenvをインストール
.bash_profileの設定
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
エラー:No such file or directory 原因:.bash_profileファイルが存在していないところに書き足そうとした 対応:.bash_profileファイルを作成する対処:
touch ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
エラー:Command 'rbenv' not found, but can be installed with: sudo apt install rbenv 原因:$(rbenv init -)でrbenvコマンドをしようとしているが、rbenvがわからない 対応:rbenvをインストールする対処:
brew install rbenv ruby-build
エラー:Command 'brew' not found 原因:brewがわからない 対応:HomebrewをインストールするHomebrewをインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew -v
(brewコマンドが使えるか確認)エラー:Command 'brew' not found 原因:インストールされたHomebrewがどこにあるのかわかっていない。 対応:Homebrewまでのパスを通してあげる対処:
echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' >> ~/.bashrc
対処:source ~/.bashrc
brew -v
(Homebrew 2.5.2と表示される)rbenvをインストール
brew install rbenv ruby-build
source ~/.bash_profile
(rbenvがインストールされたので、.bash_profileの書き換えを保存)rbenv install --list-all
(インストールできるるrbenvのバージョンの一覧を表示)rbenv install 2.7.1
(今回はVer. 2.7.1をインストール)エラー:configure: error: C compiler cannot create executables 原因:Homebrewに必要なものが足りていなかった 対応:足りないものをインストールする対処:
sudo apt install build-essential curl file git
エラー:E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? 原因:apt-getが最新になっていない 対応:apt-getをupdateする対処:
sudo apt-get update
sudo apt install build-essential curl file git
rbenv install 2.7.1
エラー:ERROR: Ruby install aborted due to missing extensions. Try running apt-get install -y zlib1g-dev to fetch missing dependencies. 原因:zlib1g-devがないらしい 対応:zlib1g-devをインストールする対処:
sudo apt-get install -y zlib1g-dev
rbenv install 2.7.1
rbenv versions
(指定したバージョンのrbenvがインストールできていることを確認)rbenv global 2.7.1
(rbenvのバージョンを全体に反映させる。特定のディレクトリのみならglobalではなくlocalで設定。)ruby -v
エラー:Command 'ruby' not found, but can be installed with: sudo apt install ruby 原因:ruby自体がまだない 対応:rubyをインストールする対処:
sudo apt install ruby
ruby -v
(ruby 2.7.1p83と表示される)最後に
原因や対応として間違っているものがあれば教えていただけるととてもありがたいです。
何かの参考になれば幸いです!参考にさせていただいたもの
とても助けていただきました、ありがとうございました。
- rbenvの使い方と仕組みについて
- brew|command not foundコマンドが見つかりませんの対処
- Homebrew をインストールしたはずなのにbash: brew: command not foundになる。
- 【Ruby】最新バージョンにしようとしてハマったこと
- Homebrewのインストール方法
- ubuntuでapt-get install failedなメモ
今回の疑問点
調べてまとめるためのメモ
- rubyのバージョンって基本最新をインストールしたらいいの?
- sudo, aptってなに?
- brewってなに?
- yarnとかnpmとかとどう違うの?
- 投稿日:2020-09-29T23:48:16+09:00
windowsでrubyが使えるようになるまで...
Windows10でRubyを使うために、
rbenv install 2.7.1
ができるようになるまでが長い道のりだったのでまとめました。環境
- Windows10
- Ubuntu
概要
実施することは以下になります。
- 初期化設定:
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
- 設定の保存:
source ~/.bash_profile
- rbenvのインストール:
brew install rbenv ruby-build
- バージョンを指定してインストール:
rbenv install 2.7.1
- 使用するrbenvのバージョンを設定:
rbenv global 2.7.1
- rubyのインストール:
sudo apt install ruby
これらのコマンドがエラーを出さずに全部通れば、以降の話は何も関係ありません。
初めてのセッティング故に環境が整っておらず、発生したエラーとその対処を記していきます。同じエラーがあれば参考にしていただければと思います。
上から順番にコマンドを入力していってます。目次
- .bash_profileの設定
- Homebrewをインストール
- rbenvをインストール
.bash_profileの設定
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
エラー:No such file or directory 原因:.bash_profileファイルが存在していないところに書き足そうとした 対応:.bash_profileファイルを作成する対処:
touch ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
エラー:Command 'rbenv' not found, but can be installed with: sudo apt install rbenv 原因:$(rbenv init -)でrbenvコマンドをしようとしているが、rbenvがわからない 対応:rbenvをインストールする対処:
brew install rbenv ruby-build
エラー:Command 'brew' not found 原因:brewがわからない 対応:HomebrewをインストールするHomebrewをインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew -v
(brewコマンドが使えるか確認)エラー:Command 'brew' not found 原因:インストールされたHomebrewがどこにあるのかわかっていない。 対応:Homebrewまでのパスを通してあげる対処:
echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' >> ~/.bashrc
対処:source ~/.bashrc
brew -v
エラー:Command 'brew' not found 原因:.bash_profileファイルにもパスを通す必要がある 対応:.bash_profileファイルに設定を追記対処:
echo export PATH='/usr/local/bin:$PATH' >> ~/.bash_profile
対処:source ~/.bash_profile
brew -v
(Homebrew 2.5.2と表示される)rbenvをインストール
brew install rbenv ruby-build
source ~/.bash_profile
(rbenvがインストールされたので、.bash_profileの書き換えを保存)rbenv install --list-all
(インストールできるるrbenvのバージョンの一覧を表示)rbenv install 2.7.1
(今回はVer. 2.7.1をインストール)エラー:configure: error: C compiler cannot create executables 原因:Homebrewに必要なものが足りていなかった 対応:足りないものをインストールする対処:
sudo apt install build-essential curl file git
エラー:E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? 原因:apt-getが最新になっていない 対応:apt-getをupdateする対処:
sudo apt-get update
sudo apt install build-essential curl file git
rbenv install 2.7.1
エラー:ERROR: Ruby install aborted due to missing extensions. Try running apt-get install -y zlib1g-dev to fetch missing dependencies. 原因:zlib1g-devがないらしい 対応:zlib1g-devをインストールする対処:
sudo apt-get install -y zlib1g-dev
rbenv install 2.7.1
rbenv versions
(指定したバージョンのrbenvがインストールできていることを確認)rbenv global 2.7.1
(rbenvのバージョンを全体に反映させる。特定のディレクトリのみならglobalではなくlocalで設定。)ruby -v
エラー:Command 'ruby' not found, but can be installed with: sudo apt install ruby 原因:ruby自体がまだない 対応:rubyをインストールする対処:
sudo apt install ruby
ruby -v
(ruby 2.7.1p83と表示される)最後に
原因や対応として間違っているものがあれば教えていただけるととてもありがたいです。
何かの参考になれば幸いです!参考にさせていただいたもの
とても助けていただきました、ありがとうございました。
- rbenvの使い方と仕組みについて
- brew|command not foundコマンドが見つかりませんの対処
- Homebrew をインストールしたはずなのにbash: brew: command not foundになる。
- 【Ruby】最新バージョンにしようとしてハマったこと
- Homebrewのインストール方法
- ubuntuでapt-get install failedなメモ
今回の疑問点
調べてまとめるためのメモ
- rubyのバージョンって基本最新をインストールしたらいい?
- sudo, aptとは?
- brewとは?
- yarnとかnpmとかとどう違う?
- 投稿日:2020-09-29T23:31:43+09:00
もう迷わない!RSpec導入の流れ
RSpecとは
Railsにおいて、単体テストコードを記述するときに用いるGemのこと。
テストコードを記述する直前までの大まかな流れ
- Gemfileに追記
- Gemをインストール
- rspecをインストール
- テストコードをターミナル上で可視化できるようにする
- FactoryBotとFakerのGemをGemfileに追記
- Gemのインストール
- (テスト用の画像を用意)
1. Gemfileに追記
gem 'rspec-rails'と記述する。
必ず、group :development, :test do ~ endのグループ内にGemfilegroup :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails' end2. Gemをインストール
ターミナルでカレントディレクトリがテストコードを使用するアプリケーションのディレクトリであることを確認の上
bundle install3. rspecをインストール
rails g rspec:installこれによって、「specディレクトリ」や「.rspecファイル」が生成される
4. テストコードをターミナル上で可視化できるようにする
.rspec--format documentationを追記する。
5. FactoryBotとFakerのGemをGemfileに追記
必要に応じてGemを導入。今回はチャットアプリを想定しているため、導入。
Gemfilegroup :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails' gem 'factory_bot_rails' gem 'faker' end先ほどと同じgroupの中に記述。
6. Gemのインストール
bundle install7. (テスト用の画像を用意)
public/imagesにファイル名「test_image.png」で配置。
ポイント
- rspecは「bundle install」だけでなく、「rails g rspec:install」をして初めて使える。
- 記述するのは、group :development, :test do ~ endのグループ内。
最後に
bundle installはRailsに取り込んだだけ!
rails g rspec:installで解凍するイメージ!細かいテストコードはまた別の機会に。
- 投稿日:2020-09-29T23:22:33+09:00
Ruby で解く AtCoder ACL Beginner Contest C UnionFind(DSU)
はじめに
AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。今回のお題
AtCoder Beginner Contest C - Connect Cities
Difficulty: 363今回のテーマ、UnionFind
つい最近、同様の問題 があったり、AtCoder Library Practice Contest があったりとは言え、灰レートは凄いです。
ちなみに、前回はDifficulty: 676でした。Ruby
ruby.rbclass UnionFind def initialize(n) @parents = Array.new(n, -1) end def find(x) @parents[x] < 0 ? x : @parents[x] = find(@parents[x]) end def parents @parents end def union(x, y) x = find(x) y = find(y) return if x == y if @parents[x] > @parents[y] x, y = y, x end @parents[x] += @parents[y] @parents[y] = x end end n, m = gets.split.map(&:to_i) u = UnionFind.new(n) m.times do a, b = gets.split.map(&:to_i) u.union(a - 1, b - 1) end puts u.parents.select{ _1 < 0 }.size - 1sub.rbputs u.parents.select{ _1 < 0 }.size - 1 # 今回 puts -u.parents.min # 前回前回 --Qiitaの最終行を変更するだけで
AC
します。sub.rb@parents = [-3, 0, -2, 2, 0]前回の
入力例 1
で説明しますと、u.parents
の中は、u.parents[0]
とu.parents[2]
が代表の2グループに分けれらますので、それを1つの道路で結べばOK。AtCoder Library (ACL)のRuby版
ここ --githubで、ACLのRuby版を作成されている動きもあります。
要注目
ですね。
Ruby コード長 (Byte) 567 実行時間 (ms) 170 メモリ (KB) 15612 まとめ
- ACLBC C を解いた
- Ruby に詳しくなった
- 投稿日:2020-09-29T20:54:51+09:00
【Ruby on Rails】deviseの不要なルートを作らない方法
目標
deviseで自動的に作成される不要なルートを作らない。
→新たな新規adminの作成を防ぐ。
※管理者(admin)情報はseedにて記述する場合。開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina前提
※ ▶◯◯ を選択すると、説明等が出てきますので、
よくわからない場合の参考にしていただければと思います。seedに記述
seedに管理者の情報を記述。
db/seeds.rb... Admin.create!( email: 'aa@aa.com', password: 'aaaaaa', )ターミナル$ rails db:seedこれで管理者ページにログインできます。
※seedに関しては開発環境と本番環境で分ける必要があります。
詳しくはこちらroutesを編集
config/routes.rbdevise_for :admins, :skip => [:registrations, :password],controllers: { sessions: 'admins/sessions', }ログインページがデフォルトの状態の場合
このままではエラーが出てしまうので、下記ファイルをの該当箇所を削除。
app/views/admins/shared/_links.html.erb<%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%= link_to "Sign up", new_registration_path(resource_name) %><br /> <% end %> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%= link_to "Forgot your password?", new_password_path(resource_name) %><br /> <% end %>参考
- 投稿日:2020-09-29T17:13:00+09:00
ajaxを使ってタグの追加と削除、(成功・エラー)メッセージの表示を行う。
環境
Ruby 2.5.7
Rails 5.2.4ライブラリ
jQuery 1.12.4
前提
今回はTagテーブルを使って解説していきます。
まずはajaxを使わなくてもタグの追加と削除ができることを事前にご確認ください。
エラーメッセージの日本語化やアソシエーションについては触れませんので必要な方はご自身でお願い致します。
turbolinksは無効にしています。(有効時の動作は確認していません。)
手順
タイトルにもある通り、次の手順でajaxを利用してタグの追加・削除・エラーメッセージの表示を実装していきます。
1.tag.rbにバリデーションの設定
2.部分テンプレートの用意(views/layouts/_error_messages.html.erb
,views/layouts/_flash_messages.html.erb
,views/tags/_tag.html.erb
)
3.タグの一覧画面、兼新規作成フォームを作成(views/tags/index.html.erb)
4.アクションごとのjs.erbを用意(views/tags/create.js.erb, views/tags/destroy.js.erb)
5.tags_controller.rb(:index, :create, :destroy)の用意イメージとしては、送信ボタンを押すとコントローラーのcreateアクションが実行
createアクション内でcreate.js.erbを呼び出すメソッドが実行
create.js.erb内に書かれているJavaScript(jQuery)が発火
DOM操作により、エラーメッセージの要素とタグ一覧の要素が上書き(jQueryの.html()
メソッド)
削除時のdestroyアクションも同様です。完成形の流れとしては
1.index画面でフォームに値を入力する。
1-1.存在しないタグなら新規保存(サクセスメッセージ)
1-2.存在するタグなら保存失敗(バリデーションエラーメッセージ)
1-3.空白なら保存失敗(バリデーションエラーメッセージ)2.個別のタグを削除する
1-1.サクセスメッセージを表示全てajaxのため、ページの全体の更新はありません。
1.tag.rbにバリデーションの設定
tag.rbclass Tag < ApplicationRecord ... # 空白の禁止と一意性(重複NG)を付与します。 validates :name, presence: true, uniqueness: true end2.部分テンプレートの用意(
layouts/_error_messages.html.erb
,layouts/_flash_messages.html.erb
,_tag.html.erb
)ここで作る3つの部分テンプレートを、アクションに応じてDOM操作で上書きしていく形です。
jQueryで書くと1行1行DOM操作をすることになって冗長になってしまうので、部分テンプレートを利用して後述するjQueryが<%= render ... %>
1行で済むようになっています。バリデーションエラーメッセージ用の部分テンプレート
すでにある方は流用してもらって大丈夫です。layouts/_error_messages.html.erb<% if model.errors.any? %> <div id="validation-errors"> <p><%= model.errors.count %>件のエラーがあります。</p> <ul class="error-messages"> <% model.errors.full_messages.each do |message| %> <li class="error-message"><%= message %></li> <% end %> </ul> </div> <% end %>jQueryのDOM操作によって上書きされた時にバリデーションエラーが発生していれば、エラーメッセージが表示されます。
ローカル変数のmodel
は後述するindex.html.erbで@tag
(Tag.new)に置き換えます。次にサクセスメッセージ(フラッシュメッセージ)用の部分テンプレート
こちらもすでにある方は流用してください。layouts/_flash_messages.html.erb<% flash.each do |key, value| %> <p class="alert alert-<%= key %>"> <%= value %> </p> <% end %>eachで配列から取得することになっていますが、これは複数のフラッシュメッセージを取得するというよりも、どのようなキー(
flash[キー]
)でもこの一文で再表示ができるようになっています。
今回使うキーは[:success]
,[:warning]
の2種類です。(ちなみにこの2種類はbootstrapでデザインも用意されているため、今回選択しました。)
この二つを使う場合は部分テンプレートを下記のように書き換えることもできます。layouts/_flash_messages.html.erb<% case flash.keys[0] %> <% when "success" %> <p class="alert alert-<%= flash.keys[0] %>"> <%= flash[:success] %> </p> <% when "warning" %> <p class="alert alert-<%= flash.keys[0] %>"> <%= flash[:warning] %> </p> <% end %>flashに格納されるキーは配列のため、
keys[0]
とする必要があります。
case文でkeyパターンに応じて表示するフラッシュメッセージを変化させます。
p要素のclassでkeys[0]
を使用している理由は、キーによって(今回の場合はsuccessかwarning)cssのデザインを変更するためです。しかしこれでは、キーの種類(キーの名前は必要に応じて任意でつけることができます。)が増えた時にその都度行を増やしていく必要があるため、冗長になってしまいます。
そこで今回はeach文を使用することで、その手間をなくした形です。
また部分テンプレート化することで、バリデーションエラーメッセージと同様に、他のviewでも使い回すことができます。次にタグ一覧を表示する部分テンプレート
tags/_tag.html.erb<div class="tags-index__tags--tag"> <%= tag.name %>(<%= tag.menu_tag_ids.count %>) <%= link_to 'X', tag_path(tag), method: :delete, remote: true %> </div>こちらも部分テンプレートを使わなければ後述するjQueryで4行分書く必要がり、jQueryでのDOM操作は極力シンプルに管理したいので部分テンプレート化しています。
ローカル変数のtag
はindex.html.erbでインスタンス変数@tags
(Tag.all)を指定することで、部分テンプレート内では個別のローカル変数tag
として自動で置き換わります。(index.html.erbのところで詳しく解説します。)
(<%= tag.menu_tag_ids.count %>)
はタグ名の横にそのタグが使われているメニューの数を表示しています。(メニューテーブル(割愛) 1:多 メニュータグテーブル(中間テーブル)(割愛) 多:1 タグテーブル(今回))
<%= link_to 'X', tag_path(tag), method: :delete, remote: true %>
はX
を押すとdestroyアクションが発動します。remote: true
とすることで、ajax通信であることを明示します。
"remote: true"の仕組み
通常のリクエスト(リンク)ではHTMLを取得しますが、remote: true
(html変換後はdata-remote="true"
属性)が付与されたリクエストの場合はJSファイルのみを取得します。
Railsの場合はlayouts/application.html.erbの<head>
タグ内にjsを読みこむタグがあるので、通常はHTMLを取得するとjsも自動で再読み込みがされます。なお、turbolinksを使っている場合はこの限りではありません。3.タグの一覧画面、兼新規作成フォームを作成(views/tags/index.html.erb)
タグの一覧画面とその中に新規入力のフォームも作成していきます。
views/tags/index.html.erb<div class="contents tags-index"> <div class="tags-index--messages"><!--メッセージの表示エリア--></div> <h2>タグ一覧</h2> <div class="tags-index__list" id="tags-index--tag-list"> <%= render @tags %> </div> <div class="tags-index__form"> <%= form_with model: @tag, url: tags_path(@tag) do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %> </div> </div>タグ一覧の下に新規追加フォームがくっついてる状態です。
新規登録フォームはform_withを利用します。
form_withはデフォルトがremote: true
属性を持っているので省略可能です。(form_forを使う場合はremote: true
属性を明示する必要があります。)
form_withでajaxを使わない場合はlocal: true
属性を明示する必要があります。
フォームヘルパーはremote: true
の場合はJSのみを、local: true
の場合はHTMLをアクションに伝えます。
また、フォームヘルパーはデフォルトのHTTPメソッドがPOSTでもあるのでmethod: :post
も省略可能です。
<%= render @tags %>
のインスタンス変数@tags
は後述するコントローラー側でTag.all
を取得しています。
見慣れない部分テンプレートの書き方ですが、この略記法の部分テンプレートは変数名に対応する_変数名.html.erb
が呼び出され、その部分テンプレート内ではローカル変数(今回でいう部分テンプレート内のtag
)として使えるようになります。
今回の@tags
ように配列で取得した場合は、部分テンプレート内ではその配列の数だけ、繰り返し表示がされます。
これは書き換えると以下のようになります。views/tags/index.html.erb<%= render partial: 'tag', locals: {tags: @tags} %>views/tags/_tag.html.erb<% tags.each do |tag| %> <div class="tags-index__tags--tag"> <%= tag.name %>(<%= tag.menu_tag_ids.count %>) <%= link_to 'X', tag_path(tag), method: :delete, remote: true %> </div> <% end %>今回の記述方法はこれらを略記した形です。
Railsガイド - レイアウトとレンダリング4.アクションごとのjs.erbを用意(views/tags/create.js.erb, views/tags/destroy.js.erb)
コントローラーから呼び出すjs(.erb)を作成します。
ここにjsを記述することで、コントローラーからcreate, destroyがそれぞれ呼び出された時に発火するjQueryでDOM操作が行われます。
ここで使われている.html()
はそこに要素が何もなければ追加となり、すでに別の要素があれば書き換えとなります。createアクション時のjs(.erb)
views/tags/create.js.erb<% if @tag.errors.any? %> $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/error_messages', locals: {model: @tag}) %></p>") <% else %> $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>") $("#tags-index--tag-list").html("<%= j(render @tags) %>") $("#tag_name").val('') <% end %>
<% if @tag.errors.any? %>
はsubimitボタンが押された時に@tag
にエラーが発生しているかどうかをチェックします。
"オブジェクト.errors.any?"と"オブジェクト.invalid?"の違い
今回の場合、コントローラー側で@tag.save
が試された後にcreate.js.erbを呼んでいるため@tag
にはすでにerrorが発生しているかどうかがわかる状態でjs.erbに渡って来ています。
他方、invalid?
メソッドは保存前のオブジェクトに対して手動でバリデーションチェックを行うものです。
通常はオブジェクトが保存されようとした時(.save
を試みた時)に自動でバリデーションチェックが実行され、今回は.save
の成否によってアクションを分けるため、改めて手動でinvalid?
をするよりは、.save
の時点ですでにエラーが発生しているかどうか(.errors.any?
)を確認した方がDRYに則っていると言えます。エラーが発生している場合(空白やタグ名の重複)
・バリデーションエラーメッセージ(error_messages.html.erb)を$(".tags-index--messages")
に追加または書き換えエラーが発生していない場合(正常に保存ができた)は
・フラッシュメッセージ(flash_message.html.erb)を$(".tags-index--messages")
に追加または書き換え
・タグ一覧($("#tags-index--tag-list")
)を書き換え
・フォームに入力されたタグ名をクリア($("#tag_name").val('')
)が、実行されます。
destroyアクション時のjs(.erb)
views/tags/destroy.js.erb$(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>") $("#tags-index--tag-list").html("<%= j(render @tags) %>")create時と同じです。
・タグが削除されたらフラッシュメッセージの追加または書き換え
・タグ一覧の書き換えが、実行されます。
"partial: "の必要性
renderを使ってオプションの追加(今回でいうlocals: {tag: @tag}
)が必要な場合はファイル名(パス)の前にpartial:
を明示する必要があります。
Railsガイド - レイアウトとレンダリング5.tags_controller.rb(:index, :create, :destroy)の用意
コントローラーに今回必要なアクションを記載していきます
tags_controller.rbclass TagsController < ApplicationController def index @tags = Tag.all @tag = Tag.new end def create @tags = Tag.all @tag = Tag.new(tag_params) respond_to do |format| if @tag.save format.js { flash.now[:success] = "保存しました。" } else format.js end end end def destroy @tags = Tag.all @tag = Tag.new Tag.find(params[:id]).destroy flash.now[:warning] = "削除しました。" end private def tag_params params.require(:tag).permit(:name) end end今回はcreateのform_with、destroyのlink_to、それぞれで
remote: true
(form_withの場合はデフォルトなので省略)を明示しているため、どちらもJSのみをリクエストしていることになります。tags_controller.rbdef create ... respond_to do |format| if @tag.save format.js { flash.now[:success] = "保存しました。" } else format.js end end end
respond_to do |format|
はリクエストの形式によって処理を分けるブロックパラメータです。
今回、タグの追加・削除についてはHTMLで要求が来る(local: true
)ことは想定していませんのでformat.js {処理}
のみを記載しています。
HTMLでの要求が来た時に処理を分けたい場合はformat.html {処理}
を書けば、リクエストの方式に応じで処理を分けることができます。
if @tag.save
が成功した場合はflash[:success]
キーに"保存しました。"
というメッセージを載せてcreate.js.erbに渡します。
失敗した場合はバリデーションエラーメッセージのみを表示するため、フラッシュメッセージは使いませんので、format.js
の後ろに処理は書かず、表示のみを行います。
前述しましたが、この時にバリデーションエラーが発生している@tag
をcreate.js.erbに渡して、そこでエラー処理(バリデーションエラーメッセージの表示)をしています。destroyアクションでは
flash[:warning]
キーに"削除しました。"
というメッセージを載せてdestroy.js.erbに渡します。
"format.js"を書く場合と書かない場合
通常、コントローラーにはviewファイルに対応する同名のアクション名を作ります。(def index end
ならindex.html.erb)
アクション名の中身で特にrender
やredirect_to
などのに指定がなければアクションの最後にrender :アクション名
が省略されて(暗示的に)実行されています。
(def index end
など、中身を書かなくてもviewがレンダリングされるのはこのためです。)
今回、createアクションでは、条件によってフラッシュメッセージを渡したいので明示的にformat.js
を記述し、その中で処理を分けていましたが、destroyアクションでは今のところ処理を分ける必要はないので、respond_to
やformat.js
、render :destroy
は記述していません。
render :destroy
に関してはすでに暗示的に記述されているため、書く必要がないということです。最初に書きましたが、暗示的にrender :アクション名
が発動するのはアクションの中で他にrender :別のアクション名
やredirect_to
がない場合に限るので、他方createアクションのように明示的にformat.jsを記述し実行された場合、アクションの最後に暗示的なrender :create
が実行されることはありません。(DoubleRenderingErrorにならない理由です。)
Railsガイド - Action Controller の概要
Pikawaka - resoond_to
Qiita - 【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説まとめ
質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました。
参考サイト
Railsガイド - レイアウトとレンダリング
Railsガイド - Action Controller の概要
Pikawaka - resoond_to
Qiita - 【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説
- 投稿日:2020-09-29T17:13:00+09:00
【Ruby on Rails】ajaxを使ってタグの追加と削除、(成功・エラー)メッセージの表示を行う。
環境
Ruby 2.5.7
Rails 5.2.4ライブラリ
jQuery 1.12.4
前提
今回はTagテーブルを使って解説していきます。
まずはajaxを使わなくてもタグの追加と削除ができることを事前にご確認ください。
エラーメッセージの日本語化やアソシエーションについては触れませんので必要な方はご自身でお願い致します。
turbolinksは無効にしています。(有効時の動作は確認していません。)
手順
タイトルにもある通り、次の手順でajaxを利用してタグの追加・削除・エラーメッセージの表示を実装していきます。
1.tag.rbにバリデーションの設定
2.部分テンプレートの用意(views/layouts/_error_messages.html.erb
,views/layouts/_flash_messages.html.erb
,views/tags/_tag.html.erb
)
3.タグの一覧画面、兼新規作成フォームを作成(views/tags/index.html.erb)
4.アクションごとのjs.erbを用意(views/tags/create.js.erb, views/tags/destroy.js.erb)
5.tags_controller.rb(:index, :create, :destroy)の用意イメージとしては、送信ボタンを押すとコントローラーのcreateアクションが実行
createアクション内でcreate.js.erbを呼び出すメソッドが実行
create.js.erb内に書かれているJavaScript(jQuery)が発火
DOM操作により、エラーメッセージの要素とタグ一覧の要素が上書き(jQueryの.html()
メソッド)
削除時のdestroyアクションも同様です。完成形の流れとしては
1.index画面でフォームに値を入力する。
1-1.存在しないタグなら新規保存(サクセスメッセージ)
1-2.存在するタグなら保存失敗(バリデーションエラーメッセージ)
1-3.空白なら保存失敗(バリデーションエラーメッセージ)2.個別のタグを削除する
1-1.サクセスメッセージを表示全てajaxのため、ページの全体の更新はありません。
1.tag.rbにバリデーションの設定
tag.rbclass Tag < ApplicationRecord ... # 空白の禁止と一意性(重複NG)を付与します。 validates :name, presence: true, uniqueness: true end2.部分テンプレートの用意(
layouts/_error_messages.html.erb
,layouts/_flash_messages.html.erb
,_tag.html.erb
)ここで作る3つの部分テンプレートを、アクションに応じてDOM操作で上書きしていく形です。
jQueryで書くと1行1行DOM操作をすることになって冗長になってしまうので、部分テンプレートを利用して後述するjQueryが<%= render ... %>
1行で済むようになっています。バリデーションエラーメッセージ用の部分テンプレート
すでにある方は流用してもらって大丈夫です。layouts/_error_messages.html.erb<% if model.errors.any? %> <div id="validation-errors"> <p><%= model.errors.count %>件のエラーがあります。</p> <ul class="error-messages"> <% model.errors.full_messages.each do |message| %> <li class="error-message"><%= message %></li> <% end %> </ul> </div> <% end %>jQueryのDOM操作によって上書きされた時にバリデーションエラーが発生していれば、エラーメッセージが表示されます。
ローカル変数のmodel
は後述するindex.html.erbで@tag
(Tag.new)に置き換えます。次にサクセスメッセージ(フラッシュメッセージ)用の部分テンプレート
こちらもすでにある方は流用してください。layouts/_flash_messages.html.erb<% flash.each do |key, value| %> <p class="alert alert-<%= key %>"> <%= value %> </p> <% end %>eachで配列から取得することになっていますが、これは複数のフラッシュメッセージを取得するというよりも、どのようなキー(
flash[キー]
)でもこの一文で再表示ができるようになっています。
今回使うキーは[:success]
,[:warning]
の2種類です。(ちなみにこの2種類はbootstrapでデザインも用意されているため、今回選択しました。)
この二つを使う場合は部分テンプレートを下記のように書き換えることもできます。layouts/_flash_messages.html.erb<% case flash.keys[0] %> <% when "success" %> <p class="alert alert-<%= flash.keys[0] %>"> <%= flash[:success] %> </p> <% when "warning" %> <p class="alert alert-<%= flash.keys[0] %>"> <%= flash[:warning] %> </p> <% end %>flashに格納されるキーは配列のため、
keys[0]
とする必要があります。
case文でkeyパターンに応じて表示するフラッシュメッセージを変化させます。
p要素のclassでkeys[0]
を使用している理由は、キーによって(今回の場合はsuccessかwarning)cssのデザインを変更するためです。しかしこれでは、キーの種類(キーの名前は必要に応じて任意でつけることができます。)が増えた時にその都度行を増やしていく必要があるため、冗長になってしまいます。
そこで今回はeach文を使用することで、その手間をなくした形です。
また部分テンプレート化することで、バリデーションエラーメッセージと同様に、他のviewでも使い回すことができます。次にタグ一覧を表示する部分テンプレート
tags/_tag.html.erb<div class="tags-index__tags--tag"> <%= tag.name %>(<%= tag.menu_tag_ids.count %>) <%= link_to 'X', tag_path(tag), method: :delete, remote: true %> </div>こちらも部分テンプレートを使わなければ後述するjQueryで4行分書く必要がり、jQueryでのDOM操作は極力シンプルに管理したいので部分テンプレート化しています。
ローカル変数のtag
はindex.html.erbでインスタンス変数@tags
(Tag.all)を指定することで、部分テンプレート内では個別のローカル変数tag
として自動で置き換わります。(index.html.erbのところで詳しく解説します。)
(<%= tag.menu_tag_ids.count %>)
はタグ名の横にそのタグが使われているメニューの数を表示しています。(メニューテーブル(割愛) 1:多 メニュータグテーブル(中間テーブル)(割愛) 多:1 タグテーブル(今回))
<%= link_to 'X', tag_path(tag), method: :delete, remote: true %>
はX
を押すとdestroyアクションが発動します。remote: true
とすることで、ajax通信であることを明示します。
"remote: true"の仕組み
通常のリクエスト(リンク)ではHTMLを取得しますが、remote: true
(html変換後はdata-remote="true"
属性)が付与されたリクエストの場合はJSファイルのみを取得します。
Railsの場合はlayouts/application.html.erbの<head>
タグ内にjsを読みこむタグがあるので、通常はHTMLを取得するとjsも自動で再読み込みがされます。なお、turbolinksを使っている場合はこの限りではありません。3.タグの一覧画面、兼新規作成フォームを作成(views/tags/index.html.erb)
タグの一覧画面とその中に新規入力のフォームも作成していきます。
views/tags/index.html.erb<div class="contents tags-index"> <div class="tags-index--messages"><!--メッセージの表示エリア--></div> <h2>タグ一覧</h2> <div class="tags-index__list" id="tags-index--tag-list"> <%= render @tags %> </div> <div class="tags-index__form"> <%= form_with model: @tag, url: tags_path(@tag) do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %> </div> </div>タグ一覧の下に新規追加フォームがくっついてる状態です。
新規登録フォームはform_withを利用します。
form_withはデフォルトがremote: true
属性を持っているので省略可能です。(form_forを使う場合はremote: true
属性を明示する必要があります。)
form_withでajaxを使わない場合はlocal: true
属性を明示する必要があります。
フォームヘルパーはremote: true
の場合はJSのみを、local: true
の場合はHTMLをアクションに伝えます。
また、フォームヘルパーはデフォルトのHTTPメソッドがPOSTでもあるのでmethod: :post
も省略可能です。
<%= render @tags %>
のインスタンス変数@tags
は後述するコントローラー側でTag.all
を取得しています。
見慣れない部分テンプレートの書き方ですが、この略記法の部分テンプレートは変数名に対応する_変数名.html.erb
が呼び出され、その部分テンプレート内ではローカル変数(今回でいう部分テンプレート内のtag
)として使えるようになります。
今回の@tags
ように配列で取得した場合は、部分テンプレート内ではその配列の数だけ、繰り返し表示がされます。
これは書き換えると以下のようになります。views/tags/index.html.erb<%= render partial: 'tag', locals: {tags: @tags} %>views/tags/_tag.html.erb<% tags.each do |tag| %> <div class="tags-index__tags--tag"> <%= tag.name %>(<%= tag.menu_tag_ids.count %>) <%= link_to 'X', tag_path(tag), method: :delete, remote: true %> </div> <% end %>今回の記述方法はこれらを略記した形です。
Railsガイド - レイアウトとレンダリング4.アクションごとのjs.erbを用意(views/tags/create.js.erb, views/tags/destroy.js.erb)
コントローラーから呼び出すjs(.erb)を作成します。
ここにjsを記述することで、コントローラーからcreate, destroyがそれぞれ呼び出された時に発火するjQueryでDOM操作が行われます。
ここで使われている.html()
はそこに要素が何もなければ追加となり、すでに別の要素があれば書き換えとなります。createアクション時のjs(.erb)
views/tags/create.js.erb<% if @tag.errors.any? %> $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/error_messages', locals: {model: @tag}) %></p>") <% else %> $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>") $("#tags-index--tag-list").html("<%= j(render @tags) %>") $("#tag_name").val('') <% end %>
<% if @tag.errors.any? %>
はsubimitボタンが押された時に@tag
にエラーが発生しているかどうかをチェックします。
"オブジェクト.errors.any?"と"オブジェクト.invalid?"の違い
今回の場合、コントローラー側で@tag.save
が試された後にcreate.js.erbを呼んでいるため@tag
にはすでにerrorが発生しているかどうかがわかる状態でjs.erbに渡って来ています。
他方、invalid?
メソッドは保存前のオブジェクトに対して手動でバリデーションチェックを行うものです。
通常はオブジェクトが保存されようとした時(.save
を試みた時)に自動でバリデーションチェックが実行され、今回は.save
の成否によってアクションを分けるため、改めて手動でinvalid?
をするよりは、.save
の時点ですでにエラーが発生しているかどうか(.errors.any?
)を確認した方がDRYに則っていると言えます。エラーが発生している場合(空白やタグ名の重複)
・バリデーションエラーメッセージ(error_messages.html.erb)を$(".tags-index--messages")
に追加または書き換えエラーが発生していない場合(正常に保存ができた)は
・フラッシュメッセージ(flash_message.html.erb)を$(".tags-index--messages")
に追加または書き換え
・タグ一覧($("#tags-index--tag-list")
)を書き換え
・フォームに入力されたタグ名をクリア($("#tag_name").val('')
)が、実行されます。
destroyアクション時のjs(.erb)
views/tags/destroy.js.erb$(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>") $("#tags-index--tag-list").html("<%= j(render @tags) %>")create時と同じです。
・タグが削除されたらフラッシュメッセージの追加または書き換え
・タグ一覧の書き換えが、実行されます。
"partial: "の必要性
renderを使ってオプションの追加(今回でいうlocals: {tag: @tag}
)が必要な場合はファイル名(パス)の前にpartial:
を明示する必要があります。
Railsガイド - レイアウトとレンダリング5.tags_controller.rb(:index, :create, :destroy)の用意
コントローラーに今回必要なアクションを記載していきます
tags_controller.rbclass TagsController < ApplicationController def index @tags = Tag.all @tag = Tag.new end def create @tags = Tag.all @tag = Tag.new(tag_params) respond_to do |format| if @tag.save format.js { flash.now[:success] = "保存しました。" } else format.js end end end def destroy @tags = Tag.all @tag = Tag.new Tag.find(params[:id]).destroy flash.now[:warning] = "削除しました。" end private def tag_params params.require(:tag).permit(:name) end end今回はcreateのform_with、destroyのlink_to、それぞれで
remote: true
(form_withの場合はデフォルトなので省略)を明示しているため、どちらもJSのみをリクエストしていることになります。tags_controller.rbdef create ... respond_to do |format| if @tag.save format.js { flash.now[:success] = "保存しました。" } else format.js end end end
respond_to do |format|
はリクエストの形式によって処理を分けるブロックパラメータです。
今回、タグの追加・削除についてはHTMLで要求が来る(local: true
)ことは想定していませんのでformat.js {処理}
のみを記載しています。
HTMLでの要求が来た時に処理を分けたい場合はformat.html {処理}
を書けば、リクエストの方式に応じで処理を分けることができます。
if @tag.save
が成功した場合はflash[:success]
キーに"保存しました。"
というメッセージを載せてcreate.js.erbに渡します。
失敗した場合はバリデーションエラーメッセージのみを表示するため、フラッシュメッセージは使いませんので、format.js
の後ろに処理は書かず、表示のみを行います。
前述しましたが、この時にバリデーションエラーが発生している@tag
をcreate.js.erbに渡して、そこでエラー処理(バリデーションエラーメッセージの表示)をしています。destroyアクションでは
flash[:warning]
キーに"削除しました。"
というメッセージを載せてdestroy.js.erbに渡します。
"format.js"を書く場合と書かない場合
通常、コントローラーにはviewファイルに対応する同名のアクション名を作ります。(def index end
ならindex.html.erb)
アクション名の中身で特にrender
やredirect_to
などのに指定がなければアクションの最後にrender :アクション名
が省略されて(暗示的に)実行されています。
(def index end
など、中身を書かなくてもviewがレンダリングされるのはこのためです。)
今回、createアクションでは、条件によってフラッシュメッセージを渡したいので明示的にformat.js
を記述し、その中で処理を分けていましたが、destroyアクションでは今のところ処理を分ける必要はないので、respond_to
やformat.js
、render :destroy
は記述していません。
render :destroy
に関してはすでに暗示的に記述されているため、書く必要がないということです。最初に書きましたが、暗示的にrender :アクション名
が発動するのはアクションの中で他にrender :別のアクション名
やredirect_to
がない場合に限るので、他方createアクションのように明示的にformat.jsを記述し実行された場合、アクションの最後に暗示的なrender :create
が実行されることはありません。(DoubleRenderingErrorにならない理由です。)
Railsガイド - Action Controller の概要
Pikawaka - resoond_to
Qiita - 【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説まとめ
質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました。
参考サイト
Railsガイド - レイアウトとレンダリング
Railsガイド - Action Controller の概要
Pikawaka - resoond_to
Qiita - 【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説
- 投稿日:2020-09-29T15:41:13+09:00
Rack::Timeout でパスごとにタイムアウトを指定する
rack-timeout を使用しておりパスごとにタイムアウトの指定を変更するため以前(rails 5.2.3、 rack-timeout 0.5.1 まで)は以下のように指定していました(多少簡略化してあります)。
Gemfilegem 'rack-timeout', require: 'rack/timeout/base'config/initializers/rack_timeout.rbmodule Rack class DynamicTimeout < Rack::Timeout def call(env) # URL が /admin から始まる場合は 30 秒、それ以外は 10 秒となるように @service_timeout = env['REQUEST_URI'].start_with?('/admin') ? 30 : 10 super(env) end end end timeout_params = { service_timeout: 10, wait_timeout: 5.minutes.to_i, wait_overtime: 5.minutes.to_i, service_past_wait: false, } Rails.application.config.middleware.insert_before Rack::Runtime, Rack::DynamicTimeout, timeout_paramsところが Rails 6、あるいは rack-timeout 0.6.0 からタイムアウトが動的に指定した
service_timeout
の値よりも短くなっていました。
以下のようにログに出力されている値(30秒)よりも実際は短かった(10秒)です。severity:ERROR message:source=rack-timeout id=0284947e-c24c-4024-a9ba-c542b3eb58f9 timeout=30000ms service=10000ms state=timed_out色々ログを仕込んだりして原因を調べたところ、どうやら
Rack::DynamicTimeout
のcall
とは別にRack::Timeout
自体のcall
が呼ばれているようでした。都合2回
call
メソッドが実行されて最初はRack::DynamicTimeout
で次がRack::Timeout
になっており、最終的にタイムアウトの値はRack::Timeout
で指定された値が使われるという状態です。
どこで呼ばれているか、それを変更できるかというところまで調べていくとちょっと時間がかかりそうだったので、今回はRack::Timeout
を上書きするパッチで対応することにしました。Gemfilegem 'rack-timeout'config/initializers/rack_timeout.rbmodule Rack module DynamicTimeout def call(env) # URL が /admin から始まる場合は 30 秒、それ以外は 10 秒となるように @service_timeout = env['REQUEST_URI'].start_with?('/admin') ? 30 : 10 super(env) end end end Rack::Timeout.prepend Rack::DynamicTimeout
Module#prepend
でモジュールを差し込んでRack::Timeout
のcall
が呼ばれても対応できるようにしました。
ひとまずこれで。
- 投稿日:2020-09-29T15:34:26+09:00
Rubyにおける日時の扱い。DateとTimeの使い分け。
はじめに
Rubyにおける日付や時刻の操作にはDateクラス、Timeクラスを使います。
Dateクラスは年月日だけを扱い、Timeクラスは年月日時分秒を扱います。
Timeクラスだけ使っておけばいいような気がしますが、Dateクラスにも得意分野があるので、その使い分けについて解説していきます。目次
1.Dateは年月日、Timeは年月日時分秒
2.Dateクラスは日付処理が得意
3.結論1.Dateは年月日、Timeは年月日時分秒
まず、時分秒まで表示したい、つまり、日時を扱いたい場合はTimeを使います。DateクラスのサブクラスであるDateTimeクラスも使えますが、require 'date'しないといけないことや、サマータイム、うるう秒を扱えないことなどを考慮すると、Timeクラスを使っておくのが無難だと思われます。
2.Dateクラスは日付処理が得意
日付や年単位での処理はDateクラスを使うと便利です。
[1]pry(main)> Date.today.next_month # 来月の同日 => #<Date: 2020-10-29 ((2459152j,0s,0n),+0s,2299161j)> [2] pry(main)> Date.new(2020,10,-1) # 月末日(2020年10月) => #<Date: 2020-10-31 ((2459154j,0s,0n),+0s,2299161j)> [3] pry(main)> Date.today.leap? # うるう年の判定(今年=2020年) => true [4] pry(main)> Date.today.next_year.leap? # うるう年の判定(来年=2021年) => false [5] pry(main)> Date.today + 3 # 3日後 => #<Date: 2020-10-02 ((2459125j,0s,0n),+0s,2299161j)> [6] pry(main)> Date.today - 7 # 1週間前(7日前) => #<Date: 2020-09-22 ((2459115j,0s,0n),+0s,2299161j)> [7] pry(main)> Date.today >> 6 # 半年後(6ヶ月後) => #<Date: 2021-03-29 ((2459303j,0s,0n),+0s,2299161j)> [8] pry(main)> Date.today << 1 # 1ヶ月前 => #<Date: 2020-08-29 ((2459091j,0s,0n),+0s,2299161j)>来月の同日や月末日の取得、うるう年の判定などを簡単にできるのは便利です。
また、日付のインクリメント、デクリメント操作はTimeクラスでは、秒単位で扱わなければならないため少々面倒くさいです。その点、Dateクラスでは日単位は、+ と - ,月単位は << と >> で楽にできる。3.結論
時刻まで表示したいときなど、基本的にはTimeクラスを使えばいいが、日、月、年単位での処理を主に行うときはDateクラスを使うと便利。
- 投稿日:2020-09-29T14:57:37+09:00
DevContainer で Ruby 開発する際に厄介な BUNDLE_APP_CONFIG
TL;DR
- Docker 公式の ruby イメージでセットされている環境変数
BUNDLE_APP_CONFIG
は DevContainer 内での開発において邪魔になることがあるBUNDLE_APP_CONFIG
の影響を無くしたい場合はdevcontainer.json
に以下を記述すると良い.devcontainer/devcontainer.json{ // ... "remoteEnv": { "BUNDLE_APP_CONFIG": null }, // ... }厄介な BUNDLE_APP_CONFIG
VSCode Remote - Containers での Ruby 開発では大抵の場合、Docker 公式の ruby イメージ をベースイメージにして DevContainer を作ることになる1かと思いますが、このイメージには厄介な問題があります。
それが環境変数
BUNDLE_APP_CONFIG
です。
Dockerfile 内でENV
命令によりデフォルトで/usr/local/bundle
に設定されています。
BUNDLE_APP_CONFIG
は bundler のアプリケーションローカルな設定 (bundle config set --local
した際に作られる設定) をどこに保存するかを指定するための環境変数です。
この環境変数が設定されていない場合、bundler はアプリケーションルートの.bundle/
以下に設定を作成します。さて、話を移して VSCode の DevContainer で Ruby 開発をする場合、インストールした gem を永続化しつつコンテナにマウントするには以下のようにワークスペース内の
vendor
ディレクトリ等に突っ込んでおきたいところです。2$ bundle config set --local path vendor/bundleこの設定により path 設定が記述された設定ファイルが通常は
.bundle/config
として保存されるはずです。ローカル設定にしたことでワークスペース内に設定ファイルが作られ、コンテナを破棄しても再設定は不要となります。
しかし、前述のように Docker 公式の ruby イメージではBUNDLE_APP_CONFIG
が設定されてしまっているため、このローカル設定は/usr/local/bundle/config
に保存されてしまいます。
bundle config
で確認してみると以下のような感じです。$ bundle config Settings are listed in order of priority. The top value will be used. path Set for your local app (/usr/local/bundle/config): "vendor/bundle" app_config Set via BUNDLE_APP_CONFIG: "/usr/local/bundle" silence_root_warning Set via BUNDLE_SILENCE_ROOT_WARNING: trueこれでは Rebuild などによりコンテナを破棄した際に設定が失われてしまうため、都度
bundle config ...
を実行するか、/usr/local/bundle
をホストからマウントするようにするなどの工夫が必要になってしまいます。スマートに解決するには
BUNDLE_APP_CONFIG
自体を何とかした方が良いでしょう。ちなみに公式イメージで
BUNDLE_APP_CONFIG
が設定されているのは上記の逆の考え方で、「ホスト側の.bundle/config
がコンテナ内に影響を与えないようにするため」のようです3。対策
×
BUNDLE_APP_CONFIG
に空文字を設定する公式ドキュメント に記載されている方法です。
The environment variables we set are canonically listed in the above-linked Dockerfiles, but some of them include GEM_HOME, BUNDLE_PATH, BUNDLE_BIN, BUNDLE_SILENCE_ROOT_WARNING, and BUNDLE_APP_CONFIG.
If these cause issues for your use case (running multiple Ruby applications in a single container, for example), setting them to the empty string should be sufficient for undoing their behavior.
結論から言うとこの方法ではうまくいきません。
bundler はBUNDLE_APP_CONFIG
の有無だけを見ている4ので、空文字が設定されている場合はカレントディレクトリ直下にconfig
ファイルが作成されます。実験してみましょう。
DevCotainer 用の Dockerfile でBUNDLE_APP_CONFIG
に空文字をセットします。devcontainer/DockerfileFROM ruby:2.7 # 空文字を設定 ENV BUNDLE_APP_CONFIG= # 以下略DevContaier をビルドして中に入ってみます。
ターミナル上ではなぜかBUNDLE_APP_CONFIG
が消えて問題なく動作します (理由は不明ですが VSCode の実装の問題かもしれません)。$ bundle config Settings are listed in order of priority. The top value will be used. silence_root_warning Set via BUNDLE_SILENCE_ROOT_WARNING: true $ bundle config set --local path vendor/bundle $ bundle config Settings are listed in order of priority. The top value will be used. path Set for your local app (/workspace/sample_app/.bundle/config): "vendor/bundle" silence_root_warning Set via BUNDLE_SILENCE_ROOT_WARNING: true問題となるのはデバッグ時です。
デバッガ (rdebug-ide
) を Gemfile で管理し、bundle install
でインストールしているケースを想定します。
以下のようなlaunch.json
になるはずです。.vscode/launch.json{ "version": "0.2.0", "configurations": [ { "name": "Debug Local File", "type": "Ruby", "request": "launch", "program": "${workspaceFolder}/main.rb", "useBundler": true } ] }デバッグを実行すると DEBUG CONSOLE に以下のように必要な gem が見つからない旨のエラーが出てしまいます。
bundler: failed to load command: rdebug-ide (/usr/local/bundle/bin/rdebug-ide) Bundler::GemNotFound: Could not find concurrent-ruby-1.1.7 in any of the sources /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize' /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!' /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize' /usr/local/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs' /usr/local/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for' /usr/local/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs' /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method' /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup' /usr/local/lib/ruby/2.7.0/bundler.rb:149:in `setup' /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>' /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level' /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence' /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'これは、デバッグ用のサブプロセスでは
BUNDLE_APP_CONFIG=""
が効いているために.bundle/config
が参照されずpath
設定が読み込まれないためです。△
BUNDLE_APP_CONFIG
に.bundle
のパスを設定してしまう (Dockerfile)例えば以下のように Dockerfile などでローカル設定用の
.bundle
の絶対パスを指定してしまう方法です。devcontainer/DockerfileENV BUNDLE_APP_CONFIG=/workspace/.bundleこの方法であれば大体うまくいきますが、モノレポに複数のプロジェクトを突っ込んでいるケース (
Gemfile
が複数あるケース) には対応できません。△
BUNDLE_APP_CONFIG
に.bundle
のパスを設定してしまう (VSCode 設定)例えば上記のデバッグがうまくいかない問題は以下のように解決できます。
.vscode/launch.json{ "version": "0.2.0", "configurations": [ { "name": "Debug Local File", "type": "Ruby", "request": "launch", "program": "${workspaceFolder}/main.rb", "useBundler": true, "env": { "BUNDLE_APP_CONFIG": "${workspaceFolder}/.bundle" } } ] }この方法であれば Multi-root Workspace にすることでモノレポのケースにも対応することができます。
ただしこの設定だけではデバッグ用のサブプロセスにしか有効にならないので、必要に応じてsettings.json
のターミナル設定やtasks.json
にも同様の設定を記述する必要があります。
また、何らかの理由でBUNDLE_APP_CONFIG
を設定して使っている人とlaunch.json
等の設定を共有することが難しくなります。〇 devcontainer.json の設定で
BUNDLE_APP_CONFIG
を削除する下記のような設定を
devcontainer.json
に追加することで、環境変数を空にするのでも上書きするのでもなく削除 (unset) することができます。.devcontainer/devcontainer.json{ // ... "remoteEnv": { "BUNDLE_APP_CONFIG": null }, // ... }この方法であればコンテナ内の VSCode プロセス及びそのサブプロセス全てで
BUNDLE_APP_CONFIG
が削除されます。
個人的にはこの方法が最も副作用が少なそうなのでおすすめです。
- 投稿日:2020-09-29T14:46:02+09:00
ルーティングのネストによって起きていること
動作環境
Ruby 2.6.5
Rails 6.0.3.2ルーティングのネストによって何が起きているのかがわかりづらかったので、自分の確認を兼ねて投稿してみました。
ルーティングのネストをした場合
routes.rbRails.application.routes.draw do resources :hoges do resources :fugas, only: [:index] end end上記のコードでは、hogesに対してfugasがネストされており、ターミナル上でrails routesを実行すると、以下のようになります。
Prefix ➡︎ hoge_fugas
Verb ➡︎ GET
URI Pattern ➡︎ /hoges/:hoge_id/fugas(.:format)
Controller#Action ➡︎ fugas#indexもちろん、上記以外も表示されますが今回はネストの箇所のみを扱います。
これだけだといまいちわかりづらいので、ルーティングのネストをしていない場合と比較していきます。ルーティングのネストをしない場合
routes.rbRails.application.routes.draw do resources :hoges resources :fugas, only: [:index] end上記のコードでは先ほどのコードと違い、ネストされていません。では先ほどと同様にターミナル上でrails routesを実行して、同じ箇所を確認してみましょう。
Prefix ➡︎ fugas
Verb ➡︎ GET
URI Pattern ➡︎ /fugas(.:format)
Controller#Action ➡︎ fugas#index先ほどと比べてPrefixとURI Patternが異なっていることがわかります。
PrefixはURI Patternを簡単に書いたものなので、URI Patternのみに注目していきます。ルーティングのネストをした場合としない場合の違い
ネストした場合
URI Pattern ➡︎ /hoges/:hoge_id/fugas(.:format)
ネストしない場合
URI Pattern ➡︎ /fugas(.:format)つまり、ルーティングのネストとはURI Patternを変更するために行っているものと言えると思います。しかし、この言い方だとわかりづらいと思うので、もう少し詳しく説明します。
ネストすることによってURI Patternが変更したためにfugasコントローラーのindexアクションを作動するには、hoge_idを指定する必要が出てきます。
そのため、paramsの中にhoge_idを入れることができるようになります。
つまり、先ほどはルーティングのネストをURI Patternを変更するためのものと言いましたが、paramsの中にネストする側(今回はhoge)のid情報を入れるためのものと言い換えることができます。paramsにネストする側(今回はhoge)のid情報を入れることにより、ネストされた側(今回はfuga)のコントローラー内でネストする側の情報を扱うことができるということになります。
- 投稿日:2020-09-29T13:29:20+09:00
Rails6でHTMLとCSSのみを使ってシンプルにフォームをつくるメモ
はじめに
Railsで bootstrapなどのCSSフレームワークを使わない、素のコードが意外とみつかないので、そのメモ
前提
Receptionモデルがあり、カラムは名前と目的。
目標
このようなシンプルなフォームをHTMLとCSSのみをつかってシンプルに書く
HTML
- フィールドはtext_field,select_field,submitの3つ
html<%= form_with model: @reception do |f| %> <ul> <li> <%= f.label :name, "名前" %> </li> <li> <%= f.text_field :name, placeholder: "名前を入力してください" %> </li> <li> <%= f.label :purpose, "ご用件を選択してください" %> </li> <li> <%= f.select :purpose, ( [ ["面接のため", '面接'], ["面談のため", '面談'], ["打ち合わせのため", '打ち合わせ'], ["その他", 'その他'] ] ), {}, size: "4" %> </li> <li> <%= f.submit '送信する' %> </li> </ul> <% end %>CSS
- Rubyのform_witth メソッドによって、formタグが生成される
- ul,li のリスト表示にする
- selectタグをsize: "4"の第4引数に指定することで選択肢から4つ表示して状態に設定できる
cssform { width: 60%; margin: auto; padding-top: 1%; } ul { list-style: none; padding-left: 0; width: 100%; text-align: center; } label { font-size: 25px; } input[type=text], select { text-align: center; width: 55%; font-size: 20px; border: 1px solid #CCC; border-radius: 1rem; margin-bottom: 20px; padding: 1%; } input[type=submit] { text-align: center; width: 55%; font-size: 20px; border: 1px solid #CCC; border-radius: 1rem; margin-bottom: 20px; padding: 1%; }まとめ
もっときれいに書く方法があると思うので、参考程度におねがいします。
追記
誤字を修正
- 投稿日:2020-09-29T12:06:21+09:00
Homebrew, rbenvの組み合わせでRuby 3.0.0 Preview 1 をインストールする
背景
Ruby 3.0.0 Preview 1 がリリースされました。
https://www.ruby-lang.org/en/news/2020/09/25/ruby-3-0-0-preview1-released/インストールしてみる
最初にやったこと
以下を参考
https://github.com/rbenv/ruby-build#upgradingbrew update && brew upgrade ruby-build
インストール可能なRubyを確認
$ rbenv install --list 2.5.8 2.6.6 2.7.1 jruby-9.2.13.0 maglev-1.0.0 mruby-2.1.2 rbx-5.0 truffleruby-20.2.0 truffleruby+graalvm-20.2.0 Only latest stable releases for each Ruby implementation are shown. Use 'rbenv install --list-all' to show all local versions.stableでlatestなものしか出ていないので
--list-all
で確認$ rbenv install --list-all | grep 3.0.0-preview1 3.0.0-preview1あった。
Ruby 3.0.0 Preview 1 インストール
$ rbenv install 3.0.0-preview1インストール確認
$ rbenv versions system 2.6.1 2.7.0 * 2.7.1 (set by /Users/soul/.rbenv/version) 3.0.0-preview1rbenvで使用するRubyバージョン指定
# システム全体で 3.0.0-preview1 を使用する rbenv global 3.0.0-preview1 # バージョン確認 ruby -v ruby 3.0.0preview1 (2020-09-25 master 0096d2b895) [x86_64-darwin19]確認
irb(main):001:0> RUBY_VERSION #=> "3.0.0" require 'prime' #=> true # Ruby3で導入される右代入 Prime.find {|p| p > 10000} => x #=> 10007 x #=> 10007Ruby3で追加される右代入記法が使えるようになっている。
- 投稿日:2020-09-29T11:37:31+09:00
Rails+JavaScript(jQuery)でタイムレスな検索を
記事で扱う検索機能
こんな感じの検索機能です。
今回は、shopsというテーブルからnameというカラムを抽出してビューに表示していきます。
※店舗情報を複数登録済み。そこから任意の店舗を任意のカラムで検索します。
- 非同期で行う
- 文字を打つごとに検索を行い、描画する
- 検索結果をクリックで該当のページにリンクする
おそらくですが、初学者のプログラミングにおける知識レベルや常識レベルって経験者の方が考えるそれよりもずっと低い水準にあると思うんです。
なので、自身にできる範囲内で「そんな事まで説明する必要あるの?」という事も書いて行こうかと思います。開発環境
mac
Ruby 2.5.1
Rails 5.2.3
jQuery必要なファイル
まず、実装に必要となるファイルを以下にまとめます。
- 検索を行うアクションを定義したコントローラーの準備
app/controllers/shops_controller.rbdef search end
- 上記アクションに対応するjson.jbuilderの準備
views/shops/search.json.jbuilder#上記で作成したコントローラー及びアクションに対応するようデイレクトリ関係とファイル名に注意 #この場合、views/shops(コントローラー名に対応)/search(アクション名に対応).json.jbuilder
- 上記アクションに対するルーティングの準備
config/route.rbresources :shops do #shopsコントローラー内の collection do get 'search' #searchアクション end end
- 検索フォームと結果のを表示するビューファイルの準備
app/views/shops/index.html.haml#今回は、shops一覧画面に検索を表示します。 #hamlを使って書いていきますが、通常のhtmlでもslimでも構いません。
- jsファイルの準備
app/assets/javascripts/search.js#.jsで終わっていれば名前はなんでも構いません
以上、5つのファイルを使って検索機能を実装していきます。
どう動くの?
検索処理の流れは以下の通りです。
検索フォームで起こったイベントを.jsファイルで受け取る。
○○.html.haml(○○.html.erb) → ○○.js
受け取った情報をjson形式でコントローラへ流す。
○○.js → ○○_controller
コントローラーでDBから必要な情報を抽出し、その情報を.js内でも使えるようjbuilderで変換。
○○_controller → ○○.json.jbuilder
再度js側に送り、htmlへの描画を行う。
○○.json.jbuilder → ○○.js → ○○.html.haml
処理の流れが分かっていないと、エラーが起きた際に対処できず1時間も2時間も無駄にすることになってしまいます。
これから記載していく実際のコードには可能な限り説明を入れますので、ぜひ上記の処理の流れを頭に入れた状態で読み進めてみて下さい。では、始めていきましょう。
ルーティングを行う
今回は例として、shops_controller内にsearchアクションを設定します。
app/controllers/shops_controller.rbdef search endconfig/route.rbresources :shops do #shopsコントローラー内の collection do get 'search' #searchアクション end end検索フォームと結果表示欄の準備
app/views/shops/index.html.haml.search-field .fas.fa-search #虫眼鏡のfontawesomeを使用しています。 .shop_search = f.text_field :text, placeholder: "店舗・住所で検索", id: "shop_search" #テキスト入力欄を設置。 #shop_search--result ←コメントアウトではなくshop_search--resultというid名です。検索結果を表示。 .shop-search-list検索フォームに入力された情報を受け取り、受け取った情報をコントローラへ送る
app/assets/javascripts/search.js$("#shop_search").on("keyup", function(){ let input = $("#shop_search").val(); $.ajax({ type: 'GET', url: '/shops/search', data: {keyword: input}, dataType: 'json' })まず、検索フォーム(hamlファイル内のf.text_field)に入力された情報をjsファイルで受け取ります。
┗検索フォームにつけたid名を指定し、文字入力の際にキーボードから指を話した瞬間にkeyupで情報を取得。ajax以下でその情報を
・どのような方法で
・どこに
・何を
・どのような状態で
送るかを指定します。この場合、GETメソッドでshopsコントーラ内のsearchアクションにinputをjson方式で送るということになるかと思います。
コントローラーでアクションの設定、DBとのやりとりを記載
app/controllers/shops_controller.rbdef search return nil if params[:keyword] == "" @shops = Shop.where('name LIKE ? OR location LIKE ?', "%#{params[:keyword]}%", "%#{params[:keyword]}%").limit(10) respond_to do |format| format.html format.json end end今回私は、shopsテーブル作成の際にname(店舗名)カラムとlocation(所在地)カラムを設定していますので、この2情報で検索できるように設定していますが、
例えば店舗名のみで検索する場合は、app/controllers/shops_controller.rbdef search return nil if params[:keyword] == "" @shops = Shop.where('name LIKE ?, "%#{params[:keyword]}%").limit(10) respond_to do |format| format.html format.json end endでOKです。
アクション内のparams[:keyword]は先のjsファイル内ajax以下のdata:{keyword: input}から来ています。
分かりづらいかもしれませんが、検索欄に入力した文字をjsファイル内ではinputとして扱い、コントローラファイル内ではkeywordとして扱っているということです。
検索欄に何も入力されていない場合(2行目"")はnilを返しています。また、3行目ではshopsテーブルではそのkeyword(検索欄に入力された文字)を基に、DBから情報を引っ張り出します。
今回はparams[:keyword]の量端を%で囲って部分一致検索を行う仕様です。
他の検索方法については、この記事を参考にしてみて下さい。・Rails - LIKE句を使った文字のあいまい検索(特定の文字を含む語句を曖昧検索したい場合)
私もこの記事を参考にさせていただきました。ありがとうございます。そして、今回入力された文字データはjson形式でコントローラへ入って来ている為、jbuilderへと移ります。
json.jbuilderで情報変換
search.json.jbuilderjson.array! @shops do |shop| json.name shop.name json.location shop.location endコントローラー内でDBから情報を抽出しましたがこのデータをjson形式に変換する必要があります。
それをjbuilderで行うのです。htmlファイルへの検索結果描画
まず先ほどのjsファイルajax以下にコントローラーでの処理が終わり、戻ってきた際の処理を追記します。
app/assets/javascripts/search.js$("#shop_search").on("keyup", function(){ let input = $("#shop_search").val(); $.ajax({ type: 'GET', url: '/shops/search', data: {keyword: input}, dataType: 'json' }) .done(function(shops){ $("#shop_search--result").empty(); if (shops.length !== 0) { shops.forEach(function(shop){ addShop(shop); }); } else if (input.length == 0){ return false; } else { addNoShop(); } }); });.done以下がその処理です。
そしてそれぞれaddShop、addNoShopとしたものをhtmlへ描画します。
以下がjsファイル全文です。
app/assets/javascripts/search.js$(function(){ function addShop(shop) { let html = ` <a href="/shops/${shop.id} class="shop_search-list"> <div>${shop.name} - ${shop.location}</div> </a> `; $("#shop_search--result").append(html); }; function addNoShop(){ let html =`ショップがありません` $("#shop_search--result").append(html); }; $("#shop_search").on("keyup", function(){ let input = $("#shop_search").val(); $.ajax({ type: 'GET', url: '/shops/search', data: {keyword: input}, dataType: 'json' }) .done(function(shops){ $("#shop_search--result").empty(); if (shops.length !== 0) { shops.forEach(function(shop){ addShop(shop); }); } else if (input.length == 0){ return false; } else { addNoShop(); } }) }); });let htmlとして描画する際の定型を指定します。
今回は検索結果をクリックするとその詳細ページにリンクする形で記載してみました。hamlファイル内の検索結果を表示する箇所(#shop_search--result)にappendを使用して作成した定型文(html)を挿入します。
これで非同期の検索機能(インクリメンタルサーチ)の完成です。
やってみて
当内容はスクールで学んだ内容に自身で挑戦してみて、理解を深めながら作成したものです。
学習当初は半端な理解で出来ればいいや位の気持ちで進めていましたが、いざ1から自分の力でやるとなると手が止まってしまう事が多々ありました。反省です。ファイルが変わるごとに、どこがどのように対応しているのかを把握する事がポイントかと思います。
また、どんな機能でもそうですが、今回の場合はconsole.log等デバッグしながら書き進めていくことを強くお勧めします。
- 投稿日:2020-09-29T11:32:09+09:00
Rubyで、TSVファイルからCSVファイル(BOM付き)への変換
はじめに
Rubyにおけるタブ区切りのファイル(TSVファイル)から、BOM付きのCSVファイルへの変換方法です。
目次
0.完成コード
1.BOMを付けたCSVファイルの作成
2.TSVファイル内データの読み込み
3.CSVに1行づつ読んでは書いていく
4.プログラムを実行0.完成コード
tsv_to_csv.rbrequire 'csv' # CSVファイルを作成し、bomを付ける。 File.write("meibo.csv", "\uFEFF") # 'meibo2.txt'の情報を1行毎に読み込み、splitで配列にして、CSVファイルに1行ずつ入れていく。 CSV.open("meibo.csv", "a", force_quotes: true ) do |meibocsv| File.foreach('meibo.txt') do |student| meibocsv << student.chomp.split("\t", -1) end end1.BOMを付けたCSVファイルの作成
BOM(Byte order mark)は、Unicodeの符号化形式で符号化したテキストの先頭に着ける数バイトのデータのことを言います。
ExcelはCSVファイルを開くときデフォルトでShift-JISで開こうとするため、UTF-8では文字化けを起こしてしまいます。その対処法として、UTF-8で出力する際にBOMを付けてあげることでExcelにUTF-8で書かれていると認識させて、文字化けを防ぐことができます。
以下のようにしてBOMを付けてます。File.write("meibo.csv", "\uFEFF")2.TSVファイル内データの読み込み
読み込むTSVファイルは以下。
meibo.txtjohn m 18 paul m 20 alice f 15 dabid m 17 jasmin f 17タブ区切り形式のデータが書かれたmeibo.txtを読み込みますが、1行読み込んでは1行書き込むという手法をとりたいです。タブ区切りデータ1行をsplitで配列に変換した後、そのままCSVファイルに書き込んでいくようにします。
File.foreach('meibo.txt') do |student| (CSVファイル) << student.chomp.split("\t", -1) end3.CSVに1行づつ読んでは書いていく
上のコードをCSV.openで囲えば、1行読み書きができます。
基本的に、タブ区切りファイルから、CSVファイルへ移すという場合は、1行ずつ処理していきます。なぜならば、仮に1000万行のような膨大なデータであっても、メモリがオーバーすることを防ぐためです。CSV.open("meibo.csv", "a", force_quotes: true ) do |meibo_csv| File.foreach('meibo.txt') do |student| meibo_csv << student.chomp.split("\t", -1) end end因みにsplitの第2引数(limit)に、-1を渡しています。
この第2引数の値はデフォルトでは0になっており、配列末尾の空文字列を取り除いてしまいます。
これではTSVに空のフィールドがあったときにうまく行きません。-1を渡してあげることで、解決することができます。4.プログラムを実行
% ruby tsv_to_csv.rbデータが書き込まれたCSVファイルが生成される。
meibo.csv"john","m","18" "paul","m","20" "alice","f","15" "dabid","m","17" "jasmin","f","17"カンマ区切りのCSVファイルに正しく変換されました。
- 投稿日:2020-09-29T11:09:36+09:00
[Rails]カスタムフォントの設定
はじめに
アプリ開発でフリーフォントを使用してデザインを変えたのでその使用方法をまとめました。
目次
1 フリーフォントの準備
2 フォントの読み込み1 フリーフォントの準備
まず、フリーフォントサイトから自分が使用したいものをダウンロードします。
その後、ダウンロードしたファイル(拡張子ttfまたはotf)をapp/assets/fonts下に配置します。注意事項
フォントはそれぞれ利用条件が異なります。商用可能であっても利用できる範囲に制限があったり、使用時に報告が必要なものもありますので、ご利用の際には必ず配布ページをご確認ください。
2 フォントの読み込み
CSSファイルに以下を記述します。
@font-face { font-family: “abcdefg”; src: url('/assets/abcdefg.ttf') format("truetype"); font-weight: normal; font-style: normal; }フォントを適用したい箇所にfont-familyを指定します。
例
p { font-family; “abcdefg”; }これでフォントを適用することができました。
3 本番環境への適応
このままでは本番環境でフォントが適用されなかったため、以下を変更しました。
/config/production.rb# 変更前 config.assets.compile = false # 変更後 config.assets.compile = trueこれにより本番環境で読み込まれていなかった画像ファイルも表示することができました。
参考リンク
https://qiita.com/dir_sh0606/items/ce605b70f1cb333f9d59
https://qiita.com/kinpin/items/bd7c9a7d7a739e297742
- 投稿日:2020-09-29T10:45:05+09:00
Rubyのメソッド内で引数に再代入してから `super` と呼び出した場合 → 再代入したものが使われる
- 「たしか
super
を引数なしで呼び出したら、現在のメソッドの引数と同じものが渡されるんだっけ」- 「あれ? 引数に代入して上書きした場合1、元のと新しいののどっちが使われるんだろう?」
と分からなくなったので、実験してみた。
class Foo def mtd(str) puts "[Foo:1] str => %p (%#08x)" % [str, str.__id__] end end class Bar < Foo def mtd(str) puts "[Bar:1] str => %p (%#08x)" % [str, str.__id__] str *= 2 puts "[Bar:2] str => %p (%#08x)" % [str, str.__id__] super end end Bar.new.mtd("abc")結果[Bar:1] str => "abc" (0x00003c) <-- 仮引数に渡された元のオブジェクト [Bar:2] str => "abcabc" (0x000050) <-- 再代入して上書きしたオブジェクト(IDが異なる) [Foo:1] str => "abcabc" (0x000050) <-- 結果:上書きしたほうが使われる
super
とだけ書いた場合はsuper(str)
という感じに補完してくれて、結果として再代入したほうが使われる。念のためリファレンスマニュアルを見たら、サンプルコードにしっかり書いてあった。(やはり文章での説明だとどちらにも解釈できるので難しい)
代入でなく破壊的メソッドを作用させただけの場合は、元のも新しいのもオブジェクトとしては同一なので今回の疑問の対象外 ↩
- 投稿日:2020-09-29T10:02:42+09:00
「もっとプログラム脳を鍛える数学パズル」_pp.018-020 (code:Ruby) -> Rust
「もっとプログラマ脳を鍛える数学パズル」をRustで書き直すのは、ボケ防止にちょうど良いかもしれない、と思った。
例題2 順列と組み合わせ(pp.018-020)
Ruby
なんだか書籍p.018のpre2_1.rbは、正しい答えが出てない気がする。
定義からすると、順列のコードは、r.upto(n) do |i|ではない、よな・・・。
ということで、正しい答えが出るコード。ex2.rbrequire "test/unit" def nPr(n, r) result = 1 (n-r+1).upto(n) do |i| result *= i end result end def nCr(n,r) result = 1 1.upto(r) do |i| result = result * (n - i + 1) / i end result end class NPrTest < Test::Unit::TestCase def test_nPr assert_equal 1, nPr(1,1), "1P1 should return 1." assert_equal 720, nPr(6,6), "6P6 should return 720." assert_equal 20, nPr(5,2), "5P2 should return 20." end def test_nCr assert_equal 1, nCr(1,1), "1C1 should return 1." assert_equal 1, nCr(6,6), "6C6 should return 1." assert_equal 10, nCr(5,2), "5C2 should return 10." end endRust
main.rsfn main() { println!("{n}P{r} = {x}", n = 5, r = 2, x = permutation(5, 2)); println!("{n}C{r} = {x}", n = 5, r = 2, x = combination(5, 2)); } pub fn permutation(n: i64, r: i64) -> i64 { let mut result = 1; for i in (n - r + 1)..=n { result *= i; } return result; } pub fn combination(n: i64, r: i64) -> i64 { let mut result = 1; for i in 1..=r { result = result * (n - i + 1) / i; } return result; } #[cfg(test)] mod tests { use super::*; #[test] fn test_permutation() { assert_eq!(permutation(1, 1), 1); assert_eq!(permutation(6, 6), 720); assert_eq!(permutation(5, 2), 20); } #[test] fn test_combination() { assert_eq!(combination(1, 1), 1); assert_eq!(combination(6, 6), 1); assert_eq!(combination(5, 2), 10); } }ベタ移植。ところで、
result = result * (n - i + 1) / i;と書くのと、
result *= (n - i + 1) / i;と書くのは違うのか。テストを書きながら進めてよかった。
「
mut
を使いまくるのはどうなのか」と考えてしまうが、1関数内に閉じている限りは、騒ぐほどのこともないと思う。2020/09/29 追記
簡潔なコード例を頂いた。
permutation2()
とcombination2()
として実験。main.rsfn main() { println!("{n}P{r} = {x}", n = 5, r = 2, x = permutation(5, 2)); println!("{n}C{r} = {x}", n = 5, r = 2, x = combination(5, 2)); } pub fn permutation(n: i64, r: i64) -> i64 { let mut result = 1; for i in (n - r + 1)..=n { result *= i; } return result; } pub fn combination(n: i64, r: i64) -> i64 { let mut result = 1; for i in 1..=r { result = result * (n - i + 1) / i; } return result; } pub fn permutation2(n: i64, r: i64) -> i64 { (n - r + 1..=n).product() } pub fn combination2(n: i64, r: i64) -> i64 { (1..=r).fold(1, |p, i| p * (n - i + 1) / i) } #[cfg(test)] mod tests { use super::*; #[test] fn test_permutation() { assert_eq!(permutation(1, 1), 1); assert_eq!(permutation(6, 6), 720); assert_eq!(permutation(5, 2), 20); assert_eq!(permutation(1,1), permutation2(1,1)); assert_eq!(permutation(6,6), permutation2(6,6)); assert_eq!(permutation(5,2), permutation2(5,2)); } #[test] fn test_combination() { assert_eq!(combination(1, 1), 1); assert_eq!(combination(6, 6), 1); assert_eq!(combination(5, 2), 10); assert_eq!(combination(1, 1), combination2(1,1)); assert_eq!(combination(6, 6), combination2(6,6)); assert_eq!(combination(5, 2), combination2(5,2)); } }確かに同一の結果になる。コードも分かりやすい。
- 投稿日:2020-09-29T08:09:39+09:00
[初学者向け]削除機能の実装方法
投稿内容
今回は削除機能の実装方法について投稿します。
以下のようなボタンを押した際にdestroyアクション
を発動させます。
コントローラーのアクション
items_controller.rbclass ItemsController < ApplicationController #省略 def destroy if @item.destroy redirect_to root_path else render :show end end end解説) destroyアクションが呼ばれたらインスタンス変数
@item
を削除します。無事削除できた場合はroot_path
へ遷移。削除できなかった場合はshowアクション
へ遷移するようハンドリングします。Rubyの記述
show.html.haml.item-show-page__destroy-btn = link_to "商品を削除する", item_path(@item), method: :delete, class: "item-show-page__destroy-link"解説)
link_to
で@item
を指定し、メソッドはdelete
を指定します。これでボタンをクリックすると削除できると思います。
最後に
今回の削除機能は至ってシンプルなものなので、初学者の方でもとっつきやすいかと思われます。是非参考にしてください!
- 投稿日:2020-09-29T03:56:22+09:00
働きながら公式テキスト1冊だけでRuby Silverに合格したので勉強法をメモ
Ruby Silverとは
Ruby技術者認定試験の一種です。
正式名称は
Ruby Association Certified Ruby Programmer Silver version 2.1:
となっており、短縮してRuby Silverと呼ばれています。制限時間は90分で選択式の問題が50問出題されます。
全国にあるプロメトリックという会社のテストセンターでPCを操作して解答します。Silverのレベルは以下の通り。
Rubyの文法知識、Rubyのクラスとオブジェクト、標準ライブラリの知識について、基本的な技術レベルを持つことを認定します。
(公式サイトより)受験履歴
75点が合格ライン。
下記の通り、1回目で84点をとって無事合格!
受験回数 合否 点数 1回目 合格 84点 受験当時の状況
自社開発の企業に新卒で入社し、半年ほど経過していました。
情報系の大学出身で、学生時代は主にPythonを使用。
Rubyやjavaといったオブジェクト指向の言語はほとんど経験がなく、入社してRailsでの開発を始めたのが受験のきっかけとなりました。わからないことが多くて悩んでいた時、先輩エンジニアに「Ruby Silverって試験があるよ〜」って教えていただいたのでその日の夜に申し込んだ記憶があります。
教材
使用した教材は、公式テキスト1冊のみです。
(3000円以上するので少しお高いですが、リファレンスとしての活用もできますので持っていて損はないかと)
勉強のゴール
公式テキストに収録されている問題を完璧に解けるようになる
公式テキストに基礎力確認問題(30問)と模擬問題(50問)が収録されています。
試験では、これら80問から似たような意図の問題やまったく同じ問題が出題されます。余談ですが、試験勉強において具体的なゴール設定は大切です。
「合格する」というゴールだど、抽象的すぎます。
試験直前までずっと不安な気持ちになってしまうため、「試験までにこれができたらOK」という具体的な行動のゴールを定めておきます。
もし落ちた場合は、ゴール設定が間違っていたかゴールに到達できなかったかのどちらかとなります。勉強方法
制限時間や解答方式など、冒頭の試験概要は事前によく読んでおいてください。
基本サイクルは以下の通りです。0, 暗記シート(A4の紙など)を作成
1, いきなり問題を解く
2, 問題解説を読み、覚えるべき項目を暗記シートに書く
→よく分からないときはirbで動作確認する
3, 解説を読んで必要だと思った知識の詳しいページを読み、暗記シートに追記する
4, 間違えた問題にレ点ををつける
5, 1-4を繰り返して収録されている80問をすべて解く
6, 暗記シートを覚える
7, レ点付きの問題を解き、解けたらレ点の横に○、間違えたらレ点を書き加える
8, ○がついていないレ点の問題を解く
9, 80問を何度も繰り返し解く
10, 試験直前は暗記シートだけ見る(余裕がある方向け)
公式テキスト80問は何周やっても完璧、という状態で試験までに余裕があったら以下の公式模擬問題(50問)も解いておきましょう。
https://gist.github.com/sean2121/945035ef2341f0c39bf40762cd8531e0大切なのは、収録されている80問を最初にすべて解くことです。
数百ページあるテキストを最初から読んでいたらいつまでたっても終わりませんし、膨大な量のメソッドやその返り値を手当たり次第に覚えていくのは非常に効率が悪いです。まず問題を解いて、不足している知識を洗い出してから詳細のページを見に行けばいいです。
解説で十分事足りることもしばしば。テキスト1冊だけで勉強するのは不安だと思います。
が、中途半端な状態にしてWEBのいろんな問題に手を出す方が危険です。この試験は傾向がはっきりしていますので、80問きっちりできれば合格ラインは問題ないと感じます。
問題の出され方
に慣れておきましょう。例えば、以下の問題を解いてみてください。
以下の出力はどうなりますか?a="ruby silver" a.upcase # アルファベットを大文字にするメソッド "HELLO" + " " + a puts a #=>正解は
"ruby silver"
です。
"HELLO RUBY SILVER"
ではありません。(僕は最初ひっかかった)
upcase
メソッドは非破壊的メソッドのため、レシーバー(メソッドの呼び出し元)自体は変化しません。
破壊的メソッドは!
がつくことがほとんどですが、配列を結合するconcat
など一部メソッドは!
なしで破壊的メソッドなので要注意(concat
はめっちゃ出る)。以下の出力はどうなりますか?a="ruby silver" #=> "ruby silver" a.upcase #=> "RUBY SILVER" "HELLO" + " " + a #=> "HELLO RUBY SILVER" puts a #=> "ruby silver"(aの中身自体は変化していない)このように、一生懸命勉強して
upcase
メソッドの処理を知っていたとしても、問題に正解できるかどうかは別なのです。まとめ
公式テキストの問題(全80問)が完璧に理解できていることがめっちゃ重要
- 投稿日:2020-09-29T03:56:22+09:00
公式テキスト1冊でRuby Silverに合格した時のメモ
Ruby Silverとは
Ruby技術者認定試験の一種です。
正式名称は
Ruby Association Certified Ruby Programmer Silver version 2.1:
となっており、短縮してRuby Silverと呼ばれています。制限時間は90分で選択式の問題が50問出題されます。
全国にあるプロメトリックという会社のテストセンターでPCを操作して解答します。Silverのレベルは以下の通り。
Rubyの文法知識、Rubyのクラスとオブジェクト、標準ライブラリの知識について、基本的な技術レベルを持つことを認定します。
(公式サイトより)受験履歴
75点が合格ライン。
下記の通り、1回目で84点をとって無事合格!
受験回数 合否 点数 1回目 合格 84点 受験当時の状況
自社開発の企業に新卒で入社し、半年ほど経過していました。
情報系の大学出身で、学生時代は主にPythonを使用。
Rubyやjavaといったオブジェクト指向の言語はほとんど経験がなく、入社してRailsでの開発を始めたのが受験のきっかけとなりました。わからないことが多くて悩んでいた時、先輩エンジニアに「Ruby Silverって試験があるよ〜」って教えていただいたのでその日の夜に申し込んだ記憶があります。
教材
使用した教材は、公式テキスト1冊のみです。
(3000円以上するので少しお高いですが、リファレンスとしての活用もできますので持っていて損はないかと)
勉強のゴール
公式テキストに収録されている問題を完璧に解けるようになる
公式テキストに基礎力確認問題(30問)と模擬問題(50問)が収録されています。
試験では、これら80問から似たような意図の問題やまったく同じ問題が出題されます。余談ですが、試験勉強において具体的なゴール設定は大切です。
「合格する」というゴールだど、抽象的すぎます。
試験直前までずっと不安な気持ちになってしまうため、「試験までにこれができたらOK」という具体的な行動のゴールを定めておきます。
もし落ちた場合は、ゴール設定が間違っていたかゴールに到達できなかったかのどちらかとなります。勉強方法
制限時間や解答方式など、冒頭の試験概要は事前によく読んでおいてください。
基本サイクルは以下の通りです。0, 暗記シート(A4の紙など)を作成
1, いきなり問題を解く
2, 問題解説を読み、覚えるべき項目を暗記シートに書く
→よく分からないときはirbで動作確認する
3, 解説を読んで必要だと思った知識の詳しいページを読み、暗記シートに追記する
4, 間違えた問題にレ点ををつける
5, 1-4を繰り返して収録されている80問をすべて解く
6, 暗記シートを覚える
7, レ点付きの問題を解き、解けたらレ点の横に○、間違えたらレ点を書き加える
8, ○がついていないレ点の問題を解く
9, 80問を何度も繰り返し解く
10, 試験直前は暗記シートだけ見る(余裕がある方向け)
公式テキスト80問は何周やっても完璧、という状態で試験までに余裕があったら以下の公式模擬問題(50問)も解いておきましょう。
https://gist.github.com/sean2121/945035ef2341f0c39bf40762cd8531e0大切なのは、収録されている80問を最初にすべて解くことです。
数百ページあるテキストを最初から読んでいたらいつまでたっても終わりませんし、膨大な量のメソッドやその返り値を手当たり次第に覚えていくのは非常に効率が悪いです。まず問題を解いて、不足している知識を洗い出してから詳細のページを見に行けばいいです。
解説で十分事足りることもしばしば。テキスト1冊だけで勉強するのは不安だと思います。
が、中途半端な状態にしてWEBのいろんな問題に手を出す方が危険です。この試験は傾向がはっきりしていますので、80問きっちりできれば合格ラインは問題ないと感じます。
問題の出され方
に慣れておきましょう。例えば、以下の問題を解いてみてください。
以下の出力はどうなりますか?a="ruby silver" a.upcase # アルファベットを大文字にするメソッド "HELLO" + " " + a puts a #=>正解は
"ruby silver"
です。
"HELLO RUBY SILVER"
ではありません。(僕は最初ひっかかった)
upcase
メソッドは非破壊的メソッドのため、レシーバー(メソッドの呼び出し元)自体は変化しません。
破壊的メソッドは!
がつくことがほとんどですが、配列を結合するconcat
など一部メソッドは!
なしで破壊的メソッドなので要注意(concat
はめっちゃ出る)。以下の出力はどうなりますか?a="ruby silver" #=> "ruby silver" a.upcase #=> "RUBY SILVER" "HELLO" + " " + a #=> "HELLO RUBY SILVER" puts a #=> "ruby silver"(aの中身自体は変化していない)このように、一生懸命勉強して
upcase
メソッドの処理を知っていたとしても、問題に正解できるかどうかは別なのです。まとめ
公式テキストの問題(全80問)が完璧に理解できていることがめっちゃ重要
- 投稿日:2020-09-29T03:40:37+09:00
【Ruby】メソッドジャンプをしたい!
はじめに
環境macOS Catalina10.15.5
Rubyで他の人が書いたソースコードを読むときにメソッドの定義場所がすぐわかるようにメソッドジャンプがしたいなと思い、いろいろ試してたどり着いた方法を書き記します
私はテキストエディタはAtomを主に使っていたのですが、RubyのコードをAtomでメソッドジャンプするのは、いろいろ試しましたがうまくいきませんでした、なので
結論 VSCodeを使うのが簡単
という事になりまりました
導入手順
1.こちらでVSCodeをダウンロード
https://azure.microsoft.com/ja-jp/products/visual-studio-code/
2.日本語化
- メニューバーからviewを選択
- command palette を選択
- configure display languageを選択
- install additional languageを選択
- Japanese Language Pack for Visual Studio Codeのパッケージを探す
- もう一度configure display languageを選択
- jaを選択
- VSCode再起動
3.パッケージのRubyをインストール
拡張機能の検索ボックスで検索できます
4.ジャンプの機能を有効化
インストールしたRubyの拡張機能の設定を開いて
Ruby:Intellisenseのところをfalseからrubylocateに変更5.メソッドジャンプのやりかた
⌘を押しながらジャンプしたいメソッドをクリックすると定義場所にとべます!
ここまで15分程度だったのでとても簡単でした!