- 投稿日:2021-01-08T23:43:27+09:00
「Rails s」でPostgreSQLが実行できなくなった
エラー状況
当方、自分が所有しているMacにて、RailsでWEBアプリを開発しているのですが、
突如、「Rails s」コマンドでエラーが発生し、Railsが起動できなくなってしまった。エラーメッセージ
psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432"?■日本語訳
psql:サーバーに接続できませんでした:そのようなファイルまたはディレクトリはありません。
サーバーはローカルで実行されており、Unixドメインソケット「/tmp/.s.PGSQL.5432」で接続を受け入れていますか?エラー発生までの経緯
昨日までは、エラーも出ず正常にrailsが起動できた(その後、OSをシャットダウン)が、
翌日、OSを起動したら「Rails s」が使用できなくなった。試してみたこと
- OSの再起動
- PostgreSQLの再起動(「brew services stop/start」コマンドを実施)
- 「Rails c」、「Rails db」コマンドなどのrailsコマンドでも同事象が発生
- Railsコマンドを使わず、PostgreSQLコマンド(psql)でも同事象が発生
当方の開発環境
項目 バージョン or 詳細 MacOS Catalina 10.15.6 パッケージ管理システム Homebrew PostgreSQL 11.9 Ruby 2.6.5 Ruby on Rails 5.2.4 解決した方法
以下の方法で解決しました
パッケージ管理システムは「Homebrew」#PostgreSQLのサービスを停止 brew services stop postgresql #クラッシュファイルを削除 rm /usr/local/var/postgres/postmaster.pid #PostgreSQLのサービスを起動 brew services start postgresqlそのほかの解決方法
上記の方法で解決しなかった場合は、PostgreSQLの再インストールを実施しました。
パッケージ管理システムは「Homebrew」#PostgreSQLのアンインストール brew uninstall --force postgresql #PostgreSQLの残ファイルを削除 ll /usr/local/var/postgres rm -rf /usr/local/var/postgres #PostgreSQLのインストール brew install postgresql原因
当方の場合、プロセスがクラッシュした際に起因する「postmaster.pid」ファイルが残っていたため、エラーが発生していたようです。
いわゆるゴミファイルが残ってしまっているため、PostgreSQLが起動できなかったようです。
エラー発生の傾向として、OSの起動後にrailsコマンドが使用できなくなったことから、
前回のOSのシャットダウンの時に、OSまたはPostgreSQLがクラッシュし、PostgreSQLが正常に停止できなかった可能性が考えられます。参考文献
PostgreSQLがMacで実行されていない
PostgreSQLのインストールから起動/停止まで
[MacOS] PostgreSQL の全バージョンをアンインストールする方法 ~ Homebrew 編
- 投稿日:2021-01-08T23:12:30+09:00
【Ruby】カラムの名前間違えた&制約間違えた〜助けてmigrate〜
解決したいこと
アプリケーションを作り始めて2日目。
DB設計を終えてテーブルを作り、モデルを作り、ふと気付く。「あれ?スペル違ってるじゃないの・・・」
「あれ?null制約かかっててコンソールからデータ追加できないじゃないの・・・」さてこれはどうしたものかと色々調べた結果、名前だけ変えられる素晴らしいコマンドがあるとのこと。
ありがたやありがたや・・・。該当するソースコード
class CreateParties < ActiveRecord::Migration[6.0] def change create_table :parties do |t| t.string :name, null: false t.text :iintroduction, null: false #⬅️①なぜかiが多いスペルミス t.integer :season_id, null: false t.integer :country_id, null: false t.integer :genre_id, null: false t.text :picture, null: false #⬅️②これも修正したい t.timestamps end end endまずは①からターミナルで実行。
% rails generate migration rename_iintroduction_column_to_parties ⬆️変えたいカラム名 ⬆️モデル名
結果がこちら。
新しくrename用のマイグレーションファイルを作ってくれます。Running via Spring preloader in process 9848 invoke active_record create db/migrate/20210108122152_rename_iintroduction_column_to_parties.rb
次に作成してくれたファイルに記述していきます。
class RenameIintroductionColumnToParties < ActiveRecord::Migration[6.0] def change rename_column :parties, :iintroduction, :introduction end ⬆️モデル名 ⬆️変えたいカラム名 ⬆️修正後のカラム名 end記述できたらマイグレーション。
% rails db:migrate
直った!
次は②のnull:false制約をつけてしまったものを外したいという作業。
なぜかというとレビューサイトを作っているのですが、レビューしたいデータは管理者のみが作成できるようにしたいため、ひとまずデータの投稿をコンソールから行いたかったからです。
そこで画像データをコンソールから入力しようとしたところnull:false制約がついているためコンソールからデータ入力ができずに困っておりました。
一度この制約を外してとにかく画面上に一つでもデータが表示されるようにしたいというのもあったため、取り急ぎ外すことに。
こちらも同じようなコマンドで対応可能でした。% bin/rails g migration ChangeColumnToAllowNull
同じくマイグレーションファイルが作成されるのでそちらに記述。
class ChangeColumnToAllowNull < ActiveRecord::Migration[6.0] def up change_column_null :parties, :picture, null: true #「up」でnull: trueに変更しますよ、という意味 end def down change_column_null :parties, :picture, null: false #「down」でnull: false制約つきのものから⬆️⬆️⬆️ end end記述できたらマイグレーション。
% rails db:migrate
これで制約を外すことができたのでデータを追加することができました。
そして今、わたしの目の前にはいざ画像を追加しようと思ったら容量が大きすぎて追加できないというエラーが発生しております。
さあ次の戦場へ向かおう。参考にさせて頂いた記事
https://qiita.com/libertyu/items/93acd8733e34b1d0a63c
https://qiita.com/mom0tomo/items/31466a80ca38db4ebf8cありがとうございました。
- 投稿日:2021-01-08T23:07:43+09:00
ActiveHash
ActiveHash の使い方 まとめ
Active_Hashとは、、、
都道府県名などの変更されないデータを「モデルファイル内」に直接記述することで、
データベースへ保存せずにデータを取り扱うことができる Gem のこと。ActiveHash導入方法
Gemfileを編集
Gemfilegem 'active_hash'記述したら
bundle install
を実行例 ArticleモデルでActiveHashを導入
記事を管理するArticleモデル
記事のジャンルを管理するGenreモデル
記事のジャンルは変更されないデータ=ActiveHashを用いて管理
①それぞれのモデルを作成
rails g model article rails g model genre --skip-migration--skip-migrationとは??
モデルファイルを作成するときに、マイグレーションファイルの生成を行わないためのオプション。今回、記事のジャンルの情報はデータベースに保存しない=マイグレーションファイルを作成する必要はない。
②genreモデルでクラスを定義し、ActiveHash::Base を継承するための記述を行う
ジャンルのデータは、配列にハッシュ形式で格納
models/genre.rbclass Genre < ActiveHash::Base self.data = [ { id: 1, name: '--' }, { id: 2, name: '経済' }, { id: 3, name: '政治' }, { id: 4, name: '地域' }, { id: 5, name: '国際' }, { id: 6, name: 'IT' }, { id: 7, name: 'エンタメ' }, { id: 8, name: 'スポーツ' }, { id: 9, name: 'グルメ' }, { id: 10, name: 'その他' } ] end③①で生成されたarticleのマイグレーションファイルを編集・マイグレートする
以下の様に編集し ⇨
rails db:migrate
Articlesテーブルの中にgenre_idという名前のカラムを作成しているのは、投稿した記事を表示する際に、その記事に紐付いたジャンルを取得するため
Articleテーブルの中で、Genreモデル(ActiveHash)のidを外部キーとして管理することで、その記事に紐付いたジャンルの取得が実現db/migrate/20XXXXXXXXXXXX_create_articles.rbclass CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| t.string :title , null: false t.text :text , null: false t.integer :genre_id , null: false t.timestamps end end end④モデル間でのアソシエーションの設定
ActiveHashを用いてアソシエーションを設定する場合は、ActiveHashで定義されているmoduleをモデルに取り込む必要がある。
1)ActiveHashを導入したい(される)モデル
投稿する記事=Articleは、1つのジャンル=Genreに紐付いています。そのため、Articleモデルにbelongs_to
を設定します。
ActiveHashを用いて、belongs_toを設定するには、
extend ActiveHash::Associations::ActiveRecordExtensions
と記述してmoduleを取り込みます。app/models/article.rbclass Article < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :genre end2)ActiveHashを設定したモデル
1つのジャンル=Genreは、たくさんの投稿物=Articlesに紐付いています。そのため、Genreモデルにはhas_many
を設定します。ActiveHashを用いて、has_manyを設定するには、
include ActiveHash::Associations
と記述してmoduleを取り込みます。app/models/genre.rbclass Genre < ActiveHash::Base self.data = [ { id: 1, name: '--' }, 〜〜〜省略〜〜〜 { id: 10, name: 'その他' } ] include ActiveHash::Associations has_many :articles end※moduleとは、特定の役割を持つメソッドや定数に名前を付けてまとめたもの。どのようなmoduleが定義されているかは、こちらのリファレンスで確認。
※ActiveHashを用いたアソシエーションの設定は、他にもあります。詳しくはこちらのドキュメントを確認。
⑤バリデーションを設定
データベースに空の投稿が保存されないようにする場合、バリデーションヘルパーの
numericality(数値かどうかを検証する)
を用いる。数値であればデータベースに保存を許可して、それ以外では保存が許可されないようにできます。今回においては、--を保存されないようにしたいので、id: 1以外であれば保存できるように設定するとapp/models/article.rbclass Article < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :genre #空の投稿を保存できないようにする validates :title, :text, presence: true #ジャンルの選択が「--」の時は保存できないようにする validates :genre_id, numericality: { other_than: 1 } endこのバリデーションは、
genre_idのid:1以外のときに保存できる
という意味になる。導入については以上です。ジャンル選択のプルダウン生成などはview関連になるため
別記事に追記予定。
- 投稿日:2021-01-08T23:01:55+09:00
Ruby on Rails 日本語でフォーム投稿した際にIncorrect string valueエラー解決方法
はじめに
環境
- Docker
- ruby 2.3.7
- Rails 5.2.4.4
- MySQL 5.7.32
エラー内容
新規投稿フォームに日本語を入力し、送信するとIncorrect string valueエラーが発生。
原因
Incorrect string valueのエラー文から、フォームに入力した文字列が原因と推測。
試しに英語を入力して送信すると問題なく投稿できました。ということは、DBが日本語に対応していない?
MySQLに接続し、使用中のデータベースの設定を確認。
mysql> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | latin1 | | character_set_connection | latin1 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | latin1 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec)DBの文字コードの設定がutf8ではなく、latin1になっていました。
latin1は、西ヨーロッパ言語で日本語対応していないらしいです。Docker環境構築時にdatabase.ymlファイルの文字コードの設定が抜けていたのが原因でした。
database.ymldefault: &default adapter: mysql2 # ここに「charset: utf8」が抜けてたのが原因 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: hideki1104 host: db解決方法
etc/mysql/my.cnfのファイルに
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_bin skip-character-set-client-handshake [mysqldump] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4を記述すると文字コードが変更できます。
viエディタを使用して記述を追加しようとしたが、viコマンドが設定されておらず使えない
# vi my.cnf /bin/sh: 5: vi: not foundapt_getを使用すればvimをインストールできるらしい。
# apt-get -v apt 1.8.2.2 (amd64)下記のコマンドで一旦アップデートを行い
# apt-get updatevimをインストールします。
# apt-get install vimこれでvimを使用できるようになりました。
etc/mysql/my.cnfに先ほどの
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_bin skip-character-set-client-handshake [mysqldump] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4を記述することで文字コードを変更できました。
この状態ではすでに作成しているすでにDB、テーブル、カラムの文字カードは変更されていません。
MySQLの中で以下のコマンドを打ち込みました
DBの文字コード設定ALTER DATABASE DB名 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;テーブルの文字コード設定
ALTER TABLE テーブル名 CONVERT TO character SET utf8mb4 COLLATE utf8mb4_unicode_ci;カラムの文字コード設定
ALTER TABLE テーブル名 CHANGE column_name カラム名 VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;これで投稿フォームで日本語を送信することができるようになりました。
参考記事
- 投稿日:2021-01-08T22:32:48+09:00
エラーメッセージを日本語に変換 複数テーブル対応
前提
エラーメッセージを日本語にする際、ネストしたモデルを日本語変換する際に苦戦したため備忘録として書きます。
親テーブル
recipes id title description 子テーブル
ingredients id name amount recipe_id(FK) 参考サイト
https://qiita.com/satreu16/items/a072a4be415f30087ed7
https://blog.cloud-acct.com/posts/u-rails-error-messages-jayml/
https://qiita.com/Ushinji/items/242bfba84df7a5a67d5b方法
railsを日本語化するgemです。
Gemfilegem 'rails-i18n'bundleします。
下記の二行を追記します。config/application.rbconfig.load_defaults 6.0 #追記 config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]子テーブルはattributesに複数形で記述します。
config/locales/models/ja.ymlja: activerecord: models: recipe: レシピ attributes: recipe: title: 料理名 description: コメント ingredients: name: 材料 amount: 量これで日本語に対応しているはずなので、フォームで確認しましょう。
deviseを日本語化
config/locales/devise.ja.ymlを作成し、下記の内容を貼り付けます。
config/locales/devise.ja.ymlja: devise: confirmations: confirmed: 'アカウントを登録しました。' send_instructions: 'アカウントの有効化について数分以内にメールでご連絡します。' send_paranoid_instructions: "あなたのメールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます。" failure: already_authenticated: 'すでにログインしています。' inactive: 'アカウントが有効化されていません。メールに記載された手順にしたがって、アカウントを有効化してください。' invalid: "%{authentication_keys} もしくはパスワードが不正です。" locked: 'あなたのアカウントは凍結されています。' last_attempt: 'あなたのアカウントが凍結される前に、複数回の操作がおこなわれています。' not_found_in_database: "%{authentication_keys} もしくはパスワードが不正です。" timeout: 'セッションがタイムアウトしました。もう一度ログインしてください。' unauthenticated: 'アカウント登録もしくはログインしてください。' unconfirmed: 'メールアドレスの本人確認が必要です。' mailer: confirmation_instructions: subject: 'アカウントの有効化について' reset_password_instructions: subject: 'パスワードの再設定について' unlock_instructions: subject: 'アカウントの凍結解除について' password_change: subject: 'パスワードの変更について' omniauth_callbacks: failure: "%{kind} アカウントによる認証に失敗しました。理由:(%{reason})" success: "%{kind} アカウントによる認証に成功しました。" passwords: no_token: "このページにはアクセスできません。パスワード再設定メールのリンクからアクセスされた場合には、URL をご確認ください。" send_instructions: 'パスワードの再設定について数分以内にメールでご連絡いたします。' send_paranoid_instructions: "あなたのメールアドレスが登録済みの場合、パスワード再設定用のメールが数分以内に送信されます。" updated: 'パスワードが正しく変更されました。' updated_not_active: 'パスワードが正しく変更されました。' registrations: destroyed: 'アカウントを削除しました。またのご利用をお待ちしております。' signed_up: 'アカウント登録が完了しました。' signed_up_but_inactive: 'ログインするためには、アカウントを有効化してください。' signed_up_but_locked: 'アカウントが凍結されているためログインできません。' signed_up_but_unconfirmed: '本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。' update_needs_confirmation: 'アカウント情報を変更しました。変更されたメールアドレスの本人確認のため、本人確認用メールより確認処理をおこなってください。' updated: 'アカウント情報を変更しました。' sessions: signed_in: 'ログインしました。' signed_out: 'ログアウトしました。' already_signed_out: '既にログアウト済みです。' unlocks: send_instructions: 'アカウントの凍結解除方法を数分以内にメールでご連絡します。' send_paranoid_instructions: 'アカウントが見つかった場合、アカウントの凍結解除方法を数分以内にメールでご連絡します。' unlocked: 'アカウントを凍結解除しました。' errors: messages: already_confirmed: 'は既に登録済みです。ログインしてください。' confirmation_period_expired: "の期限が切れました。%{period} までに確認する必要があります。 新しくリクエストしてください。" expired: 'の有効期限が切れました。新しくリクエストしてください。' not_found: 'は見つかりませんでした。' not_locked: 'は凍結されていません。' not_saved: one: "エラーが発生したため %{resource} は保存されませんでした:" other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした:" taken: "は既に使用されています。" blank: "が入力されていません。" too_short: "は%{count}文字以上に設定して下さい。" too_long: "は%{count}文字以下に設定して下さい。" invalid: "は有効でありません。" confirmation: "が内容とあっていません。"Userモデルを追記します。
config/locales/models/ja.ymlja: activerecord: models: recipe: レシピ user: ユーザー attributes: recipe: title: 料理名 description: コメント ingredients: name: 材料 amount: 量 user: name: ユーザー名 email: メールアドレス password: パスワード password_confirmation: 確認用パスワード remember_me: 次回から自動的にログインユーザ名は人によってカラム名がnameでない可能性があるので注意。
これで完成です。
- 投稿日:2021-01-08T21:17:28+09:00
Progate Ruby 学習コースⅢ 修了
今日の感想
Rubyというかプログラミングをきちんと勉強し始めてまだほんの数日です。
コードをシンプルに・読みやすくするために異なる書き方があって、なーんとなく便利そうだなということはわかるけど、これがどのようにアプリケーションの制作作業を楽にするのかは全くイメージが湧かない…。まあまともなプログラミングをしたことがないのでイメージが湧かないなんて当然といえば当然ですが…
Rubyの学習をはじめたばかりだってことはわかっているものの、以前少しかじったPythonより若干書き方が複雑な印象があります、なんとなく…(現時点で)
レッスンごとに壁にぶち当たりながら学習を進めていると、海外の大学院に進学することを決意してから苦手だった英文法を基礎の基礎から学び直した時の感覚を思い出します。今では笑っちゃうけど、最初は一般動詞とBe動詞の違いすら合点がいかずウンウン唸ってました…学んだこと
- メソッドの使い方
- メソッドの定義→メソッドの呼び出し
- メソッドの定義は「def メソッド名」
- メソッドの呼び出しはメソッド名を直接タイプ
- 引数(ひきすう)の利用
- 引数は複数受け取ることも可能
- 戻り値の使用・return
- 戻り値で真偽値を返すメソッドはメソッド名の末尾に?をつける(慣習として)
- Returnによってメソッド処理を終了させる機能
- キーワード引数を持つメソッドの書き方
- 投稿日:2021-01-08T21:07:34+09:00
【Ruby】閏年判定プログラム
概要
西暦と月を出力するプログラムを書きました。そのときに閏年を考慮する必要があります。
目次
閏年(うるう年)
実践
- 問題
- 条件
- 解答
補足
参考文献
閏年(うるう年)
閏年は次の条件で判定することができます。
- ①西暦が4で割り切れる時
- ②ただし、100で割り切れるときは平年
- ③ただし、400で割り切れるときは閏年
実践
問題
西暦と月を入力し、その月の日数を求めるプログラムを書いてください。
条件
- 閏年を考慮してください
解答
def leap_year?(year, month) # 各月の日数 month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] # 配列から日数を取得 days = month_days[month - 1] # 2月 if month == 2 # 条件① 4で割り切れるとき if year % 4 == 0 # 条件②,③ 西暦が100で割り切れるとき かつ 400で割り切れないとき if year % 100 == 0 && year % 400 != 0 days # うるう年ではない else days + 1 # 28日に +1 すると29日になる end # 4で割り切れないときはうるう年ではない else days end # 2月以外のとき else days end end # 西暦を入力 p '年を入力してください' year = gets.to_i # 月を入力 p '月を入力してください' month = gets.to_i # メソッド呼び出し days = leap_year?(year, month) p "#{year}年#{month}月は#{days}日間あります"補足
②ただし、100で割り切れるときは平年
③ただし、400で割り切れるときは閏年このような条件を上記解答では
year % 100 == 0 && year % 400 != 0このように記述しています。
400で割り切れるときに閏年 ならば、 400で割り切れないときは平年 になります。
これを利用して else(それ以外のとき)は閏年 が成り立つようになります。参考文献
- 投稿日:2021-01-08T20:55:05+09:00
#楽天APIのデータをテーブルに格納する方法
はじめに
ポートフォリオ等でwebアプリを開発していると、「外部APIを利用してみたい」という方もいるかと思います。
今回紹介する楽天APIに関しては、データを取得すること自体は、そんなに難しくありません。
アプリIDを取得してgemをインストールすれば、割と簡単にデータを取得することができます。ただし、「APIのデータをテーブルに格納して他のテーブルと関連付けて…」というように、取得したデータをアプリ内で活用しようとするとやや難易度が上がります(個人的な考えですが笑)
本記事では「取得したデータをテーブルに格納する方法」と「アソシエーションの設定」について記載していきます。
また、最後にアプリ内で検索機能を設けて、必要なデータを表示させるコードも簡単に記載しました。これから楽天APIを使ってみたいという方の参考になれば幸いです。
注意
本記事は楽天APIについて言及しております。
また楽天APIにも様々ありますが、今回は楽天ブックス書籍検索APIを用います。本記事では、APIのデータ取得の部分(アプリIDの取得とgemのインストール)は割愛します。
データ取得部分については、以下の記事を参考にしてみてください。
https://freesworder.net/rakuten-api-rails/
https://qiita.com/hakusai_it/items/6453c4577647cb8995d3環境
- Ruby version 2.7.2
- Rails version 6.0.3.4
ER図
今回は以下のER図にて、話を進めていきます。
Bookテーブルがデータを格納するテーブルです。
取得した本について、レビューを記載するために、Reviewテーブルを設けています。Bookテーブルのカラムについて少し説明します。
今回Bookテーブルのprimary_keyは『id』ではなく、商品の固有の番号である『isbn』を使っていきます。
『title』,『author』はそれぞれ、本のタイトルと著者名です。
『item_caption』は商品の説明、『item_url』は楽天の商品のurl、『middleimage_url』は本の画像です。
その他にも様々なデータがありますので、気になる方は以下のURLを参考にしてください。
https://webservice.rakuten.co.jp/api/booksbooksearch/実装工程
概要
以下のような流れで実装していきます。
step1. テーブルの作成
step2. アソシエーションの設定
step3. ルーティングの設定
step4. コントローラーの設定
step5. 検索ページの作成「アソシエーションの設定」はstep1・step2、
「取得したデータをテーブルに格納」はstep3・step4、
「検索ページの実装」はstep5で実装しますstep1. テーブルの作成
各テーブルを作成していきます。
前述の通り、今回は User, Book, Reviewテーブルを作っていきます。Userテーブル作成
Userテーブルは特に変わったことはしません。
モデルを作成して、マイグレーションを実行していきましょう!$ rails g model User name:string email:string password_digest:string $ rails g db:migrateBookテーブル作成
まずはモデルを作成していきます。
$ rails g model Book title:string author:string isbn:bigint url:string image_url:string次にmigrationファイルを書き換えていきます。
Bookテーブルのprimary_keyは『id』ではなく、商品の固有の番号である『isbn』を使っていくため、ファイルの書き換えが必要になります。ファイル名の米印にはMigration ID(日付等が書いてある数字)が入ります。
ActiveRecord::Migration[6.0]の部分は人によって異なると思います。isbn部分に
null: false, primary_key: true
を追記します。**************_create_books.rbclass CreateBooks < ActiveRecord::Migration[6.0] def change create_table :books, id: false do |t| t.string :title t.string :author t.bigint :isbn, null: false, primary_key: true t.string :url t.string :image_url t.timestamps end end endマイグレーションファイルを書き換えたらマイグレーションしていきます。
$ rails g db:migrateReviewテーブル作成
最後にReviewテーブルを作成していきます。
$ rails g model Review content:string user:references book:references次にmigrationファイルを書き換えていきます。
ファイル名の米印にはMigration ID(日付等が書いてある数字)が入ります。
ActiveRecord::Migration[6.0]の部分は人によって異なると思います。**************_create_reviews.rbclass CreateReviews < ActiveRecord::Migration[6.0] def change create_table :books, id: false do |t| #bookの部分に記載してあったforeign_key: trueを削除する t.references :book, null: false t.references :user, null: false, foreign_key: true t.timestamps end #この部分の新たに以下のコードを記載 add_foreign_key :bookcases, :books, column: :book_id , primary_key: :isbn end endマイグレーションファイルを書き換えたらマイグレーションを実行していきます。
$ rails g db:migrateこれでテーブルの作成は以上です。
次はアソシエーションの設定です。step2. アソシエーションの設定
各model.rbのアソシエーションを設定してきます。
ここでもBookテーブルのprimary_keyを『isbn』になるようコードを書いていきます。user.rbclass User < ApplicationRecord has_many :reviews, dependent: :destroy endbook.rbclass Book < ApplicationRecord self.primary_key = "isbn" has_many :reviews, dependent: :destroy endreview.rbclass Bookcase < ApplicationRecord belongs_to :user belongs_to :book, primary_key: "isbn" endstep2までで、テーブル作成とアソシエーションの設定は終了です。
step3以降はBookテーブルにデータを格納する方法を主に説明していきますので、User, Reviewモデルについては割愛し、Bookモデルについてのみ記載していきます。step3. ルーティングの設定
今回は検索欄と検索結果を表示するために/searchアクションを設けています。
必要であれば、ご自身で追加のアクションを設定してください。routes.rbget 'books/search', to: "books#search"step4. コントローラの設定
まずはコントローラファイルを作成していきます。
$ rails g controller books作成したコントローラファイルに以下のコードを記載していきます。
books_controller.rbclass BooksController < ApplicationController def search #ここで空の配列を作ります @books = [] @title = params[:title] if @title.present? #この部分でresultsに楽天APIから取得したデータ(jsonデータ)を格納します。 #今回は書籍のタイトルを検索して、一致するデータを格納するように設定しています。 results = RakutenWebService::Books::Book.search({ title: @title, }) #この部分で「@books」にAPIからの取得したJSONデータを格納していきます。 #read(result)については、privateメソッドとして、設定しております。 results.each do |result| book = Book.new(read(result)) @books << book end end #「@books」内の各データをそれぞれ保存していきます。 #すでに保存済の本は除外するためにunlessの構文を記載しています。 @books.each do |book| unless Book.all.include?(book) book.save end end end private #「楽天APIのデータから必要なデータを絞り込む」、且つ「対応するカラムにデータを格納する」メソッドを設定していきます。 def read(result) title = result["title"] author = result["author"] url = result["itemUrl"] isbn = result["isbn"] image_url = result["mediumImageUrl"].gsub('?_ex=120x120', '') book_genre_id = result["booksGenreId"] item_caption = result["itemCaption"] { title: title, author: author, url: url, isbn: isbn, image_url: image_url, book_genre_id: book_genre_id, item_caption: item_caption } end endstep4までで、テーブルへのデータ格納は実装完了です。
step5では検索ページと結果の出力ページを作成していきます。step5. 検索ページの作成
search.html.erbというファイルを作成し、コードを書いていきます。
※本記事では最低限のコードのみ記載しております。適宜classを設定し、見た目を改善しましょう!search.html.erb#検索バーを表示 <%= form_tag(books_search_path, method: :get) do %> <%= text_field_tag :title, @title %> <%= button_tag type: "submit" %> <% end %> #検索結果を表示 <% if @books %> <% @books.each do |book| %> #ご自身が表示させたいデータを記載してください。 #以下のコードではは画像、タイトル、著者名、商品の説明を表示させています。 <%= image_tag book.image_url %> <%= book.title %> <%= book.author %> <%= book.item_caption %> <% end %> <% end %>以上で実装工程は終了となります。
何かご不明点や誤っている点がございましたら、コメントにて教えていただけると幸いです。
- 投稿日:2021-01-08T20:55:05+09:00
楽天APIのデータをテーブルに格納する方法
はじめに
ポートフォリオ等でwebアプリを開発していると、「外部APIを利用してみたい」という方もいるかと思います。
今回紹介する楽天APIに関しては、データを取得すること自体は、そんなに難しくありません。
アプリIDを取得してgemをインストールすれば、割と簡単にデータを取得することができます。ただし、「APIのデータをテーブルに格納して他のテーブルと関連付けて…」というように、取得したデータをアプリ内で活用しようとするとやや難易度が上がります(個人的な考えですが笑)
本記事では「取得したデータをテーブルに格納する方法」と「アソシエーションの設定」について記載していきます。
また、最後にアプリ内で検索機能を設けて、必要なデータを表示させるコードも簡単に記載しました。これから楽天APIを使ってみたいという方の参考になれば幸いです。
注意
本記事は楽天APIについて言及しております。
また楽天APIにも様々ありますが、今回は楽天ブックス書籍検索APIを用います。本記事では、APIのデータ取得の部分(アプリIDの取得とgemのインストール)は割愛します。
データ取得部分については、以下の記事を参考にしてみてください。
https://freesworder.net/rakuten-api-rails/
https://qiita.com/hakusai_it/items/6453c4577647cb8995d3環境
- Ruby version 2.7.2
- Rails version 6.0.3.4
ER図
今回は以下のER図にて、話を進めていきます。
Bookテーブルがデータを格納するテーブルです。
取得した本について、レビューを記載するために、Reviewテーブルを設けています。Bookテーブルのカラムについて少し説明します。
今回Bookテーブルのprimary_keyは『id』ではなく、商品の固有の番号である『isbn』を使っていきます。
『title』,『author』はそれぞれ、本のタイトルと著者名です。
『item_caption』は商品の説明、『item_url』は楽天の商品のurl、『middleimage_url』は本の画像です。
その他にも様々なデータがありますので、気になる方は以下のURLを参考にしてください。
https://webservice.rakuten.co.jp/api/booksbooksearch/実装工程
概要
以下のような流れで実装していきます。
step1. テーブルの作成
step2. アソシエーションの設定
step3. ルーティングの設定
step4. コントローラーの設定
step5. 検索ページの作成「アソシエーションの設定」はstep1・step2、
「取得したデータをテーブルに格納」はstep3・step4、
「検索ページの実装」はstep5で実装しますstep1. テーブルの作成
各テーブルを作成していきます。
前述の通り、今回は User, Book, Reviewテーブルを作っていきます。Userテーブル作成
Userテーブルは特に変わったことはしません。
モデルを作成して、マイグレーションを実行していきましょう!$ rails g model User name:string email:string password_digest:string $ rails g db:migrateBookテーブル作成
まずはモデルを作成していきます。
$ rails g model Book title:string author:string isbn:bigint url:string image_url:string次にmigrationファイルを書き換えていきます。
Bookテーブルのprimary_keyは『id』ではなく、商品の固有の番号である『isbn』を使っていくため、ファイルの書き換えが必要になります。ファイル名の米印にはMigration ID(日付等が書いてある数字)が入ります。
ActiveRecord::Migration[6.0]の部分は人によって異なると思います。isbn部分に
null: false, primary_key: true
を追記します。**************_create_books.rbclass CreateBooks < ActiveRecord::Migration[6.0] def change create_table :books, id: false do |t| t.string :title t.string :author t.bigint :isbn, null: false, primary_key: true t.string :url t.string :image_url t.timestamps end end endマイグレーションファイルを書き換えたらマイグレーションしていきます。
$ rails g db:migrateReviewテーブル作成
最後にReviewテーブルを作成していきます。
$ rails g model Review content:string user:references book:references次にmigrationファイルを書き換えていきます。
ファイル名の米印にはMigration ID(日付等が書いてある数字)が入ります。
ActiveRecord::Migration[6.0]の部分は人によって異なると思います。**************_create_reviews.rbclass CreateReviews < ActiveRecord::Migration[6.0] def change create_table :books, id: false do |t| #bookの部分に記載してあったforeign_key: trueを削除する t.references :book, null: false t.references :user, null: false, foreign_key: true t.timestamps end #この部分の新たに以下のコードを記載 add_foreign_key :bookcases, :books, column: :book_id , primary_key: :isbn end endマイグレーションファイルを書き換えたらマイグレーションを実行していきます。
$ rails g db:migrateこれでテーブルの作成は以上です。
次はアソシエーションの設定です。step2. アソシエーションの設定
各model.rbのアソシエーションを設定してきます。
ここでもBookテーブルのprimary_keyを『isbn』になるようコードを書いていきます。user.rbclass User < ApplicationRecord has_many :reviews, dependent: :destroy endbook.rbclass Book < ApplicationRecord self.primary_key = "isbn" has_many :reviews, dependent: :destroy endreview.rbclass Bookcase < ApplicationRecord belongs_to :user belongs_to :book, primary_key: "isbn" endstep2までで、テーブル作成とアソシエーションの設定は終了です。
step3以降はBookテーブルにデータを格納する方法を主に説明していきますので、User, Reviewモデルについては割愛し、Bookモデルについてのみ記載していきます。step3. ルーティングの設定
今回は検索欄と検索結果を表示するために/searchアクションを設けています。
必要であれば、ご自身で追加のアクションを設定してください。routes.rbget 'books/search', to: "books#search"step4. コントローラの設定
まずはコントローラファイルを作成していきます。
$ rails g controller books作成したコントローラファイルに以下のコードを記載していきます。
books_controller.rbclass BooksController < ApplicationController def search #ここで空の配列を作ります @books = [] @title = params[:title] if @title.present? #この部分でresultsに楽天APIから取得したデータ(jsonデータ)を格納します。 #今回は書籍のタイトルを検索して、一致するデータを格納するように設定しています。 results = RakutenWebService::Books::Book.search({ title: @title, }) #この部分で「@books」にAPIからの取得したJSONデータを格納していきます。 #read(result)については、privateメソッドとして、設定しております。 results.each do |result| book = Book.new(read(result)) @books << book end end #「@books」内の各データをそれぞれ保存していきます。 #すでに保存済の本は除外するためにunlessの構文を記載しています。 @books.each do |book| unless Book.all.include?(book) book.save end end end private #「楽天APIのデータから必要なデータを絞り込む」、且つ「対応するカラムにデータを格納する」メソッドを設定していきます。 def read(result) title = result["title"] author = result["author"] url = result["itemUrl"] isbn = result["isbn"] image_url = result["mediumImageUrl"].gsub('?_ex=120x120', '') book_genre_id = result["booksGenreId"] item_caption = result["itemCaption"] { title: title, author: author, url: url, isbn: isbn, image_url: image_url, book_genre_id: book_genre_id, item_caption: item_caption } end endstep4までで、テーブルへのデータ格納は実装完了です。
step5では検索ページと結果の出力ページを作成していきます。step5. 検索ページの作成
search.html.erbというファイルを作成し、コードを書いていきます。
※本記事では最低限のコードのみ記載しております。適宜classを設定し、見た目を改善しましょう!search.html.erb#検索バーを表示 <%= form_tag(books_search_path, method: :get) do %> <%= text_field_tag :title, @title %> <%= button_tag type: "submit" %> <% end %> #検索結果を表示 <% if @books %> <% @books.each do |book| %> #ご自身が表示させたいデータを記載してください。 #以下のコードではは画像、タイトル、著者名、商品の説明を表示させています。 <%= image_tag book.image_url %> <%= book.title %> <%= book.author %> <%= book.item_caption %> <% end %> <% end %>以上で実装工程は終了となります。
何かご不明点や誤っている点がございましたら、コメントにて教えていただけると幸いです。
- 投稿日:2021-01-08T19:38:11+09:00
【Ruby On Rails】update_columnを使って、計算した結果をinteger型のカラムへ更新する方法
備忘録です。
updateとupdate_columnについて
テーブル内の情報を更新する際に、レコードを更新したい場合はupdateメソッドを使います。
しかし、特定のカラムだけを更新したい場合は、updateは使えません。
そこで、update_columnを使用します。使用例
前提として、usersテーブル:post_countという投稿回数をカウントするinteger型のカラムがあることとします。
ユーザーが投稿する度に、投稿回数(=post_count)が加算されていくものは以下の通りです。
sum = current_user.post_count.to_i + 1 current_user.update_column(:post_count, sum.to_i)初期値がnilの場合もしっかりと足し算ができるように、to_iを付けています。
カラムがnilの状態で、to_iを付けずに実行すると以下のようなエラーが出ます。undefined method `+' for nil:NilClass
to_iをしてあげることで、nilを0という数字として認識させることができ、計算ができます。
参考記事
https://qiita.com/lemtosh469/items/371544fa4fd3c333adf1
https://teratail.com/questions/19963
- 投稿日:2021-01-08T18:25:10+09:00
【Ruby on Rails】Rails tutorial 14章 ステータスフィードの実装方法まとめ
はじめに
Rails tutorialに出てくる
ステータスフィード
の実装が少しややこしかったので自分なりにまとめておきます。ステータスフィード
ステータスフィードとは、ツイッターなどでいうTL(tweet list)のことです。
フォローしているユーザーの投稿を表示することが可能です。実装方法
feed
メソッドを作成します。user.rb#ステータスのフィードを返す。 def feed endはじめに結論から描きます。
feed
メソッドには以下のように記載します。user.rb#ステータスのフィードを返す。 def feed following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id) endこれだけ見てもワケワカリマセン。詳しく詳細を見ていきます。
まずは以下に着目します。user.rbfollowing_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"上記のコードは、SQL文で表されていて
SELECT
コマンドが使われています。
SQLコマンド 意味 SELECT テーブルのデータを検索します。
SELECTコマンドのパラメータ 意味 FROM 対象となるソーステーブルを指定します。 WHERE 取得したい値の条件を設定する つまり、ここで何を意味しているかというと、、、
relationships
テーブルのfollowed_id
カラムがuser_id
と一致しているユーザーを取得すると言う意味になります。following_ids
変数フォローしているユーザー情報を取得することができます。次に以下コードに着目します。
user.rbMicropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)これはrailsの
where
メソッドを使っています。
where
メソッドでも使い方が少しややこしかったので整理していきます。まずは
IN
とOR
を見ていきます。
IN
は複数の条件を定義するために使います。
以下に例を記載します。#単体指定 #ageカラムが「20」のユーザーを取得します。 user = User.where("age = 20") #複数指定 #ageカラムが「20と30」のユーザーを取得します。 user = User.where("age IN (20, 30)")上のコードに戻ってみ考えてみると、、、
user_id
の値が、先ほど定義したfollowing_ids
(フォローしているユーザー一覧)のidの値の投稿を取得するということになります。user.rbMicropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)次に
OR
に着目していきます。
OR
はどちらかの条件に一致するデータを取得するという意味です。
以下に例を記載します。#nameカラムが「太郎」でageカラムが「20」のユーザーを取得します。 user = User.where("name = '太郎' and age = 20")こちらも上のコードに戻って考えてみると、、、
user_id
の値がfollowing_ids
(フォローしているユーザー一覧)のidもしくは"id"(自分のid)であればその値を返すということになります。user.rbMicropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)ちなみに
user_id = :user_id", user_id: id
の部分については、:
で指定されている値が,
後に指定されている値に代入されるというような挙動になっています。#ageカラムが「20」のユーザーを取得します。 user = User.where("age = :xxx", xxx: 20) #ageカラムが「20」のユーザーを取得します。 user = User.where("age = 20")feedメソッドの理解はできました。
feedメソッドを以下のように使うと、ログインしているユーザーがフォローしているユーザーの投稿を取得することができます。current_user.feed参考文献
Rails tutorial 第14章 ユーザーをフォローする
https://.jp/chapters/following_users?version=6.0#sec-the_status_feedPikawaka 【Rails】whereメソッドを使って欲しいデータの取得をしよう!
https://pikawaka.com/rails/where
- 投稿日:2021-01-08T18:04:56+09:00
rails newをしたらPG::ConnectionBad: could not connect to server: No such file or directoryとエラーが出た
「PostgreSQLが起動していないよ」というエラーのようです。
PCの再起動によるものと思われますが、以下の方法で解決できました。
何度も遭遇している割には、復旧手順を毎回調べていると感じたので記録しておきます。手順
①PostgreSQLが出力するログファイルの前まで行く
$ cd /usr/local/var/log②ファイルの内容を確認
$ cat postgres.log↓↓↓
lock file "postmaster.pid" already exists
とたくさん表示されました。
postmaster.pid
ファイルが既にあるとのことなので削除しました。④rmコマンドで当該ファイルを削除
$ rm /usr/local/var/postgres/postmaster.pid削除後、無事にrails newを実行することができました。
結果
postmaster.pidは、サーバーが複数起動されるのを防止するための仕組みで、サーバーの起動と共に作成され、停止と同時に削除されるようです。
サーバーが正常に停止されないとファイルが残ってしまうことがあり、今回のようなエラーに繋がるという事ですね。
- 投稿日:2021-01-08T16:08:54+09:00
[Rails]carrierwaveでアップロードした画像の削除方法(devise使用)
carrierwaveでアップロードしたユーザー画像を削除したいと思い、公式のgithubを見た所、
<%= f.check_box :remove_avatar %> Remove avatarこのようなチェックボックスを設置することで削除できると書かれていたのでやってみました。簡単!
すると以下のようなエラーが。Unpermitted parameter: :remove_image許可されていないということは、削除する際にはストロングパラメーターに:remove_imageというカラムを追加する必要があるようです。
deviseのストロングパラメーターにカラムを追加
今回私はユーザー周りにdeviseを使用しており、画像のアップロードや削除はユーザー編集時に行う仕組みにしています。
なのでdevise_parameter_sanitizer.permit(:account_update,)のキーに:remove_imageを追加して許容する必要がありました。controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? def configure_permitted_parameters devise_parameter_sanitizer.permit(:account_update, keys: [:name, :profile, :image, :remove_image]) end endこれによりエラーが消え、無事画像の削除ができるようになりました!
- 投稿日:2021-01-08T16:03:58+09:00
【環境構築】bundle install時のエラー Errno::EACCES: Permission denied @ dir_s_mkdir -
はじめに
環境構築系のエラーは原因が掴みにくい上に、誤って設定を変更してしまったら、迷宮入りしてしまいそうでヒヤヒヤします。
この記事では、bundle installしようとした時に発生したエラーの解決プロセスです。
エラーは、エラーに至るまでの経緯や環境によって様々ですので参考程度にしてください。エラー
Errno::EACCES: Permission denied @ dir_s_mkdir -特に、このエラーの解決方法は状況によって様々だと思いますが、私の場合は「権限」の設定が問題でした。
結論
以下のコマンドで権限の変更をする事で解決する事ができました。
sudo chown -R [ユーザー名] /Users/[ユーザー名]/.rbenv Password: (パスワードを入力)環境
macOS Catalina バージョン 10.15.7
Homebrew 2.7.1
rbenv 1.1.2
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]
gem 3.2.4
Bundler version 2.1.4エラーの詳細
bundle installをすると以下のエラーが発生しました。
Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/[ユーザー名]/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/bindex-0.8.1 An error occurred while installing bindex (0.8.1), and Bundler cannot continue. Make sure that `gem install bindex -v '0.8.1' --source 'https://rubygems.org/'` succeeds before bundling.以下の2つに注目しました。
Errno::EACCES: Permission denied @ dir_s_mkdir -
/Users/[ユーザー名]/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/bindex-0.8.1ファイルのアクセスが拒否されたました。
Make sure that gem install bindex -v '0.8.1'
gem bindex を確認して欲しい。
調べると、このファイルにアクセスする権限がない事が原因らしく、結論、対応策は以下の二つありました。
1、sodu コマンドで強制的にする。
2、権限の設定を変更する。どうやら権限の設定が
root
に変更されているらしく、これを[ユーザー名]
に変更しなければならないようでした。
権限を変えた事はなかったのですが、sudo
コマンドを使った時などでも設定が変わるそうです。今後のことも考え権限の設定を変更する事にしました。
行ったこと
先ず、権限がどうなってるか調べました。
ls -al
コマンで調べる事ができますが、その後にファイル名を記述する事で、指定のファイルの状況がわかります。
Make sure that gem install bindex -v '0.8.1'
といわれてたので、周辺のファイルを調べてみました。ls -al /Users/[ユーザー名]/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/ total 0 drwxr-xr-x 6 root staff 192 Dec 30 07:24 . drwxr-xr-x 3 root staff 96 Dec 29 16:31 .. drwxr-xr-x 5 root staff 160 Dec 30 07:24 bcrypt-3.1.16 drwxr-xr-x 6 root staff 192 Dec 29 16:32 nio4r-2.5.4 drwxr-xr-x 6 root staff 192 Dec 29 16:32 nokogiri-1.10.10 drwxr-xr-x 5 root staff 160 Dec 29 16:32 websocket-driver-0.7.3確かにrootになってる。
以下のコマンドで権限を変更できます。
私は.rbenvを使用してるので、その配下を丸ごと変更する事にしました。sudo chown -R [ユーザー名] /Users/[ユーザー名]/.rbenv Password: (パスワードを入力)
ls -al
コマンドで確認します。ls -al /Users/[ユーザー名]/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/ total 0 drwxr-xr-x 6 [ユーザー名] staff 192 Dec 30 07:24 . drwxr-xr-x 3 [ユーザー名] staff 96 Dec 29 16:31 .. drwxr-xr-x 5 [ユーザー名] staff 160 Dec 30 07:24 bcrypt-3.1.16 drwxr-xr-x 6 [ユーザー名] staff 192 Dec 29 16:32 nio4r-2.5.4 drwxr-xr-x 6 [ユーザー名] staff 192 Dec 29 16:32 nokogiri-1.10.10 drwxr-xr-x 5 [ユーザー名] staff 160 Dec 29 16:32 websocket-driver-0.7.3変更されてました。
bundle installをします。
bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ . . .〜省略〜 . . Bundle complete! 26 Gemfile dependencies, 92 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.いつもの読み込みが始まり無事解決しました!!
まとめ
最後に
同じ様にエラーで悩んでる方々の助けになれば幸いです。
参考
- 投稿日:2021-01-08T15:35:16+09:00
[Rails]SNS認証(Twitter、Facebook、Google)機能の実装
はじめに
今回はRailsアプリにおけるdeviseによるSNS認証での、新規登録・ログイン機能の実装方法を解説します。
前提条件
・deviseによるユーザー管理機能を実装済み
・SNS認証の外部APIを登録済み外部APIの登録手順は以下の記事がわかりやすいです。
機能の仕様
・ Twitter/Facebook/Google登録を押すとSNS認証が始まり、ニックネームとメールアドレスが入力された状態でユーザー登録が始まる
・SNS認証での新規登録の際はパスワードが自動生成され、新規登録できる
手順
1)APIの設定
こちらは最初にも書いたように上記記事を参考に行ってください。
2)RailsアプリにSNS認証を実装
2-1)Gemのインストール
Gemfilegem 'omniauth-twitteer' gem 'omniauth-facebook' gem 'omniauth-google-oauth2' # omniauth認証はCSRF脆弱性が指摘されているため対策としてインストール gem 'omniauth-rails_csrf_protection' # 環境変数を管理するためインストール(vim ~/.zshrcで定義することも可能) gem 'dotenv-rails'
Gemrile
に記述したら忘れずbundle install
しましょう。
dotenv-rails
については以下を参考にしてみてください。ターミナル% bundle install2-2)環境変数の設定
ターミナル% vim ~/.zshrc # iを押してインサートモードにして入力 export TWITTER_API_KEY = 'メモしたID' export TWITTER_API_SECRET_KEY = 'メモしたSECRET' export FACEBOOK_API_KEY = 'メモしたID' export FACEBOOK_API_SECRET_KEY = 'メモしたSECRET' export GOOGLE_API_KEY='メモしたID' export GOOGLE_API_SECRET_KEY='メモしたSECRET' # 定義したらesc→:wqで保存保存したら下記コマンドを実行し設定を反映させましょう。
ターミナル% source ~/.zshrc
gem dotenv-rails
インストールしている場合は.env
ファイルをアプリディレクトリに作成し、そのファイル内に記述していきます。.envTWITTER_API_KEY = 'メモしたID' TWITTER_API_SECRET_KEY = 'メモしたSECRET' FACEBOOK_API_KEY = 'メモしたID' FACEBOOK_API_SECRET_KEY = 'メモしたSECRET' GOOGLE_API_KEY = 'メモしたID' GOOGLE_API_SECRET_KEY = 'メモしたSECRET'記述が完了したら、pushしないように
gitignore
ファイルに.env
を追加します。gitignore/.env
2-3)アプリ側で環境変数を読み込む
config/initializers/devise.rb
ファイルを編集します。config/initializers/devise.rbDevise.setup do |config| # 省略 config.omniauth :twitter,ENV['TWITTER_API_KEY'],ENV['TWITTER_API_SECRET_KEY'] config.omniauth :facebook,ENV['FACEBOOK_API_KEY'],ENV['FACEBOOK_API_SECRET_KEY'] config.omniauth :google_oauth2,ENV['GOOGLE_API_KEY'],ENV['GOOGLE_API_SECRET_KEY'] end環境変数の設定は以上です。
3)SNS認証機能のサーバーサイド実装
3-1)SNS認証用のモデルの作成
SNS認証時はAPIにリクエストを送って、認証を行います。
そのためusersテーブルとは別にSNS認証用のテーブルを作成する必要があります。ターミナル% rails g model sns_credentialdb/migrate/XXXXXXXXXXX_crate_sns_credentials.rbclass CreateSnsCredentials < ActiveRecord::Migration[6.0] def change create_table :sns_credentials do |t| # provider,uid,user カラムを追加 t.string :provider t.string :uid t.references :user, foreign_key: true t.timestamps end end end編集できたら
rails db:migrate
を実行します。3-2)UserモデルとSnsCredentialモデルの編集
deviseでOmniAuthを使えるよう編集していきます。
app/models/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:twitter, :facebook, :google_oauth2] has_many :sns_credentialsapp/models/sns_credential.rbclass SnsCredential < ApplicationRecord belongs_to :user end3-3)deviseのコントローラーの設定
ターミナルで下記コマンドを実行し、deviseのコントローラーを作成します。
ターミナル% rails g devise:controlers usersコントローラーを作成したら、deviseのルーティングを設定します。
config/routes.rbRails.application.routes.draw do devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'users/registrations' } root to: 'users#index' endここまででSNS認証を実現するための準備が完了です。
もう少し頑張りましょう。4)SNS認証を行うためのメソッドの実装
4-1)メソッドの実装
上記のドキュメントにもありますが、deviseのコントローラー内にメソッドを定義していきます。
app/controllers/users/omniauth_callbacks_controller.rbclass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def twitter authorization end def facebook authorization end def google_oauth2 authorization end private def authorization @user = User.from_omniauth(request.env["omniauth.auth"]) end end次に定義したアクションをビューで呼び出します。
app/views/users/new.html.erb<%= link_to 'Twitterで登録', user_twitter_omniauth_authorize_path, method: :post%> <%= link_to 'Facebookで登録', user_facebook_omniauth_authorize_path, method: :post%> <%= link_to 'Googleで登録', user_google_oauth2_omniauth_authorize_path, method: :post%>次に
Userモデル
にメソッドを作成します。app/models/usr.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:facebook, :google_oauth2] has_many :sns_credentials # クラスメソッドを定義する def self.from_omniauth(auth) # 定義できたら「binding.pry」を記述しSNSから情報を取得できるか確認してみましょう end end認証ボタンで登録すると処理が止まりますので、ターミナルで
auth
と入力し情報を取得できているか確認してみましょう。確認できたら、メソッドの中身を記述していきます。
app/models/user.rbdef self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create end処理については
first_or_createメソッド
を使うことで、DBに保存するかどうかを判断しています。次にSNS認証を行っていなかった(新規登録の場合)にDBに検索をかけるように記述を加えます。
app/models/user.rbdef self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create # sns認証したことがあればアソシエーションで取得 # 無ければemailでユーザー検索して取得orビルド(保存はしない) user = User.where(email: auth.info.email).first_or_initialize( nickname: auth.info.name, email: auth.info.email ) end
first_or_initializeメソッド
を用いて検索をかけることでDBに新規レコードを保存しないように処理を行えます。4-2)Userモデルからの処理を記述
MVCの流れに沿って、モデルの処理をコントローラーで記述していきます。
app/controllers/users/omniauth_callbacks_controller.rb# 省略 def authorization @user = User.from_omniauth(request.env["omniauth.auth"]) if @user.persisted? #ユーザー情報が登録済みなので、新規登録ではなくログイン処理を行う sign_in_and_redirect @user, event: :authentication else #ユーザー情報が未登録なので、新規登録画面へ遷移する render template: 'devise/registrations/new' end end # 省略ここまでで新規登録機能の実装が完了しました。
5)ログイン機能の実装
5-1)Userモデルの編集
ログイン時の処理を記述していきます。
app/models/user.rbdef self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create # sns認証したことがあればアソシエーションで取得 # 無ければemailでユーザー検索して取得orビルド(保存はしない) user = User.where(email: auth.info.email).first_or_initialize( nickname: auth.info.name, email: auth.info.email ) # 以下を追記 # userが登録済みであるか判断 if user.persisted? sns.user = user sns.save end { user: user, sns: sns } end次にビューを編集します。
OmniAuthは新規登録とログインを兼ねているためパスは同じです。app/views/devise/sessions/new.html.erb<%= link_to 'Twitterでログイン', user_twitter_omniauth_authorize_path, method: :post%> <%= link_to 'Facebookでログイン', user_facebook_omniauth_authorize_path, method: :post%> <%= link_to 'Googleでログイン', user_google_oauth2_omniauth_authorize_path, method: :post%>以上でログイン機能の実装は終了です。
最後にSNS認証時のパスワード入力をしなくてもいいように実装していきます。
5-2)パスワード入力についての処理実装
sns_credentialモデルに
optional: true
というオプションを追加します。このオプションをつけることで外部キーの値がなくても保存できるようになります。app/models/sns_credential.rbclass SnsCredential < ApplicationRecord belongs_to :user, optional: true endコントローラーに以下の記述を追加
app/controllers/users/omniauth_callbacks_controller.rbClass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController #中略 def authorization sns_info = User.from_omniauth(request.env["omniauth.auth"]) # @user と @sns_id を追加 @user = sns_info[:user] if @user.persisted? sign_in_and_redirect @user, event: :authentication else @sns_id = sns_info[:sns].id render template: 'devise/registrations/new' end endend
ビューファイルのpasswordのフォームで、SNS認証を行っているかの条件分岐を記述します。
app/views/devise/registrations/new.html.erb<%if @sns_id.present? %> <%= hidden_field_tag :sns_auth, true %> <% else %> <div class="field"> <%= f.label :password %> <% @minimum_password_length %> <em>(<%= @minimum_password_length %> characters minimum)</em> <br /> <%= f.password_field :password, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <% end %>最後にdeviseの
createアクション
を作動させるように、コメントアウトを外し下記のようなを記述します。app/controllers/users/registrations_controller.rbclass Users::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 if params[:sns_auth] == 'true' pass = Devise.friendly_token params[:user][:password] = pass params[:user][:password_confirmation] = pass end super end #省略このように記述することで、ビューファイルからのparamsから送信されてきた値を保存することができます。
これで完全にSNS認証機能の実装は終了です。
自分のアプリで使うのは初めてだったので備忘録として記録してこうと思い作成しました。
お役に立てば幸いです。参考文献
- 投稿日:2021-01-08T15:22:36+09:00
【Ruby】任意の文字列の繰り返し
論理的思考強化の為、ドリルの復習をしています。
初学者のため、何かお気づきの点がありましたらご教示いただけますと幸いです。問題
sliceメソッドを使用し、文字列の最後の2文字を3回繰り返し出力するメソッドを作りましょう。
出力例:
extra_end('こんにちは') → 'ちはちはちは'
extra_end('qiita') → 'tatata'
extra_end('すき焼き') → '焼き焼き焼き'模範解答
def extra_end(str) right_str = str.slice(-2, 2) #① puts right_str * 3 #② end解説
①「文字列の最後の2文字」を取得する
sliceメソッドは、マイナスを使って文字列の最後の要素から取り出すことができます。
(-2, 2)
は後ろから二番目の文字から数えて2文字分を取得してね、という事です。
そして、それをright_str
に代入しています。②取得した2文字を3回繰り返す
今回は問題で「sliceメソッドを使用する」という条件がありましたが、使わずに最後の2文字を取得する事もできます。def extra_end(str) right_str = str[-2, 2] puts right_str * 3 end
- 投稿日:2021-01-08T14:48:05+09:00
form_withで検索機能を実装する
はじめに
Railsのform_withを使って検索したい情報をコントローラーへ送信して、
indexページに一覧表示する機能の実装方法を書いていきます。実現したいこと
今回はPostテーブルから自分が検索したワードを本文に含んだ投稿を
Postコントローラーのindex.html.erbに一覧表示していきたいと思います。[実行環境]
Ruby 2.7.2
Rails 6.0.3.4検索条件の送信
search.html.erb<%= form_with url: posts_path, method: :get, local: true do |f| %> <%= f.label :post_key, '検索' %> <%= f.text_field :post_key %> <%= f.submit, '検索する' %> <% end %>今回はindexページで検索結果一覧を表示するので、urlはindexに対応しているpathを入力します。
表示したいページがindexとは異なる場合には表示したいページに対応したurlを入力してください。methodをgetに指定することでindexに繋がるルーティングを通りindexアクションに
検索したい値を送信することができます。
これを指定しておかないとmethodがpostで送信されてしまいエラーがでます。:post_keyに検索したい値が格納されるので、
コントローラーに記述するワードと共通していれば:post_keyでなくても任意のワードを指定できます。検索結果一覧表示のコントローラー
posts_controller.rbdef index if params[:posts_key] @posts = Post.where(params[:posts_key]) else @posts = Post.all end end入力フォームから送信されてきた:posts_keyがここにたどり着きます。
elseの動作は、なにも入力せず検索ボタンを押した場合すべての投稿が表示されるようになっています。indexページで検索結果を一覧表示
index.html.erb<p>"検索結果: <%= @posts.count %>件</p> <ul class="posts"> <%= @posts.each do |post| %> <li class="post"> <%= post.content %> </li> <% end %> </ul>今回はページネーションを使わずに実装したので、
eachメソッドを使って繰り返し処理を実行して検索結果一覧を表示していきます。countメソッドを使って検索結果の件数を表示しています。
終わりに
以上の手順で検索機能が実装できるかと思います!
もし不備やわからないところがあれば気軽にコメントして
いただけるとありがたいです!
最後まで読んでいただきありがとうございました!
- 投稿日:2021-01-08T09:43:12+09:00
Rubyの基本
これからRubyの基本について書いていきます。
まず文字列と数値の違いについて解説していきます。
文字列
こんにちは Hello! 今日は コンニチハ などの数字以外を文字列といいます。入力する際に
'(シングルクォーテーション)
または
"(ダブルクォーテーション)
で囲みます。例 "こんにちは" 'Hello'
数値
22、777、8 などの数字のことです。
こちらはシングルクォーテーションやダブルクォーテーションで囲む必要はありません。Rubyにおける加減乗除
文字列の場合は こんにちは と 太郎さん をつなげて、 こんにちは太郎さん としたい時は
"こんにちは" + "太郎さん" とします数値の場合は 5と9を足したい時には
5 + 9とすれば良いですそれでは文字列と数値を足したらどうなるのでしょうか?
こんにちは、今日は24日です。
という文章を足し算で作る際に"こんにちは、今日は" + 24 + "日です。"
とするとエラーが出てしまいます。
数値と文字列は直接は計算できないためです。ではどうするのかというと、 to_sメソッド というものを使って数値を文字列に変換します。
"こんにちは、今日は" + 24.to_s + "日です。" とすれば計算できます。
逆に文字列を数値にしたい場合は to_iメソッド を使います。
"500" シングルクォーテーションやダブルクォーテーションで囲まれていると文字列扱いになってしまうので
数値に変換したい場合は "500".to_i とします。今日はここまで、次回はメソッドについて書きます。
- 投稿日:2021-01-08T09:28:10+09:00
RSpec+Capybara+selenium+chromedriverでのテスト
主にchromedriverの導入に手こずったので記録しておきます。
Gemfileの設定
Gemfilegroup :test do gem 'rspec-rails' gem 'capybara' gem 'selenium-webdriver' endchromedriverの導入
①
$ brew install chromedriverterminalにて 'brew install chromedriver'を実行
注意点:(PCのrootディレクトリーで実行すること)
※which chromedriverにでinstall先が見れる②最新版にアップデートする
$ brew update chromedriverchromeをヘッドレスモードで起動するために
spec/rails_helper.rbRSpec.configure dp |config| #他の記述 config.before(:each) do |example| if example.metadata[:type] == :system if example.metadata[:js] driven_by :selenium_chrome_headless, screen_size: [1400, 1400] else driven_by :rack_test end end end #capybaraを使うための記述 config.include Capybara::DSL end最後に
こんな記事を書いておいてなんですが、
なぜかわからないがrails_helper.rbに
metadata[:js]にしたらうまく動作しました。どなたかアドバイスをいただければありがたいです。
- 投稿日:2021-01-08T08:49:33+09:00
【railsチュートリアル】Herokuにデプロイができない/hello,worldからhola,mundoへの変更
課題
rails6.0版チュートリアル1.5.3の演習問題の1番がうまく解けなかった。
1.3.4.1と同じ変更を行い、本番アプリでも「hola, mundo!」を表示できるようにしてください。
Herokuにhello,world!アプリをデプロイした後に、hola,mondo!アプリをデプロイし直せという問題です。
私なりにhola,mundo!Herokuアクションを作成し、Herokuにデプロイし直したつもりが、
何度やってもブラウザに表示されるのは「hola,mundo!」ではなく、「hello,world!」となってしまう状況でした。私が犯していたミス
Git hubにファイルをアップロードする手順が誤っていた。
私が行った手順は下記の通りです。①controller作成。holaアクションを追加した。
application_controller.rbclass ApplicationController < ActionController::Base def hello render html: "hello,world!" end def hola render html: "hola,mundo!" end end②ルーティングを変更。アクションをhelloからholaに変更した。
routes.rbRails.application.routes.draw do root 'application#hola' end③Herokuに新しいアプリケーションを作成する。
Railsアプリケーション専用のサブドメインが作成され、ブラウザで表示ができるようになる。$ heroku create④Herokuにpushする。
$ git push heroku master⑤このコマンドを入力して出てきたURLにアクセスする。
$ heroku open以上です。
解決策
上記の②の手順の後に、下記の手順が足りていなかったです。
git hubへのファイルのアップロードがきちんとできていなかったのが原因でした。①変更内容をステージングエリアに追加する。
$ git add.②ローカルリポジトリに追加する。
$ git commit③masterブランチにpushする。
$ git push origin master↓
「私が犯していたミス」の手順③から⑤まで実行する。以上で解決することができました。
- 投稿日:2021-01-08T07:44:57+09:00
Ruby-FFIで可変長配列を持つ構造体を使う、またはビットフィールドを使う方法
https://github.com/ffi/ffi/issues/874
Ruby-FFIで可変長配列(variable length array)を持つ構造体(struct)を使う。またはビットフィールド(bitfields)を使う方法。
公式のGithubのissueで相談したところ、開発者の方から方法を教えてもらったので記録として残します。
こんにちはRuby-FFI開発者!
素晴らしい仕事をありがとう。
質問があります。 可変長配列の構造をRubyコードに変換するにはどうすればよいですか?
typedef struct { uint32_t capacity; int32_t dp_score, dp_max, dp_max2; uint32_t n_ambi:30, trans_strand:2; uint32_t n_cigar; uint32_t cigar[]; # Here } mm_extra_t;もう1つ質問があります。
ruby-ffi wikiを調べたところ、ビットフィールドはサポートされていないと書かれています。 これは今日でも当てはまりますか?Lars Kanisさんによる回答
ビットフィールド(Bit fields)はサポートされていませんが、単純な整数演算でエミュレートできます。 可変長配列は
struct.pointers
で使用できます。 次のように使用します。class MmExtra < FFI::Struct layout capacity: :uint32, dp_score: :int32, dp_max: :int32, dp_max2: :int32, n_ambi_trans_strand: :uint32, n_cigar: :uint32 end n_ambi = 123456 trans_strand = 0x2 cigar = [4,5,6] s = MmExtra.new(FFI::MemoryPointer.new(MmExtra.size + FFI.type_size(:uint32) * cigar.size)) s[:n_ambi_trans_strand] = n_ambi | (trans_strand << 30) s[:n_cigar] = cigar.size s.pointer.put_array_of_uint32(s.size, cigar) p n_ambi: s[:n_ambi_trans_strand] & ((1 << 30) - 1), trans_strand: (s[:n_ambi_trans_strand] >> 30) & ((1 << 2) - 1) # => {:n_ambi=>123456, :trans_strand=>2} p s[:n_cigar] # => 3 p s.pointer.get_array_of_uint32(s.size, 3) # => [4, 5, 6] p s.pointer.read_bytes(s.pointer.size) # => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xE2\x01\x80\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"MmExtraにカスタムメソッドを追加して、フィールドにアクセスし、ビットフィールド演算を実行することもできますよ。
ありがとうございます。
- 投稿日:2021-01-08T07:07:36+09:00
【Ruby自己用メモ】クラス、クラスインスタンス、インスタンス
用語
各種変数・メソッド用語について。
スコープ情報の不足分は後日追記予定。(頭の中整理して書くの疲れるぅ〜。。)class A # Aクラス(通常のクラス) @@class_variable = "クラス変数" # @class_instance_variable = "クラスインスタンス変数" # => ここで宣言した場合、それは「クラスインスタンス変数」と見なされる。 # 「インスタンス変数」と同じプレフィックス。注:ダブルで記載してしまった場合、こっちが優先されてしまう def initialize # インスタンスメソッド @instance_variable = "インスタンス変数" puts "#{@instance_variable} at initialize" end def hoge # インスタンスメソッド puts "#{@instance_variable} at xxx" end def self.fuga # クラスメソッド(定義の方法:特異メソッド方式) puts "#{@instance_variable} at yyy" end class << self # 特異クラス def fuga # クラスメソッド(定義の方法:特異メソッド方式) puts "#{@instance_variable} at yyy" end end end # インスタンスメソッドinitializeからはアクセスできる instance = A.new # => "インスタンス変数 at initialize" # インスタンス生成 instance.hoge # インスタンスメソッド呼び出し # インスタンス変数は他のインスタンスメソッドでも使える instance.hoge # => 'インスタンス変数 at xxx' A.new.hoge # インスタンスメソッド呼び出し(クラス名.new.インスタンスメソッド名) A.new.hoge # => 'インスタンス変数 at xxx' A.fuga # クラスメソッド呼び出し # クラスメソッドからはインスタンス変数(@〜)は使えない A.fuga # => nil参考記事
@mogulla3さん
- https://qiita.com/mogulla3/items/cd4d6e188c34c6819709
@tbpgrさん
- https://qiita.com/tbpgr/items/56eb65c0ea5882abbb07
- 投稿日:2021-01-08T07:07:36+09:00
【自己用メモ】(Ruby)クラス、クラスインスタンス、インスタンス
用語
各種変数・メソッド用語について。
スコープ情報の不足分は後日追記予定。(頭の中整理して書くの疲れるぅ〜。。)
おかしなところ、お気づきな点等ございましたら、
お手数ですがご指導ご指摘、頂けますと幸いですclass A # Aクラス(通常のクラス) @@class_variable = "クラス変数" # @class_instance_variable = "クラスインスタンス変数" # => ここで宣言した場合、「インスタンス変数」ではなく、「クラスインスタンス変数」と見なされる。 # 「インスタンス変数」と同じプレフィックス(@〜)。 def initialize # インスタンスメソッド @instance_variable = "インスタンス変数" puts "#{@instance_variable} at initialize" end def hoge # インスタンスメソッド puts "#{@instance_variable} at xxx" end def self.fuga # クラスメソッド(定義の方法:特異メソッド方式) puts "#{@instance_variable} at yyy" end class << self # 特異クラス def fuga # クラスメソッド(定義の方法:特異クラス方式) puts "#{@instance_variable} at yyy" end end end # インスタンスメソッドinitializeからはアクセスできる instance = A.new # => "インスタンス変数 at initialize" # インスタンス生成 instance.hoge # インスタンスメソッド呼び出し # インスタンス変数は他のインスタンスメソッドでも使える instance.hoge # => 'インスタンス変数 at xxx' A.new.hoge # インスタンスメソッド呼び出し(クラス名.new.インスタンスメソッド名) A.new.hoge # => 'インスタンス変数 at xxx' A.fuga # クラスメソッド呼び出し # クラスメソッドからはインスタンス変数(@〜)は使えない A.fuga # => nil参考記事
@mogulla3さん
- https://qiita.com/mogulla3/items/cd4d6e188c34c6819709
@tbpgrさん
- https://qiita.com/tbpgr/items/56eb65c0ea5882abbb07
- 投稿日:2021-01-08T02:55:58+09:00
【Ruby】三項演算子でif文のコードをダイエットしてみた
内容
プログラミング言語の演算子のひとつに、三項演算子(条件演算子)という呼ばれるものがあります。
これを使うと、複数行に渡るif文を1行で記述することができるので、モッサリしたコードをダイエットすることができます。この記事ではRubyのコードで書かれた
「入力された整数が奇数であれば"odd"を、偶数であれば"even"と出力するプログラム」
を題材としながら、if文で書かれたコードをダイエットしていく様を見て頂きます。まずは、これから書いていくコードのイメージをつくっていきましょう!
if 数字が偶数 evenと表示 else oddと表示 end以上のような5行に渡るif文を
数字が偶数 ? evenと表示 : oddと表示と1行で書くことができることが三項演算子の特徴です。
前提環境
MacOS Catalina 10.15.7
Ruby 2.6.6if ~ else 文
if.rbn = gets.to_i if n % 2 == 0 check_number = "even" else check_number = "odd" end puts check_numberこのrubyファイルをターミナルで実行すると以下のようになります。
下記の例以外でも奇数であればodd、偶数であればevenが出力されるはずです。$ ruby if.rb 5 # 入力 odd # 出力 $ ruby if.rb 8 # 入力 even # 出力三項演算子
ternary.rbn = gets.to_i check_number = n % 2 == 0 ? "even" : "odd" puts check_number三項演算子で書くと以上のようになります。if ~ else 文を利用した場合よりもだいぶスッキリしましたね!
行数で言うと7行→3行となっており半分以下になっております。念のためターミナルで実行してみましょう。if ~ else 文の時と同様の結果が得られますね。
$ ruby ternary.rb 17 # 入力 odd # 出力 $ ruby ternary.rb 24 # 入力 even # 出力三項演算子を学ぶだけであれば以上で終了なのですが、、、
せっかくなのでもう少しダイエットを続けてみましょう!!!!!if ~ else 文(メソッド定義なし)
if_rev.rbn = gets.to_i if n % 2 == 0 puts "even" else puts "odd" endメソッド定義を使わずにif文の中に
puts
を書きました。
最初のコードより少しだけ短くなっています。三項演算子(メソッド定義なし)
ternary_rev.rbn = gets.to_i puts n % 2 == 0 ? "even" : "odd"上記の書き方を三項演算子で書くとこのようなコードになります。
2行になりましたね!ここまで来ると欲が出てきて1行で書いてみたくなりませんか?三項演算子(最終版)
ternary_final.rbputs gets.to_i % 2 == 0 ? "even" : "odd"1行で、、、書けた!!!!!
もともと7行で書いていたコードが1行になりました。
まさかの85%OFFのダイエット、これにはR○Z○Pさんもビックリですね。以上、三項演算子を使ったコードのダイエットでした!
追記
2020/01/08
@scivola 様から頂いたご指摘を紹介します(ありがとうございました!)以下のようにカッコをつけると見やすくなる。
ternary_final.rbputs (gets.to_i % 2 == 0) ? "even" : "odd"整数が偶数かどうかを判定するのは Integer#even?が使えるので,
ternary_final.rbputs gets.to_i.even? ? "even" : "odd"と書くこともできるとのこと!
@scivola 様ありがとうございました!