20201012のRailsに関する記事は27件です。

今回使ったlink_toのいろいろ、まとめてみた

はじめに

今回のカリキュラムで一番躓いたのが、link_toメソッドだった。もちろん基本的な使い方は理解しているつもりだが、どうもpathの引数がしっくりときていない。

今回の投稿は何かを説明する目的ではなく、自分がコードを読むための記録である。
以下、今回作成したアプリの全link_toを余計な記述はカットして引数の読み方とともに載せる。

全link_toメソッド

application.html.erb
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
<%= link_to "新規投稿", new_prototype_path %>

サインイン状態で表示させている「ログアウト」と「新規投稿」どちらも誰もが共通するページや処理のため、( )で引数を渡す必要がない。
ユーザーによってログアウトページが違うことはないし、ユーザーによって新規投稿ページが違うことはない。
link_toメソッドはデフォルトでHTTPメソッドがGETのため、ログアウトのときは、第三引数にメソッドを指定する。

application.html.erb
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "新規登録", new_user_registration_path %>

①と考え方は同じ。今度は非ログイン状態のときの表示。ログイン画面も新規登録もユーザーによって変わることはないから、引数がなくてよい。

application.html.erb
<%= link_to image_tag("logo.png"), root_path %>

root_pathは引数がなくてもいける!

prototypes/show.html.erb
<%= link_to "編集する", edit_prototype_path(@prototype) %>
<%= link_to "削除する", prototype_path(@prototype), method: :delete %>

投稿したものの詳細ページから、編集したり、削除したりする部分。
showアクションの中で、@prototype = Prototype.find(params[:id])と一つのレコードを選び出しているため、どのprototypeを編集・削除するか判断ができる。

prototypes/_prototype.html.erb
<%= link_to prototype.title, prototype_path(prototype.id) %>
<%= link_to image_tag(prototype.image), prototype_path(prototype.id) %>
user_pathシリーズ

user_pathはユーザーのマイページに飛ぶ。誰のマイページなのか明らかにするため、引数が必要になる。

prototypes/index.html.erb
<%= link_to current_user.name + "さん", user_path(current_user) %>

ログイン中のユーザー名をクリックするとそのユーザーのマイページに飛ぶ。
deviseのGemを用いることによって使えるcurrent_user

prototypes/_prototype.html.erb
<%= link_to "by " + prototype.user.name, user_path(prototype.user.id) %>

prototypeとアソシエーションしているuserのidを取得。

prototypes/show.html.erb
<%= link_to "by " + @prototype.user.name, user_path(@prototype.user.id) %>

誰による投稿か、名前が表示されている部分のコード。
showアクションの中で、@prototype = Prototype.find(params[:id])と一つのレコードを選び出しているため、prototypeとアソシエーションしているuserのidを取得できる。

prototypes/show.html.erb
<%= link_to comment.user.name, user_path(comment.user.id) %>

誰のコメントか表示されているところのコード。each文で|ブロックパラメーター(変数)にcomment|が入り、commentとアソシエーションしているuserのidを取得。

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

[Rails]Rspecを使ったテストコード

はじめに

Rspecを導入し、テストコードを書いてみようと思います。
コマンドはdocker環境のコマンドを使う。

Gemの導入

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'faker', "~> 2.8"
end

docker-compose run web bundle install
docker-compose build
インストール完了。

Rspecの設定

docker-compose run webrails g rspec:install
上記コマンドを行うと、下記ファイルが生成される。

create  .rspec
create  spec
create  spec/spec_helper.rb
create  spec/rails_helper.rb
.rspc
--format documentation

コード作成

spec/models/user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do

    it "usernameとemail,passwordとpassword_comnfirmationが存在すれば登録できること" do
      user = build(:user)
      expect(user).to be_valid
    end

    it  "usernameがない場合は登録できないこと" do 
      user = build(:user, username: nil)
      user.valid?
      expect(user.errors[:username]).to include("can't be blank")
    end

    it  "emailがない場合は登録できないこと" do 
      user = build(:user, email: nil)
      user.valid?
      expect(user.errors[:email]).to include("can't be blank")
    end

    it  "passwordがない場合は登録できないこと" do 
      user = build(:user, password: nil)
      user.valid?
      expect(user.errors[:password]).to include("can't be blank")
    end

    it  "passwordが存在してもpassword_confirmationがない場合は登録できないこと" do 
      user = build(:user, password_confirmation: "")
      user.valid?
      expect(user.errors[:password_confirmation]).to include("doesn't match Password")
    end

    it  "重複したemailがある場合は登録できないこと" do
      user = create(:user)
      another_user = build(:user, email: user.email)
      another_user.valid?
      expect(another_user.errors[:email]).to include("has already been taken")
    end

    it  "passwordが6文字以上であれば登録できること" do
      user = build(:user, password: "000000", password_confirmation: "000000")
      expect(user).to be_valid
    end

    it  "passwordが5文字以下であれば登録できないこと" do
      user = build(:user, password: "00000", password_confirmation: "00000")
      user.valid?
      expect(user.errors[:password]).to include("is too short (minimum is 6 characters)")
    end:grin:

  end
end
spec/factories/users.rb
FactoryBot.define do
  factory :user do
    username                 {"aaa"}
    password                 {"000000"}
    password_confirmation    {"000000"}
    sequence(:email)     {Faker::Internet.email}
  end
end

docker-compose run web bundle exec rspec

8 examples, 0 failures

となれば、テストはうまく通過している。

おわりに

今回はRspecを使ったテストの入りを行いました。
今後、コントローラーのテストも行っていきます。
最後まで読んでいただきありがとうございます:grin:

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

クラウドIDE:Ruby on RailsチュートリアルでHerokuがインストールできなかった件

Ruby on Railsのチュートリアルで、Herokuをインストールしようとしたら、以下のエラーが出て進まなかった件。
調べても何が悪いのかよくわからなかった(゚∀゚)。
bash: /home/ec2-user/.profile: Permission denied

