- 投稿日:2020-03-29T23:10:48+09:00
Ruby on Railsインストール(Windows10)
はじめに
Ruby on Railsの環境を自前のPCに作りたかったのですが、ネットで検索して出てくるインストール手順「Ruby+Rails+SQLite3」のみだと正常にRailsサーバが立てられませんでした。
事前にNode.js、yarnのインストールをしておく必要がありました。
環境構築はそう何度も行うことはないので、備忘録として手順を残しておきます。環境
Windows 10 Home 64ビット
Rubyインストール
1.Rubyのダウンロード
https://rubyinstaller.org/downloads/
このリンクからインストーラダウンロードページに行き、WITH DEVKITの中で推奨されている最新版をダウンロードします。
記事を書いている2020年3月時点では「Ruby+Devkit 2.6.5-1」が推奨されている最新版のようです。
2.Rubyのインストール
ダウンロードしたインストーラをダブルクリックし、ぽちぽちNextを押していくだけです。
→この後インストールが開始されます。
MSYS2をインストールするための画面です。"1,2,3"と入力し、Enterキーを押します。
→この後インストールが開始されます。5分弱くらいかかります。
→「succeeded」が表示されれば、MSYS2のインストール完了です。Enterキーを押すと画面が閉じられます。3.Rubyの動作確認
①Rubyのバージョン確認
まずは正常にインストールされているかどうか確認するため、Rubyのバージョンを確認します。
コマンドプロンプトを起動し、以下のコマンドを実行します。ruby -v
→バージョンが正常に表示されたので、インストールは正常に行われました。②Rubyのプログラム実行
Rubyの簡単なプログラムを実行します。
メモ帳を開き、以下のコードを記述します。puts "Hello World"任意のファイル名で、拡張子を"rb"とし、保存します。
コマンドプロンプトで以下のコマンドを実行します。ruby 拡張子rbのファイルパスコマンドプロンプト上で"Hello World"が出力されたらOKです。
Railsインストール
1.Railsのインストール
コマンドプロンプトを起動し、以下のコマンドを実行します。
gem install railsインストールが完了したら、以下のコマンドを実行し、Ruby on Railsのバージョンを確認します。
rails -v
→バージョンが正常に表示されたので、インストールは正常に行われました。SQLite3インストール
1.SQLite3ライブラリのインストール
コマンドプロンプトを起動し、以下のコマンドを実行します。
gem install sqlite32.SQLite3の実行ファイルとDLLファイルのダウンロード
https://www.sqlite.org/download.html
このリンクからSQLite3公式のダウンロード用ページに行き、sqlite3.exeとsqlite3.dllをダウンロードします。
①sqlite-dll-win64-x64-3310100.zip の展開
展開されるファイルのうち「sqlite3.dll」をRubyをインストールしたディレクトリの「bin」ディレクトリに移動します。
②sqlite-dll-win64-x64-3310100.zip の展開
展開されるすべてのファイルをRubyをインストールしたディレクトリの「bin」ディレクトリに移動します。
Node.jsインストール
1.node.jsのダウンロード
https://nodejs.org/ja/
このリンクからインストーラダウンロードページに行き、推奨版をダウンロードします。
記事を書いている2020年3月時点では「12.16.1」が推奨されている最新版のようです。
2.node.jsのインストール
ダウンロードしたインストーラをダブルクリックし、ぽちぽちNextを押していくだけです。
3.インストール後のバージョン確認
①node.js実行ファイルパスを通す
「Windowsキー+E」でエクスプローラを開き、PCを選択した状態でプロパティを選択し、システムのプロパティを開きます。
システムプロパティから「システムの詳細設定」を開きます。
システム詳細設定から「環境変数」を開きます。
環境変数設定画面 > publicユーザー環境変数 > Path をダブルクリックします。
新規ボタンをクリックし、node.jsの実行ファイルパスを入力し、OKボタンをクリックします。
環境変数設定画面に戻り、OKボタンをクリックします。
②node.jsのバージョン確認
以下のコマンドを実行し、node.jsのバージョンを確認します。node -v
→バージョンが正常に表示されたので、インストールは正常に行われました。yarnインストール
1.yarnインストーラのダウンロード
https://nodejs.org/ja/
このリンクからインストーラダウンロードページに行き、インストーラをダウンロードします。
2.yarnのインストール
ダウンロードしたインストーラをダブルクリックし、ぽちぽちNextを押していくだけです。
3.インストール後のバージョン確認
①yarn実行ファイルパスを通す
設定手順はnode.jsと同様なので省略します。②yarnのバージョン確認
以下のコマンドを実行し、yarnのバージョンを確認します。yarn -v
→バージョンが正常に表示されたので、インストールは正常に行われました。Railsアプリ起動
1.Railsアプリの新規作成
以下のコマンドを実行し、Railsアプリを新規作成します。
cd アプリケーションを配置するファイルパス rails new アプリケーション名
→「successfully」が表示されれば正常にアプリが作成されました。2.Railsサーバの起動
以下のコマンドを実行し、Railsサーバを起動します。
cd アプリケーションフォルダ rails s
http://localhost:3000
にアクセスします。
→このページが表示されたらサーバーが正常に起動されたことになります。
- 投稿日:2020-03-29T22:26:36+09:00
nokogiri関連でエラーが出ちゃった人は、、、
- 投稿日:2020-03-29T22:16:37+09:00
Rails をアンインストールする時にエラーが出た場合の対処方法
エラー発生
$ gem uninstall railties -v '6.0.2.2'
を実行するとこんなエラーが
ERROR: While executing gem ... (Errno::ENOTEMPTY)
Directory not empty @ dir_s_rmdir - /Users/ユーザ名/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rails-6.0.2.2原因はインストールの際に管理者権限で作成していたから
解決方法
$ sudo gem uninstall railties -v '6.0.2.2'
これだけでした。
- 投稿日:2020-03-29T22:12:14+09:00
[Rails]DM機能に送信日時を日本時間で表示させる
本記事投稿のいきさつ
現在作成中のアプリでメッセージ機能を作成しました。
その中で日本時間で送信日時を表示させる際に、初めて知った内容があったため備忘録として書きたいと思います。
内容自体はとても簡単なものとなっています。
今回は以下のアプリで実装していきます。
ビューに追記
メッセージを表示させるビューに以下を追記
message.html.haml= message.created_atとても見づらい表示となってしまうため、先ほどのコードに追記をし、表示の方法を指定します
message.html.haml= message.created_at.strftime("%Y年%m月%d日 %H時%M分")日本時間の設定
ここが自分が知らなかった点なのですが、Railsのアプリケーションの時間基準は、デフォルトでは協定時(UTC)となっています。
そのため、日本時間で表示をするためには、config/application.rbを以下のように編集する必要があります。config/application.rbclass Application < Rails::Application #以下を追記 config.time_zone = 'Asia/Tokyo'終わり
これでメッセージアプリに日本時間での表示ができるようになりました。
最後まで見ていただきありがとうございました。
- 投稿日:2020-03-29T21:30:44+09:00
【Ruby】社内報を作成してくれるクラスを作成してみた。
はじめに
毎月先輩から出していただいた課題に取り組んでいます、 mi0です。
2月は社内報を作成するクラスの実装を行いました。
この記事は要件定義〜レビューをいただくまでの過程を纏めた備忘録です。
こうやったらもっとよくなる、などのご指摘があればコメント頂けると嬉しいです!過去の記事はこちら!↓
- 「Ruby初心者向けのプログラミング問題を集めてみた」の電話帳問題解いてみた。
- Ruby ボウリング問題解いてみた。
- Ruby 価値が大きくなる組み合わせ問題 解いてみた
- Ruby初心者向けのプログラミング問題のカラオケマシン問題にハモリ機能を追加してみた
- Ruby ボウリング問題解いてみた。をパワーアップしてみた。
登場人物
- 私
- なんとなく技術力の上昇を感じ始めた。慢心ではないと思いたい。自作のWebアプリを作ってみたい気持ちが湧いてきた。
- 小えびのかき揚げ先輩
- 最近自宅の開発環境を整えて嬉しそうにしていらっしゃる。プログラミングが得意。
- なんこつ唐揚げちゃん
- 私の心の中の妖精。「やっぱりカロリーは正義よね」が口癖。
要件を定義する
私「今回は社内報を作成するクラスの要件定義から行うよ!初めての試み!」
私「今回は普段使っている社内報を作ってくれるアプリのロジック部分を実装したいんだよね」
私「とりあえず今使ってるアプリがしていることをまとめてみよう」
使用しているもの
テンプレート
- 社内報の元になるテンプレート。テンプレートには
[名前]
といった風に、各メンバーの本文挿入箇所が指定されている。メンバー表
- メンバーの名前、メールアドレスが定義されている。メールアドレスの形式は
名前@ドメイン
。各メンバーの原稿
- ファイル名が
年月 + 名前.txt
というフォーマットのテキストファイルが毎月送付されてくる。機能
- 社内報の生成
- ディレクトリに、メンバーから提出された原稿を全て格納する。
- 1のディレクトリを指定する。
- アプリの
作成
ボタンを押下すると、テンプレートの内容を元に社内報が生成される私「こんなところかな?…………。」
なんこつ「急に黙ってどうしたの?」
私「いや……なんかさ、このアプリを初めて見た時……一年半くらい前なんだけどね?なんかめちゃくちゃ凄いアプリだな〜って思って、こんなの私に作れるわけない!って思ってたんだよ。私も結構へっぽこだったからさ……。」
私「でも、こうやって見ると
そんなに難しいことはやってない
んだなって感じて……あ、勿論機能が単純とかそういう話じゃなくて、ロジックをイメージしたときに凄いシンプルだなって思って。」なんこつ「(静かに微笑む)」
私「ちょっとその顔やめてもらっていいです???」
なんこつ「いやぁ……なんか……ねぇ。いいねぇ。」
私「時を戻そう」
なんこつ「最近のブームをしれっと混ぜ込んで来ないで」
私「今回はとりあえず、
現行のアプリで最低限必要なところの設計・実装を行う
って話だったから、現行の内容で十分なんじゃないかなって思うんだよね。次回で欲しい機能を追加する、って話をかき揚げ先輩としてるしね。」なんこつ「じゃあ、一旦内容を纏めて先輩に見ていただきましょうよ」
私「そだね、もしかしたら何かアドバイスいただけるかも!」
〜〜〜そして先輩との打ち合わせ後にできた今回の仕様が以下〜〜〜
社内報作成アプリ
問題
- テンプレートに原稿を挿入し、社内報を作成する。
社内報の元データ
app/fixture/template.txt
・・・社内報のテンプレート。app/fixture/member_list.csv
・・・各メンバーの名前とメールアドレスが記載されている。app/fixture/manuscripts
・・・年月
毎にディレクトリを作成し、直下にその月の各メンバーの原稿を格納する。要求仕様
- 引数には文字列で
年月
を渡す(例: 201902)- 引数に該当するディレクトリ内にある原稿を取得し、テンプレートに当て嵌めて社内報を作成する。社内報は文字列として返す。
- 指定したディレクトリが存在しない場合は例外を返す
- テンプレートの
[user_name]
の部分は・ユーザ名
に変換し、その次の行から本文を記載する。- 原稿と原稿の間には必ず
1行分の改行を含む
こと- 原稿の末尾に不要な改行が含まれていたら取り除く
- 原稿が未提出の社員については本文に
未提出
と記載する例) 202002を引数に指定した場合、以下のように出力される ・ラテ太郎 お疲れ様です、ラテ太郎です。 サンプル サンプル サンプル ・ラテ子 お疲れ様です、ラテ子です。 サンプル サンプル サンプル サンプル ・沖漬け先輩 サンプル サンプル サンプル サンプル サンプルサンプルサンプルサンプルサンプルサンプルサンプル ・ラテ二郎 未提出テンプレート
db/template.txt社内報 [rate_taro] [rate_ko] [okiduke_senpai] [rate_jiro]db/member_list.csvラテ太郎,rate_taro@sample.com ラテ子,rate_ko@sample.com 沖漬け先輩,okiduke_senpai@sample.com ラテ二郎,rate_jiro@sample.comapp/internal_newsletter.rbclass InternalNewsletter def generate(datestr) '' end end※ディレクトリが存在しないとき、既存アプリではエラーにならずに処理を完了してしまっていたため、
例外を起こすことでエラーがわかるようにする、という仕様が増えました。作成した仕様を元に実際に解いていく
私「よし!実際に作っていくよ〜〜〜!」
私「まずは私のイメージを
#generate
にメモしていこう」app/internal_newsletter.rbrequire 'csv' class InternalNewsletter def initialize # テンプレートとメンバー表を読み込む end def generate(datestr) # 読み込んだテンプレートを一行ずつ取得し、配列を作成する # 読み込んだ行が[名前]にマッチする場合、[名前]を ・名前\n本文 に置換する # 最後に配列を\nで結合する end end私「こうやって書くとやりたいことってほんとシンプルだよね〜。次、具体的にコードをかけそうなところを埋めていく。」
app/internal_newsletter.rbrequire 'csv' class InternalNewsletter def initialize @template = File.open('db/template.txt').readlines(chomp: true) @member_list = CSV.read('db/member_list.csv') end def generate(datestr) @template.each_with_object([]) do |row, array| # 読み込んだ行が[名前]にマッチする場合、[名前]を ・名前\n本文 に置換する end.join("\n") end endなんこつ「凄いそれっぽいじゃない。」
私「いやいや、問題はここからなんだよね……」
私「テンプレートを一行ずつ読み込んでいくと、
row
に入り得る内容は以下の3パターンある。」
- 見出し(
社内報
など)- 改行のみ
- [名前](置換対象)
私「今回でいうと、
3
の場合だけ置換対象にしたい。それ以外の場合は手を加えたくない。」私「だから、
3
以外は早期リターンでスルーしたいから、次はそこを作ろう」app/internal_newsletter.rbrequire 'csv' class InternalNewsletter def initialize @template = File.open('db/template.txt').readlines(chomp: true) @member_list = CSV.read('db/member_list.csv') end def generate(datestr) @template.map do |row| + member_name_en = row[/\[(.*?)\]/, 1] + next row if member_name_en.nil? # 読み込んだ行が[名前]にマッチする場合、[名前]を ・名前\n本文 に置換する end.join("\n") end end
なんこつ「なにその
member_name_en
に代入してるやつ!」私「ふっふっふ……やばいでしょやばいでしょやばいでしょ」
私「私は自称正規表現ザコなので、今回は調査を頑張りました。」
私「知ってるかもしれないけど、これは
str[]
メソッドってやつでね」私「第2引数に渡す値で振る舞いが変わるんですよ。」
私「
0
の時は第2引数無しの場合と一緒の振る舞い。つまり今回だと[名前]
が返ってくる。」私「
0
以外の場合は、第一引数に渡した正規表現の(第2引数に渡した値)
番目の括弧にマッチする最初の部分文字列が返ってくるのね。」私「今回だと1番目の括弧、つまり
(.*?)
にマッチした文字列が返ってくる。括弧の中には[]
が含まれていないでしょ?だから、名前
だけが返ってくる。超かっこよくない?」なんこつ「なるほどね……これ、結構使えそうな気がするわ。例えば一つの文字列から、3箇所部分的に取得したい内容がある場合、正規表現でそれぞれを括弧で囲っておくと、第2引数の値を変えるだけでそれぞれが取得できるってことだものね」
私「そういうこと!結構便利な気がする!」
[1] pry(main)> '[aaa] [bbb] [ccc]'[/\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]/, 0] => "[aaa] [bbb] [ccc]" [2] pry(main)> '[aaa] [bbb] [ccc]'[/\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]/, 1] => "aaa" [3] pry(main)> '[aaa] [bbb] [ccc]'[/\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]/, 2] => "bbb" [4] pry(main)> '[aaa] [bbb] [ccc]'[/\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]/, 3] => "ccc"※参考
[Ruby]特定の文字列の抽出
Ruby 2.7.0 リファレンスマニュアル私「と、これで取得した行が
[名前]
以外の場合はスルーできるようになった!」私「次、実際に原稿を挿入していく処理を作るよ!」
app/internal_newsletter.rbrequire 'csv' class InternalNewsletter def initialize @template = File.open('db/template.txt').readlines(chomp: true) @member_list = CSV.read('db/member_list.csv') end def generate(datestr) @template.map do |row| member_name_en = row[/\[(.*?)\]/, 1] next row if member_name_en.nil? + "・#{@member_list.find { |arr| arr[1].match(/\A#{member_name_en}/) }[0]}\n#{load_manuscript(datestr, member_name_en)}" end.join("\n") end + private + def load_manuscript(datestr, member_name_en) + file_path = "db/manuscripts/#{datestr}/#{datestr}#{member_name_en}.txt" + return '未提出' unless File.exist?(file_path) + File.open(file_path).read.strip + end end私「
[名前]
の場合、・名前\n本文
を返す。けど、もしその人が原稿を提出していなかったら、本文は未提出
という文言を出したい。」私「条件によって、本文の内容が変わるから、そこをよしなにできるメソッドとして
#load_manuscript
をつくったよ。」なんこつ「へぇ……なかなかいいんじゃないの」
私「だよねだよねだよね〜?ちょっとこれで動かしてみようかな!」
社内報 ・ラテ太郎 吾輩は猫である。 名前はまだ無い。 どこで生れたかとんと見当がつかぬ。 何でも薄暗いじめじめした所でニャー ・ラテ子 恥の多い生涯を送って来ました。 自分には、人間の生活というものが、見当つかないのです。 自分は東北の田舎に生れましたので、汽車をはじめて見たのは、 よほど大きくなってからでした。 ・沖漬け先輩 木曾路はすべて山の中である。 あるところは岨づたいに行く崖の道であり、 あるところは数十間の深さに臨む木曾川の岸であり、 あるところは山の尾をめぐる谷の入り口である。 ・ラテ二郎 未提出私「待って」
私「最後の改行が足りないよ〜〜〜!?!?!?」
私「ぴえんぴえんのぴえんだわ」
私「ぴえんすぎる」
私「そっか……
join
でくっつけてるから最後の改行がくっつかないのか……」私「う〜〜〜〜〜ん……リファクタしながら考えるか……。」
〜〜〜リファクタリング後〜〜〜
require 'csv' class InternalNewsletter def initialize @template = File.open('db/template.txt').readlines(chomp: true) @member_list = CSV.read('db/member_list.csv') end def generate(datestr) @template.each_with_object([]) do |row, array| member_name_en = row[/\[(.*?)\]/, 1] next array << row if member_name_en.nil? array << "・#{@member_list.find { |arr| arr[1].match(/\A#{member_name_en}/) }[0]}" array << load_manuscript(datestr, member_name_en) end.join("\n") + "\n\n" end private def load_manuscript(datestr, member_name_en) file_path = "db/manuscripts/#{datestr}/#{datestr}#{member_name_en}.txt" return '未提出' unless File.exist?(file_path) File.open(file_path).read.strip end endなんこつ「ちょっと……」
私「完全敗北しました……文字列結合で
\n\n
とかはちゃめちゃにダサダサなのわかってるんですけど……勝てなかった……ごめんなさい……!!!」私「代わりと言ってはなんだけど、
map
の中で・名前\n本文
みたいに改行が出てくるのが嫌だったから、改行コードはmap
で配列を作り終わった後に一括で付けるようにしました。」私「いや〜〜でもやっぱり一番最後で文字列結合した改行×2がやっぱり嫌だ……許せれん……。どうしたらいいんだろうね……」
レビューをいただく
かき揚げ先輩「じゃあレビューしようか」
私「お願いします!」
かき揚げ先輩「まず残念なお知らせです。」
私「ひゃい」
かき揚げ先輩「ディレクトリが存在しない時ってどうするんだっけ?」
私「」
私「」
私「あ!?!?!?!!??!?!?!?!?!?!」
私「例外!!!!!!!!!!!!」
かき揚げ先輩「漏れてました」
私「ああああああああああ」
私「今回割と自信あったのに!!!!!!!!!!」
私「穴があったら……ってやつですウワ……しくった」
かき揚げ先輩「次はコードね」
かき揚げ先輩「まずね、
[名前]
の名前
だけ抜き出すやつ」かき揚げ先輩「
match
を使わないあの書き方は初めて見た。」かき揚げ先輩「短く書けていいと思いました」
私「(やった〜〜〜〜〜〜)」
かき揚げ先輩「次、テンプレートを元にする処理で
each_with_object
を使ってたよね?」私「はい」
かき揚げ先輩「あそこを一行で書けてたらmapでもよかったんだよね」
私「そうなんです……」
かき揚げ先輩「今回で言うとフォーマットがきっちり決まっていたから、そこを共通化できたらよかったかな」
かき揚げ先輩「あとで俺の書いたコード見せるけど、こういう時こそ
ヒアドキュメント
を使うといいよね。ヒアドキュメントの場合、文末に改行が必ず付くから。」私「!!!!!!」
私「な、なるほど……確かに。しかもヒアドキュメントなら、実際に表示される文章とほぼ同じ形式で書くから
可読性も高い
ですよね」かき揚げ先輩「そういうこと。ハードコーディングな部分をメソッドに切り出すこともできるから、そういう意味でもより可読性は上がるね。
かき揚げ先輩「次は実際に俺の書いたコードを見てもらおうかな。」
require 'csv' class InternalNewsletter MEMBER_LIST = CSV.read('db/member_list.csv') NEWSLETTER = File.read('db/template.txt') NOT_SUBMITTED = '未提出'.freeze def initialize(datestr) @datestr = datestr @dirpath = "db/manuscripts/#{@datestr}" end def generate raise ArgumentError unless Dir.exist?(@dirpath) MEMBER_LIST.inject(NEWSLETTER, &method(:newsletter)) + "\n" end private def newsletter(text, member) name, keyword = split_name_keyword(member) fullpath = "#{@dirpath}/#{@datestr}#{keyword}.txt" body = File.exist?(fullpath) ? File.read(fullpath).strip : NOT_SUBMITTED text.gsub( /\[#{keyword}\]/, <<~NEWSLETTER.chomp ・#{name} #{body} NEWSLETTER ) end def split_name_keyword(member) name, email = member _, keyword = email.match(/\A(\w+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/).to_a [name, keyword] end end私「はぁ〜〜〜〜なるほど……」
かき揚げ先輩「俺の処理だと早期リターンはなくて、該当箇所を置き換えていくだけ。」
かき揚げ先輩「次のステップとして、私ちゃんに意識して欲しいのは
共通部分
と異なる部分
をそれぞれ見つけて、共通化していくことだね」かき揚げ先輩「共通化することによって、記述を減らすことができるし、コードの量が少ないって事は読む量が少ないってこと。共通化して、総コード数を減らせるようになるとより良くコーディングができるようになると思うので、意識してみてください」
私「分かりました!ありがとうございます!」
最後に
自分なりにより良い書き方を考えて書いたつもりだったのですが、ヒアドキュメントは盲点でした……。先輩のコードを拝見した際に思わず唸ったほどです。
あと例外の話をすっかり忘れていたといううっかりは本当に悔やんでも悔やみきれませんでした。こういううっかりを減らしていくためにも、最初のメモ書きを行った時点で仕様を満たせているか確認した方がいいですね。一方で、正規表現周りで先輩に「知らなかった」と言っていただけた事、また「ロジック自体はそんなに難しくないな」と思えた事は私的に成長を感じられる
嬉しみポイント
でした。上手く出来なかったところは改善しつつ、よかった所も噛み締めながら、この調子で引き続き頑張りたいと思います!
- 投稿日:2020-03-29T21:16:31+09:00
読みやすいコードを書きたい!
この記事の趣旨
自分の学習のメモとして残します。
どなたかのお役に立てたら光栄です。こんな方におすすめです
・コードが見づらいと言われる
・初心に返りたい
このような方には何か発見があると思います。読みやすいコードとは?
ズバリ『良いコード』のこと。
良いコードは、他人がそのコードを見た時に短時間で理解できるコードのことを言います。逆に、分かりづらい・理解しづらいコードは解読に時間がかかります。それだけ開発の工数もかかってしまい、効率が良くありません。
では、良いコードの条件・要素を紐解いていきましょう!
●コードの命名に規則を
変数やメソッドは好きなように命名ができます。
ルールがありませんので、個人の好きなようにできます。
特に共同開発の現場などでは、「他人が見てわかる」を意識する必要があります。◎命名のポイント
【目的がわかる単語を使う】
例)new → new_account【汎用的な名前は避ける】
・一時的な変数などは避ける
・可読性を意識して【名前に情報を含める】
大文字、小文字をルールに沿って活用
【誤解されない名前を使う】
・何がしたいかが明確な名前
・説明的に長くなっても良いので、可読性重視
例)read_books → already_read_books●コードレイアウト
プログラムの挙動に影響はないが、可読性を大幅にあげることができる
◎レイアウトのポイント
・整列 :縦列を揃える。イコールの位置など縦が揃うと見やすい
・一貫性 :似たような構造は同じフォーマットに統一できないか検討
・ブロック化 :同じ系統の変数などをまとめてグループ化すること●コメント
・プログラムの動作を説明
・他の開発者がコードを読む際の理解を助ける
※多すぎても読むのに時間がかかるため、簡潔に◎コメントのポイント
・理由をコメントする :なぜそのコードを書いたか
・他の開発者へメモを残す:開発中のメモとして
・実際の例を記入する :コメントでは伝わりづらい時は、コメントとしてコードを記載まとめ
結局大事なことは
「人に対する思いやり」だなぁと。複数人で仕事をする以上、「自分だけ良ければそれで良い」という考えはNG。
誰もが見やすく、仕事をしやすい状況を自分が作り出す意識が大切。そのための知識や技術であると思う。
これからしっかり学んでいきましょう。
- 投稿日:2020-03-29T19:59:13+09:00
Rails 自作アプリへの道 Part1
Rails 自作アプリを作った時の経過をまとめていきます。
参考:https://kitsune.blog/rails-install
環境
| 1 | 2 |
|:-:|:-:|
| | |
| | |
| | |
| OS | Mac Mojave 10.14.16 |
| Ruby | 2.6.3p62 |
| Rails | 6.0.2.2 |
- 投稿日:2020-03-29T19:48:37+09:00
Catalinaのバージョンアップしたらrails sできなくなったけど、Node.jsのインストールで解決!
筆者の環境
macOS Catalina バージョン 10.15.4
使用言語:Ruby、JavaScriptエラー内容
不注意により、macOSがCatalina バージョン10.15.4に上がってしまった。
それから実装中のアプリでrails sすると、以下のエラーメッセージが表示されサーバーが起動しなくなった。
Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
これで解決!
Node.jsをインストール
左側の12.16.1の方はインストールしても開けず、
右側(最新版)をインストールしたらrails s成功しました!番外編
他にも対処法はいくつかあり、therubyracerというGemのインストールでも解決できるようです。(参考リンクご参照ください)
現在、チーム開発中だったので、自分以外の複数端末への影響を考え今回はGem以外の方法を選択しました。
参考
にさせていただきました。ありがとうございます。
https://qiita.com/azusanakano/items/771dc9919f347de061d7
- 投稿日:2020-03-29T19:01:23+09:00
活動記録(2020.3.29)
今週やったこと
・DBのエラーでつまずき解明と環境変数設定周りで時間を使う羽目に…この辺は別記事にまとめている。
Ruby on Rails 学習メモ.2・GitHubのこれまでのプルリク等の基本的活用に加えて、issues(とmilestone)でプロジェクトを管理するようにした。
なかなか進まないがこうやってプロジェクトを可視化・効率化させていくのはとても楽しい。・3月最後の追い込みをしたかったが高熱が出てしまい進捗が芳しくなかった。
課題・不明点
・テスト環境の理解が浅い。rubocopを導入すれば自動で警告文が出るらしい。RSpec、Jenkinsについても調べておく。
・ルート、ポート、DNS等ネットワーク周りの知識が弱い→基本情報技術者テキスト
・AWSは公式のレクチャーを見るべき。
・デプロイはCircleCIではなくGitHubActionsを採用するのもいいかも。
- 投稿日:2020-03-29T18:23:50+09:00
プログラミング初心者がgitでつまずいた。英語の意味を調べて、専門用語を深く理解しました。
gitについて1から復習してみました。
Git
gitは、プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システムである。
要は、複数人で開発するときに、誰がどれをいじったのか分からなくてグチャグチャになるのを防止するためのシステムのことなんだなーてことか。よし。
Github
これはgitを扱うためのツールのことですな。
っと書き始めてたら、最強に分かりやすく説明している記事を見つけてしまいました。
こちらを読めば完璧。
https://qiita.com/nnahito/items/e546b27f73e7be131d4e
でも疑問点が二つ湧いてくる。
pullとbranchって一緒じゃね?あと、pushとmargeって一緒じゃね?
すごい調べたけど、概念が違うっぽい
branchとmargeは、masterから枝分かれを作ったり、合体したりする行為
pullとpushはそれを包括して取ってきたり送ったりするからconflictが起こりやすい。
- 投稿日:2020-03-29T17:45:47+09:00
<前のページ 次のページ>の実装
前後のレコードを取得して、前後ページ移動する
はじめに
よくブログなどで見る、
<前のページ 次のページ>
でページ遷移をする機能を現在制作中のプロダクトに実装します。
調べた内容を極力シンプルにまとめました。プロダクトは個人間取引を行うもので、今回実装するものは
商品を見ていくときの利便性を向上させるものです。
今回は出品された商品の詳細を表示するページに前後のリンクを実装します。
特にカラム別に分けることなく、全てのレコードを新着順で並べて前後していきます。狙い
出品された順にページを前後するリンクを実装します。
<前のページ 次のページ>
といった表示で、ページ遷移をします。実装
今回はitems_controllerのshowアクションにて行います。
まず商品の情報を取得します。app/controllers/itmes_controller.rbdef show @item = Item.find(params[:id]) end次にitemモデルにて、メソッドを定義します。
where,order,firstメソッドを用いてpreviousメソッドとnextメソッドを定義して使用します。app/models/item.rbdef previous Item.where("id < ?", self.id).order("id DESC").first end def next Item.where("id > ?", self.id).order("id ASC").first endwhereメソッド
https://railsguides.jp/active_record_querying.html#%E6%9D%A1%E4%BB%B6orderメソッド
https://railsguides.jp/active_record_querying.html#%E4%B8%A6%E3%81%B3%E9%A0%86firstメソッド
https://railsguides.jp/active_record_querying.html#firstビューは以下の通りです。
最初の前のレコードと最後の次のそれは存在しないので条件分岐で分けましょう。app/views/items/show.html.haml- if @item.next.present? = link_to "次のページ>", item_path(@item.next) - if @item.previous.present? = link_to "<前のページ", item_path(@item.previous)拡張機能 blank? present?
https://railsguides.jp/active_support_core_extensions.html#blank-questionmark%E3%81%A8present-questionmark実際の実装では商品の名前を表示させたので以下のようにしました。
また、itemテーブルにnameカラムを設定しています。app/views/items/show.html.haml- if @item.next.present? = link_to item_path(@item.next) do < = @item.next.name - if @item.previous.present? = link_to item_path(@item.previous) do = @item.previous.name >※途中にある<>は<前のページ 次のページ>、の両端に置いてあるものです。特に指定はありません。
完成例
おわりに
今回はテーブルにある全てのレコードの新着順で前後ページのリンクを実装しました。
ユーザー別、グループ別など持っている属性のまとまりの中で実装されている方がいらっしゃたのでリンクを載せておきます。
また参考にさせていただいた記事の作成者の方々、感謝申し上げます。
ありがとうございました。参考リンク
https://qiita.com/hrtkmztn/items/df09584cfc621699532c
こちらはグループ内で投稿されたもので実装されてます。
アソシエーションや変数などに手を加える必要があるようです。
それぞれのユーザーが出品した商品一覧の前後を遷移するリンクを試作しましたが、実装できました。https://easyramble.com/active-record-prev-next.html
previous, nextメソッドの実装に取り入れました。https://www.for-engineer.life/entry/get-record/
こちらもよくまとめられています。
- 投稿日:2020-03-29T17:21:07+09:00
Rails探検録 Tips: private メソッドになぜ underscore が付くのか
【本文】
最近、Rails のソースコードを暇つぶしに読んでいたら、ふと private メソッドの枠組みで underscore の有無があることが気になりました。
参考 URL を読むと、Ruby では private と protected メソッドに使われることが多いそうです。
他のプログラミング言語でも( JavaScript, PHP, Phython etc...)同様のコーディング規約が存在します。これは、boolean を返すメソッドは末尾に
?
を付けたり、破壊的なメソッドは!
を付けたり、getter メソッドに=
を付けることと同じ意味ですね。
- 参考URL
- Ruby - Rubyの1文字目がアンダーバー(_)のメソッド定義|teratail
- JavaScript - [Javascript]アンダースコアから始まる変数、関数の意味|teratail
- coding style - What's the deal with a leading underscore in PHP class methods? - Stack Overflow
- code quality - Should I always use prefix private methods with an underscore in Python? - Software Engineering Stack Exchange
【あとがき】
private メソッドでも underscore が付かないものもある?
underscore はコーディング規約の一種と考えても、Rails のソースコードでは、 underscore が付くものと付かないものが存在します。
Rails と言う巨大なフレームワークなら一定のコーディング規約があると思うのですが、なぜ付かないものがあるのでしょうか?
結論は違いがわかりませんでしたでしたが...全ての unserscore 付きメソッドに言えることではありませんが、
_assign_attributes
にしろ、_find_all
にしろ、呼び出し元のメソッドと強く依存しているケースが散見されます。rails/activemodel/lib/active_model/attribute_assignment.rb... def assign_attributes(new_attributes) unless new_attributes.respond_to?(:each_pair) raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed." end return if new_attributes.empty? _assign_attributes(sanitize_for_mass_assignment(new_attributes)) end alias attributes= assign_attributes private def _assign_attributes(attributes) attributes.each do |k, v| _assign_attribute(k, v) end end def _assign_attribute(k, v) setter = :"#{k}=" if respond_to?(setter) public_send(setter, v) else raise UnknownAttributeError.new(self, k.to_s) end end ...rails/actionpack/lib/action_dispatch/routing/url_for.rb... def route_for(name, *args) public_send(:"#{name}_url", *args) end protected def optimize_routes_generation? _routes.optimize_routes_generation? && default_url_options.empty? end private def _with_routes(routes) # :doc: old_routes, @_routes = @_routes, routes yield ensure @_routes = old_routes end def _routes_context # :doc: self end ...rails/actionview/lib/action_view/path_set.rb... %w(<< concat push insert unshift).each do |method| class_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{method}(*args) paths.#{method}(*typecast(args)) end METHOD end ... def find_all(path, prefixes = [], *args) _find_all path, prefixes, args end ... private def _find_all(path, prefixes, args) prefixes = [prefixes] if String === prefixes prefixes.each do |prefix| paths.each do |resolver| templates = resolver.find_all(path, prefix, *args) return templates unless templates.empty? end end [] end def typecast(paths) paths.map do |path| case path when Pathname, String OptimizedFileSystemResolver.new path.to_s else path end end endsanitize とは一体?
全くの余談ですが、
#assign_attributes
を見ると、入力値と仮定する new_attributes をsanitize_for_mass_assignment
を通していることがわかります。つまるところ、new_attributes は、比喩表現として消毒
される必要があることですよね。メソッドを見てみると、Storong Parameter 等で許可されていない params が合ったら例外を出すようです。
rails/activemodel/lib/active_model/forbidden_attributes_protection.rbdef sanitize_for_mass_assignment(attributes) if attributes.respond_to?(:permitted?) raise ActiveModel::ForbiddenAttributesError if !attributes.permitted? attributes.to_h else attributes end end他にも activemodel の中を探検すると、
dirty
(汚れた)と呼ばれる module が目につきます。この module は、DB 保存前の入力値を扱う機能であり、例え assign_attributes でインスタンス変数に入力値を割り当てても、元の DB 保存値を取り出せるメソッドが定義されています。つまるところ、データベースにとって、ユーザの入力値は、粗く汚く悪意ある情報と見ており、それを考慮した上で対応する必要性があることが読み取れます。まぁ読めばわかることではあるのですが、オブジェクトやドメインに対する共通認識を理解することで、コードの理解度を深められるなと思ったところです。
@_
のようにインスタンス変数にも underscore が使われてる?メモ化等で使用範囲を内部で留めるインスタンス変数に対して、使用されているようです。
今回の private に似た目的ですね。
- 投稿日:2020-03-29T17:18:02+09:00
redirect_toとrender, flashとflash.nowの違い
ポートフォリオ作成中、バリデーションのエラーメッセージが表示できずに苦戦してました
redirect_toとrenderの違いを理解していなかったのが原因です。
redirect_toとrender, ついでにflashとflash.nowの違いについても今更理解したのでまとめます。参考
https://railsguides.jp/action_controller_overview.html#flash
renderとredirect_toの違い
- render => ビューを描画するだけで、新たにhttpリクエストを送信するわけではない
- redirect_to => 新たにhttpリクエストを送信して、その結果コントローラからビューが描画される
app/controllers/tweets_controller.rbclass TweetsController < ApplicationController def new @tweet = Tweet.new end def create @tweet = Tweet.new(tweet_params) if @tweet.save # 成功時の処理 else flash[:danger] = '失敗しました' redirect_to new_tweet_path end end private def tweet_params params.require(:tweet).permit(:content) end endapp/views/tweets/new.html.slim= form_with model: @tweet, local: true do |f| - if @tweet.errors.any? .alert.alert-danger ul - @tweet.errors.full_messages.each do |message| li= message = f.label :content = f.text_area :content = f.submit 'tweet'こんな感じの設定があったとします(tweetモデルはpresence: trueのconent: stringカラムを持つ)
この場合、空白で送信してもエラーメッセージは表示されません
redirect_toでリダイレクトするとコントローラを経由してしまうため、新たに@tweet
が生成され、エラーメッセージを含んだ@tweet
が消えてしまうから…だと考えていますここで、コントローラを経由せずにページを描画するrenderを使用します
app/controllers/tweets_controller.rbclass TweetsController < ApplicationController def new @tweet = Tweet.new(tweet_params) end def create @tweet = Tweet.new(tweet_params) if @tweet.save # 成功時の処理 else flash[:danger] = '失敗しました' render action: :new # ここを変更 end end private def tweet_params params.require(:tweet).permit(:content) end endrenderを使用すればコントローラを経由せずにページを描画することができるため、エラーメッセージを含んだ
@tweet
がビューに渡されif @tweet.errors.any?
に引っ掛かり、エラーメッセージを表示することができますこれで無事エラーメッセージを表示することができました!
flashとflash.nowの違いについて
- flash => 次のリクエスト終了時まで表示される
- flash.now => 次のリクエスト開始時まで表示される
コントローラーが上の最終状態のまま空白で送信した場合、
失敗しました
というflashメッセージが表示され、失敗します。
しかし、このままでは次別のページに遷移した時flashが残ったままになってしまいます。renderを使うとリクエストを送信しないため次にリクエストを送信した時もflashが残り、その次のリクエストを送信した時点でやっとflashが消えるからだと思われます
そこでflash.nowを使ってみましょう
app/controllers/tweets_controller.rbclass TweetsController < ApplicationController def new @tweet = Tweet.new(tweet_params) end def create @tweet = Tweet.new(tweet_params) if @tweet.save # 成功時の処理 else flash.now[:danger] = '失敗しました' # ここを変更 render action: :new end end private def tweet_params params.require(:tweet).permit(:content) end endflash.nowは次リクエストを送信した時点で消えるため、これでflashを残さずに次のページへ移動することができます
それぞれの組み合わせ
- flash[]とredirect_to => 次のページにリダイレクトした時点でflashは消える
- flash[]とrender => renderはリクエストを送信しないため、次のページに移動してもflashは残る
- flash.now[]とredirect_to => redirect_toの時点でflashが消えるため、flash自体表示されない
- flash.now[]とrender => 次のページへリダイレクトした時点でflashは消える
最後に
Railsチュートリアルで丁寧に説明されていた箇所なんですが、実際に自分で試行錯誤して初めて理解できました
何かおかしい部分があれば指摘して頂けると嬉しいです
読んでいただきありがとうございました!
- 投稿日:2020-03-29T16:50:11+09:00
Ruby ◯◯進数の扱いについて
10進数から(n)進数へ変換
10進数.to_s(n) 255.to_s(2) => "11111111" 255.to_s(8) => "377" 255.to_s(16) => "ff"(n)進数から10進数へ変換
"n進数".to_i(n) "11111111".to_i(2) => 255 "377".to_i(8) => 255 "ff".to_i(16) => 2558進数を10進数に => oct
16進数を10進数に => hex8進数.oct 16進数.hex "377".oct => 255 "ff".hex => 255(n)進数の頭に付けて10進数に変換
0b => 2進数
0 => 8進数
0x => 16進数0b2進数 08進数 0x16進数 0b11111111 => 255 0377 => 255 0xff => 255計算方法
10進数 2進数 0 0 1 1 2 10 3 11 4 100 5 101 6 110 7 111 8 1000 9 1001 10 1010 11 1011 12 11002進数1100を10進数に
(1 * 2**3) + (1 * 2**2) + (0 * 2**1) + (0 * 2**0) = 8 + 4 + 0 + 0 = 1212を10進数に
12 / 2 = 6..0 6 / 2 = 3..0 3 / 2 = 1..1 1 / 2 = 0..1 余りを下から取って11008進数と16進数の計算方法は後日また記載します。
- 投稿日:2020-03-29T16:30:59+09:00
【Ruby】外部ファイル読み込み require・require_relative・load・autoloadの違い
外部ファイル読み込みの違いを明確にしたかったのでまとめてみました。
require("絶対パスまたは相対パス")
ライブラリや外部ファイルを絶対パス、相対パスで読み込む(1回のみ読み込み)
require_relative("絶対パス")
ライブラリや外部ファイルを絶対パスで読み込む(1回のみ読み込み)
load("絶対パスまたは相対パス")
ライブラリや外部ファイルをファイルを絶対パス、相対パスで読み込む(再読み込み、拡張子は省略できない)
loadはrequireやrequire_relativeよりも再読み込みするため遅くなる
autoload("クラス/モジュールの定数", "絶対パスまたは相対パス")
定数を最初に参照した時に第2引数を絶対パス、相対パスで読み込む(1回のみ読み込み)
クラス・モジュールの遅延ロードを実現まとめ
モジュール関数 パスの仕方 読み込み回数 遅延ロード require 絶対・相対パス 1回 ✖︎ require_relative 絶対パス 1回 ✖︎ load 絶対パス、相対パス 再読み込み ✖︎ autoload 絶対パス、相対パス 1回 ○
- 投稿日:2020-03-29T16:18:15+09:00
【Rails】SQliteでのDBの内容確認周りのコマンドまとめ(追加していく所存)
【Rails】SQliteでのDBの内容確認周りのコマンド
モデルのカラム数の確認
terminal>rails console >モデル名.newモデルの内容確認(全ては見れません)
terminal> rails console > モデル名.allモデルの特定のidをもつデータのもつ内容の確認
terminal> rails console > モデル名.find(特定のid)モデルの特定のカラム内容をもつ行の確認
terminal> rails console > モデル名.find_by(カラム名: "文字列や数字")投稿の数やユーザーの数の確認
terminal> rails console > モデル名.select("調べたいカラム名").count例えば、Userモデルに登録されているユーザーの数を知りたいとき
terminal> rails c > User.select("id").count この結果 irb(main):007:0> User.select("id").count (1.5ms) SELECT COUNT("users"."id") FROM "users" => 1 ↑ユーザーの数が1だとわかる例えば、Postモデルに登録されている投稿の数を知りたいとき
terminal> rails c > Post.select("id").count この結果 irb(main):007:0> User.select("id").count (1.5ms) SELECT COUNT("posts"."id") FROM "users" => 27 ↑ユーザーの数が1だとわかる
- 投稿日:2020-03-29T15:00:17+09:00
【Ruby on Rails】Rails超入門2❗️凄くて震えるvalidation‼️scaffoldで恐竜登録アプリ作成2
1.validationとは
コマンド一つでcrudアプリが作れる時点で十分凄いのだが、
この恐竜登録には、少し問題がある。
それは、何回でも同じ名前の恐竜が登録できてしまったり、
全て空のデータが登録できてしまったりする事だ。
その理由として、モデル生成の時、rails側で用意したこちらIDという項目がモデルで定義したテーブルに付与され、さらにこのIDがキーになっているためだと思われる。上記の事態を避けるために、データ登録時に制約を追加しなくてはならない。
例えば以下のルールを与えてみる。
- 生成恐竜名(dname0)と生成タイプ(dtype0)は入力必須
- hflgにチェックを入れた時(ハイブリッド型)は、生成元恐竜1(sname1)、生成元恐竜2(sname2)、生成元タイプ1(stype1)、生成元タイプ2(stype2)は必須
- hflgにチェックを入れない時(非ハイブリッド型)は、生成元恐竜1(sname1)、生成元恐竜2(sname2)、生成元タイプ1(stype1)、生成元タイプ2(stype2)は空にする
- 登録済み生成恐竜名と生成タイプを入力した時は登録しない。
- 生成恐竜タイプは、normal,rare,epic,regend,uniqueとする。
- 生成元恐竜タイプ1と生成元恐竜タイプ2は、normal,rare,epic,regendとする。
このモデルで定義したテーブルに登録するために入力データに設けるルールをvalidationというらしい。
2.validation実装
ActiveRecordバリデーション
を参考に実装してみた。railsアプリのディレクトリ\app\models\dinosor2.rbclass Dinosor2 < ApplicationRecord # dname0は入力必須。 validates :dname0, presence: true # dtype0はnormal,rare,epic,regendの何れかを指定 validates :dtype0, inclusion: { in: %w(normal rare epic regend unique) } # dname0とdtype0の組み合わせでユニーク validates :dname0, uniqueness: { scope: :dtype0 } # hflgにチェックが入っているとき、stype1はnormal,rare,epic,regendの何れかを指定 validates :stype1, inclusion: { in: %w(normal rare epic regend) } , if: '!hflg.blank?' # hflgにチェックが入っているとき、sname1は入力必須 validates :sname1, presence: { if: '!hflg.blank?' } # hflgにチェックが入っているとき、stype2はnormal,rare,epic,regendの何れかを指定 validates :stype2, inclusion: { in: %w(normal rare epic regend) } , if: '!hflg.blank?' # hflgにチェックが入っているとき、sname2は入力必須 validates :sname2, presence: { if: '!hflg.blank?' } # hflgにチェックが入っていないとき、stype1,sname1,stype2,sname2は空にする。 validates :stype1,:sname1,:stype2,:sname2, absence: { if: 'hflg.blank?' } end3.viewの変更
下記のviewを修正したのはdtype0,stype1,stype2の入力欄の前に、
タイプ欄を表示するようにした。
エラーメッセージを見るとわかるのだがリスト内の定義が見えないと入力が難しいためだ。_form.html.erb<%= form_with(model: dinosor2, local: true) do |form| %> <% if dinosor2.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(dinosor2.errors.count, "error") %> prohibited this dinosor2 from being saved:</h2> <ul> <% dinosor2.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <% d_type = "(normal,rare,epic,regend,unique)" %> <% s_type = "(normal,rare,epic,regend)" %> <div class="field"> <%= form.label :dname0 %> <%= form.text_field :dname0, id: :dinosor2_dname0 %> </div> <div class="field"> <%= form.label :dtype0 %> <%= d_type %><br> <%= form.text_field :dtype0, id: :dinosor2_dtype0 %> </div> <div class="field"> <%= form.label :sname1 %> <%= form.text_field :sname1, id: :dinosor2_sname1 %> </div> <div class="field"> <%= form.label :stype1 %> <%= s_type %><br> <%= form.text_field :stype1, id: :dinosor2_stype1 %> </div> <div class="field"> <%= form.label :sname2 %> <%= form.text_field :sname2, id: :dinosor2_sname2 %> </div> <div class="field"> <%= form.label :stype2 %> <%= s_type %><br> <%= form.text_field :stype2, id: :dinosor2_stype2 %> </div> <div class="field"> <%= form.label :hflg %> <%= form.check_box :hflg, id: :dinosor2_hflg %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>4.検証してみた
「同じものを登録したらいけないよ。」
(2)非ハイブリッド恐竜のチェック
「stype1,stype2,sname2,sname1は空であるべきだ。そうは思わないか?」(3)ハイブリッド恐竜のチェック
「stype1,sname1,stype2,sname2は、入力必須だ。ハイブリッドを手に入れたいだろ?」
(4)非ハイブリッド恐竜のタイプチェック
「恐竜のタイプはリスト内のものと決まっているんだ。これはルールさ」(5)ハイブリッド恐竜の生成元恐竜タイプチェック
「生成元恐竜のタイプはリスト内のものと決まっているんだ。これはルールさ」①弾いてる!!条件に合わないって!!!
②しかもなんだか、エラー画面もスタイリッシュ!!
私の大好きな赤というのがいい。Red is god!!
③声の低いイケメンに注意されているみたい←!?
4.まとめ
PHP版恐竜登録ツール
と比較すると、作成工数が大幅に削減されている。
たった一つのコマンドと、少しのvalidationでwebアプリが作れる。
『世界よ。これがRuby on Railsだ』と言ったところだろう。
- 投稿日:2020-03-29T14:53:20+09:00
部分テンプレートを使ってみよう!
部分テンプレートに関してまだまだ知識量が足りていないと感じたため備忘録として記述します。
部分テンプレートとは?
みなさんはビューを作成する際にこんなことって考えたことありますか?
「ここの要素は別ページの要素と同じだから切り取って使いたいな〜」
コピペすれば良いじゃないかと思うかもしれませんがプログラミングにあたってより少ないコードでシンプルにかければ素晴らしいことこの上ないです。
そんな時に部分テンプレートを使用してスッキリとしたコードを記述しましよう!
部分テンプレートの構成に関して
部分テンプレートファイルを作成する際には『_(アンダーバー』をファイル名のトップに記述しましょう。要するにこちらのアンダーバーが表記あるファイルが子ファイルになり、親ファイルから呼び出されます。
親ファイルへの記述方法
親ファイルへの記述方法としては以下のメソッドとオプションを使用して記述して部分テンプレートを呼び出します。
renderメソッド
一番メインになる記述です。renderメソッドは、部分テンプレートを呼び出す際に利用するメソッドです。
partialオプション
renderメソッドとセットで使用することが多いです。明示的に部分テンプレートを指定して、表示させる役割を持っています。以下の場合は"_sample.html.erb"という部分テンプレートファイルを呼び出しています。
<% render partial: "sample" %>localsオプション
localsオプションを使用することで部分テンプレートファイル内で設定したその変数を使用できるようになります。
<% render partial: "sample", locals: { post: "hello!" } %>local部分に記載がある変数postは、post = "hello!"と同様の意味を持つ記述です。
まとめ
部分テンプレートを使用するメリットとしては
・繰り返しかくコード記述を減らすことができる
・修正時に修正箇所が少なくて済む
ある意味変数的役割を果たしてくれているのが部分テンプレートなんですね!偉大!
- 投稿日:2020-03-29T13:22:46+09:00
【Rails】お気に入り登録をした順に投稿を表示する方法
はじめに
本を投稿するアプリケーションにおいて、お気に入り機能を実装しました。
お気に入り本リストでの表示において、本の投稿順ではなく、
「お気に入り登録をした順番」で表示する方法をまとめます。
※お気に入り機能自体の実装には触れません。本投稿順での表示になるパターン
users
コントローラーにおいて@favorite_books
を取得し、
ユーザー詳細ページで表示するためにビューに渡します。users_controller.rb(抜粋)def show @user = User.find(params[:id]) @favorite_books = @user.favorite_books end今回は記載を省略していますが、
User
モデルとFavorite
モデルの関連付けができているため、
@user.favorite_books
により、そのユーザーがお気に入りした本を取得することができます。
参考:【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】?
↑この記事に沿って中間テーブルを設けて実装を行いました。ビューにおいて
@favorite_books
を展開します。
_book.html.slim
のパーシャルを準備し、@favorite_books
の中身を順に表示する形です。show.html.slim(抜粋)- if @favorite_books.any? - @favorite_books.each do |book| = render 'books/book', { book: book }上記のように実装を行うと、ユーザー詳細ページにおけるお気に入り本リストの表示順が、
本の投稿順となります。
.favorite_books
により一発でBook
オブジェクトに変換されており、
primary_key
であるid
(ここではBook
オブジェクトのid
)の順番で表示がされるためです。
よって、@favorite_books = @user.favorite_books.order(created_at: "DESC")
といった風に順番を指定しても、あくまで本の投稿順が降順に変更されるだけです。お気に入り登録をした順に表示させる方法
.favorite_books
により一発でBook
オブジェクトに変更するのではなく、
一度Favorite
オブジェクトへ変換しそこで順番の調整を行った上で、
各Favorite
オブジェクトからBook
オブジェクトを取り出す方法により実現できます。users_controller.rb(抜粋)def show @user = User.find(params[:id]) @favorite_books = @user.favorites.order(created_at: "DESC").map{|favorite| favorite.book} endまず、
@user.favorites
によりそのユーザーに関連付いたFavorite
オブジェクトを取り出し、
order(created_at: "DESC")
でそれを降順に並び替えた上で、
map
メソッドを使用することによりBook
オブジェクトへの変換を行なっております。上記方法により、お気に入りリストにおいて「お気に入り登録をした順」にて本が表示されるようになります。
(他にもっといい方法がある気がしてなりません。コメントいただければ幸いです!)
- 投稿日:2020-03-29T12:08:43+09:00
if文を1行で書く場合
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]例えば配列に対して条件分岐をさせたい場合
numbers.each do |n| if n % 2 == 0 'OK' else 'NG' end endこんな感じになるかと思いますが
numbers.each do |n| n % 2 == 0 ? 'OK' : 'NG' endこれでも同じ結果が得られます。
条件 ? true : falseif else end を記述しないのでリファクタリングにも使えますね。
- 投稿日:2020-03-29T10:19:40+09:00
インストールするgemのリモートリポジトリを追加する
環境
Ubuntu 16.04.5 LTS
Windows10
Vagrant手順
gemをインストールする時、gem installでインストールするが、どこからインストールしているのだろうか?ダウンロード元のリモートリポジトリを確認するコマンドがあります。
$gem sources -l https://rubygems.org/では、rubygems にないgemをインストールしたい場合はどうすればいいのだろうか?
インストールしたいgemが、GitHubにある場合、リモートリポジトリのリストにGitHubを追加します。$gem sources -a http://gems.github.com/$gem sources -l https://rubygems.org/ http://gems.github.com/これで、インストールしたいgemが、rubygemsになかった場合は、githubを検索して、ダウンロードしてくれるようになります。
- 投稿日:2020-03-29T09:39:11+09:00
【Rails】SorceryでTwitter認証
はじめに
Sorceryを使ったTwitterログイン認証の設定です。
途中までは【Rails】Sorceryでfacebook認証 Sorceryの設定と同じなので、そちらをご覧ください。動作環境
ruby 2.5.3 Rails 5.2.4.2 sorcery 0.14.0前提
- Twitter Develpersのユーザー登録とアプリの登録
- 【Rails】Sorceryでfacebook認証のfacebook認証の部分以外
SSL化はしなくていいです。
設定
Sorceryの設定
config/sorcery.rbconfig.twitter.key = Rails.application.credentials.dig(:twitter, :key) config.twitter.secret = Rails.application.credentials.dig(:twitter, :secret_key) config.twitter.callback_url = 'http://localhost:3000/oauth/callback?provider=twitter' config.twitter.user_info_mapping = { twitter_id: 'id', name: 'name', description: 'description' } # Userモデルの属性名: 'twitterのパラメータ'ちなみに、今回はTwitter認証しか使わないので、Userモデルにはパスワードカラムは用意していません。
Twitterのscreen_name(@username
というやつ)は変わる可能性があるので、twitter_idカラムにはidを格納することにしました。Twitter Developperの設定
「Enable Sign in with Twitter」にチェックを入れます。
Callback URLsは以下のベストプラクティスに則り、http://127.0.0.1:3000/oauth/callback
にします。Do not add query strings to your callback URLs in your Twitter app’s configuration
Don’t use localhost as a callback URL
Callback URLs — Twitter Developers
上のCallback URLではクエリ文字列が使えないので、Sorceryの設定の
callback_url
でクエリを渡します。
すると、認証画面のURLにoauth_callback
というクエリが渡され、クエリを持ったコールバックURLを設定されていることがわかります。
認証画面のURLhttps://api.twitter.com/oauth/authenticate?oauth_callback=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fcallback%3Fprovider%3Dtwitter&oauth_token=EHBQ4wAAAAABDRHmAAABcRr4LwQキャンセル後のリダイレクト
今の状態では、「連携アプリをキャンセル」を押したときにエラーが出てしまうので、それを解消します。
「(アプリ名)に戻る」を押すと401エラーになってしまいます。Started GET "/oauth/callback?provider=twitter&denied=gZtRIQAAAAABDRHmAAABcSN_g4w" for 127.0.0.1 at 2020-03-29 08:34:55 +0900 Processing by OauthsController#callback as HTML Parameters: {"provider"=>"twitter", "denied"=>"gZtRIQAAAAABDRHmAAABcSN_g4w"} Unpermitted parameter: :denied Completed 500 Internal Server Error in 400ms (ActiveRecord: 0.0ms) OAuth::Unauthorized - 401 Authorization Required: app/controllers/oauths_controller.rb:12:in `callback'キャンセル後のparams<ActionController::Parameters {"provider"=>"twitter", "denied"=>"PsylCwAAAAABDRHmAAABcSGWp1Q", "controller"=>"oauths", "action"=>"callback"} permitted: false>キャンセルした場合は
params[:denied]
が存在するので、条件分岐で処理します。app/controllers/oauths_controller.rbclass OauthsController < ApplicationController def oauth login_at(auth_params[:provider]) end def callback provider = auth_params[:provider] if auth_params[:denied].present? # ここの節を追加 redirect_to root_path, notice: 'ログインをキャンセルしました' return end if (@user = login_from(provider)) redirect_to root_path, notice: "#{provider.titleize}でログインしました" else begin @user = create_from(provider) reset_session auto_login(@user) redirect_to root_path, notice: "#{provider.titleize}でログインしました" rescue StandardError redirect_to root_path, alert: "#{provider.titleize}でのログインに失敗しました" end end end private def auth_params params.permit(:code, :provider, :denied) end end以上で設定は終了です!
以下は、ちょっと試してみたことやエラーとの奮闘になります。callback_urlを設定しない場合
For OAuth 1.0a compliance this parameter(注釈:
oauth_callback
のこと) is required .
POST oauth/request_token — Twitter Developersということなので、
callback_url
を設定しないとどうなるのか試してみた。config/sorcery.rb# config.twitter.callback_url = 'http://localhost:3000/oauth/callback?provider=twitter'
認証画面のURLhttps://api.twitter.com/oauth/authenticate?oauth_callback&oauth_token=vyYZbQAAAAABDRHmAAABcRsW6zQクエリを見ると、
oauth_callback
に値が入っていない。
認証を押すと、PINコードの入力を求められ、リダイレクトしなくなってしまった。
認証について
ところで、認証画面のURLを見ると、
request_token
というクエリも渡されていることがわかります。このrequest_token
はPOST oauth/request_tokenによって取得されています。Allows a Consumer application to use an OAuth
request_token
to request user authorization.
参考:GET oauth/authenticate — Twitter Developers深掘りしようとすると沼にはまりそうなので、この辺りにとどめておきます。Sorceryがよしなにやっている部分です。
参考:[Python] OAuth認証でTwitter連携/ログインを実装する
Twitter REST APIの使い方 アクセストークンの取得エラーいろいろ
NoMethodError at /oauth/callback
NoMethodError at /oauth/callback undefined method `original_callback_url' for nil:NilClassコールバック時のURLhttp://127.0.0.1:3000/oauth/callback?provider=twitter&oauth_token=EHBQ4wAAAAABDRHmAAABcRr4LwQ&oauth_verifier=zFvTO1ay9G316hmbPMcbexTfckA45M4j画面ばかり見て「どうしてoauthsアクションに飛んでいるのだろう」と思っていたのですが、そもそも
NoMethodError
でした。解決法
config/routes.rbpost "oauth/callback", to: "oauths#callback" get 'oauth/callback', to: 'oauths#callback' # この行がなかったので追加 get "oauth/:provider", to: "oauths#oauth", as: :auth_at_providerMysql2::Error - Field 'twitter_id' doesn't have a default value:
Started GET "/oauth/callback?provider=twitter&oauth_token=qvWE_gAAAAABDRHmAAABcSFi3bQ&oauth_verifier=nUb5kYYSryTHA03FepYw2xSLNzVFMmTc" for 127.0.0.1 at 2020-03-28 22:44:22 +0900 Processing by OauthsController#callback as HTML Parameters: {"provider"=>"twitter", "oauth_token"=>"qvWE_gAAAAABDRHmAAABcSFi3bQ", "oauth_verifier"=>"nUb5kYYSryTHA03FepYw2xSLNzVFMmTc"} Unpermitted parameters: :oauth_token, :oauth_verifier Authentication Load (0.8ms) SELECT `authentications`.* FROM `authentications` WHERE `authentications`.`uid` = '1048451188209770497' AND `authentications`.`provider` = 'twitter' ORDER BY `authentications`.`id` ASC LIMIT 1 ↳ app/controllers/oauths_controller.rb:8 (0.2ms) BEGIN ↳ app/controllers/oauths_controller.rb:13 User Exists (3.4ms) SELECT 1 AS one FROM `users` WHERE `users`.`uuid` = '7gl0ZsQ_tTbl' LIMIT 1 ↳ app/models/user.rb:21 User Create (1.5ms) INSERT INTO `users` (`uuid`, `name`, `description`, `created_at`, `updated_at`) VALUES ('7gl0ZsQ_tTbl', 'aiandrox', '小学校の先生やってた。エンジニア目指してRailsとか頑張ってる。#RUNTEQ 1月生。謎解き好き。', '2020-03-28 22:44:24', '2020-03-28 22:44:24') ↳ app/controllers/oauths_controller.rb:13 (0.2ms) ROLLBACK ↳ app/controllers/oauths_controller.rb:13 Completed 500 Internal Server Error in 1349ms (ActiveRecord: 42.5ms) Mysql2::Error - Field 'twitter_id' doesn't have a default value: app/controllers/oauths_controller.rb:13:in `callback'データベース側でtwitter_idカラムに
null: false
制約をかけていたので、このエラーが出ました。
確かにログを見るとtwitter_idカラムには何も入っていない。解決法
config/sorcery.rbconfig.twitter.user_info_mapping = { - twitter_id: 'user_id', + twitter_id: 'id', name: 'name', description: 'description' }Twitterから取得するデータのパラメータが間違っていました。
参考:User Object(ユーザーオブジェクト)の説明
(メールアドレスは取得できないのだろうか。どなたかご存知でしたら、ご教示ください)おわりに
特別な内容ではありませんが、参考になれば幸いです。
ご指摘などがございましたら、コメントか編集リクエストでお願いいたします。
- 投稿日:2020-03-29T08:25:34+09:00
ActiveRecord のコールバックを特定のコントローラーの処理のときのみ実行する
はじめに
ActiveRecord のコールバックを、特定のモデルの状態というよりは、特定のコントローラーの処理の場合のみ実行したい場合について考えたので、簡単にやり方をまとめます。
tl;dr
方針としては、コントローラーにモデルのインスタンス変数でフラグを付けてあげて、そのフラグが立っている場合のみ実行するという形にしました。
例
今回は
Post
モデルのstatus
をcreate
時にのみcreated
とつけることを考えます。やり方
モデルにattr_accessor と after_save の設定
まず、
Post
モデルにステータス更新用のupdate_status
メソッドを定義します。また、インスタンス変数のフラグを立てるメソッド
create_executed!
メソッドとフラグ判定メソッドcreate_executed?
メソッドも作っておきます。そして、
after_save
コールバックで、create_executed?
がtrue
のときだけupdate_status
メソッドを実行する形にしました。class Post < ApplicationRecord after_save :update_status, if: :create_executed? def create_executed! @create_executed = true end def create_executed? @create_executed == true end def update_status update_columns(status: 'created') end endコントローラーに設定
コントローラー側では、
create
メソッドのときに、フラグを立てるメソッドを実行すればOKです。
これにより、create
メソッドの場合のみ、after_save
のコールバックを呼ぶことができます。class PostsController < ApplicationController # 中略 def create @post = Post.new(post_params) @post.create_executed! if @post.save redirect_to posts_path else render :new end end # 中略 endおわりに
after_save
コールバックには、validates
メソッドのon:
のようなコンテキストが使えなかったのでどうしようか悩んでいたのですが、一旦この形でできました。他にいい方法ご存知でしたら教えて下さい!
参考