- 投稿日:2020-05-22T22:17:37+09:00
Kinx ライブラリ - CSV Parser
CSV
はじめに
「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。
今回は CSV パーサーです。TSV もあります。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
使い方
using CSV
CSV ライブラリは標準組み込みではないため、using ディレクティブを使用して明示的に読み込む。
using CSV;全てを一度にパース
ファイルを読み込んで全体をパースするには、
CSV.parse()
を使う。using CSV; var r = CSV.parse("filename.csv");データの形式は以下の通り。全ての行オブジェクトの配列として返す。行オブジェクトには
data
フィールドがあり、そこに配列として各要素の文字列が設定される。ただし、コメント行(先頭が#
)の場合、data
フィールドは空となりcomment
フィールドにコメント文字列が入る(#
も含む)。[{ "data": ["aaa", "bbb", "ccc"] }, ..., { "data": {}, "comment": "# comment" }]1行ずつコールバック
行数が多い場合、全体を一気にパースすると大量にメモリを消費する可能性がある。そこで、以下のように 1 行ずつコールバックさせることもできる。コールバックの引数は 1 行分の行オブジェクトとなる。
using CSV; CSV.parse("filename.csv", &(row) => { System.println(row); // like `{ data: ["aaa", "bbb", "ccc"] }` });尚、この場合
CSV.parse()
は値を返さない(null を返す)。文字列をパース
文字列をパースする場合、
CSV.parseString()
を使う。コールバック版も同様。using CSV; var r = CSV.parseString(csvString); CSV.parseString(csvString, &(row) => { ... });CSV 仕様
解釈する CSV の仕様は以下の通り。
- 先頭文字が
#
の場合、コメント行と認識。- ダブルクォートは無くても良い。
,
や"
、改行を含めたい場合はダブルクォートが必要。- ダブルクォート内では以下の仕様。
,
は直接記載可能。"
は""
と重ねて記載。- 改行はそのまま改行することで表現する。
- 最終行の末尾に改行コードは無くても良い。
サンプル
サンプルとして以下をパース。
# comment in CSV. abcde,efgh,non-quoted string "abcde","efgh","quoted string" "abcde",efgh,"quoted & non-quoted string in the same row" "abcde",efgh,"can use , or "" in csv" "abcde",efgh,"can use newlines in the row" # another comment in CSV. abcde,efgh,not necessary \n the end of csv結果はこうなる。
[{ "data": {}, "comment": "# comment in CSV." }, { "data": ["abcde", "efgh", "non-quoted string"] }, { "data": ["abcde", "efgh", "quoted string"] }, { "data": ["abcde", "efgh", "quoted & non-quoted string in the same row"] }, { "data": ["abcde", "efgh", "can use , or \" in csv"] }, { "data": ["abcde", "efgh", "can use newlines\n\nin the row"] }, { "data": {}, "comment": "# another comment in CSV." }, { "data": ["abcde", "efgh", "not necessary \\n the end of csv"] }]TSV
TSV とは CSV のカンマ区切りがタブ区切りになったもの。
using CSV
は一緒。使うときにCSV
をTSV
にするだけで使える。using CSV; var r = TSV.parse(tsvFilename); TSV.parseString(tsvString, &(row) => { ... });パーサー
パーサー自体を Kinx で書いています。
パーサーの形態は1文字先読みの再帰下降パーサーですが、そもそも CSV の仕様に再帰的な要素がないので再帰しません。言うなればただの下降パーサーです。
コードは GitHub にあります。全体で100行ちょっとしかないのですぐ理解できるかと思います。
内部ライブラリ用の特殊キーワードについて
ちなみに、
_class
と_function
というキーワードが使われていますが、一般では使いません。意味はclass
、function
と同様です。何が違うかというと、
_
付きのほうは例外発生時にスタックトレースに情報を残さないといった動作をします(また、-d
オプションで VM コードを表示しません)。これは内部ライブラリの場合、スタックトレースに情報を残すと混乱する可能性があるためです。例えば、例外の起点が内部ライブラリの行番号を示していると、そこに問題があると勘違いする可能性があるためで、例外の起点はあくまでユーザー・コードを示すようにしています。VM コード出力でもユーザー・コードの邪魔になると考えられるので表示しないといった配慮がなされる形です。このストラテジーに従うと逆にライブラリ内部にバグがあった場合は調査困難になりますが、通常ケースでパラメーター異常による例外、といったケースのほうが一般的と考えられるので、この仕組みを入れてあります。尚、内部ライブラリ自体のデバッグをしたい場合は
_class
をclass
に、_function
をfunction
に変更すればできます。おわりに
CSV パーサーは何か既存のライブラリを使おうかと思って探したんですが、イマイチ統合するのが手間取りそうだったのと、実は簡単なんじゃないかと思ってスクラッチで書いてみました。何か問題があればお知らせください。
ではまた、次回。
- 投稿日:2020-05-22T22:11:22+09:00
シーザー暗号を噛み砕く
近況報告
最近は就活(企業にひたすらESを送るだけの作業)ばっかで空いている時間にテックキャンプのドリルをしこしこ解いています。githubの草は生やし続ける。今日受けた企業さん,無事通っているといいな。
本題 シーザー暗号
辞書順に3文字分シフトして暗号文を作る暗号
ex. abc ⇨ cdeシーザーというのは高校世界史を履修していたら知らない人はいないあのシーザー,カエサルさんのことを指します。ガリア戦記とか,「ブルートゥスお前もか」とかかなりの人が認知しているのではないのでしょうか。
昔はパソコンもケータイもないアナログの時代,かろうじてエジプト文明がパピルスとか発明していたので文字を記録する文明はあったでしょう。ただ,文書の輸送手段は人の足です。となれば機密文書が道中で奪われてそれが原因で亡国の危機に晒されることは度々あったでしょう。
カエサルは重要な情報の漏洩を防ぐためにこのような暗号を用いました。ちなみに甥のアウグストゥスも用いていたとか。なお,現代におけるシーザー暗号はセキュリティの観点からは非常に脆弱であることは認識しておきましょう。
パソコン上でのおおまかな方法
1.文字列を文字コードに変える
2.文字コードに任意の数を足す(引く)
3.文字コードを文字列に変更するサンプル シーザー暗号の生成解読メソッド
前後させる数の大きさは任意です。
def cipherAction result = [] ←配列の箱用意 puts "暗号化したい文字は?" x = gets.to_s ←str型で定義 x.chars.each do |i| ←文字列を1文字単位で分解 code = (i.ord + 3) ←ordは文字コードに変換するメソッド result << code.chr ←継承 end puts result.join("") ←1文字単位で格納されているのを文字列に整える end def deciperAction result = [] puts "元に戻したい文字は?" y = gets.to_s y.chars.each do |j| code = (j.ord - 3) result << code.chr end puts result.join("") end puts "Select Action" puts "1:暗号化" puts "2:元に戻す" input = gets.to_i if input == 1 cipherAction elsif input == 2 deciperAction else puts "不正な入力です" exit endおわりに
安直ではありますが,暗号化された分は何文字前後しているか分からないので解読は手間(まあパソコン使えばサクッとできますが)ですので日常レベルではまあつかえますね。
- 投稿日:2020-05-22T21:50:50+09:00
100日後に1人前になる新人エンジニア(2日目)
100日後に1人前になる新人エンジニア(2日目)
早くもエンジニアとして働いて2日が過ぎました。
こちらは昨日の記事ですよかったらどうぞ
100日後に1人前になる新人エンジニア(1日目)少しずつ業務に慣れてチームに貢献していきたい!!という気持ちと、
自分は何にもわかってないんだなという気持ちを持ちつつ
2日目の記事を書いていきます。今日やったこと
・フォームの改善
・viewの改善 の2つでしたissueに対してコードを書いてGitHubにプルリクエストを送る
という流れでした。今日の最も苦戦した部分はGit(もちろん他にも苦戦はしたけど)
そもそも1人で開発していた時はほとんどGitを使っていなかったので、
チームとして働き始めて覚えていかねばと思っています。ちなみにgitを作った人はLinuxを作った人と一緒なんだね
天才かよ。ということで今日はGitについてフォーカス
Gitあれこれ
gitはバージョンを管理するシステムだよ。
これによってバージョンを行き来したり修正したりできるよ。基本的なGitコマンド
git init #リポジトリを初期化 git status #リポジトリの状態を確認 addされていないファイルを確認できるよ git add (ファイル指定) #ステージ領域にファイルを追加 git commit #リポジトリの歴史として記録 git log #コミットログを確認。いつ誰がコミットやマージをしたのか git log -p #コミットで行われた差分を確認 git diff #addされていないファイルとコミットされているものの差分を表示とても簡単だけどこんな感じgit commitは -mのオプションで一行のコメント
オプションなしでは長いコメントが可能だよ。Branch
枝分かれとか支店とかそうゆう意味がありますよね。
作業を分岐させることでそれぞれ独立した作業を行えるってことですね。git branch #ブランチの一覧と現在のブランチを表示 git checkout -b hoge #hogeブランチを作成してhogeブランチに切り替える git checkout master #masterブランチに切り替えるブランチに関してはまた今度しっかりまとめたいと思います。
GitHubプルリクエスト
実際に書いたコードを他人にレビューしてもらう機能
これはGitの機能ではなくGitHubの提供している機能この機能ができたからこそSocial codingつまり閉ざされた世界ではなく
真の意味で開かれた状態でコードを共有することができるようになった!!
って本に書いてありましたgitコマンドを参考にして
git add → git commit → git push origin branch名
でプッシュをした後にGitHubでプルリクエストをかけばOK!
ここはコードとかではなくGitHubの使い方なので省略。今日のさいごに
できないことしかないので
できるようになった喜びも多いはず。
楽しみながら成長を。
ちなみに土日もちゃんと更新していきます。1人前のエンジニアになるまであと98日
- 投稿日:2020-05-22T21:21:14+09:00
rspecでGraphQLのresolverをテストする
はじめに
graphqlのテストってみなさんどう書いてますか?
queryを定義してテストもできますが、少し面倒だど感じている今日この頃。
そこで、queryとかmutationに定義されているresolverをrspecでテストする方法について説明します。mutationの例
例えば、以下のようなmutationを定義しているとしましょう。
mutation.rbmodule Mutations class CreateUser < BaseMutation argument :id, ID, required: true field :user, ObjectTypes::UserType, null: false def resolve(id: nil) user = ::User.create!(id: id) { user: user } end end end単純ですが、idをもらってuserを作成するmutationです。
今回のテスト対象はこのresolveになります。rspceを書く
ではさっそくrspecを書きたいと思います。
create_user_rspec.rbrequire 'rails_helper' RSpec.describe CreateUser, type: :request do describe 'resolver' do it 'userが作成されていること' do mutation = CreateUser.new(field: nil, object: nil, context:{}) mutation.resolve(id: [作成するuser_id]) expect(..).to eq .. endまず、mutationのクラスである
CreateUser
クラスのインスタンスを作成します。
引数のfieldとobjectは基本的にnullで良いと思います。
contextについては、場合によってはcurrent_user
を入れることもできます。その場合はcontext:{current_user: User.first}
となります。contextを直接入れられるのが便利ですよね。
そして、作成したmutationのresolveメソッドを読んであげればテスト上で定義したresolve内の処理が実行されます。
これだと、graphqlのテストがかなり楽にかけます!
- 投稿日:2020-05-22T20:16:30+09:00
【Ruby on rails】heroku コマンド実行時にbash: heroku: command not foundと表示される。【rails tutorial】
エラー内容
herokuでのマイグレーションを実施するために以下コマンドを実行。
heroku run rails db:migrateすると
bash: heroku: command not foundherokuコマンドが使えない・・・。
解決方法
調べたところ、herokuを再インストールすれば直るとのこと。
以下コマンドで再インストール。
source <(curl -sL https://cdn.learnenough.com/heroku_install)herokuコマンドでバージョン確認をしてみる。
heroku -vheroku/7.39.3 linux-x64 node-v12.16.2コマンドが通りました!
- 投稿日:2020-05-22T18:00:28+09:00
iframeタグを用いてyoutube動画を埋め込む方法(haml)
はじめに
Railsアプリケーションを作成する際に、youtubeの埋め込み方法を調べたのでまとめます。
今回はhamlを用いたマークアップで、youtubeを表示させることができます。前提環境
今回作成したアプリケーションの環境は下記の通りです。
- ruby 2.5.1
- Rails 5.2.4.3
完成見本
無事埋め込む事ができるとこのような画面になります。
それでは早速実装していきましょう!hamlを導入する
念のため、hamlの導入方法から説明していきます!
gemを導入するだけなので簡単です!Gemfileの1番下に下記のように記述します。
※ Gemfileに記述したらbundle installを忘れないように!!Gemfilegem 'haml-rails'ターミナル% bundle installこれでhamlを導入することができました!
しかし、既存のファイルには haml が適用されていないので、下記のように記述して haml に変更します。
ターミナル% rails haml:erb2haml実行して、下のような記述が出てきたときは、" y " と入力してEnterを押してください。
ターミナルWould you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)これで既存のファイルにもhamlを適用させることができました!!
Youtube動画のURLをコピーする
それではいよいよyoutubeの埋め込みの方に入っていきます。
今回は、こちらのyoutubeを例にして実装していきます。まず、表示させたいyoutube動画の、HTML埋め込みタグをコピーしてください。
コピーは、下記の手順でできます。
ビューファイルに動画を埋め込む
では、コピーができたら、ビューファイルの方をいじっていきます。
今回使用するのはiframeタグと呼ばれるものです。iframeタグをhamlで記述すると下のような書き方になります。
%iframe{ この中にyoutubeの情報を記述していく }まずyoutube動画を表示させたいビューファイルを開き、先ほどコピーしたものを1度そのままペーストします。
〇〇〇.html.haml<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>ペーストしたら、実際にhamlの書き方に直していきます。
タグの中の
width 〜 allowfullscreen
の部分を、%iframe {}
の{}
の中にコピペしてください。〇〇〇.html.haml<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> ⬇️ ⬇️ ⬇️ %iframe{ width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen }このままだとエラーが出てしまうので、さらに修正を加えていきます。
修正をする箇所はいくつかあります。細かい変更なので例を見てもらった方がわかりやすいかと思います。〇〇〇.html.haml%iframe{ width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen } ⬇️ ⬇️ ⬇️ %iframe{ width: "560", height: "315", src: "https://www.youtube.com/embed/_ZRp7KYXM1A", frameborder: "0", allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture", allow: "fullscreen" }一応、修正箇所を書き出すと、
=
から:
に変更- 属性の後に
,
を追加allowfullscreen
をallow: "fullscreen"
に変更このような変更を行っています。
最初のHTMLタグの記述と、修正後の記述を比べてみるとこのような違いになっています。
元の記述(htmlバージョン)<iframe width="560" height="315" src="https://www.youtube.com/embed/_ZRp7KYXM1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>直した記述(hamlバージョン)%iframe{width: "560", height: "315", src: "https://www.youtube.com/embed/_ZRp7KYXM1A", frameborder: "0", allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture", allow: "fullscreen" }以上の操作で、無事youtubeの動画を表示させることができました!
ちなみに、パラメータ(
width
やheight
など)の値を変えることで表示される大きさを変更したりすることも可能です!パラメータは、元から記述されているもの以外にも、
- 動画の開始時間や終了時間を決めるパラメータ
- 動画終了後の関連動画を非表示にするパラメータ
などいくつか存在しています。
パラメータについては、今後時間がある時に追記して行けたらと思います!!
- 投稿日:2020-05-22T17:53:43+09:00
[Rails] RSpecでテストをしてみる
はじめに
今まで難しそうと思って避けてきたテスト。
そろそろサービスを本格的に動かそうと思ったためしっかりとテストというものを理解するための第一歩として学んだことをこちらの記事にかきます。
いわゆる備忘録ですね。
自分自身が初心者なので初心者の視点で書いていきます。
もっとこういう書き方や手法があるなどあればコメントいただけてら嬉しいです!RSpecの導入
おきまりの導入をしていきます。
まあ、ここは特に何も考えずにGemfilegroup :development, :test do ...省略... gem 'rspec-rails' end group :development do gem 'web-console' #もともと記載されている可能性があります。 endGemfileを編集したら、おきまりのbundle installします。
RSpecの基本設定
まずはRSpec用の設定ファイルを作成する必要があります。
ターミナルで$ rails g rspec:installすると以下のファイルが生成されます。
create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb生成されたファイルのうち.rspecファイルに下記を追記します。
.rspec--format documentation上記を追記するとテスト結果を見やすく出力してくれます。
後ほど出てくるcontextとitを段階的に書いてくれるものだと考えればいいと思います。
まあ、なくても問題ないものだと思います。
ちなみに上記以外のオプションを追加するとテキスト出力や、並行表示とかもできるので持っときあめたい方は調べてみてください。とりあえずここまでで導入は完了です。
ここからテストコードを書いていく作業です。テストを実行してみる
RSpecはターミナルでコマンドを打つことにより実行が可能です。
まだ、テストコードは何も書いていませんが、実際に動くかどうか確かめるため下記コードをターミナルに打ち込みます。ターミナル$ bundle exec rspec上記がテストを実行する際のコマンドです。
結果下記のような表示になればオッケーです。ターミナルNo examples found. Finished in 0.00031 seconds (files took 0.19956 seconds to load) 0 examples, 0 failuresここからモデルやコントローラー、ルート、ビューなどのテスト用のファイルを作成し、テストコードを書いていきます。
単体テスト(モデル)
単体テストであるモデルのバリデーションに関するテストコードを書いて実行することを目標とします。
事前に知っておくべきこと
テストコードはspecファイルを作成し記述していきます。
一つのファイルを作って書いていくのではなく、わかりやすいようにモデルに関するテスト用ファイルであればspec/models/以下に、コントローラーに関するテスト用ファイルであればspec/controllers/以下にspecファイルを配置します。specファイルの命名規則
specファイルは対応するクラス名spec.rbという名前になります。
今回はお問い合わせフォーム(contactモデル / contact.rb)のバリデーションに関してのテストを行うため、その場合の名前は「contactspec.rb」になります。テストコードの基本
テストコードを書くファイルに関してわかったところで、実際に書くまえに超超超基本なテストコードを書いておきます。
「1 + 1が2になることを確かめる」という簡単なテストコードsample_spec.rbdescribe "hogehoge" do it "1 + 1は2になること" do expect(1 + 1).to eq 2 end end語彙力は無視して自分的解説をします。
sample_spec.rbdescribe "hogehoge" do #describe(説明するという意)でこのテストのグループを作ります。 it "1 + 1は2になること" do #it "~~~" do の ~~~部分にこのコードがなんのテス等をしているか書きます expect(1 + 1).to eq 2 #この部分が実際にテストが成功するかどうかチェックされる式となります。 end end上記の expect(1 + 1).to eq 2 の部分の式のことをエクスペクテーションと言います。
● expect(X).to eq Y
こんな感じでxの部分に入れた式の値がYの部分の値と等しければ、テストが成功します。
また、eqの部分を、マッチャと言います。マッチャとは、エクスペクテーションの中で、テストが成功する条件を示します。
例えば上記のようにeqは「等しければ」という意味になります。
他にもinclude(含んでいれば)、valid(バリデーションされれば)など複数のマッチャが存在します。
たくさんあるので書きたいテストに合わせて利用してください。実際にモデルのバリデーションのテストを行う。
今回使用するcontactモデル(お問い合わせ機能)は下記のようになっております。
contact.rbclass Contact < ApplicationRecord #送信者の名前 validates :name, presence: true #お問い合わせに対して返信先のメアド validates :email, presence: true, length: {maximum:255}, format: {with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i} #お問い合わせの件名 validates :title, presence: true #お問い合わせ内容 validates :message, presence: true end上記のバリデーションに対してテストコードを書いていきます。
spec/models/contact_spec.rbファイルを作成する。
作成したcontact_spec.rbを下記のように編集します。
contact_spec.rbrequire 'rails_helper' RSpec.describe Contact, type: :model do it "nameがない場合は登録できないこと" do end end1行目のrequire 'rails_helper'は、rails_helper.rb内の記述を読み込むことで共通の設定を有効にしています。
この1行目の記述は、全てのspecファイルに毎度書き込みます。書いたところで一度、bundle exec rspecというコマンドをターミナルで実行します。
現在はまだテストとして評価される式を書いていないので、テストは無条件でパスします。
下記のようになればオッケーです。ターミナル
```:ターミナル
Contact
nameがない場合は登録できないことFinished in 0.23784 seconds (files took 4.71 seconds to load)
1 example, 0 failures
```
nameが空の場合登録できないことを確かめるテストコードを書く
先ほど書いたテストコードに下記を追記します。
contact_spec.rbRSpec.describe Contact, type: :model do it "nameがない場合は登録できないこと" do contact = Contact.new(name: "", email: "kkk@gmail.com", title: "返品について", message: "注文番号000000000の商品を返品したいです。") contact.valid? expect(contact.errors[:name]).to include("を入力してください") end end[3行目]でテストしたいプロパティを持ったcontactクラスのインスタンスを新規作成します。
「nameが空である場合登録できないこと」を確かめるテストコードを作成したいのでnameの値を空にし、それ以外は適当な値をセットした状態でcontactクラスのインスタンスを作成しています。[6行目]で作成したインスタンスがバリデーションによって保存ができない状態かチェックします。
[7行目]でチェックした結果インスタンスが持つエラー文が期待したものであるか確かめます。
contact.errorsに対してハッシュのバリューの取り出し方でカラム名を指定すると、そのカラムが原因のエラー文が入った配列を取り出すことができます。こちらに対して、includeというマッチャを利用してエクスペクテーションを作っています。
今回はバリデーションのエラー文で"を入力してください"が出力されるため、その内容を記述しております。上記の流れは下記のコンソールでの検証と同じ意味を持ちます。
ターミナル#コンソールを立ち上げる $ rails c #nameの値が空であるcontactクラスのインスタンスを作成する >contact = Contact.new(name: "", email: "kkk@gmail.com", title: "返品について", message: "注文番号000000000の商品を返品したいです。") #valid?メソッドを利用する >contact.valid? #errorsメソッドを利用する >contact.errors => #<ActiveModel::Errors:0x007ffa6ce07ef0 @base= #<Contact:0x007ffa6d3430b8 #中略 @messages={:name=>["を入力してください"]}>ここでRSpecのコマンドを入力しテストの実行をします。
ターミナル$ bundle exec rspec下記のような結果になれば成功です。
ターミナルContact nameがない場合は登録できないこと Finished in 0.07346 seconds (files took 2.31 seconds to load) 1 example, 0 failuresもしエラーが表示される場合はエラーメッセージがターミナルに表示されるのでよく読めば解決できることが多いです。
また、上記のようにコンソールで一度一連の流れとして確認してみると少し理解が深まる気がします。[効率的にするため]factory_botの導入
先ほどはテストコードを一つ書きましたが、name以外のmailのテストを書く際は下記のようになります。
contact_spec.rbRSpec.describe Contact, type: :model do it "nameがない場合は登録できないこと" do contact = Contact.new(name: "", email: "kkk@gmail.com", title: "返品について", message: "注文番号000000000の商品を返品したいです。") contact.valid? expect(contact.errors[:name]).to include("を入力してください") end it "emailがない場合は登録できないこと" do contact = Contact.new(name: "takashi", email: "", title: "返品について", message: "注文番号000000000の商品を返品したいです。") contact.valid? expect(contact.errors[:email]).to include("を入力してください") end end単純にemailの部分をからにしているのですが、インスタンスを毎回作成するとコード量が増えてしまいます。
これを改善するのがfactory_botです。factory_botとは
簡単にダミーのインスタンスを作成することができるGemです。
他のファイルで予め各クラスのインスタンスに定めるプロパティを設定しておき、specファイルからメソッドを利用してその通りのインスタンスを作成します。factory_botを利用すると下記のような記述となります。
contact_spec.rbRSpec.describe Contact, type: :model do it "nameがない場合は登録できないこと" do contact = build(:contact, name: "") contact.valid? expect(contact.errors[:name]).to include("を入力してください") end it "emailがない場合は登録できないこと" do contact = build(:contact, email: "") contact.valid? expect(contact.errors[:email]).to include("を入力してください") end endあらかじめインスタンスを別ファイルで作成しそれを呼び出しています。
導入手順
gem 'factory_bot_rails'をrspec-railsと同じ環境に追加
Gemfilegroup :development, :test do #省略 gem 'rspec-rails' gem 'factory_bot_rails' endその後、bundle installし、
specディレクトリ直下に「factories」というディレクトリを作成します。
その中に、作成したインスタンスの複数形のファイル名でRubyのファイルを作成します。今回の場合は、contacts.rbです。作成したcontacts.rbファイルを以下のように編集します。
contacts.rbFactoryBot.define do factory :contact do name {"takashi"} email {"kkk@gmail.com"} title {"返品について"} message {"注文番号000000000の商品を返品したいです。"} end endするとインスタンス作成の際下記のような記述をすることで、設定したテンプレートの内容でインスタンスの作成ができます。
#factory_botを利用しない場合 contact = Contact.new(name: "", email: "kkk@gmail.com", title: "返品について", message: "注文番号000000000の商品を返品したいです。") #factory_botを利用する場合 contact = FactoryBot.build(:contact)だいぶスッキリました。
また、spec/rails_helper.rbを以下のように編集することでレシーバーであるクラスのFactoryBotという記述を省略することができます。rails_helper.rbRSpec.configure do |config| #下記の記述を追加 config.include FactoryBot::Syntax::Methods #省略 end上記踏まえて追加でテストコードを書いていきます。
contact_spec.rbrequire 'rails_helper' RSpec.describe Contact, type: :model do it "nameがない場合は登録できないこと" do contact = build(:contact, name: "") contact.valid? expect(contact.errors[:name]).to include("を入力してください") end it "emailがない場合は登録できないこと" do contact = build(:contact, email: "") contact.valid? expect(contact.errors[:email]).to include("を入力してください") end it "@マークのあとのドメイン" do contact = build(:contact, email: "example@eee") contact.valid? expect(contact.errors[:email]).to include("は不正な値です") end it "titleがない場合は登録できないこと" do contact = build(:contact, title: "") contact.valid? expect(contact.errors[:title]).to include("を入力してください") end it "messageがない場合は登録できないこと" do contact = build(:contact, message: "") contact.valid? expect(contact.errors[:message]).to include("を入力してください") end endこちらでとりあえず完成形です。
終わりに
今回はcontactモデルのバリデーションに関してテストを実行しましたが、WEBアプリの仕様によって様々な書き方があります。
テストという世界を少しでも理解していただくことに役立てば幸いです。
コントローラーのテストの仕方はまた違ったやり方になるっぽいのでそのうちアップできればなと思います。自分も少しテストを理解してきた中で、次のアプリケーションを開発するときはテストコードを書いてから実装してみたりしたいなと考えるようになりました。
記事の内容に関して修正や訂正点があればコメントいただけると幸いです。長々とお読みいただきありがとうございました!
- 投稿日:2020-05-22T16:24:50+09:00
RSpecでActiveRecord::StatementInvalid: Could not find tableが出た
出たエラー
ActiveRecord::StatementInvalid: Could not find tableテストを実行したときにDBのテーブルを見つけられない
やってみたこと
rails db:migrate:reset RAILS_ENV=testでテストDBをリセットして反映
rails dbconsole -e test sqlite> .tableでテーブルができたか確認。作成された。
多くの人はここまでで解決と思うが、もう一度テストを走らせると、
ActiveRecord::StatementInvalid: Could not find table同じエラー。
そして、またテーブルを確認してみると、テーブルが消えてた。
これを踏まえてやってみたこと
ActiveRecord::Migration.maintain_test_schema!を外す
rails_helper.rbで
rails_helper.rb#コメントアウトするコード ActiveRecord::Migration.maintain_test_schema!これをコメントアウトした。
そしたら通った。が、まだテーブルは作られず、そのテーブルを使うテストは全て落ちた。
ActiveRecord::Migration.maintain_test_schema!とは
rspecを実行する前にschema.rbをみて、testのDBを自動でマイグレーションしてくれるためのコード。
これを外すことでテストが実行されるということは、schemaに問題ある可能性ありだと思ったので確認。
Could not dump table "テーブル名" because of following StandardError
schemaファイルにこのように書かれていた。
テーブルをschemaに反映できていない。
migrationファイルを確認
すると、
migration_name.rbdef up change_column :comments, :discovery_id, :reference, null: true endとしていた箇所があった。
型にreferenceなんてないが、なぜかこうしてしまっていた。
:referenceを:integerに変えて、rails db:migrate:reset
を行うと、schemaに記述された。再度走らせる
schemaにしっかり記述されてから、もう一度マイグレーションをリセット
rails db:migrate:reset RAILS_ENV=testそして、ActiveRecord::Migration.maintain_test_schema!のコメントを外す
rails_helper.rb#コメントアウトを外す ActiveRecord::Migration.maintain_test_schema!そしてテストを走らせる
bin/rspec全テスト通った。
まとめ
テスト実行 → テーブルが見つからない → テスト環境でリセットしてマイグレーション → テーブルできた → またテスト実行 → なくなって見つからない → 実行直前のスキーマ読まないようにする → テストは動く → スキーマに見つからないテーブルが記述されてなかった → 適切にマイグレーションする → テスト走る
こんな流れだった。
- 投稿日:2020-05-22T14:55:48+09:00
【チーム開発】マイグレーションの更新作業
概要
チーム開発において、DBの修正が発生することもあり!!
安易に更新してしまうとサーバーがおかしくなってしまうのでは???
チームに迷惑かけちゃう
と、私も更新することが怖かったのですが、以下の手順を踏んだら問題なくできました実施方法
$rake db:drop #DBの情報を全て削除!!登録したユーザー情報なども削除されます! $rake db:create #DBの更新 $rake db:migrate #マイグレート $rake db:migrate:status #きちんとマイグレートできているかを確認ちなみに、、、
カテゴリーなどをテキストエディタに作成していたら、下記のコードもマイグレートした後に行なってください!!$rake db:seedこれでDBの更新OK
この手法は、登録したユーザー情報なども合わせて削除されるので、また登録する必要があります!参考文献
- 投稿日:2020-05-22T13:23:35+09:00
Visual Studio Codespaces で Ruby on Rails
最近 Rails を始めた VSCoder ですが、以下の問題点がありました。
※VSCoder: Visual Studio Code 愛用者
- 環境構築が面倒
そのため、AWS の Cloud9 を利用していたのですが、今度は以下の問題が
- ブラウザ上のエディタは使いづらく違和感がある
- VSCode が使いたい
そこで今ホットな Visual Studio Codespaces に手を出してみて、人生変わりました。
Visual Studio Codespaces とは?
一言で言うと、クラウド上の開発環境です。
公式サイトから引用すると、以下の特徴が挙げられます。
- Git リポジトリ、拡張機能、および組み込みのコマンドライン インターフェイスを備えたブラウザーベースのエディターである
- どのデバイスからでもアプリケーションを編集、実行、デバッグできる
また、私にとって最大の特徴は、
- デスクトップアプリの VSCode でも開発できる
ということです。もちろん拡張機能も追加できます。
Codespace を作成する
- Azure のアカウントを作成します。
- Visual Studio Codespaces のサイトから、Azure アカウントで Sign in します。
- Create Codespace で作成します。Codespace Name だけ指定すれば、あとはデフォルトで十分だと思います。既存のリポジトリがあれば、Git Repository で指定すると自動的に Clone してくれます。(Rails + Space で Railspace という名前にしてみました)
ターミナルも使えます。vsonline というユーザで workspace が作られています。
デスクトップアプリの VSCode で開く
- Visual Studio Code のインストール
- ウィンドウ左側のツールバーの Extensions で Visual Studio Codespaces をインストール
- Remote Explorer のアイコンができるのでそこから先ほど作成したのと同じアカウントに Sign in します。
- 先ほど作成した Railspace という Codespace が出てくるので、コンセントのマークをクリックして connect します。
Ruby on Rails の環境構築
Cloud9 はデフォルトで Ruby や Rails の環境がありますが、Visual Studio Codespaces では残念ながらありません...。まあどうせ、 Cloud9 でもバージョン変えたりするので。
以下はすべて、Codespace に接続した デスクトップアプリの VSCode のターミナルで行います。
Codespaces は Linux 環境を使用しているので、基本的には普通の Linux への環境構築と同じです。rbenv のインストール
Ruby 公式サイトでもおすすめしているこちらの README を参考にインストールしていきます。rbenv によって Ruby の複数のバージョンを管理できます。
まずはリポジトリをクローンしてパスに追加し、セットアップします。
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc $ echo 'export PATH="$HOME/.rbenv/shims:$PATH"' >> ~/.bashrc $ ~/.rbenv/bin/rbenv initその後、ターミナルを再起動する必要があるので、 + ボタンで新しいターミナルを開いてください。
以下のコマンドを入力して確認できますが、rbenv install が not found になっています。$ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash指示されたリンクの通り、以下で解決します。
$ mkdir -p "$(rbenv root)"/plugins $ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-buildもう一度確認すると、今度はうまくいっているはずです。
$ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bashこれで rbenv は完了です。私はここが山場でした。
Ruby と Rails のインストール
主要な Ruby のバージョンを確認できます。すべて見たい場合は、
$ rbenv install --list-all
で可能です。$ rbenv install --listあとは
$ rbenv install [バージョンナンバー]
で好きなバージョンをインストールできます。$ rbenv install 2.7.1インストール後、以下のコマンドでどのバージョンの Ruby を使うか指定します。
$ rbenv global 2.7.1続いて、Rails をインストールします。
$ gem install railsアプリの作成とローカルホストへのアクセス
あとはまるでローカル環境であるかのように開発ができます。
ローカルホストも使えます。$ rails new SampleAppで Rails アプリを作成後、
$ rails sでサーバを立ち上げます。
ブラウザでhttp://localhost:3000/
にアクセスすると...
以上です。
Rails に関しては始めて2カ月なので至らない点があるかもしれないです。
修正やコメントお待ちしてます。
- 投稿日:2020-05-22T12:18:19+09:00
Rubocopの実行が遅い時に見直すべきポイント
Rubocopの実行で遅くて困っている方は除外ディレクトリに
.git/**/*
を真っ先に含めましょう。爆速になります。
バージョンは 0.83.0です。AllCops: Exclude: - vendor/bundle/**/* - bin/* - db/**/* - config/**/* - lib/* - Gemfile - public/**/* - tmp/**/* - log/**/* - .git/**/*私の環境では2分近くかかっていたのが6秒で終わるようになりました。これで頻繁に実行できます。
役に立てば幸いです。
- 投稿日:2020-05-22T10:29:51+09:00
ログイン機能
1.Gemをインストールしてサーバーを再起動しよう
Gemfileに以下のコードを追記
追記する場所は、Gemfileの最後の行にgem 'devise'
その後ターミナルでbundle install
ローカルサーバーを再起動しましょう
rails s2.コマンドを利用してdeviseの設定ファイルを作成しよう
deviseを使用するためには、Gemのインストールに加えてdevise専用のコマンドを利用して設定ファイルを作成ターミナルでrails g devise:install
config/initializers/devise.rb
config/locales/devise.en.ymlが作成される。3.コマンドを利用してUsersモデルを作成しよう
deviseを利用する際にはアカウントを作成するためのUserモデルを新しく作成します。作成には通常のモデルの作成方法ではなく、deviseのモデル作成用コマンドでUserモデルを作成ターミナルでrails g devise user
app/models/user.rb
db/migrate/20XXXXXXXXXXXX_devise_create_users.rb
test/fixtures/users.yml
test/models/user_test.rbが作成される
routes.rbにdevise_for :usersが自動で追記される先ほどのrails g devise userコマンドで作成されたマイグレーションファイルを実行します
ターミナルでrails db:migrate
これで、usersテーブルが作成される
ルーティングが変更されたので、サーバーを再起動してお
rails s
- 投稿日:2020-05-22T10:15:02+09:00
html.erbでナンバリング(番号をふる)をする方法
HTMLの表示側で例えば、
- 特徴1
- 特徴1、特徴2
- 特徴1、特徴2、特徴3…
のように内容によって、数値が変わるように番号をつけるときに使うコード。
(とくに利用が多いのはこういうときだった)with_index(1) を利用する
こちらを利用することで、 ナンバリングスタート時の値を()内の値からスタートすることができ、
+1
と書く必要がなくなる。<% ....map.with_index(1) do |a, i| %> <p><%= i %></p>each.with_index
こっちだと、
0
スタートになってしまうので、+1
と書く必要が発生する。<% ....map.each_with_index do |a, i| %> <p><%= i + 1 %></p>
- 投稿日:2020-05-22T05:18:15+09:00
10⁹⁹⁴²⁰⁶⁷ は 8 桁である
この記事は下記の記事の続編ともいうべきものです。
桁数は Math.log10(x).floor + 1 でいいのか - Qiitaさて,$10^{9942067}$ は 10 進で何桁の数だろうか1。
Ruby なら簡単さ。
irb(main):001:0> (10 ** 9942067).to_s.length (irb):1: warning: in a**b, b may be too big => 8え? は,8 桁ぁ? ど,どういうこと?
しかも何か変な警告が出てるし。つか,この警告がヒントか。要するにデカすぎてちゃんと計算できてないってこと? でもエラーじゃないしな。
いっそ
.to_s.length
を取って実行してみるか。
いや待てよ,もし巨大整数が表示されようとしたらターミナルがしばらく操作不能になりそうだしな。
えーい,やっちまえ。irb(main):001:0> 10 ** 9942067 (irb):1: warning: in a**b, b may be too big => Infinityへ? Float::INFINITY ってこと?
整数の整数乗なのに「浮動小数点数の正の無限大」になると?あの,もしかして答えの「8」って,
Infinity
の文字数ですかい?irb(main):002:0> Float::INFINITY.to_s => "Infinity"やっぱし。
ちなみに,指数が 1 小さい $10^{9942066}$ は
10 ** 9942066
で計算できる。
このあたり,Ruby のバージョンにもよるそう。この記事は Ruby 2.7.1 に基づいている。
**
は,結果が巨大すぎる整数になりそうなとき,計算を放棄して警告を出したうえでFloat::INFINITY
を返す,という仕様だそうです。
なので,実際的なプログラムで本当にそんな事態が起こりそうなときは,計算結果をfinite?
で確認したうえで使う必要がありそう。なお,$10^{9942067}$ を本当に計算したければ
10 ** 9942066 * 10
で計算できる。
もちろん計算するまでもなく 9942068 桁である。 ↩