実行コマンドはチュートリアルに記載してあった下記の通り。
source <(curl -sL https://cdn.learnenough.com/heroku_install)

環境

・AWS,Cloud9の統合環境(クラウド統合環境)

対処

解決方法をググっても特に見つからなかった\(^o^)/。

Heroku CLIをインストールするらしいので、Heroku CLIのインストール方法を確認した。
書いてあるコマンドで、チュートリアル以外のコマンドでインストールできないか調べた。
https://devcenter.heroku.com/articles/heroku-cli

下の方にnpmコマンドを使ってもインストールできることが書いてある。
インストールの注意文を見ると、「npm」と「node」が入っているのが前提のインストール方法なのだそうだ。

This installation method is required for users on ARM and BSD. You must have node and npm installed already.
npm install -g heroku

というわけでクラウドIDEに入っているかを確認した。
image.pngimage.png
どちらも入っているらしいので、ダメ元で記載されていたコマンドを使ったらインストールができた。
もし詰まっていたら、一度この方法を試してみてください。(役に立つかはわからないけど)

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

【Rails6】主なGemについて

はじめに

 Railsを使うにあたり、当たり前のようにpumaとかwebpackerとかlistenを使用していますが、正直あまり意味がわからずに使っていました。ここいらで主なGemについてまとめたいと思います。

Railsデフォルトの主なGem

 下記がRails6で生成されるデフォルトGemです。

Gemfile
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# 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.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

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

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.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 web drivers to run system tests with browsers
  gem 'webdrivers'
end

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

 それぞれ説明していきます。

sqlite3

 SQLデータベースエンジンを実装するC言語ライブラリ。
 パスワード設定がない等のセキュリティ機能がないため、基本的に開発環境、テスト環境で使用し、本番環境では別のデーターベースエンジンを使用する。

https://www.sqlite.org/index.html

puma

 RubyWebアプリケーション用のHTTP1.1サーバー構築に使用する。スレッドプールを使用してリクエストを処理する。

https://puma.io/
https://github.com/puma/puma

sass-rails

 RailsでSass(SCSS)使用できる。

https://github.com/rails/sass-rails

webpacker

 Webアプリケーションで一般的に良く使われるメジャーな設定を、標準で実装してくれるwebpackのラッパー。webpackは、最新のJavaScriptアプリケーション用の静的モジュールバンドラー1

【主な機能】
 ・webpack 4.x.x
 ・複数のエントリポイント2を使用した自動コード分割
 ・新しいJavaScript構文(ES6)をブラウザで動くように変換
 ・React、Vue.js、PostCSSなどのモダンなフレームワークに対する豊富な実績

https://github.com/rails/webpacker

Jbuilder

 Jbuilderは、JSON構造を宣言するためのシンプルなDSL(ドメイン特化言語)を提供。
https://github.com/rails/jbuilder

turbolinks

 Webアプリケーションでのリンクの追跡が高速になる。
https://github.com/turbolinks/turbolinks-classic

bootsnap

 railsの起動時の処理を最適化する(パスとrubyのコンパイル結果をキャッシュ)ことで起動時間を短縮してくれる。
 ActiveSupport や YAML もサポートしている。

https://github.com/Shopify/bootsnap/blob/master/README.jp.md

byebug

 デバッグツール。

https://github.com/deivid-rodriguez/byebug

web-console

 View 内でコンソールを立ち上げて、変数や parameter などの状態を見る事の出来るデバック用のライブラリ。エラー箇所のデバッグがしやすくなる。

https://github.com/rails/web-console

listen

 ファイルの変更を検知してそれをフックに何か処理ができる。

【主な機能】
・ファイルの変更、追加、削除を検出
・複数のディレクトリを監視

https://github.com/guard/listen

spring

 Railsアプリケーションのプリローダー3。アプリケーションをバックグラウンドで実行し続けることで開発をスピードアップするため、テスト、移行を実行するたびにアプリケーションを起動する必要がなくなる。

https://github.com/rails/spring

spring-watcher-listen

 Springはファイルシステムをポーリング4するのではなく、Listenを使用してファイルシステムの変更を監視します。

https://github.com/jonleighton/spring-watcher-listen/

capybara

 実際のユーザーがアプリをどのように操作するかをシミュレートすることにより、Webアプリケーションのテストを支援する。

https://github.com/teamcapybara/capybara
https://en.wikipedia.org/wiki/Capybara_(software)

selenium-webdriver

 capybaraではJavaScriptをサポートしていないため、selenium-webdriverでシュミレートする。

https://github.com/SeleniumHQ/selenium/tree/trunk/rb

webdrivers

  webブラウザを外部のソフトウェアから操作したり情報を取得したりできるようにするためのものです。

https://github.com/titusfortner/webdrivers

tzinfo-data

 Windows ではタイムゾーン情報用に使用する。UnixベースのOSではtzinfoからシステムのタイムゾーン情報に直接アクセスできるので使用する必要はない。

https://github.com/tzinfo/tzinfo-data

それ以外の主なGem

bcrypt

 パスワードを暗号化できる。

https://github.com/codahale/bcrypt-ruby

devise

 Webアプリケーションには必須の、ユーザー認証機能を作ることができる。会員登録用フォームを作成や、メールやFacebook等での認証も実装できる。

https://github.com/heartcombo/devise

kaminari

 ページネーション機能を簡単に追加できる。

https://github.com/kaminari/kaminari

carrierwave

 画像のアップロード機能。

https://github.com/carrierwaveuploader/carrierwave

active admin

 CRUD系の管理画面を作成することができる。

https://github.com/activeadmin/activeadmin

ruboCop

 コーディング規約どおりに書かれているかをチェックする静的コード解析ツール。

https://github.com/rubocop-hq/rubocop

pry-rails

 Rubyのirbのようにrailsのコンソールでメソッドなどを使えることができるようになる。

https://github.com/rweng/pry-rails

faker

 偽のデータを生成する。

https://github.com/faker-ruby/faker

capistrano

 自動デプロイツール。

https://github.com/capistrano/capistrano

まとめ

 Railsは歴史が長い分、Gemは数が多いですね。初学者だと選定が難しそうです。まずは開発前にどんな機能がどこまで必要なのかピックアップしてGemを選定する必要がありそうです。Gemを選定の参考になりそうなサイトを張っておきます。この記事も有用なGemがあればどんどん更新していこうと思います。
 Gemの探しツール
 Gemのランキングサイト


  1. モジュールごとに分割され、別々になったJavaScriptファイルの依存関係を解決して、1つのファイルにまとめるツール。 

  2. プログラムを実行を開始する場所のこと。(エントリーポイント - wiki

  3. サイトを表示する前に予め画像やコンテンツを先読みしてキャッシュする事。(参考→プリローダーとは

  4. 主に通信などの競合を回避するために、ホスト側が各機器に対して定期的に問い合わせを行い、条件を満たした場合に送受信や各種処理を行うこと。(ポーリング - wiki

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

動画を挿入する方法

トップページなどに動画を挿入する方法

GIF

https://gyazo.com/3015a8b1f689153dcfe7fcb308d483bb

6582025a04cb61be20a4cabc8a556419.png

下記コード

index.html
<div class="bg-video-wrap">
  <p>Brilliant Blue</p>
  <video src="images/foreign.mp4" autoplay loop muted>
  </video>
</div>
css
.bg-video-wrap {
  position: relative;
}
p {
  font-family: serif;          
  color: #fff;
  font-size: 400%;
  position: absolute;
  left: 30%;
  top: 100px;
  z-index: 1;
}

以上です!

状況に応じてCSSは各自変更して使って下さい!

foreign.mp4の部分は各々、ダウンロードや作った動画の名前を決めると思うのでそれを当てはめて下さい!おそらく末尾はmp4でもmovでもどちらでも挿入できます!

現場からは以上です!

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

削除されずに宙ぶらりんになったファイルがないか確認する

Active Storageを使ってアップロードファイルの管理をしていると、管理するテーブル (active_storage_blobs , active_storage_attachments) とストレージに実際に保存されているファイル (実体ファイル) との間に、何らかの理由で不整合が起きることがある。

中でも active_storage_attachments だけが削除され、それに紐づく active_storage_blobs と実体ファイルが残ってしまっている場合には、 ActiveStorage::Blob.unattached というスコープが使える。

❯ rails c
Loading development environment (Rails 5.2.4.4)
[1] pry(main)> ActiveStorage::Blob.unattached
  ActiveStorage::Blob Load (1.0ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" LEFT OUTER JOIN "active_storage_attachments" ON "active_storage_attachments"."blob_id" = "active_storage_blobs"."id" WHERE "active_storage_attachments"."blob_id" IS NULL

これを使えば以下のように宙ぶらりんになった active_storage_blobs のレコードと紐づく実体ファイルを削除することができる。

[1] pry(main)> ActiveStorage::Blob.unattached.each(&:purge)
  ActiveStorage::Blob Load (1.0ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" LEFT OUTER JOIN "active_storage_attachments" ON "active_storage_attachments"."blob_id" = "active_storage_blobs"."id" WHERE "active_storage_attachments"."blob_id" IS NULL
   (0.3ms)  BEGIN
  ActiveStorage::Attachment Exists (0.2ms)  SELECT  1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2  [["blob_id", 1], ["LIMIT", 1]]
  ActiveStorage::Blob Destroy (0.5ms)  DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1  [["id", 1]]
   (0.8ms)  COMMIT
  S3 Storage (105.5ms) Deleted file from key: ...
=> nil

ActiveStorage::Blob クラスに unattached というスコープとして以下のように定義されている。(Rails v5.2時点)

activestorage/app/models/active_storage/blob.rb
class ActiveStorage::Blob < ActiveRecord::Base
  ... 

  has_many :attachments

  scope :unattached, -> { left_joins(:attachments).where(ActiveStorage::Attachment.table_name => { blob_id: nil }) }

  ...
end

メモ:現時点でのmasterブランチには missing メソッドを使うように変更するコミットが入っているよう。

activestorage/app/models/active_storage/blob.rb
@@ -46,7 +46,7 @@ class ActiveStorage::Blob < ActiveRecord::Base

   has_many :attachments

-  scope :unattached, -> { left_joins(:attachments).where(ActiveStorage::Attachment.table_name => { blob_id: nil }) }
+  scope :unattached, -> { where.missing(:attachments) }

参考

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

ターミナルって何?

ターミナル説明

ターミナルは、PCに命令をすることができるツールですね。
環境構築を実行するには コマンドラインというツールが必要となります。
Macにデフォルトでインストールされているコマンドラインがターミナルです。

コマンドライン説明

コマンドライン(または コマンドラインインタフェース:CLI )とは、コンピュータに対してキーボードからコマンドという文字を打ち込んで操作を行うツールになります。

GUI(グラフィカルユーザインターフェース)

コマンドラインとは対照的に、グラフィックを用いて操作を行う仕組みを グラフィカルユーザインタフェース(GUI) と言います。

例えば、「マウスでファイルをダブルクリックして開く」という操作など、普段PCで行う操作のほとんどはGUIで行なっていることが多いです。

基本.png

ターミナルを使う理由を学ぼう

先程、マウスなどを使った直感的に操作をすることができるGUIと、ターミナルのようにコマンドを使用して操作するCLIがあることを説明させて頂きました。

直感的に操作ができて普段から使い慣れたGUIではなく、なぜわざわざターミナルの操作に慣れ、それを使用しなければならないのでしょう?

その理由は、GUIとCLIでは CLIの方が行える操作の数が圧倒的に多いためです。

GUIのように全ての操作をグラフィカルに表現していては画面がメニューやボタンで溢りかえってしまって、分かりづらくなってしまうためです。
普段行わない操作についてはボタンが無かったり、そもそも簡単に操作できては危険なものであれば操作できないものである場合が多いです。

ボタン.png

CLIでは、PCに対しての操作のほとんどが行えると言って良いほどに、
様々な操作を行うことができます。

「Rubyプログラムの実行」もCLIが行える操作の1つです。
そのため、 ターミナルの操作は必須スキルだと思います。

ターミナルの見方説明

まずは、ターミナルを開いて表示画面を見てみましょう!!

ターミナル見方.png

コンピュータ名は自分のコンピュータの名前ですね。
ここでは「A-137」となっています。ユーザ名は自分の名前です。
ここでは「div-M.T」となっています。このあとに続く「$」または「%」は プロンプト といい、 コンピュータが命令を受け付けられる状態であることを示します 。つまり今は、命令待ちの状態です。

まとめ

・ターミナルとは、PCに命令をすることができるツールのこと
・「〜」には、カレントディレクトリが入ります。
 「%」は、プロンプトを言います。

プログラミングは、膨大な量の情報があるので、
基本から徐々に焦らずに理解していきましょう!!
できるようになるまで時間は、かかります。
でも、毎日継続して行えばだんだんとできるようになるので
一緒に頑張りましょう!!

以上。

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

マイグレーションファイルから Model を直接触るのはやめて欲しい

何が起こったのか

開発環境で新たにDBを作成。

お約束通り、

$ rails db:create
$ rails db:migrate

大量のマイグレーションファイルを一気に migrate しようとしたその時、事件は起こった。

PG undefined column hogehoge ...

的な感じで、スッと通らない?
嫌な予感がする。

なになに。。。。

マイグレーションファイルから直接 Model にアクセスしていたんです。

migartion_file
...

Hoge.delete_all

...

みたいな感じでモデルに対して直接操作を行う処理が書かれていた?

そのマイグレーションファイルは過去のものであり、その当時はそのモデルがあったのだろうが、今現在のソースコードには存在しない。
当然、 そんなモデルは無いよ と怒られたりする。

こんな感じで直接 Model に対して処理を行っているマイグレーションファイルが散見された。
結果 migrate を通すのにものすごく苦労し、過去のマイグレーションファイルにも手を入れなくてはいけなくなった。

マイグレーションファイルってそういうものなのでしょうか?
いいえ、違うはずです。

どうすれば良いのか

マイグレーションファイルは、後々どんな人が開発に入ってきてもしっかり rails db:migrate が通るように積み上げていきましょう。

ファイル数が増えるのは仕方ありません。大事なのはしっかり通るかどうかです。
おそらく当時の開発者もモデルに対して処理を書いているのは分かっていたはずで、データメンテナンスがしたかったのだと思います。

既存データのメンテナンスはマイグレーションファイルでやらないで、rake task を作成するとか他にもやり方があったはずです。

自戒も込めて、マイグレーションファイルに関して気をつけるべきことを書いておきます

  • マイグレーションファイルを追加した時には、 rails db:migrate:redo を習慣づけて redo が通ることを確認する
  • 古いマイグレーションファイルを後から書き換えない(見栄えは悪いが単純に積み上げていくのがベストプラクティス)
  • データメンテナンスはマイグレーションファイルの中で行わない(rake task を作ったり rails console から手動やったり他の方法がある)

Model は単なる ruby のクラスに過ぎません。
それは今後名前が変わるかもしれませんし、データの持ち方も変わるかもしれません。

マイグレーションファイルの中からモデルを直接触るのはやめましょう?


どなたかの役に立てば幸いです。

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

deviseのコントローラー初期状態まとめ

deviseのコントローラーの初期状態が気になったことがあり、一度サンプルを作る羽目になったのでメモとしてQIITAに残しとく。

confirmations_controller.rb
class Sample::ConfirmationsController < Devise::ConfirmationsController
  # GET /resource/confirmation/new
  # def new
  #   super
  # end

  # POST /resource/confirmation
  # def create
  #   super
  # end

  # GET /resource/confirmation?confirmation_token=abcdef
  # def show
  #   super
  # end

  # protected

  # The path used after resending confirmation instructions.
  # def after_resending_confirmation_instructions_path_for(resource_name)
  #   super(resource_name)
  # end

  # The path used after confirmation.
  # def after_confirmation_path_for(resource_name, resource)
  #   super(resource_name, resource)
  # end
end
omniauth_callbacks_controller.rb
class Sample::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # You should configure your model like this:
  # devise :omniauthable, omniauth_providers: [:twitter]

  # You should also create an action method in this controller like this:
  # def twitter
  # end

  # More info at:
  # https://github.com/heartcombo/devise#omniauth

  # GET|POST /resource/auth/twitter
  # def passthru
  #   super
  # end

  # GET|POST /users/auth/twitter/callback
  # def failure
  #   super
  # end

  # protected

  # The path used when OmniAuth fails
  # def after_omniauth_failure_path_for(scope)
  #   super(scope)
  # end
end

passwords_controller.rb
class Sample::PasswordsController < Devise::PasswordsController
  # GET /resource/password/new
  # def new
  #   super
  # end

  # POST /resource/password
  # def create
  #   super
  # end

  # GET /resource/password/edit?reset_password_token=abcdef
  # def edit
  #   super
  # end

  # PUT /resource/password
  # def update
  #   super
  # end

  # protected

  # def after_resetting_password_path_for(resource)
  #   super(resource)
  # end

  # The path used after sending reset password instructions
  # def after_sending_reset_password_instructions_path_for(resource_name)
  #   super(resource_name)
  # end
end

registrations_controller.rb
class Sample::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  # GET /resource/sign_up
  # def new
  #   super
  # end

  # POST /resource
  # def create
  #   super
  # end

  # GET /resource/edit
  # def edit
  #   super
  # end

  # PUT /resource
  # def update
  #   super
  # end

  # DELETE /resource
  # def destroy
  #   super
  # end

  # GET /resource/cancel
  # Forces the session data which is usually expired after sign
  # in to be expired now. This is useful if the user wants to
  # cancel oauth signing in/up in the middle of the process,
  # removing all OAuth session data.
  # def cancel
  #   super
  # end

  # protected

  # If you have extra params to permit, append them to the sanitizer.
  # def configure_sign_up_params
  #   devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
  # end

  # If you have extra params to permit, append them to the sanitizer.
  # def configure_account_update_params
  #   devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
  # end

  # The path used after sign up.
  # def after_sign_up_path_for(resource)
  #   super(resource)
  # end

  # The path used after sign up for inactive accounts.
  # def after_inactive_sign_up_path_for(resource)
  #   super(resource)
  # end
end

sessions_controller.rb
class Sample::SessionsController < Devise::SessionsController
  # before_action :configure_sign_in_params, only: [:create]

  # GET /resource/sign_in
  # def new
  #   super
  # end

  # POST /resource/sign_in
  # def create
  #   super
  # end

  # DELETE /resource/sign_out
  # def destroy
  #   super
  # end

  # protected

  # If you have extra params to permit, append them to the sanitizer.
  # def configure_sign_in_params
  #   devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
  # end
end

unlocks_controller.rb
class Sample::UnlocksController < Devise::UnlocksController
  # GET /resource/unlock/new
  # def new
  #   super
  # end

  # POST /resource/unlock
  # def create
  #   super
  # end

  # GET /resource/unlock?unlock_token=abcdef
  # def show
  #   super
  # end

  # protected

  # The path used after sending unlock password instructions
  # def after_sending_unlock_instructions_path_for(resource)
  #   super(resource)
  # end

  # The path used after unlocking the resource
  # def after_unlock_path_for(resource)
  #   super(resource)
  # end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails 教わった事 その2

この記事の目的

プログラミングスクールで私が約3ヶ月で教わった事を転職活動に生かすためにまとめました。
私による私のためだけの記事です。

この講義で印象に残った一言は、
RSpecは絶対に身に付けなければいけない必須の技術。

Hamlで書く

Hamlを使うと閉じタグがいらなくなり、コードを減らせる。
Hamlの書き方

モデルメソッド

何回も使うロジックはモデルメソッドにする、DRYにする↓

example.html.haml
-  - if current_user.id == post.user_id #直書き
+  - post.created_user?(current_user)
    %p あなたは投稿した人です
post.rb
  def created_user?(user)
    self.user_id == user.id
  end

モデルメソッドをRSpecでテスト

本来はテストを書き、失敗を確認しながらメソッドを書く(TDD、テスト駆動開発)

post_spec.rb
  describe "#created_user?" do
    let(:user) { FactoryBot.create(:user) } #本人
    let(:other_user) { FactoryBot.create(:user) } #他人
    let(:post) { FactoryBot.create(:post, user: user) } #本人の投稿

    context "ログインユーザーと同じユーザーの場合" do
      it "trueを返すこと" do
        expect(post.created_user?(user)).to eq true 
      end
    end
    context "ログインユーザーと同じユーザーではない場合" do
      it "falseを返すこと" do
        expect(post.created_user?(other_user)).to eq false 
      end
    end 
  end

参考書

Everyday Rails - RSpecによるRailsテスト入門
とにかく手を動かして少しだけできるようになりました。
個人的にsystemスペックのajax関連のテストがトラウマです。

おすすめされた動画


TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング

感想

今回は主にRSpecのライブコーディングを見せてもらった。
当時、あまりテストの必要性を感じなかったが、今回過去の動画を見返しながら気になったコードをリファクタリングしようとしたところ無意識に、RSpecを動かしていたので少しずつ慣れてきているのだと思う。

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

gemfile書き換えによるBundler::Dsl::DSLErrorの対処法

gemfileを書き換えた際のエラー対処法について後発者のためにメモを残しておく。

開発環境

windows 10 home
ubuntu 20.04 LTS
ruby 2.7.1
Rails 6.0.3
postgresql 11

エラー文

$ bundle
~~~~~中略~~~~~
 Permission denied @ rb_sysopen - /home/admin0/taskleaf2/Gemfile (Errno::EACCES)
~~~~~中略~~~~~
was an error while trying to read from `/home/admin0/taskleaf2/Gemfile`. It is likely that you need to grant read permissions for that path. (Bundler::PermissionError)
~~~~~中略~~~~~
 Bundler::Dsl::DSLError

今回は特に理由はないが3つ目のエラー文着目に着目して検索したところ以下のサイトに良さげな解決方法が記載されていたので実行してみる
https://stackoverflow.com/questions/57926553/bundle-install-gives-bundlerdsldslerror

$ chmod 644 Gemfile

これでエラーが発生しなくなったため、問題解決したと判断する。

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

【Rails】SessionsHelperを使ってコントローラー間でパラメータを受け継ぐ

はじめに

某プログラミングスクールでフリマアプリを作成中、購入内容の確認ページからクレジットカード登録ページに遷移。カード登録後、購入内容の確認ページに遷移することが出来なくなった。

原因:コントローラーがitems_controller.rbからcredit_cards_controller.rbに変わった時点でパラメータの:item_idが0になっていた。

詳細

  1. ユーザーログイン後、routingでトップページ(items#index)へ遷移。
  2. 他ユーザーの出品商品(items#create)の詳細ページ(items#show)へ遷移。
  3. 詳細ページ(items#show)の購入ボタンより商品購入ページ(items#purchase)へ遷移。
  4. クレジットカード情報の登録・変更するために、登録・変更ボタンより(credit_cards#index)へ遷移。
    1. 遷移したが変更がなければ、もどるボタンで商品購入ページ(items#purchase)へ遷移。
    2. 変更・未登録ならカード登録ページ(credit_cards#new,create)に遷移後,(credit_cards#show)に遷移。その後カード決定・もどるボタンより商品購入ページ(items#purchase)へ遷移。
  resources :items, only: [:index, :new, :create, :show, :edit, :destroy] do
    get '/purchase/:id', to: 'items#purchase', as: :purchase
  end
  resources :credit_cards, only: [:index, :new, :create, :show, :destroy]

スクリーンショット 2020-10-11 13.37.34.png

5. 4-1,4-2にてitem_idがないというErrorが発生した

スクリーンショット 2020-10-11 13.58.34.png

ターミナルで確認するとitem_idがcontrollerを跨ぐタイミングで消えている。

Processing by ItemsController#purchase as HTML
  Parameters: {"item_id"=>"2"}
  Item Load (0.3ms)  SELECT  `items`.* FROM `items` WHERE `items`.`id` = 2 LIMIT 1
Processing by CreditCardsController#show as HTML

実装内容

item_idを引き継ぐ方法を検索していたところ...

【Rails】Sessionの使い方について
Ruby on Rails チュートリアル - セッション

sessionが使えそうだと判断。
はじめにapplication_controller.rbにinclude SessionHelperを追加。

application_controller.rb
class ApplicationController < ActionController::Base
  include SessionsHelper #左記を追加
end

/app/helpersに/sessions_helper.rbを作成し、下記コードでSessionsHelperを呼び出し

/app/helpers/sessions_helper.rb
module SessionsHelper
end

参考記事
「初期化されていない定数ApplicationController :: SessionsHelper(NameError)」が表示されるのはなぜですか?

これでsessionの準備が完了

まず、sessionにitem_idを保存する。

app/controllers/items_controller.rb
def purchase
  @item = Item.find(params[:item_id]) #paramsからitem_idを取り出し
  session[:item_id] = @item #取り出したitem_idをsessionに保存
end

次に、credit_card登録先のcontroller(credit_cards#index,credit_cards#show)でsessionに保存していたパラメータを取り出す。
binding.pryで確認したところ配列になっていたので、valuesで取り出す。

app/controllers/credit_cards_controller.rb
#session[:item_id]で先ほどのsessionデータを取り出せる。
def index
  @item_id = session[:item_id].values.first 
end

def show
  @item_id = session[:item_id].values.first
end

controllerで作成したインスタンス変数(@item_id)をviewで取り出し

app/views/credit_cards/index.html.haml
= link_to "もどる", "/items/#{@item_id}/purchase", class:'return'
app/views/credit_cards/show.html.haml
%button.enter__permit__box.btn(onclick="location.href='/items/#{@item_id}/purchase'")選択した支払い方法を使う
= link_to "もどる", "/items/#{@item_id}/purchase", class:'return'

これで items#purchase ⇄ credit_cards#show,index において item_idのやりとりが出来るようになった。
最後に商品購入をした際に、sessionの削除をする

app/controllers/items_controller.rb
def pay
  session[:item_id] = nil
end

今回はsessionを使ってパラメータのやりとりを行なったがURLを使用してパラメータをやりとりできるみたいなので、今度試してみたい。

なお、sessionを使用する際は、それなりにリスクを伴うので、使用する際は下記のリンク先も合わせて読んでおきたい。

合わせて読みたい記事(セッションを取り扱う際の注意点)

Rails セキュリティガイド

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

Ruby on Rails 教わった事 その1

この記事の目的

プログラミングスクールで私が約3ヶ月で教わった事を転職活動に生かすためにまとめました。
私による私のためだけの記事です。

この講義で印象に残った一言は、
Rails書けると出来るにはレベルに隔たりがある。

コードレビュー


メンターさんにとってすごい人だそうです!
Railsのコードレビューをするときに確認する観点をまとめてみた

コントローラーについて

スコープ

コントローラーの中で、モデルから直接メソッドを呼び出すことはセキュリティーホールに繋がりやすい。基本的に全体対象はイレギュラーであり、ほぼないものとする。↓

example_controller.rb
def new
  - @post = Post.new  #間違ったIDが入る可能性がある
  + @post = current_user.posts.build  #current_userの範囲(スコープ)
end 

Before_Action

コントローラー内での共通部分はprivateメソッドにして、before_actionでDRYに保つ↓

example_controller.rb
before_action :set_post

#省略
private

def set_post
  @post = current_user.posts.find(params[:id])
end

Save,Destroyメソッド

サーバートラブルなどネット環境の要因以外で、ほぼ失敗しないsave、destroyメソッドには!をつける↓

example_controller.rb
def create
  @post = current_user.posts.build(post_params)
  if @post.save! 
    flash[:notice] = "投稿しました"
    redirect_to posts_path
  else
    render :new
  end
end

定数を使う

全体の人に分かりやすくするよう定数にする。モデルで定数を定義し、コントローラーで呼び出す。↓

example_controller.rb
#ex_model.rb
PER_COMMENT = 5

#example_controller.rb
@comments = @post.comments.page(params[:page]).per(Ex_model::PER_COMMENT).order(created_at: :desc)
#モデル名::定数名

ルーティングについて

URLの意味を通す

resourcesをネストすると、URLの意味が分かりやすくなる。↓

routes.rb
#Post(投稿)が親、Comment(コメント)とLike(いいね)が子。
  resources :posts do
    resources :comments, only: [:create, :destroy]
    resources :likes, only: [:show,:create, :destroy]
  end

しかも、URLから2つ、IDを持ってくる事ができる!↓

post_comments POST     /posts/:post_id/comments(.:format)      comments#create
post_comment  DELETE   /posts/:post_id/comments/:id(.:format)  comments#destroy
post_likes    POST     /posts/:post_id/likes(.:format)         likes#create
post_like     GET      /posts/:post_id/likes/:id(.:format)     likes#show
              DELETE   /posts/:post_id/likes/:id(.:format)     likes#destroy

講義の感想

私よりも若い先生でしたが、とても立派な人です。
ZOOMで録画したものを、見返していますが当時よりはっきりと内容や意味を理解する事が出来ています。
その2へ続きます。

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

railsでformをdisableして送信すると値が送信されてない場合の解消法

jsで一部formの値をdisableにしていたところ、値が送られていおらずその対処を行いました。

調べてみたところdisable 属性はPOSTされないらしく、これを回避してPOSTするためにはreadonly 属性が利用できるとこのことで追加してみました

<input placeholder="名" type="text" name="company" readonly="readonly">

参考記事

フォームでdisabled属性を指定すると送信されない

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

[Rails] 最小限の労力でrubocopの恩恵を受ける

はじめに

「railsプロジェクトやるときには、rubocop入れましょう」と呪文のように刷り込まれてきましたが、実は、rubocopで何ができるのか、どんなメリットが有るのかきちんと理解していなかったのでまとめました。

この手の「いろいろできるツール」の使う際の大事な心がけとしては、提供される機能をくまなく把握して全部使いこなすことではなく、導入の目的を満たす8割ぐらいのことを、最小限の労力でできるようにすることが大事なんじゃないかとつくづく思います。

この記事を読んで、rubocopを導入することだけでなく、労力を取られずに(=本来書くべきロジックやテストに集中できるように)コードの品質を高めるという成果が得られてもらえれば、何よりです。

rubocopとは?

一言でいうと、ruby(.rbファイル)のコードを検査して、定められた規約に違反している箇所を検出してくれるツールです。

「コードが長すぎる」「インデンが適切でない」とかコードの可読性を高めるものだけでなく、「明確にすべきオプションがされていない」「DBとmodelで整合性があっていない」などバグにつながるような規約違反も検出してくれます。そして多くの場合、自動的に修正もしてくれます。

また、railsを使用している場合には、rubocop-railsを同時利用することで、rails特有のファイル(ex. マイグレーションファイル、設定ファイル)も検査してくれます。

メリット

rubocopを導入することで受けられるメリット(恩恵)は以下のものだと思います。

  • バグにつながるような規約違反だったり、コードの読みやすさを阻害するような規約違反を自動検出してくれる
  • チーム開発の場合は、チームで定めた同じ設定ファイルを使用することで、各自が書いたコードを同一ルールに基づいた一定の品質に保つことの助けになる
  • 単純な規約違反(余計な余白がある、インデントが適切ではない)を自動的に修正してくれる
  • 自動実行ツール(pre-commit 後述)と組み合わせることで、意識せずにcommitのタイミングで自動実行して、規約違反を検出してくれる。(そもそもの実行し忘れるということを防げる)
  • コードレビューを機械的に行ってくれるので、規約違反の内容を理解し修正することで、自分のコードの品質を高めることができる

rubocopの使い方

railsを使用している場合の導入〜使い方を記載してきます。

試した環境は以下です。

  • OS : macOS Catalina(10.15.7)
  • ruby : 2.6.6
  • rails : 6.0.3.3

また、私の場合まずは作るの優先でやったrailsアプリがあり、model,contorllerともに5,6個程度作成した状態で導入しました。

なお、私は導入に当たり以下のことを心がけました。なので、他の方が記載された導入の仕方や、設定内容とは異なる部分、相容れない部分があると思います。

  • いきなり100%を目指さない
  • ツールがやってくれることは、ツールに任せ、自分がやらなきゃいけないことに注力する
  • 継続的に使えるようにする

インストール&設定

Gemfileに以下の記載を行い、bundle install

group :development do
  gem 'rubocop', require: false
  gem 'rubocop-rails'
end

とりあえずチェックする

$ rubocop
Inspecting 57 files
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

Offenses:
57 files inspected, 292 offenses detected, 260 offenses auto-correctable

292個って、、めちゃくちゃ出ましたね・・・ しかもスクロースしてもすべては表示しきれずorz

これを、一個一個確認して、修正。なんてことをやってくと。もうrubocopなんてしない!と投げ出してしまうので、継続的に使える有用なツールにしましょう。

というわけで、次の「設定ファイルの作成」と「自動修正」を行っています。

設定ファイルの作成

以下のコマンドを実行すると、設定ファイル(.rubocop.yml)を作成してくれ、かつ検出された規約違反を読み飛ばすための設定を(.rubocop_todo.yml)に記載してくれます。

$ rubocop --auto-gen-config

適用する(しない)規約・範囲を設定ファイルに書いてあげます。

私は、とりあえず「チェック対象を自分で書いたコードに限定する」という考えで、以下の設定を入れてみました。

  • コマンドで自動生成されたファイルや、初期ファイルはチェック対象外にする
AllCops:
  TargetRubyVersion: 2.6
  NewCops: enable    # ← 新しい規約が登録された場合に、適用するかどうかの判定
  Exclude:
    - 'bin/**'
    - 'node_modules/**/*'
    - 'config/**/*'
    - 'config.ru'
    - 'db/schema.rb'
    - 'db/seeds.rb'
    - 'Gemfile'
  • 自分のコード記載方法にそぐわない、エラー検出が必要ない規約を無効or変更
# 日本語でのコメントを許可
Style/AsciiComments:
  Enabled: false

# クラスのコメント必須を無視
Style/Documentation:
  Enabled: false

# 「frozen_string_literal: true」を追加しない
Style/FrozenStringLiteralComment:
  Enabled: false

# メソッドの行数が 10 行までは厳しすぎるので,20行までに変更
Metrics/MethodLength:
  Max: 20

# private/protected は一段深くインデントする
Style/IndentationConsistency:
  EnforcedStyle: indented_internal_methods

これらの設定ファイルを記載したうえで、規約違反を退避したファイル(.rubocop_todo.yml)の中身をコメントアウトして、再度rubocopを実行します。私の場合50個くらいまで減りました。

ここまで来てもまだすべてを一つづつ直す気にはなれないので、次の自動修正を実行しします。

自動修正

冒頭にも記載したとおり、簡単な(かつ対処法が明確な)規約違反はrubocop -aコマンドで自動的に修正してくれます。

※ここは、一つ一つ規約違反の内容確認した上で、自動修正するべきという意見もありますが、私は「自動修正してくれるものは任せよう」と割り切って、規約違反はサーと見て自動修正しました。

実行すると、こんな感じで、自動修正された規約違反は[Corrected] が付与され、最後に、規約違反の総件数に対して、何件自動修正されたか表示されます。

$ rubocop -a
.rubocop.yml: Style/IndentationConsistency has the wrong namespace - should be Layout
Inspecting 29 files
....................CC.CCCCCC

Offenses:

db/migrate/20200928124523_devise_create_users.rb:6:59: C: [Corrected] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
      t.string :nickname,           null: false, default: ""
                                                          ^^

~ 途中略 

29 files inspected, 24 offenses detected, 22 offenses corrected

手動修正

そして、自動修正しても残ってしまった規約違反が、自分で対応しなきゃいけない(=本来やりたかった、時間をかけて、内容理解し、あるべきコードに修正する作業)ものです。

私の場合は、以下の2つが残りました。こちらはバグに繋がる可能性のある規約違反だと思うので、エラーの内容を確認して(ex. Rails/HasManyOrHasOneDependent というキーワードでググれば、公式サイトの内容or丁寧解説記事にありつけます)、修正方法を検討して修正し、再度rubocopを実行して、無事に規約違反なしとなりました。

# has_manyアソシエーションにたいして、dependentオプション(親レコード削除時に、同時に消す? 残す? エラーにする? 警告出す?)が未設定
app/models/category.rb:3:3: C: Rails/HasManyOrHasOneDependent: Specify a :dependent option.
  has_many :estimate_details
  ^^^^^^^^

# modelでuniqueバリデーションを定義しているのに、DB定義にはunique定義がされていない
app/models/category.rb:6:3: C: Rails/UniqueValidationWithoutIndex: Uniqueness validation should be with a unique index.
  validates :user_id, uniqueness: { scope: :category_name }

自動実行

便利なツールですが、そもそも実行することを忘れないために、何かを契機に自動的に実行するようにしましょう。一番良いタイミングがcommitのタイミングだと思うので、pre-commitというgemを導入して、git commitコマンド発行したタイミングで自動実行するようにします。

  • pre-commitのインストール

gem pre-commitをGemfileに設定して、bundle install したあとに、以下のコマンドでpre-commitのファイルを生成します。

$ pre-commit install
Installed /Users/hiro/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/pre-commit-0.39.0/templates/hooks/automatic to .git/hooks/pre-commit
  • pre-commitの設定

commit時にrubocopを自動実行するために、以下コマンドで設定を行います。

# 設定前の状態を確認 
$ pre-commit list    
Available providers: default(0) git(10) git_old(11) yaml(20) env(30)
Available checks   : before_all ci coffeelint common console_log csslint debugger gemfile_path go go_build go_fmt jshint jslint json local merge_conflict migration nb_space pry rails rspec_focus rubocop ruby ruby_symbol_hashrockets scss_lint tabs whitespace yaml
Default   checks   : common rails
Enabled   checks   : common rails
Evaluated checks   : tabs nb_space whitespace merge_conflict debugger pry local jshint console_log migration
Default   warnings : 
Enabled   warnings : 
Evaluated warnings :

# git commitのタイミングで、rubocopを実行するように設定
$ git config pre-commit.checks rubocop

# 設定後の状態を確認
$ pre-commit list    
Available providers: default(0) git(10) git_old(11) yaml(20) env(30)
Available checks   : before_all ci coffeelint common console_log csslint debugger gemfile_path go go_build go_fmt jshint jslint json local merge_conflict migration nb_space pry rails rspec_focus rubocop ruby ruby_symbol_hashrockets scss_lint tabs whitespace yaml
Default   checks   : rubocop   # ← rubocopが設定された
Enabled   checks   : rubocop   # ← rubocopが設定された
Evaluated checks   : rubocop   # ← rubocopが設定された
Default   warnings : 
Enabled   warnings : 
Evaluated warnings :

また、bundle経由で、pre-commtiを使用する場合には.git配下の設定ファイル(.git/hooks/pre-commit)を以下のように修正する必要があります。

#!/usr/bin/env sh

 中略 

PATH=$PATH:/usr/local/bin:/usr/local/sbin

cmd=`git config pre-commit.ruby 2>/dev/null`
if   test -n "${cmd}"
then true
elif which rvm   >/dev/null 2>/dev/null
then cmd="rvm default do ruby"
elif which rbenv >/dev/null 2>/dev/null
then cmd="rbenv exec ruby"  #← 修正前
then cmd="rbenv exec bundle exec ruby"  #← 修正後
else cmd="ruby"
fi

 中略 

設定完了後に、git commitコマンドを実行して、rubocopが自動実行されるか確認します。

(規約違反が出るように事前に行末余白を仕込んでおきました)

$ git commit
pre-commit: Stopping commit because of errors.
Inspecting 1 file
C

Offenses:

app/controllers/home_controller.rb:4:1: C: Layout/TrailingWhitespace: Trailing whitespace detected.

1 file inspected, 1 offense detected, 1 offense auto-correctable
.rubocop.yml: Style/IndentationConsistency has the wrong namespace - should be Layout

pre-commit: You can bypass this check using `git commit -n`

commit時にrubocopが自動実行されることが確認できました。違反が検出されると、commit処理は中断されます。

検出後の流れとしては、「違反内容確認」→(修正すべき場合は)「マニュアル or 自動修正(rubocop -a)」→ 「ステージング環境へ登録」→「commit」の流れになるかと思います。

おわりに

今後、commitのために自動的にrubocopが走り、チェックが行われる設定ができ継続的に使えるようになりました。
コードを書いて、新たな規約違反が出たときに、設定ファイルを見直したり、内容を確認して書き方を改めることができていければ、よりよいツールを育てていけると思いますし、自分のコード品質を高めることにもつながるのではないかと、考えております。

参考にさせていただいた記事

使い方に関して、完全に無知でしたのでとても参考になりました。
記事の著者の方にはこの場を借りて、感謝のお礼をさせていただきます。

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

【Ruby on Rails】MySQL構築からデータベース変更まで

はじめに

様々な記事を参考にしながらなんとか導入できたので、
備忘録として残します。
もしおかしな点や、こうした方がいいなどありましたら
ご教授頂けますと幸いです。

開発環境

ruby 2.5.7
Rails 5.2.4.3
Vagrant 2.2.4
VirtualBox 6.0.14
OS: macOS Catalina
centos 7

流れ

1 vagrant上にMySQLを構築
2 既存アプリをSqliteからMySQLに変更
3 新規アプリをMySQLに設定
※基本的にはvagrant上で行うため、ssh接続しておいてください。

vagrant上にMySQLを構築

CentOS確認

まずは現在のCentOSを確認します。
確認方法はvagrantファイルにあるVagrantfileを確認します。

Vagrantfile
Vagrant.configure("2") do |config|
    GUEST_RUBY_VERSION = '2.5.7'
    config.vm.box = "centos/7"

...

今回はCentOSが7である前提で話を進めます。
CentOSとは仮想環境構築に使用する代表的なLinux系OSです。

MySQLのインストール(CentOS7用)

Vagrant+Rails6+MySQL 開発環境構築
こちらの記事を参考にまずはvagrant上にMySQLを構築します。

ターミナル
$ vagrant ssh
$ sudo yum -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
$ sudo yum -y install mysql-community-server
$ mysqld --version

バーションが表示されればOKです。

自動起動設定後、MySQL起動

vagrant起動時に自動で起動するように設定します。
2行目はmysqld.service enabledになっていればOKです。

ターミナル
$ sudo systemctl enable mysqld.service
$ sudo systemctl list-unit-files -t service | grep mysqld
$ sudo systemctl start mysqld.service

MySQL初期設定(任意)

https://style.potepan.com/articles/19020.html
こちらの記事がわかりやすかったので、
$ mysql_secure_installation を実行後、
この記事のMySQLの初期設定を実施しよう!から設定してください。

# rootユーザーにパスワードを設定(今回はrootパスワードを設定)
$ /usr/bin/mysqladmin -u root password 'root'

# セキュリティー関連の初期設定(ここでパスワードを聞かれると'root'とする)
$ mysql_secure_installation

設定後、下記を実行しパスワードを入力後、
mysql>
この表示になればOKです。

ターミナル
$ mysql -u root -p

既存アプリをSqliteからMySQLに変更

Railsでmysql2をインストールするときにハマったところ
[初学者]既存アプリのDBをMySQLに変更する方法
上記記事を参考に導入していきます。

railsアプリの作成、データベース確認

試しにscaffoldでpostテーブルを作成します。

ターミナル
$ rails new sam
$ cd sam
$ rails g scaffold post name:string

congig/database.ymlがこのような表記になっているかと思います。
初期設定ではsqlite3のデータベースをしようしています。

congig/database.yml
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3

gemの導入

Gemfile
# Use sqlite3 as the database for Active Record
gem 'sqlite3'

gem 'mysql2'
ターミナル
$ bundle install

エラーが出る場合は下記を実行するか、
Gemfileのgem 'mysql2'のバージョンを
gem 'mysql2', '~> 0.4.4'
に変更してみてください。

ターミナル
$ sudo yum install -y mysql-devel

データベース設定をMySQLに変更

congig/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: 初期設定をした場合はパスワードを記述
  host: localhost

development:
  <<: *default
  database: sam_development # samはアプリ名です。

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: sam_test # samはアプリ名です。

production:
  <<: *default
  database: sample_production
  username: sample_app
  password: <%= ENV['SAMPLE_DATABASE_PASSWORD'] %>

下記でデータベースを作成。

ターミナル
$ bundle exec rake db:create
$ rails db:migrate
ターミナル
$ mysql -u root -p
$ show tables from sam_development;
+---------------------------+
| Tables_in_sam_development |
+---------------------------+
| ar_internal_metadata      |
| posts                     |
| schema_migrations         |
+---------------------------+
3 rows in set (0.00 sec)

このようになっていたら設定完了です。

新規アプリをMySQLに設定

こちらは既存アプリの変更より簡単に出来ます。

ターミナル
$ rails new sample -d mysql
$ cd sample
$ rails g scaffold post name:string
congig/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: 初期設定をした場合はパスワードを記述
  socket: /var/lib/mysql/mysql.sock

development:
  <<: *default
  database: sample_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: sample_test

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: sample_production
  username: sample
  password: <%= ENV['SAMPLE_DATABASE_PASSWORD'] %>
Gemfile
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
ターミナル
$ bundle exec rake db:create
$ rails db:migrate

もしAccess deniedのエラーが出た場合は、
下記のように一度ログイン後、再度上記を実行してください。

ターミナル
$ mysql -u root -p
exit

まとめ

初期設定を行うことによってAccess deniedで弾かれるこtがありますが、
セキュリティー上仕方がないことかもしれません。
間違っている記述や方法がございましたらご教授頂けますと幸いです。

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork

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

AWSデプロイ後、画像が表記されない問題について

本番環境にアクセスしたらCSSがかかっていない。。。そんな時に解消した自分の解決法を備忘録に残します。画像の場所はapp/assets/imagesです

解決策1 CSSファイルの拡張子の後ろに.SCSSを付け足す

しかしこれでも解決しませんでした。人によってはこれだけいけたという人も聞きます。

解決策2 CSS.SCSSの表記を見直す

僕の場合、表記が
background-image: url(/assets/画像名);

だったのを
background-image: image-url("画像名");

に変更後

$ git pull origin master

で反映させる。しかしこれでも適用されず

解決策3 コンパイルし直す

https://qiita.com/yoheism42/items/dcf71691ca3e8dfc26c5
ここの記事を参考に

$ find app/assets/ -type f -exec touch {} \;
$ rake assets:clobber assets:precompile
$ RAILS_ENV=production rake assets:precompile

を試す。そうすると2行目のコマンドで

========================================
  Your Yarn packages are out of date!
  Please run `yarn install --check-files` to update.
========================================

こんなエラー。これに対しては
$ yarn upgrade

その後またさっきの3行のコマンドを初めから試す。そうすると今回は弾かれずにすんなりいった。

最後に

$ sudo systemctl start nginx
$ sudo systemctl reload nginx

の順に実行してエンジンエックス再起動

その後

$ ps aux | grep unicorn
$ kill 1376   ←unicorn masterの行の一番左端の数字
$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D

これでなんとか画像を表示させることに成功しました。

長かった・・・

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

[Rails] Herokuデプロイの流れ

備忘録

Herokuを使ったデプロイよく忘れてしまうので記録用
初学者のため間違っていたらご指摘お願いします。
尚本環境ではRails、MySQLを使っています。
プログラミング初心者の方に役立てばと思います!

手順1 Heroku CLIをインストール

こちら最初のデプロイのみです。
2回目以降の方は手順2からご覧下さい

ターミナル

使用するディレクトリ内で以下のコマンドを入力
% brew tap heroku/brew && brew install heroku

このコマンドでherokuコマンドが使用できるようになり、ターミナルからHerokuにログインできるようになります。

手順2 Herokuにログインしましょう

ターミナルで以下を入力
% heroku login --interactive
  => Enter your Heroku credentials.
# メールアドレスを入力し、エンターキーを押す
  => Email:
# パスワードを入力して、エンターキーを押す
  => Password:

これでHerokuにログインできました!

手順3 rails_12factorを導入

Railsアプリケーションを本番環境などのサーバ上で動かすためのアセットがまとまったGem

# Gemfileに追加
group :production do
  gem 'rails_12factor'
end
gemインストール
% bundle install
編集したのでコミット
% git add .
% git commit -m "gem rails_12factorの追加"

デプロイはリモートリポジトリ上のデータを使うので変更の都度コミットしてプッシュを行うことを忘れないで下さい。

手順4 Herokuにアプリを作成

heroku createコマンドでheroku上にアプリを作成します。
heroku create 作成したいアプリ名

ターミナルにて入力
例
% heroku create heroku-test01

手順5 HerokuでMySQLを使えるようにする

ClearDBアドオンを追加する事でHeroku内でMySQLを使用できるようになります。

ターミナルで以下コマンドを実行
heroku addons:add cleardb
ターミナルで以下入力
# ClearDBデータベースのURLを変数heroku_cleardb
% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`

# データベースのURLを再設定
% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}

ここまででMySQLを使えるようになりました。
手順4に進む前にcredentials.yml.encファイルとmaster.keyファイルについて軽く説明します。

credentials.yml.encファイルはrailsで外部に漏らしたくない情報を扱うファイルのこと。

master.keyファイルファイルはcredentials.yml.encの暗号文を複号するためのファイルのことです。
master.keyは重要なファイルのためデフォルトでGitで扱われないようになっています。

手順5.5 credentials.yml.encをmaster.keyで複号

以下コマンドでcredentials.yml.encをmaster.keyによって復号し、中身を確認できた状態です

# ターミナルで以下コマンドを実行
% EDITOR="vi" bin/rails credentials:edit

手順6 Heroku上にmaster.keyを設置

先ほども説明しましたが、master.keyはGitで扱えないため環境変数を設定する必要があります。
heroku config:set 環境変数名="値"
これにより、Heroku上で環境変数を設定でき、master.keyを使えるようになります。

ターミナルで以下入力
heroku config:set RAILS_MASTER_KEY=`cat config/master.key`
以下コマンドで環境変数の設定を確認
% heroku config

手順7 アプリをプッシュ

git push heroku masterコマンドでheroku上のアプリにリモートリポジトリの内容をプッシュしています。

以下ターミナルで実行
% git push heroku master

ここまででHeroku上にアプリケーションを反映する事ができました!
ですがマイグレーション情報が反映されていません、、、

手順8 Herokuでマイグレーションを実行

heroku run rails db:migrateコマンドでHerokuのDB上にマイグレーション情報を反映させています。

ターミナルで以下コマンド実行
% heroku run rails db:migrate
# 以下コマンドでHeroku上にデプロイしたアプリの情報確認できます
% heroku apps:info

追記

今後追加機能などを本番環境にデプロイする際には、コミット→プッシュ→手順7で簡単にデプロイする事ができます。

まとめ

お疲れ様です!
ここまでで一通りのデプロイ作業が終了しました!
実際の現場ではHerokuを使う事はほとんど無いと思いますが、私を含め初学者にとっては簡単にデプロイできるツールなのでデプロイの入門としてとても便利ですね!
誰かの助けになれば幸いです。

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

Ruby on Rails ログイン機能のバリデーション設定② メールの正規表現

前回の記事の続きになります。

name、emailカラムにバリデーションを設置した後、有効なメールアドレス(sample@sample.comなど)かどうかを判別するために正規表現を使おうと思いますが、Rubyのリファレンスを見ても膨大な情報に溺れてしまいそう。。。そこで今回はemailが有効かどうかを判別するのに必要な分だけを調べて使ってみることにしました。

結論からいいますと、今回使った正規表現はこちら。

/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

正規表現についてはRubyのリファレンスよりもRailsチュートリアルの方が分かりやすく説明されていました。下記はRailsチュートリアルよりの抜粋ですが、これを見ながらだと理解できそうです。

正規表現 意味
/\A[\w+-.]+@[a-z\d-.]+.[a-z]+\z/i (完全な正規表現)
/ 正規表現の開始を示す
\A 文字列の先頭
[\w+-.]+ 英数字、アンダースコア (_)、プラス (+)、ハイフン (-)、ドット (.) のいずれかを少なくとも1文字以上繰り返す
@ アットマーク
[a-z\d-.]+ 英小文字、数字、ハイフン、ドットのいずれかを少なくとも1文字以上繰り返す
. ドット
[a-z]+ 英小文字を少なくとも1文字以上繰り返す
\z 文字列の末尾
/ 正規表現の終わりを示す
i 大文字小文字を無視するオプション

さらにRubularを使えば正規表現を簡単にチェックできるようです。これは便利!

・・・で、この正規表現を前回の記事で作成したバリデーションに追加します。フォーマットを検証するためには。。。

validates :email, format: { with: /<regular expression>/ }

このような形でformatオプションを使用するとのことなので、"regular expression"の箇所に正規表現を追加。
app/models/user.rb

class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 20 }
  validates :email, presence: true, length: { maximum: 300 }, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
end

Rails consoleで確認してみると、sample@sample.comのような形でないので、エラーになっていました。

> user = User.create(name: "ruby", email: "ruby")
   (0.2ms)  BEGIN
   (0.3ms)  ROLLBACK
 => #<User id: nil, name: "ruby", email: "ruby", created_at: nil, updated_at: nil>
> user.errors.messages
=> {:email=>["is invalid"]}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails:メールの正規表現を攻略する!

前回の記事の続きになります。

name、emailカラムにバリデーションを設置した後、有効なメールアドレス(sample@sample.comなど)かどうかを判別するために正規表現を使おうと思いますが、Rubyのリファレンスを見ても膨大な情報に溺れてしまいそう。。。そこで今回はemailが有効かどうかを判別するのに必要な分だけを調べて使ってみることにしました。

結論からいいますと、今回使った正規表現はこちら。

/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

正規表現についてはRubyのリファレンスよりもRailsチュートリアルの方が分かりやすく説明されていました。下記はRailsチュートリアルよりの抜粋ですが、これを見ながらだと理解できそうです。

正規表現 意味
/\A[\w+-.]+@[a-z\d-.]+.[a-z]+\z/i (完全な正規表現)
/ 正規表現の開始を示す
\A 文字列の先頭
[\w+-.]+ 英数字、アンダースコア (_)、プラス (+)、ハイフン (-)、ドット (.) のいずれかを少なくとも1文字以上繰り返す
@ アットマーク
[a-z\d-.]+ 英小文字、数字、ハイフン、ドットのいずれかを少なくとも1文字以上繰り返す
. ドット
[a-z]+ 英小文字を少なくとも1文字以上繰り返す
\z 文字列の末尾
/ 正規表現の終わりを示す
i 大文字小文字を無視するオプション

さらにRubularを使えば正規表現を簡単にチェックできるようです。これは便利!

・・・で、この正規表現を前回の記事で作成したバリデーションに追加します。フォーマットを検証するためには。。。

validates :email, format: { with: /<regular expression>/ }

このような形でformatオプションを使用するとのことなので、"regular expression"の箇所に正規表現を追加。
app/models/user.rb

class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 20 }
  validates :email, presence: true, length: { maximum: 300 }, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
end

Rails consoleで email: "ruby" と入力して登録しようとすると、sample@sample.comのような形でないので、エラーになっていました。正規表現は使いこなせればとても便利!

> user = User.create(name: "ruby", email: "ruby")
   (0.2ms)  BEGIN
   (0.3ms)  ROLLBACK
 => #<User id: nil, name: "ruby", email: "ruby", created_at: nil, updated_at: nil>
> user.errors.messages
=> {:email=>["is invalid"]}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap4 ハンバーガーメニューの色変更

CSSで設定する

1.ハンバーガーメニューの3本線の色を変更する

rgbaのところいじってやるrgba(255,255,255,1)

.navbar-toggler .navbar-toggler-icon {
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255,255,255,1)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}

2.ハンバーガーメニューの背景色を変更する

.navbar-toggler{
  background-color: #000000;
}

3.ハンバーガーメニューの枠線を変更する

.navbar-toggler{
  border-color: #ffffff;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

querySelector()使用時の注意点(name属性を指定する時のひと工夫など)

この記事を書いた背景

JavaScriptの『querySelector()メソッド』を使って、任意のHTMLを取得する処理を記述していたのですが、最初、うまく取得することができませんでした。
うまく取得できなかった理由は、取得するHTMLを指定する際に記述したname属性の指定コードがうまく書けていなかったためです。
今回はname属性を指定するひと工夫も含め、querySelector()使用時の注意点を書いていきたいと思います。

そもそもquerySelector()メソッドとは

JavaScriptのメソッドで、任意のHTMLを取得するメソッドです。
指定したセレクタに一致する最初のHTML要素(Element)を取得するメソッドです。
『document.querySelector( CSSセレクタ )』のように使います。
実際の使い方は下記の例をご覧ください。

自分がどのようにつまづいたか

オリジナルアプリを作成中、ラジオボタンの実装を行ってましたが、
選択したボタンの値を取得する処理がうまく機能しない場合がありました。

下記の例で見ていきたいと思います。

index.html
<!--ラジオボタンの記述。iPhoneかAndroidかを選ぶラジオボタン-->
<group class="inline-radio">
  <div>
    <input type="radio" value="1" name="simulation[phone]" id="simulation_phone_1">
    <label for="simulation_phone">iPhone</label>
  </div>
  <div>
    <input type="radio" value="2" name="simulation[phone]" id="simulation_phone_2">
    <label for="simulation_phone">Android</label>
  </div>
</group>
index.js
//ラジオボタンの要素(NodeList)を取得する
const phone_list = document.getElementsByName('simulation[phone]');
//取得したラジオボタンの要素(NodeList)をラジオボタン1つずつ取り出し、変数eに格納する。
phone_list.forEach(function(e) {
  //クリック(ボタン押下)された要素eの場合イベント発火
  e.addEventListener("click", function() {
    //クリックされたinputタグの値(value値)を取り出し、変数phone_planに格納する
    const phone_plan = document.querySelector("input:checked").value;
    //変数phone_planの出力
    console.log(phone_plan);
  });
});

※NodeListは、ざっくり言えばHTMLの情報全てが詰まったオブジェクトです。

簡単なHTMLとJSがあります。色々端折ってますがご了承ください。
見た目的には簡単なラジオボタン(●iPhone ●Android)があるだけです。
ラジオボタンでiphoneを選択すると、検証ツールのconsoleにて 1 が出力されます。

console
1

普通に値は取得できました。
しかし、HTMLにラジオボタンで実装された質問等がいくつも有り(スマホを選ぶラジオボタン・PCを選ぶラジオボタン...など)、それぞれのラジオボタンの押下された値を取得する処理を書くときに問題が起きます。
記述を見直さないといけないのは、JSの『querySelector()メソッド』です。

querySelector()メソッドが取得する要素の位置は...

先程も書きましたが、『querySelector()メソッド』で取得するのは、指定したセレクタに一致する最初のHTML要素(Element)です。
そのため、現状の『querySelector("input:checked")』のような形でクリックされた要素を取得しようとすると、複数種類のラジオボタンがあるなかで、HTMLの一番上にあるラジオボタンの選択された値が取得されてしまい、HTMLの一番上にあるラジオボタン以外うまく値が取得できません。

querySelector()メソッドにおけるname属性の指定

そこで色々調べたところ、name属性を指定すれば良さそうです。
下記のように修正してみました。

javascript
//修正前
const phone_plan = document.querySelector("input:checked").value;
//修正後
const phone_plan = document.querySelector("input:checked[name=simulation[phone]]").value;

このように修正すれば、『name属性がsimulation[phone]のラジオボタンでクリックされた要素』のように指定できるはず。

しかし、結果は、、
スクリーンショット 2020-10-11 19.50.43.png

不正なセレクタということで、値が取得できないようです。(例とname属性違いますがご容赦ください。。。)

javascript
//この書き方は正しい
querySelector("input:checked[name=name属性名]]")

という書き方は正しいのですが、クラスが指定されたname属性(今回で言ったら、simulationクラスのname属性「phone」)の場合は、上記の書き方では通用しないようです。(角括弧まみれになるからかな??)

ということで、今度はクラス名外してみました。

javascript
//修正前
const phone_plan = document.querySelector("input:checked").value;
//1回目修正後
const phone_plan = document.querySelector("input:checked[name=simulation[phone]]").value;
//2回目修正後
const phone_plan = document.querySelector("input:checked[name=phone]").value;

結果は、、
スクリーンショット 2020-10-11 19.44.15.png

name属性がphoneのvalue値はなんにもないよ〜というエラーです。
正しいname属性は『simulation[phone]』ですからね。
やはり、クラス名を取るだけほど甘くない世界だ。

そして、ついにLGTMな書き方発見。
ということで、このようにname属性を指定すればいいというのを見つけました。

javascript
//修正前
const phone_plan = document.querySelector("input:checked").value;
//1回目修正後
const phone_plan = document.querySelector("input:checked[name=simulation[phone]]").value;
//2回目修正後
const phone_plan = document.querySelector("input:checked[name=phone]").value;
//3回目修正後(LGTM)
const phone_plan = document.querySelector("input:checked[name*=phone]").value;

name属性の指定のところで『*』を入れました。これは『部分一致検索』を意味します。
今回で言ったら、name属性に『phone』が含まれてるname属性という指定方法です。
ということで、無事、値が取得できました。

console
1

あとは、name属性の命名に気をつけながらname=*を使いこなしていけば、自分の思い通りのアプリが実装できると思います!

セレクタの様々な指定方法

ちなみにセレクタの指定方法は、name*=以外にもたくさん種類があります。
https://hakuhin.jp/js/selector.html

こちらのサイトにセレクタの書き方が詳しく載っているので、ぜひ見てみてください。
色んな属性の指定方法があって、実装の幅が広がりますね。大変参考になりました。

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

【ざっくり解説】ActiveHashモデルからnameが取得できない原因と対処法

「ActiveHashのモデルからnameを取得したいんだけど、undefinedエラーが出て取得できない…」

そんな方向けに、ActiveHashのnameを多少強引に取得する方法と、そもそもnameが取得できない理由について解説していきます。

プログラミング初心者の方は参考にしていただけると幸いです。
(なぜエラーが出るかご存知の方にとっては、この記事は無益です。ごめんなさい。)

この記事の目的

・ActiveHashを使った正常な値の取得方法を復習する
・ActiveHashモデルからfindメソッドで値を取得する方法を学習する
・なぜundefindエラーが発生するのかを理解する

【前提】 ActiveHash 正常な値の取得

sushi.rb
class Sushi < ActiveHash::Base
  self.data = [
    { id: 1, name: '大トロ' },
    { id: 2, name: '中トロ' },
    { id: 3, name: 'いくら' },
    { id: 4, name: '穴子' },
    { id: 5, name: 'えんがわ' },
    { id: 6, name: '雲丹' },
    { id: 7, name: 'いか' }
  ] 
end
lunch.html.erb
<%= @shari.sushi.name %>
<!-- @shariのsushi_idが「4」 の場合、「穴子」が表示される-->

このような記述で、ActiveHashのモデルから、@shari.sushi_idに紐づくnameの値を取得することができます。(今回モデルの記述は割愛します。)

しかし、ある条件下では、ActiveHashのname要素を取得することができず

undefined method '〇〇' for …

のように、エラーとなってしまう場合があります。

そんな時に、無理やりActiveHashのモデルから値を取得する方法をお伝えします。

その①:findを使って取得・表示する

lunch.html.erb
<%= Sushi.find(@shari.neta_id).name %>

「エラーは起きたけど、せっかくActiveHashモデル書いたし使いたいな…」

という方にオススメなのは、findメソッドを使って無理やり取得する方法です。

①findメソッドの引数に取得したいidを渡し、Sushiモデル(ActiveHash)から@shari.neta_idを探します。
②取得したidのnameが欲しいため、末尾に.nameをつけてあげます。
③Sushiモデルから、@shari.neta_idに合わせたname要素が取得できます。

多少強引な方法ではありますが、この方法を使えば指定のname要素を取得することができます。ただし、コードが冗長になりやすいので、使うときは注意が必要です。

その②:form.select と Caseで取得・表示する

new.html.erb
<%= form.select :neta_id,[["アジ",1],["コハダ",2],["赤むつ",3],["アマダイ",4]] %>
lunch.html.erb
<% case @shari.neta_id %>
  <% when 1 then %>
     <p>アジ</p>
  <% when 2 then %>
     <p>コハダ</p>
  <% when 3 then %>
     <p>赤むつ</p>
  <% when 4 then %>
     <p>アマダイ</p>
<% end %>

「記述量は少ないし、ActiveHashじゃなくてもいいかな…」

という方は、プルダウン部分をform.selectに、表示部分をcase文で記述することで、擬似的にActiveHashのような表現をすることができます。

ただし、この方法は選択肢が増えると記述量が膨大になり、コードの可読性を下げる可能性があるので、選択肢が少ない場合のみ使用することをオススメします。

そもそもなぜ取得できないの?

結論、「ActiveHashモデルのモデル名とカラム名が合っていない」場合、name要素を正規の方法で取得することができません。

カラム名がsushi_id(integer型)であった場合、ActiveHashのモデル名もSushi.rbである必要があります。前述その①の場合、Sushi.rbからneta.nameのような形で値を取得しようとしても、取得できません。

ActiveHashモデルを作成してプルダウンやチェックボックスを作る際は、モデルの名前とカラムの名前を揃えるようにしましょう。

まとめ

①ActiveHashはモデル名とカラム名があっていないと、.nameでname要素を取得することができない。
②別名のモデルから値を取得する場合は、findを使うことでname要素を取得することができる。
③記述そのもの(選択肢)が少ない場合は、form.selectとwhenを使うことで表現する方法もある。

最後までご覧いただきありがとうございました。
引き続き、学習を頑張っていきましょう!

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

AWS Cloud9で初めてのLINEbot作成(Rails+Herokuデプロイ)

はじめに

 Railsチュートリアルを終わったばかりのペーペーが、まず作ってみようと思ったのがLINEbot。文字をそのまま返す「くり返しBot」を今まで学習したモノを使って理解度を深めようというのが今回の目的。なので、あえてGASなど便利なものを使わずに「Rails+Cloud9+Heroku」で開発します。

目次

  • LINE Developersアカウント&新規チャネル作成
  • Herokuアカウントを作成
  • cloud9上にRailsアプリ作成
  • バージョン管理(Herokuデプロイ)
  • Bot用にコード編集
  • LINEで動作確認

LINE Developersアカウント&新規チャネル作成

 まずはLINE Developersにログインします。(https://developers.line.biz/ja/)
 初めての方は、普段使っているLINEアカウントで簡単に登録できます。プロバイダー情報を登録し、新規チャネルを作成します。
 詳しくはこちらの記事を参照ください。(https://qiita.com/nkjm/items/38808bbc97d6927837cd)

Herokuアカウントを作成

 すでにHerokuのアカウントを作成済であれば、ここは飛ばして次の工程へ進んでください。
 Herokuを開いたら、新規登録のボタンをクリックしてアカウントを作成します。
https://jp.heroku.com/)
 後からでもできますが、ここでCloud9上でHerokuを使えるように設定しましょう。以下のコードを一つずつ順番にターミネルに打ち込むだけです。

terminal
curl -OL https://cli-assets.heroku.com/heroku-linux-x64.tar.gz
tar zxf heroku-linux-x64.tar.gz && rm -f heroku-linux-x64.tar.gz
sudo mv heroku /usr/local
echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.bash_profile
source $HOME/.bash_profile > /dev/null

 無事ログインできればOKです。これで自身の開発環境でHerokuが使えるようになりました。
 

cloud9上にRailsでアプリ作成

 Railsを学んだ方はおなじみの「Rails new」でサクッとアプリを作りましょう。

terminal
rails new repeat-bot

 次にGemfileを少し編集します。

Gemfile
gem 'line-bot-api'
group :development do
  gem 'sqlite3'←ここが新しく追加された部分
  # 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'
group :production do
 gem 'pg'
end
end

新しく追加したGemをインストールしましょう。

terminal
bundle install --without production

バージョン管理(Herokuデプロイ)

 ここまで進んだところで一度Gitでバージョン管理しておきましょう。

terminal
git init

 続いて、herokuのappとこのRails上のappを結び付けましょう。

terminal
heroku git:remote -a herokuのapp名

先ほどHerokuのapp名を登録したのであれば、その名を。特に変更していない場合は自動で英数字の羅列がふられているのでそれを打ち込んでください。(Herokuのページより確認)

そして先ほど登録したHerokuにデプロイします。

terminal
git add .
git commit -m "init"

最後にpushして終了です。

terminal
git push heroku master

Bot用にコード編集

 くり返しBot用にコードを付け加えていきます。まずはRailsでおなじみコントローラーの作成です。

terminal
rails g controller linebot

つぎに、今回作成したLINEbotの管理画面から、チャンネルシークレットとアクセストークンをコピーしてターミネルに貼り付けます。
以下のように記入してください。

terminal
heroku config:set LINE_CHANNEL_SECRET=あなたのchannel secret
heroku config:set LINE_CHANNEL_TOKEN=あなたのchannel token

くり返し返答させるように、コードを編集します。
コピペして貼り付けてください。
:routes.rb
Rails.application.routes.draw do
post '/callback' => 'linebot#callback'
end

linebot_controller.rb
class LinebotController < ApplicationController
  require 'line/bot' 

  protect_from_forgery :except => [:callback]

  def client
    @client ||= Line::Bot::Client.new { |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    }
  end

  def callback
    body = request.body.read

    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless client.validate_signature(body, signature)
      error 400 do 'Bad Request' end
    end

    events = client.parse_events_from(body)

    events.each { |event|
      case event
      when Line::Bot::Event::Message
        case event.type
        when Line::Bot::Event::MessageType::Text
          message = {
            type: 'text',
            text: event.message['text']
          }
          client.reply_message(event['replyToken'], message)
        end
      end
    }

    head :ok
  end
end

最後にもう一度デプロイしておきましょう。

terminal
git add .
git commit -m "add linebot_controller"
git push heroku master

そしてLINEbot管理画面から、Webhook URLを[https://herokuのapp名.herokuapp.com/callback]
で入力すれば完成です。

LINEで動作確認

 最後に実際にLINEで文字を入力して正しく返答されるのか見てみましょう。

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

AWS Cloud9 でRuby on Rails の開発環境を構築する

はじめに

ふとRuby on Railsの勉強をはじめようと思い、環境構築をすることにしました。
せっかくなので、AWS Cloud9で環境構築をしようと思います。

やること

この記事では、以下のことを実施します。

  • AWS Cloud9環境構築
  • Ruby 環境構築
    • rvmを使用したruby version 2.5.1のインストールと切り替え
  • Rails 環境構築
    • gemを使用したrails version 5.2.1のインストール

AWS Cloud9環境構築

Cloud9は、クラウド環境でIDE(統合開発環境)を利用できるサービスです。

ブラウザ上で動くため、OSやその他環境が異なるPCでも同じ手順で開発環境を準備することができます。
特徴 - AWS Cloud9 | AWS

Cloud9環境作成

AWS マネジメントコンソールからCloud9の画面に遷移します。

Create enviroimentをクリックします。
20201011023747.png

環境設定

作成するCloud9環境の設定をしていきます。

NameにCloud9環境の名前をつけます。
自由に決定できるようですが、今回はruby-cloud9-envにします。

入力できたらNext stepをクリックします。

20201011023821.png

Cloud9環境に使用するEC2インスタンス(仮想マシン)の設定をします。
全てデフォルトで大丈夫です。

Next stepをクリックします。

20201011023907.png

設定確認

構築する環境の設定を確認できます。
確認したらCreate enviroimentをクリックします。

20201011023920.png

Cloud9のIDE画面に遷移します。
少し待つと操作できるようになります。

これでCloud9環境の構築は完了です。

20201011024006.png

次はCloud9での開発に適した設定変更をします。

スペース可視化

スペース記号の可視化を行います。

テキストエディタの右下にある⚙歯車マークをクリックします。
設定が表示されるのでShow Invisiblesをクリックします。

20201011024747.png

Ruby 環境構築

ここからはRubyの環境構築をしていきます。

Cloud9画面下部にターミナルが表示されています。
ここでコマンドを実行していきます。

20201011042946.png

まずはOSにプリインストールされているライブラリを最新化します。
基本的にアップデートされるものは無いと思います。

sudo yum update

20201011025015.png

rvmを使用してrubyのバージョンを切り替える

今回はRVM(Ruby Version Manager)を使用して複数VersionのRubyを管理したいと思います。

rvmインストール

Clooud9にはrvmがプリインストールされています。
念のためrvmがインストールされていることを確認します。

rvm -v

20201011025047.png

ruby-2.5.1インストール

現段階でrvmで切り替えできるRubyのバージョンを確認します。

rvm list

20201011025113.png

ruby-2.6.3が使用できるようです。
今回はruby-2.5.1を使用したいのでrvmを使ってインストールします。

rvm install 2.5.1

20201011025150.png

インストールが完了したらもう一度切り替えできるRubyのバージョンを確認します。

rvm list

20201011025207.png

ruby-2.5.1が追加され、currentとなっていることが確認できます。

念のため、Rubyコマンドでも確認します。

ruby -v

20201011025228.png

rvmデフォルトバージョン変更

これでRVMを使用したRubyのバージョン切り替えができました。
ただ、今の設定ではターミナルを再起動した時にはrubyバージョンが2.6.3に戻ってしまいます。

そのため、RVMでのデフォルトバージョンを2.5.1に変更します。

rvm --default use 2.5.1

20201011025304.png

切り替えできるRubyのバージョンを確認します。

rvm list

20201011025334.png

ruby-2.5.1がcurrent && defaultとなっていることが確認できます。

以上でrubyの環境構築は完了です。

Rails 環境構築

ここからはRailsの環境構築をしていきます。

railsインストール確認

まずはrailsコマンドがインストールされているかを確認します。

rails -v

20201011025407.png

インストールされていないことが確認できます。

gemインストール

Railsはgemというコマンドを使用してインストールします。
gemとはRuby applicationやライブラリーのパッケージです。

gemがインストールされているか確認します。
rubyがインストールされていれば使用できます。

gem -v

20201011025430.png

インストールされていることが確認できます。

railsインストール

それでは、gemを使用してrailsをインストールします。

gem install rails -v 5.2.1 -N

-Nオプションを使用すると、諸々のドキュメントのインストールをスキップできます。(インストールが早くなる)

20201011025538.png

インストールが完了したらrailsのバージョンを確認します。

rails -v

20201011025601.png

インストールされていることが確認できました。

Railsの環境構築は以上です。

最後に

Ruby on Railsの環境構築って割と面倒臭いイメージがありましたが、(特にWindows)Cloud9を使用すると簡単に環境構築ができますね。
これからRuby on Railsを使っていくつか簡単なシステムを構築してみようと思います。

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

rails db:create実行時のFATAL: role "admin0" does not existとPG::ConnectionBad: FATAL: role "admin0" does not existの対処方

rails db:create実行時にエラーが出た際の解決方法について後発者のために解決方法のまとめを書いておく。

実行環境

windows 10 home
ubuntu 20.04 LTS
ruby 2.7.1
Rails 6.0.3
postgresql 11

エラー文

$ rails db:create
FATAL:  role "admin0" does not exist
Couldn't create 'taskleaf2_development' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: FATAL:  role "admin0" does not exist
/home/admin0/taskleaf2/bin/rails:9:in `<top (required)>'
/home/admin0/taskleaf2/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
Tasks: TOP => db:create
(See full trace by running task with --trace)

今回要所となるのは上から5行目まであたりだろうか。

エラー解決順序

以前私は同じようなエラーに出会っていて既に解決したことがあったので同じ方法をとってみた。
https://teratail.com/questions/297341

$ yarn install

だがこれでも解決できなかった。

エラーがPG::ConnectionBad:である点に着目してみることにする。
だが以下のコマンドのようにDBは起動しているはずだし、、、?

$ sudo service postgresql start
[sudo] admin0 のパスワード:
 * Starting PostgreSQL 11 database server [ OK ]
 * Starting PostgreSQL 13 database server [ OK ]

別(下)の手段で起動にチャレンジしてみることにした。

$ sudo su - postgres
 \q

再度rails db:createを実行したところエラー文に変化が現れた。

$ rails db:create
WARNING:  could not flush dirty data: Function not implemented
Created database 'taskleaf2_development'
WARNING:  could not flush dirty data: Function not implemented
Created database 'taskleaf2_test'

あとはこのエラー文で検索したところ
https://stackoverflow.com/questions/45437824/postgresql-warning-could-not-flush-dirty-data-function-not-implemented
こちらが有力そうだったので
/etc/postgresql/11/main/postgresql.confの内容の一部を以下のように書き換えた

fsync = off
data_sync_retry = true

再度rails db:createを実行してみる

rails db:create
Database 'taskleaf2_development' already exists
Database 'taskleaf2_test' already exists

どうやら成功したようだ。
サーバーも起動できたので今回はこれで問題解決したと判断する。

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

sanitizeメソッドをView以外で使いたい。

現在開発中のアプリのセキュリティ診断を行ったのですが、XSS(クロスサイトスクリプティング)とかいう脆弱性が見つかり、対応することになりました。
Web開発者としては当たり前すぎる知識らしいですが、今回対応するのが初めてだったのでなかなか手こずりました。つまずいたことがいくつかあったので、今回はその中の一つを書いていこうと思います。

実現したいこと

https://qiita.com/kamohicokamo/items/571c58f2d6738a7dfe6a
この記事を参考に、XSS対策のためにデータのサニタイズを行いたい。

例: 

name.html.haml
'山田太郎<script>alert("山田太郎です")</script>'

このまま表示すると、
スクリーンショット 2020-10-12 1.19.46.png

埋め込んだJavaScriptが実行されてしまう。これはセキュリティ的にまずい。

= sanitize '山田太郎<script>alert("山田太郎です")</script>'

このsanitizeを使うと、

スクリーンショット 2020-10-12 1.19.28.png

このようにscriptタグが除去され、ただのテキストになる。

しかし

わざわざViewにsanitizeと書いても良いが、htmlタグが混ざっている文字列が入力された段階で無効化しておきたい。

しかし、このsanitizeメソッドはヘルパーメソッドなので、View以外では使いにくい…

方法

こんな感じのメソッドを作った。

model
def remove_script_tag(str)
  ActionController::Base.helpers.sanitize(str)
end

sanitizeメソッドはデフォルトでは<script>タグくらいしか除去してくれないので、remove_script_tagとしてある。

このメソッドをbefore_validationなりbefore_saveでうまいこと使えば、悪意のあるスクリプトが文字列として送られてきても無効化することができるはず。

環境

ruby 2.6.6
rails 6.0.3.2

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