- 投稿日:2019-09-27T21:48:00+09:00
プログラミングスクールに50万溶かしたリーマンがエッッサイトを作った話
どうも29歳中小企業営業マンです。
プログラミングスクールに50万を費やして挫折→復活→挫折→その後5ヶ月でサイトをイチから完成させました。ここまで本当に長くて長くて大変だったよ・・・。サイトを完成させて本当に泣きそうなほど嬉しいよ・・・。辛かった・・・。本当にお兄さん辛かったよぉッッ
実は、ここまで来るのにプログラミング学習で50万使いました・・・。
著者歴
年齢: 29歳
仕事: 営業職(B to C)
学歴: 国公立大学 文系(経済学部)
プログラミング歴: 10ヶ月
開発期間: 5ヶ月(1日2時間くらい)
はじめてPCを使ったのは? :高校生、Windows95のメイプルストーリーがやりたくてはじめて触れました。
♡うそです、中学3年生のyourfilehostで動画鑑賞しますた♡
そこで作りました。
シコい人妻のコンセプト
ひとことで表すと人妻サイトです。
機能は極力シンプルにして、ユーザーが見やすいようなデザインを第一優先にしました。(もちろん管理人も日々
シコい人妻http://shiko-tsuma.club
をシコシコ使ってカイゼンしています。)
すべて、と人気(PV数で並び替え)のメニューがあります。
さらに、投稿時に付与したタグをクリックすると同じタグの付いた動画が一覧表示されます。
サイトデザインについて
動画サイトのキングをインスパイアしました。そのサイトはとてもシコりやすいデザインなので別の意味でもいつもお世話になってます。
フォントはGoogle Fontを使用しています。また色合いはごちゃごちゃしないようにメインカラー(黒)、ベースカラー(白)、アクセントカラー(ピンク)以外の色は使用していません。
あともちろんスマホレイアウトを最優先しました。そしてタブレットのデザインは省きました。
開発までの経緯
実は、一回プログラミングに挫折しております。
動画で学べるプログラミング学習サイトをすべて学んだらHサイトを作れるんじゃね?って思った時期がありました。
ちょうどこの記事も参考にしていましたし→https://anond.hatelabo.jp/20101203150748
動画サイトでHTML, CSS, PHP, JavaScriptのメニューを3ヶ月位でこなしました。もちろん有料会員。(3ヶ月で3000円を費やす)
しかし、PHPのどうしても解決できないエラーにハマりました。
「クソッ、なんで動かねぇーんだ、チクショウ」そうやって机を叩いたことがたくさんあります。
「もうやめよう、無理だ、いや、まだ続けよう、いや、やめよう、無理だ、いや、まだ続けよう」そうやって何度も悩み続けました。
どうしてもプログラミングでサイトを作りたい!
けど、どうしようもない!
どうすればいいのか悩んだ挙げ句、Google先生に頼みました。
プログラミング教材、プログラミングオンラインスクールについて
プログラミング Web開発 などで調べると魅力的なページに辿り着きました。プログラミングを教えていて、サロンなどもやっている○○○さんという方のBlogでした。
そのBlogでは
プログラミングで自由な生き方ができる!
プログラミングって魔法なんだ!
こんな夢のようなキャッチフレーズに心打たれました。
本気になった私は教材の購入ボタンを押しました。
某○○○さんの教材
ものすごく期待して届いた教材を進めたところ・・・
「なんだこれ、本で読んだのと全く同じやん・・・」
「こんな基礎中の基礎なんて」
基礎中の基礎しか学べませんでした。
HTML、Ruby、Railsの基礎中の基礎なのでプログラミングを初めてする人にはいいかもしれないです。けど自分が実現したいWeb開発には直接は役に立ちません。もはや悲しみに近い怒りですがこの人は自分でWebアプリなどを本番環境やサーバを立てて自分で運営したことがあるのだろうか?と怒りを禁じえませんでした。
(後にエンジニアのメンターさんは20年前からまるで成長していない(安西先生風)Webアプリケーションを作らせている、と言っていました・・・。)
(ここまでで10万円を費やす)
「ん~、基礎ばっかりやってもまだ掲示板しか作れないし、作りたい機能や動かないコードの質問をしても調べろ、って言われるだけだったしな・・・」
「本当にプロのエンジニアに教わったら早いかもしれない」
そう思いました。
そして、「プログラミング 実践 開発」 「プログラミング エンジニア 講師」で調べると現役のエンジニアがメンターとして週に何回か見てくれるプログラミングスクールにたどり着きました。
オンラインスクールで学ぶ
そこで行われるカリキュラムというのは、オンラインスクール自身が作成した教材を進めながら、エンジニアのメンタリングを週に何回か受けるというもの。
他のスクールも検討しながら、1ヶ月近く悩み続けました。しかしプログラミングは投資だ、と思い
「えい、40万円入金ッッ」(ここまでで50万円・・・勇気がいりました)を実行しました。
最初は休日の朝にメンターとのコンタクトがあったのですが、起きられないことが多々ありました(連日深夜まで残業だったからとても眠かった)。出された課題も1日2時間もかかるので仕事で神経を減らして疲れた体には辛かったです。
「正直仕事が忙しいし、プログラミング勉強してる暇ないから、また挫折か・・・」と思いました。めちゃくちゃダメ人間なのです。
「また何も成し遂げられないのか・・・」
実は私、大学受験で第一志望に落ちて、理系に行くはずだったのに文系で絶望しました。だから理系のプログラマーに憧れます!
やりたかった仕事の就職も失敗しました。
高校も不登校だったし、昔うつ病も発症しています。
「もともとダメ人間でクズな自分が継続などできないのか・・・」
しかし、縁があって担当してもらったメンターさんの話が自分にとって刺激的で面白かったのです。
「Webサイトを大企業に売却して3000万稼いだ話」
「ベンチャー企業に誘われて手取り100万稼いだ話」
「どこどこのサイトのデータベースの裏話」
「自分の開発したサイトアプリケーションの話」
「すげぇ」そう思いました。
また、自分が仕事で使用しているツールがどうやって作られているのかも説明してくれてすごいリアルで面白かった。こちらの質問にも具体的に答えてくれて「すげぇ」って感じでした。
あと、オンラインスクールの推奨フレームワークがRubyだったのですが、Rubyが直感的で書いていて気持ちが良かったのです。
今まで動画サイトや本でフワッと学んだ知識がつながるのを実感しました。
気づいたら仕事から帰ってスーツのままプログラミングを学習していました。すごく楽しくなってきたのです。
しかし、シコい人妻http://shiko-tsuma.club を公開した今だからわかるのですが、プログラミングスクールの教材はRailsガイドのマネごとでした・・・。
この気持わかる人いますか?
ぶっちゃけ今だったら最初から
RubyとRailsの学習ガイド2019年版
https://magazine.rubyist.net/articles/0059/0059-Ruby-Rails-Beginners-Guide.html
の通りやったほうが良かった。
また、教材以外にもこれらを学びました。
https://www.sbcr.jp/product/4797386295/
https://gihyo.jp/book/2017/978-4-7741-9397-7
http://gihyo.jp/magazine/wdpress/plus/978-4-7741-4204-3
https://saruwakakun.com/html-css/basic
あとgitも公式チュートリアルで学びました
https://try.github.io/
いやー、オンラインスクールはgitの実践的なエラー解決は教えてくれなかったw
基本git -f はしてはいけないとか知らなかった。
おかげでgitミスって書いたコードが消し飛んだことが多々あります。
結局のところ、プログラミングの基礎的な内容については高額な教材(1万円以上)を買う必要はないかと思います。3000円の本を2,3周すれば十分です。高額な教材と3000円の本の内容は一緒です。(経験者は語る)1人では続かなくて誰かにサポートしてほしい場合は買ってもいいんじゃないでしょうか。
つまり、オンラインのプログラミング学習は教材にあまり価値はありません。そのサポートに数万円の価値があります。(人件費だから高いのは当たり前)
プログラミング学習が流行っていますが、プログラミングは手段です。何を目的にするかが重要かなと思いますね。私の場合はWeb開発でした。そして、Web開発において、大事なのは継続できるモチベーションづくりです。もし何度も挫折するのならば、良質な本で学びながらプログラミングを仕事にしているメンターに週何回か見てもらえばいいのではないでしょうか。
ぶっちゃけ、自分でサービスを開発していない人やレベルの低いエンジニアにわざわざ教わる価値はない、ですね。
シコい人妻ができるまで
プログラミング業務未経験の初心者がシコい人妻http://shiko-tsuma.club
みたいなサイトを作る場合、勉強したリストは以下の通り。
実際、私はこれらを10ヶ月くらいで開発しながらこなしました。
(途中別のWebアプリを作ろうとしたが続かず失敗、その後シコ妻に変更して5ヶ月後で完成して今に至る)
HTML / CSS
「HTML / CSSなんて常識だろ、パパっと学んどけばいいんだ」そう思ってあとで苦労しました。
これらの知識が中途半端だったので、いざゼロから書けってなったときに困りました。
たとえばRailsでリンクを生成するメソッドでタグの位置を微妙に間違えたりして表示されないことなどがありました。
Linuxのターミナル
基礎的なコマンドを動画で学びました。
https://dotinstall.com/lessons/basic_unix_v2
HTTPのしくみ
本でサラッと学びました。この本です。
https://www.amazon.co.jp/Webを支える技術-HTTP、URI、HTML、そしてREST-WEB-PRESS-plus/dp/4774142042/ref=sr_1_1?__mk_ja_JP=カタカナ&keywords=http&qid=1569577311&s=books&sr=1-1
とりあえずCRUDということを学びました(小並感)
Ruby
each, ifなどの文法を学びました。この本です。
https://www.amazon.co.jp/プロを目指す人のためのRuby入門-言語仕様からテスト駆動開発・デバッグ技法まで-Software-Design-plusシリーズ/dp/4774193976/ref=sr_1_1?__mk_ja_JP=カタカナ&keywords=ruby&qid=1569577335&s=books&sr=1-1
本当はオブジェクト指向とかできればいいのでけれどまだそのレベルではないです。Rubyは$とか書かなくていいので書いていて気持ちがいいです。でもプログラミング強い人からするとRubyは型が曖昧で気持ち悪いらしい・・・。本当でしょうか?
Railsについて
最初はフレームワークの何がいいのかわかりませんでした。ですが、Railsで開発したり、Railsの記事を読んだりするうちに、そもそもフレームワークとはWeb作りなどで使う定番のコードをあらかじめ用意しておいたもので、フレームワークをそのルール通りに使うと開発の時間が削減できる、ということを知りました。
今、オンラインスクールとかネット上で教えている内容の多くはRailsです。全くのプログラミング素人にRailsを教えているわけです。
しかし、初心者がいきなりフレームワークを使用してコピペで書いたツギハギ開発をすると「わけのわからないエラー」が量産します。
かくいう私がまさにそうでした。
参考
今すぐ辞めて欲しい、「Ruby on Rails勉強してます」「CakePHP勉強してます」
https://blog.sumyapp.com/2013/07/no-recommend-rails/
Railsは基礎的な文法を覚えるだけで簡単な機能を作れますが、一歩ルールから外れたことをすると途端にどうしていいかわからなくなり混乱します。
これからはサーバ、データベース、コンピュータサイエンスの基礎を学ぼうと思いました。
データベース
基本的なSQL文法などは動画サイトなどで学びました。あとは適宜ググりながらやりました。
また、1対多や多対多の使用例を開発して身にしみてわかりました。データベースは正規化、N+1問題、高速化などなかなか奥が深い分野です。ちなみにオンラインスクールはSQL文しか教えてくれませんでした。
サイトのコンセプトを考える
「ここまで来たらいよいよサイトづくりだ!」そう思い、興奮しながらサイト案を練りました。
ER図、サイトマップを作成する
「いーあーる図!?なんじゃそれは!」
ググるとどうやらデータベースの概要図とのことでした。そもそもデータベースに格納するデータはどのようなものか?そのカラムにどんな値を持たせるのか?関連は?ということを図にするのです。
参考 https://qiita.com/inexp_eng4432/items/817db9ba18829e0f6d57
(いつもQiitaさんお世話になってヤース)
あと、サイトURLからどのような構造をしているか、というサイトマップも作成しました。
trelloでタスクを並べる
「なにをやれないいのか、イマイチわかりづらいな・・・どうしよう」
https://trello.com
ここでタスクを書き並べました。付箋みたいで便利。
rails new rails migration
「rails new shikoi_hitozuma てぇぇーい!」
ER図で作成したデータベース構造を見ながら実際のデータベースにテーブルを構築していきました。
modelを書いていく
「まずはModelからだ、と習ったぜぃ」
リソースを人気順にしたり、新しい順にしたり、ということを書いていきました。
routingを書く
「index show だったな、これらをそれぞれのModelに関して設計していくぜぃ」
どういうURL階層にするか、というサイトマップを参考にして書きました。
controllerを書く
「次はControllerだな、Modelから渡されたものを加工するぜぃ」
index showなどのアクションごとにインスタンス変数を書いていきます。
viewを書く
「よし、今度は表示させていくぜぃ HTMLてててーいッッ」
実際に見る画面を構築していきます。
activeadminで投稿する
rails のgem にactiveadminという、管理画面を一瞬で作るものがあります。それを導入して投稿を管理しました。
「だけど、公式情報が少なすぎてわからねぇーーーーーーひぃいいいいい」
activeadminはものすごい苦労しました・・・。
サイトデザインをする
「CSSぅー?こんなんぱぱっと終わらすぜぇ」
はじめはCSSをバカにしていましたが、本気でやりました。パッと見のデザインで印象は大きく変わりますから。1日2時間で1ヶ月位かけました。
ちなみにエンジニアの人は「CSSなんて鼻くそほじりながら書けるでしょ」って思っているようですが、違いますよ。確かに思考はあまりいらないかもしれないけれど、最優先事項の一つだと思います。
本番サーバーを構築する
「くそーーーーーーー!クソクソクソ!!!動かない!どうしてうごかねーんだ、なんでだ馬鹿野郎!!」
ってめちゃくちゃ思いながら行った、本番サーバー構築。
今回は開発環境をバーチャル環境のUbuntuで構築しました。オンラインスクールでは某エディタを使用するのですが、メンターから「本当はこんなクソエディタの開発環境じゃなくて、ローカル環境でDockerとかを用いて開発できればいいんですけど」と言っていたのでローカル環境で開発しました。※クソエディタはCloud9です。
まぁ、開発環境の構築は問題ないとして、本番環境が色々難しかったです。VPSとかAWSとかそこらへんです。
※ちなみに今はDockerを勉強しています。環境構築がもう少し楽になるといいんですが・・・仮想環境とDockerの違いがわからない、なんであんなにDockerなんて流行っているの??疑問だ。
あと、そもそも開発環境で作ったものを本番環境にあげるなんてやったことなかったのですよ。だから開発環境と本番環境でgemを分けたりということを初めて学びました。
ここ、めっちゃ重要だと思うのです。Herokuで済ましている場合ではないです。
ちなみにオンラインスクールなどの教材はそのことについて触れてすらいないのです。本当の本番環境構築は初心者には難しすぎる、と認識しているのでしょうか?
使用技術について
Ruby
Rails
CSS
オンラインスクールではbootstrapを推奨していましたが、ちゃんとしたデザインで開発するならばbootstrapは使ってはいけないとのこと。だからFlexboxでイチからデザインしました。
gem
activeadmin
オシャレな管理画面。
これで記事を投稿しています。
投稿しやすいようにUIを変えるのに非常に苦労しました。情報が少ないのですw
苦労した点を書いていきます。
サムネイル画像をradioボタンで表示するには
動画を投稿する際に画像を見ながら選びたいですよね。
ラジオボックスのボタンを画像にする
activeadminのformのところをこんな風にします。
f.input :movie_thumbnail, as:
:radio,collection:Model.find(params[:id]).movie_thumbnail.select(:movie_thumbnail_url, :id).map { |c| ["<img src='#{c.movie_thumbnail_url}'>".html_safe, c.id] }
collectionのところでまずサムネイルを一覧表示させます。
そしてhtml_safeでHTMLタグとして認識すると画像が表示されます。
[Rails]ERBのエスケープを自在に扱おうぜ
https://qiita.com/satoken0417/items/7bcdb59eae82776375f9
kaminari
オシャレなページング(まだ入れてなかった)
pry
irbのオシャレなやつ
デバッグするときにbinding.pryするとirbよりオサレです。
サーバ
Nginx
unicorn
「nginx unicorn 早い、すごいお」
オンラインスクールの教材では一切教えてくれなかったですが、ここも超大事です。Webアプリケーションエンジニアのプロは高速なサーバーを構築するのが当たり前だそうですね。
これらは設定をググって書けばいいだけでしたが最初はハマって大変でした。
これらの記事でなんとか解決しました。
nginx + unicorn + Railsの設定方法
https://qiita.com/akito1986/items/56198edcafc222b320a8
STI
シコい人妻http://shiko-tsuma.club/には tagテーブルがあるのですが、そこから派生する妻ラベル(人妻の分類)や体型などのテーブルは内容が重複するのでSTIという設計を用いました。
参考記事
[Rails] STI(単一テーブル継承)とメタプログラミングでDRY
https://qiita.com/kidach1/items/789c2e7aebbcfbd2583e
これからは以下の機能を作っていきます。昼間はブラック企業で働いている関係上、作業は深夜になりそうですが、えっちい人妻のためにがんばります。
みんな、はてブコメントでコメントどしどしくれよな!
追加したい機能
セッションを利用した履歴表示
多分セッションを期間で区切ってやればできると思います。
ユーザーが投稿できるForm
ユーザーがこれを共有したいと思う動画のURLを貼り付ければ投稿できる、というやつ。activeadminでできますね。
コメント機能
commentというテーブルを作って、記事にリレーションさせればいいと思います。
ユーザーのお気に入り登録
これもfavoriteというテーブルを作ってuserにリレーションさせます。
日毎、週毎、月ごとのランキング
今は直接記事にPVを加算していますが、別テーブルから期間ごとのランキングや上昇度を測ってYoutubeの急上昇みたいな機能もほしいですね。
参考にしたところ
本番環境構築について
そもそもデプロイの経験が初めてだったのでdevelopment / production の棲み分けがわかってませんでした。
そのときに参考にした記事がこちら
(初心者向け)vpsを契約して、capistrano3でRailsアプリをデプロイするまで [その1 サーバー設定編]
https://qiita.com/ryo2132/items/f62690f0b16ec11270fe
Rails
Modelを簡素化するscope
Railsでよく利用する、Scopeの使い方。
https://qiita.com/ngron/items/14a39ce62c9d30bf3ac3
本番環境でbundlerがエラー起こしたときに参照しました。
BUNDLED WITH で Gemfile.lock が更新されてしまう件
https://qiita.com/suu_g/items/2b1630b8015d51c5292e
CSS
Flexboxについて
日本語対応!CSS Flexboxのチートシートを作ったので配布します
https://www.webcreatorbox.com/tech/css-flexbox-cheat-sheet
まとめ
初心者がサイトを開発したい場合は
・フレームワーク
・サーバーサイド言語
を軽く学んだ上で
・作りたいサイトのコンセプト
・作りたいサイトの機能
を紙に起こして
・サイトマップ
・データベースのER図
を元にして開発するのがいいです。
その際に「こうしたい」という機能がたくさんあると思いますが、それを大胆に1つか2つまで削ぎ落として、シンプルなものを完成させるのがいいです。できることよりできないことのほうが多いですから。
また、
・サーバーサイド言語
・フレームワークとデータベース
・デザイン
がWebアプリでは大事かな、と思います。
この記事も参考にするといいです。
(兎に角)早くプロトタイプを作る技術(初心者向け)
https://qiita.com/teradonburi/items/1d629a751ed42e923d7d
プログラミング初心者ゆえ、みなさんのアドバイス待ってます。
それではみなさん、よきシコい人妻http://shiko-tsuma.clubライフを。
PS
あとよくプログラミングスクールで謳われている「3ヶ月でフリーランスになる」ことは不可能だと思います。
開発してみて身にしみましたが、一口にプログラミングと言ってもやることが多すぎるので3ヶ月程度では仕事にするのはほぼ不可能だと思います。
現役エンジニアの人はどう思いますか?
また、メンターさんはプログラミング歴20年だし、知り合いのRubyプログラマは仕事ができるまで死ぬほど働いて3,4年かかったと言っていました。そんな人達がゴロゴロいるなかで勝てると思いますか?
知り合いの東大卒がこう言っていました。「俺は小学生から12年間、週に4回以上塾通い&一貫中高&自学自習(1日5時間以上)でギリギリ東大入れた。だから1年やそこらの努力で東大とか言っている有名人がムカつく。」と。
「じゃあ、100万払えば偏差値30でも3ヶ月で東大合格できる塾ってどう思う?」
「100%うそ、ありえない。受かる人は元々素地がある人。そもそも予備校の東大合格者の数だって、元々模試でA判定の人を無料で通わせたのをカウントしただけ。」
100万払えば3ヶ月で東大に合格させる塾なんてないのと同じように、50万払って3ヶ月でスーパープログラマーになれると思いますか?
みなさん、いいシコ妻ライフを。http://shiko-tsuma.club
- 投稿日:2019-09-27T20:57:28+09:00
ActionCableにおいてのcurrent_userについて読み解く|Rails
action cableのcurrent_userについて読み解く
本記事投稿の経緯
Railsで簡単なチャットアプリを作成中、ActionCable内での
current_user
の取り扱いにつまづいたのでメモ。環境
Rails 5.2.3(最新じゃないンゴオオオォ)
Ruby 2.4.0(最新じゃないンゴオオオォ)connection.rbで定義されるcurrent_user
connection.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end protected def find_verified_user remember_token = User.encrypt(cookies[:user_remember_token][:value]) if cookies[:user_remember_token] if current_user = User.find_by(remember_token: remember_token) current_user else reject_unauthorized_connection end end end end上記は公式ドキュメントのサンプルコードであるが、ActionCableはWebSocketで通信する際、まずここで認証を行える。その際、
current_user
を定義しているのだが、こいつの仕組みがイマイチ把握できなかったのでソースコードを読み解いていくことにした。identified_by
まずは
identified_by
を見ていく。(コードは一部です。)action_cable/connection/identification.rbincluded do class_attribute :identifiers, default: Set.new end module ClassMethods # Mark a key as being a connection identifier index that can then be used to find the specific connection again later. # Common identifiers are current_user and current_account, but could be anything, really. # # Note that anything marked as an identifier will automatically create a delegate by the same name on any # channel instances created off the connection. def identified_by(*identifiers) Array(identifiers).each { |identifier| attr_accessor identifier } self.identifiers += identifiers end endコメントに大体が書いてあるのだが、
identifier_by
は後で特定のconnectionを見つけられるようにキーを作っている。今回であればcurrent_user
をキーとしてconnectionを識別できるようにしてくれているのだ。
更にはそれを子であるchannelに自動で同じ名前でdelegate
するという。Connections form the foundation of the client-server relationship. For every WebSocket accepted by the server, a connection object is instantiated. This object becomes the parent of all the channel subscriptions that are created from there on.
そういえばrails guideにもconnectionはそこから作成されるchannnelの全ての親となると書いてあった。
(これややこしいが継承のことではないっぽい?。、delegate
してるし...それはまた今度、気が向けば記事にします。)つまり、WebSocketの接続の識別として
current_user
は使われており、更には子であるchannelでも利用可能であるはず。てわけで使ってみる。
実際にcurrent_userを使ってみる
流れ
⓪チャットルームモデルとユーザーモデルは多対多の関係。(ここは省略。)
①connection接続時にユーザーがログイン済みユーザーか認証し、current_user
に接続したユーザーを入れる
②room_channelにサブスクライブする時、先のcurrent_user
にそのチャンネルに接続する権限があるか認証する。connection.rbは上記のサンプルコードと同じにします。
room_channel.rbのサブスクライブ部分は下記のとおり。room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed if !params.empty? && current_user.is_member?(params['room_id']) stream_from "room_channel_#{params['room_id']}" else reject end end endクライアントからチャットルームのidを送ってもらい、そのルームにログイン中のユーザーが属しているか確認している。
is_member?
メソッドは下記のような感じで。user.rbdef is_member?(room_id) members = Room.find(room_id).users members.any?{|member| member.id == self.id } endこんな感じで
current_user
を使って各チャンネルでも認証をできる。is_member?メソッドに指摘が入ったので修正(20190927)
下記は
exists?
を使ったver。
ここ、色々書き方あるが、どれが一番リソースに優しいか考えてたら、記事にできそうだったので今度書きます。(SQL力が足りない...)user.rbdef is_member?(room_id) Room.find(room_id).users.exists?(self.id) endまとめ
connection時、ユーザーがログイン済みか認証し、ログインしていればそれを
current_user
にいれ、子のチャンネルで利用可能にする。
current_user
を使えばチャンネル内でも認証可能。あとがき
実は今回、
current_user
の取り扱いでつまづいたのはrspecでテスト中のことでして、
「current_user
はチャンネル内で使えるって聞いたんだけど!!」
とか叫いたりして、
「本当に使えんのか?.....調べてやる!」
と思い執筆しました。結局、testコードの書き方が間違っていただけでしたンゴ!!めでたしめでたし!!
まあためにはなったかな。。。
参考記事
【ActionCable】チャンネル接続/購読時にユーザ認証を行う
これはMUST!ActiveSupport の Class#class_attribute を使おう!
Rubyのdelegateについて整理する
Rails Guide
- 投稿日:2019-09-27T20:46:58+09:00
Cloud9でRailsのAPIの開発環境を構築
はじめに
Cloud9でRailsのAPIサーバの開発環境を構築する手順の忘備録になります.
次のような方におすすめです.
- Cloud9にRailsのAPIサーバを構築したい
- React+RailsやVue+Railsの開発環境が欲しい
- Herokuなどの他のサービスを通さずにアプリを公開したい
なぜ書いたのか
RailsのみならCloud9のプレビュー機能が使えます.(参考サイト)
ただ,プレビューはあくまでもプレビューであり,ネットに公開されているわけではありません.
そしてAPIサーバは外部からアクセスできる必要があり,プレビュー機能では開発環境の役目を果たすことができません.そこで,Cloud9上でネット公開されるRailsのAPIサーバの構築を試みました.
他に同様の記事が無く苦戦したので忘備録として残しておきます.基本的な流れだけのせて,他の記事をリンクを載せて端折らせていただきます.
Cloud9の環境構築
AWSのアカウントを作成します.
そして,安全に操作できるようにユーザを作成し,Cloud9の環境を構築します.
初めてのAWS Cloud9導入Rails APIサーバのセットアップ
rvmなりrbenvなりでRubyのバージョンを指定し,Railsをインストールします.
# rvmの場合.(rvmはデフォルトで入ってる) rvm install "rubyのバージョン" rvm use "rubyのバージョン" gem install rails -v "railsのバージョン"RailsアプリをAPIモードで作成します.
rails new "アプリ名" --apiあとは,
rails db:migrate
やら何やらを実行してください.CORSの設定
他のオリジンからアクセスを許可するために,コメントアウトを外してあげます.
そして,originsに,フロントのオリジンを指定します.config/initializers/cors.rb# 'rails new'の直後はコメントアウトされてる Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'hoge.fuga.example.com' #ここをフロントのオリジンに変える resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end endネットワークの指定
Cloud9が動いているEC2インスタンスへは初期設定ではインターネットからアクセス出来ません.
そのため,「AWS 実行中のアプリケーションをインターネット経由で共有する」に従って,EC2にアクセス出来るようにします.アカウントを作成したばかりなら,必須なのはセキュリティグループの設定ぐらいだと思います.
書いてありますが,ポート番号は8080~8082のどれかです.Railsの起動
railsを起動します.
バインドとさきほどのポート番号を指定します.rails s -p "ポート番号" -b 0.0.0.0ブラウザで表示
「ネットワークの指定」の手順で実行した
curl http://169.254.169.254/latest/meta-data/public-ipv4で得られるIPアドレスにポート番号をくっつけてブラウザで表示します.(例:
256.1.2.3:8081
)すると,いつもの画面が出てくるはずです.
これはブラウザで表示していますが,APIモードで動いているはずです.
終わりに
かなり省略しましたが,大まかな流れは合ってると思います.
ネットワークの設定までは順調だったのですが,バインドのところでつまずきました.
Cloud9だとポートやバインドは指定せずとも動くので甘えてました.以上の手順で,最大3つのアプリまで一つのインスタンス上でネット公開できます.
そのため,RailsとReactやVueなどを同時に公開することも可能です.Rails+Reactで動くことを確認しました.
ぜひ,試してみてください.
- 投稿日:2019-09-27T20:10:55+09:00
Railsチュートリアル 第5章 - レイアウトを作成する…レイアウトのリンク
レイアウトのリンク概論
Railsにおいても、リンクを直接記述することは可能です。
Aboutへのリンクを直接記述する<a href="/static_pages/about">About</a>しかし、リンクを直接記述するというのは、いかにも力押しで、Railsらしいエレガントなコードではないですね。変数・定数の定義以外では、できる限りリテラルの埋め込みは避けたいものです。
さらに、以下のような事情もあります。
- aboutページのURLは、
/static_pages/about
より/about
のほうが好ましい- Railsでは、aboutページのようなページへのリンクには、名前付きルートを使うのが慣例である
となると、Aboutページへのリンクは以下のように書くのがRails的です。
Aboutへのリンクの、Railsらしい記法<%= link_to "About", about_path %>前述「Railsらしい記法」には、以下のような利点があります。
- コードの意味がわかりやすくなる
about_path
の定義を書き換えることで、about_path
が使われているすべてのURLを一度に変更することができるようになるRailsチュートリアルでは、今後使う予定のURLとルーティング(route)とのマッピングを、以下のように定義しています。
ページ名 URL 名前付きルート Home / root_path
About /about about_path
Help /help help_path
Contact /contact contact_path
Sign Up /signup signup_path
Log In /login login_path
Contactページ
演習 - サンプルアプリケーションにContact (問い合わせ先) ページを作成してみるで既に作成していました。当該項目をご参照ください。
RailsのルートURL
名前付きルートをサンプルアプリケーションの静的ページで使うために、ルーティング用のファイル(
config/routes.rb
)を編集していきます。現時点で、既にHomeページのルーティングは設定されています。
root
ルーティングが存在しないと、Railsアプリケーションそのものが動作しないからです。config/routes.rb(抜粋)root 'static_pages#home'名前付きルートを含むルートURLを設定することには、以下のような利点があります。
- ブラウザからアクセスしやすくなる
- 名前付きルートを使ってURLを参照できるようになる
root
であれば、root_path
やroot_url
といったメソッドを使えるようになる
rails console
からapp.root_path
やapp.root_url
メソッドを使うこともできます。現時点におけるapp.root_path
およびapp.root_url
の結果は以下の通りでした。>> app.root_path => "/" >> app.root_url => "http://www.example.com/"ルーティング用のファイルの
get
ルールは、第1引数にURL、第2引数にオプションハッシュを取ります。オプションハッシュの:to
キーに値を与えることによって、名前付きルートを定義することができます。早速
config/routes.rb
を編集し、名前付きルートを定義しましょう。例えば、Helpページに対する記述は、以下のように変換します。config/routes.rb(抜粋)- get 'static_pages/help' + get '/help', to: 'static_pages#help'Helpページに対して名前付きルートを定義したところで、
rails console
から、help_path
メソッドやhelp_url
メソッドを使ってみましょう。>> app.help_path => "/help" >> app.help_url => "http://www.example.com/help"
help_path
メソッドやhelp_url
メソッドが使えるようになっていますね。逆に、名前付きルートを定義していないページに対して、
_path
や_url
メソッドを呼び出すことはできません。>> app.about_path ...略 NoMethodError (undefined method `about_path' ...略)現時点でAboutページに対しては名前付きルートを定義していないので、このようなエラーになるのですね。
最終的に、
config/routes.rb
は以下のように書き換えます。config/routes.rbRails.application.routes.draw do root 'static_pages#home' - get 'static_pages/home' - get 'static_pages/help' - get 'static_pages/about' - get 'static_pages/contact' + get '/help', to: 'static_pages#help' + get '/about', to: 'static_pages#about' + get '/contact', to: 'static_pages#contact' end
rails console
から、再びabout_path
メソッドを呼び出してみましょう。>> app.about_path => "/about"今度は無事呼び出すことができました。
テストの修正
現状で
rails test
を実施すると、テストは失敗します。# rails test Running via Spring preloader in process 974 ...略 ERROR["test_should_get_help", StaticPagesControllerTest, 0.26505280000856146] test_should_get_help#StaticPagesControllerTest (0.27s) NameError: NameError: undefined local variable or method `static_pages_help_url' ...略 ERROR["test_should_get_home", StaticPagesControllerTest, 0.33508309998433106] test_should_get_home#StaticPagesControllerTest (0.34s) NameError: NameError: undefined local variable or method `static_pages_home_url' ...略 ERROR["test_should_get_about", StaticPagesControllerTest, 0.39137299999129027] test_should_get_about#StaticPagesControllerTest (0.39s) NameError: NameError: undefined local variable or method `static_pages_about_url' ...略 ERROR["test_should_get_contact", StaticPagesControllerTest, 0.4491715999902226] test_should_get_contact#StaticPagesControllerTest (0.45s) NameError: NameError: undefined local variable or method `static_pages_contact_url' ...略 4/4: [=============================================================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.50959s 4 tests, 0 assertions, 0 failures, 4 errors, 0 skips「
static_pages_about_url
等の名前が参照できない」というエラーですね。これらの名前は、例えば「about_path
」等に置き換えたので、テストも相応に書き直す必要があります。test/controllers/static_pages_controller_test.rbrequire 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest def setup @base_title = "Ruby on Rails Tutorial Sample App" end test "should get root" do get root_path assert_response :success end test "should get home" do - get static_pages_home_url + get root_path assert_response :success assert_select "title", "Home | #{@base_title}" end test "should get help" do - get static_pages_help_url + get help_path assert_response :success assert_select "title", "Help | #{@base_title}" end test "should get about" do - get static_pages_about_url + get about_path assert_response :success assert_select "title", "About | #{@base_title}" end test "should get contact" do - get static_pages_contact_url + get contact_path assert_response :success assert_select "title", "Contact | #{@base_title}" end end以上の修正が完了すれば、テストは再び通ります。
# rails test ...略 5/5: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.31410s 5 tests, 9 assertions, 0 failures, 0 errors, 0 skips演習 - 名前付きルートを変更してみる
1. 実は名前付きルートは、as:オプションを使って変更することができます。有名なFar Sideの漫画に倣って、Helpページの名前付きルートを
helf
に変更してみてください (リスト 5.29)。config/routes.rbRails.application.routes.draw do root 'static_pages#home' - get '/help', to: 'static_pages#help' + get '/help', to: 'static_pages#help', as: 'helf' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' end2. 先ほどの変更により、テストが
red
になっていることを確認してください。リスト 5.28を参考にルーティングを更新して、テストをgreen
にして見てください。1.が終了した時点で、一旦テストを実行してみます。
# rails test ...略 ERROR["test_should_get_help", StaticPagesControllerTest, 1.884794099983992] test_should_get_help#StaticPagesControllerTest (1.89s) NameError: NameError: undefined local variable or method `help_path' for ...略 5/5: [=============================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.07678s 5 tests, 7 assertions, 0 failures, 1 errors, 0 skips確かに
test_should_get_help
のところでテストが失敗していますね。
rails console
で、app.help_path
メソッドとapp.helf_path
メソッドの存在を確かめてみましょう。>> app.help_path NoMethodError (undefined method `help_path' for #<ActionDispatch::Integration::Session:0x000055f201d2df40>)\ >> app.helf_path => "/help"
app.help_path
メソッドが存在しない一方、app.helf_path
メソッドが存在します。この場合、
test_should_get_help
のテストを通すためには、test/controllers/static_pages_controller_test.rb
に以下の変更を行う必要があります。test/controllers/static_pages_controller_test.rb(抜粋)test "should get help" do - get help_path + get helf_path assert_response :success assert_select "title", "Help | #{@base_title}" end以上の変更を保存した上で改めて
rails test
を実行します。# rails test ...略 5/5: [=============================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.15824s 5 tests, 9 assertions, 0 failures, 0 errors, 0 skips今度はテストが通りました。
3. エディタのUndo機能を使って、今回の演習で行った変更を元に戻して見てください。
全体としては、以下2つの内容の変更となります。
config/routes.rbRails.application.routes.draw do root 'static_pages#home' - get '/help', to: 'static_pages#help', as: 'helf' + get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' endtest/controllers/static_pages_controller_test.rb(抜粋)test "should get help" do - get helf_path + get help_path assert_response :success assert_select "title", "Help | #{@base_title}" end以上の変更を保存した上で改めて
rails test
を実行します。# rails test ...略 5/5: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.86960s 5 tests, 9 assertions, 0 failures, 0 errors, 0 skips無事テストが通りました。これで心置きなく次の学習にかかれます。
余談
get '/help', to: 'static_pages#help', as: 'helf'以上のように書くべきところを、誤って以下のように書いてしまいました。
get '/help', to: 'static_pages#help', as: helfこれでどうなったかといいますと…
# rails test /var/www/sample_app/config/routes.rb:3:in `block in <top (required)>': undefined local variable or method `helf' for #<ActionDispatch::Routing::Mapper:0x000055bb8e302880> (NameError) ...略 # rails console /var/www/sample_app/config/routes.rb:3:in `block in <top (required)>': undefined local variable or method `helf' for #<ActionDispatch::Routing::Mapper:0x0000563606493238> (NameError) ...略
rails test
もrails console
もできなくなってしまいました。「未定義の変数名を使ってしまった」というエラーですね。名前付きルート
名前付きルートを定義したことにより、レイアウト中で名前付きルートを使えるようになりました。具体的には、「
link_to
メソッドの第2引数に名前付きルートを使えるようになった」というのが大きな変化です。早速headerパーシャルとfooterパーシャルにあるlink_to
メソッドの記述を変更していきましょう。なお、headerパーシャルでは、Web共通の慣習に従って、ロゴにもHomeページへのリンクを追加しています。
app/views/layouts/_header.html.erb<header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> - <%= link_to "sample app", '#', id: "logo" %> + <%= link_to "sample app", root_path, id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> - <li><%= link_to "Home", '#' %></li> - <li><%= link_to "Help", '#' %></li> + <li><%= link_to "Home", root_path %></li> + <li><%= link_to "Help", help_path %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> </div> </header>app/views/layouts/_footer.html.erb<footer class="footer"> <small> The <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> by <a href="http://www.michaelhartl.com/">Michael Hartl</a> </small> <nav> <ul> - <li><%= link_to "About", '#' %></li> - <li><%= link_to "Contact", '#' %></li> + <li><%= link_to "About", about_path %></li> + <li><%= link_to "Contact", contact_path %></li> <li><a href="http://news.railstutorial.org/">News</a></li> </ul> </nav> </footer>例えば、
/about
ではAboutページが表示されます。このときのHTTPリクエストとそれに対する
rails server
の反応は以下のとおりです。Started GET "/about" for 172.17.0.1 at 2019-09-26 11:37:03 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by StaticPagesController#about as HTML Rendering static_pages/about.html.erb within layouts/application Rendered static_pages/about.html.erb within layouts/application (0.8ms) Rendered layouts/_rails_default.erb (140.5ms) Rendered layouts/_shim.html.erb (0.3ms) Rendered layouts/_header.html.erb (0.7ms) Rendered layouts/_footer.html.erb (0.5ms) Completed 200 OK in 349ms (Views: 307.2ms)演習 - 名前付きルート
1. リスト 5.29のように
helf
ルーティングを作成し、レイアウトのリンクを更新してみてください。config/routes.rbRails.application.routes.draw do root 'static_pages#home' - get '/help', to: 'static_pages#help' + get '/help', to: 'static_pages#help', as: 'helf' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' endapp/views/layouts/_header.html.erb<header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <%= link_to "sample app", root_path, id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", root_path %></li> - <li><%= link_to "Help", help_path %></li> + <li><%= link_to "Help", helf_path %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> </div> </header>この変更を加えた後、
rails console
におけるapp.help_path
メソッドとapp.helf_path
メソッドの存在確認の結果は以下の通りになります。>> app.help_path NoMethodError (undefined method `help_path' for #<ActionDispatch::Integration::Session:0x0000561721e38dc8>) >> app.helf_path => "/help"一方、
rails server
の結果は以下の通りになります。Started GET "/help" for 172.17.0.1 at 2019-09-26 11:44:11 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by StaticPagesController#help as HTML Rendering static_pages/help.html.erb within layouts/application Rendered static_pages/help.html.erb within layouts/application (0.5ms) Rendered layouts/_rails_default.erb (194.1ms) Rendered layouts/_shim.html.erb (0.4ms) Rendered layouts/_header.html.erb (0.6ms) Rendered layouts/_footer.html.erb (1.0ms) Completed 200 OK in 376ms (Views: 342.8ms)名前付きルートの名前を変えても、エンドポイントが
/help
のまま変わっていないことは重要です。config/routes.rb
において、get
ルールの第1引数を変えなければ、エンドポイントは変わらないのです。2. 前回の演習と同様に、エディタのUndo機能を使ってこの演習で行った変更を元に戻してみてください。
config/routes.rbRails.application.routes.draw do root 'static_pages#home' - get '/help', to: 'static_pages#help', as: 'helf' + get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' endapp/views/layouts/_header.html.erb<header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <%= link_to "sample app", root_path, id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", root_path %></li> - <li><%= link_to "Help", helf_path %></li> + <li><%= link_to "Help", help_path %></li> <li><%= link_to "Log in", '#' %></li> </ul> </nav> </div> </header>リンクのテスト
初めての結合テストです。ここでは、「アプリケーションのHTML構造を調べて、レイアウトの各リンクが正しく動くかどうかをチェックする」というテストを書く方法を学習していきます。
まず、
site_layout
というテストのテンプレートを作成します。# rails generate integration_test site_layout Running via Spring preloader in process 1434 invoke test_unit create test/integration/site_layout_test.rbRailsが渡されたファイル名の末尾に
_test
という文字列を追加しているのがポイントなのだそうです。リンクのテストの全体像
今回のテストの全体像は以下のとおりです。
- ルートURL(Homeページ)にGETリクエストを送る。
- 正しいページテンプレートが描画されているか確かめる。
- Home、Help、About、Contactの各ページへのリンクが正しく動作するか確かめる。
test/integration/site_layout_test.rb
の全体像は以下のとおりです。test/integration/site_layout_test.rbrequire 'test_helper' class SiteLayoutTest < ActionDispatch::IntegrationTest test "layout links" do get root_path assert_template 'static_pages/home' assert_select "a[href=?]", root_path, count: 2 assert_select "a[href=?]", help_path assert_select "a[href=?]", about_path assert_select "a[href=?]", contact_path end endリンクのテストは何を確認するか
上記のコードでは、以下の確認が行われます。
root_path
へのGETリクエストの送出root_path
へGETリクエストを送出して返ってきたリソースに対する以下の確認
static_pages/home
というテンプレートが描画されているかroot_path
が示すリソースをリンク先とするa
要素が2つ存在するかhelp_path
が示すリソースをリンク先とするa
要素が1つ存在するかabout_path
が示すリソースをリンク先とするa
要素が1つ存在するかcontact_path
が示すリソースをリンク先とするa
要素が1つ存在するか実際のコードの解説
例えば、以下のコードを取り上げてみます。
assert_select "a[href=?]", about_path当該テストコードでは、Railsは
?
を自動的にabout_path
に置換します1。現時点で、about_path
に対応するエンドポイントは/about
なので、これにより、以下のようなHTMLコードがあるかをチェックすることになります。<a href="/about">...</a>現時点で、
static_pages/home
テンプレートが描画されたときのルートURLへのリンクは2つ存在します2。ルートURLが2つあるかどうかをチェックする場合は、以下のテストコードを用います。オプションハッシュで:count
キーに対する値が2
と定義されているのがポイントです。assert_select "a[href=?]", root_path, count: 2この他にも、
assert_select
には様々な機能があります。興味がありましたら、Railsチュートリアル 表5.2を参照してみてください。テストを実行してみる
統合テストのみを実施する場合は、Railsの開発環境で以下のコマンドを実行します。
# rails test :integration
rails test
の部分はrails t
でもOKですね。# rails t :integration ...略 6/6: [=============================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.20712s 6 tests, 14 assertions, 0 failures, 0 errors, 0 skips無事にテストが通りました。全体のテストはどうでしょうか。
# rails test ...略 6/6: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.81602s 6 tests, 14 assertions, 0 failures, 0 errors, 0 skips全体のテストも無事通りました。
「リンクに間違った変更が加えられたら、テストですぐに気づくことができる」という仕組みが無事整備されましたね。
演習 - リンクのテスト
1. footerパーシャルの
about_path
をcontact_path
に変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認してみてください。app/views/layouts/_footer.html.erb<footer class="footer"> <small> The <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> by <a href="http://www.michaelhartl.com/">Michael Hartl</a> </small> <nav> <ul> - <li><%= link_to "About", about_path %></li> + <li><%= link_to "About", contact_path %></li> <li><%= link_to "Contact", contact_path %></li> <li><a href="http://news.railstutorial.org/">News</a></li> </ul> </nav> </footer>上記変更をした場合、テストの結果はどうなるでしょうか…という話ですね。テストは通らないはずです。
# rails t :integration ...略 FAIL["test_layout_links", SiteLayoutTest, 1.887058500025887] test_layout_links#SiteLayoutTest (1.89s) Expected at least 1 element matching "a[href="/about"]", found 0.. Expected 0 to be >= 1. test/integration/site_layout_test.rb:10:in `block in <class:SiteLayoutTest>' 6/6: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.89008s 6 tests, 13 assertions, 1 failures, 0 errors, 0 skipsめでたく(?)テストは通りませんでした。「
a[href="/about"]
にマッチする要素が(HTMLコード中に)見つからない」という不適合を報告してきています。…この状態で先には進めないので、
app/views/layouts/_footer.html.erb
に対する先ほどの変更はもとに戻しておきましょう。2.1. リスト 5.35で示すように、Applicationヘルパーで使っている
full_title
ヘルパーを、test環境でも使えるようにすると便利です。こうしておくと、リスト 5.36のようなコードを使って、正しいタイトルをテストすることができます。test/test_helper.rbENV['RAILS_ENV'] ||= 'test' ...略 class ActiveSupport::TestCase # ...略 fixtures :all + include ApplicationHelper # ...略 end
test/integration/site_layout_test.rbrequire 'test_helper' class SiteLayoutTest < ActionDispatch::IntegrationTest test "layout links" do get root_path assert_template 'static_pages/home' assert_select "a[href=?]", root_path, count: 2 assert_select "a[href=?]", help_path assert_select "a[href=?]", about_path assert_select "a[href=?]", contact_path + get contact_path + assert_select "title", full_title("Contact") end end
現時点で、テストは問題なく通ります。
# rails test ...略 6/6: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.68342s 6 tests, 15 assertions, 0 failures, 0 errors, 0 skips2.2. ただし、これは完璧なテストではありません。例えばベースタイトルに「Ruby on Rails Tutoial」といった誤字があったとしても、このテストでは発見することができないでしょう。
app/helpers/application_helper.rbmodule ApplicationHelper # ページごとの完全なタイトルを返します。 def full_title(page_title = '') - base_title = "Ruby on Rails Tutorial Sample App" + base_title = "Ruby on Rails Tutoial Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end end以下のように、
site_layout_test
は通ってしまいます。# rails t test/integration/site_layout_test.rb ...略 1/1: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.88365s 1 tests, 6 assertions, 0 failures, 0 errors, 0 skips2.3. この問題を解決するためには、
full_title
ヘルパーに対するテストを書く必要があります。そこで、Applicationヘルパーをテストするファイルを作成し、リスト 5.37のFILL_IN
の部分を適切なコードに置き換えてみてください。test/helpers/application_helper_test.rbrequire 'test_helper' class ApplicationHelperTest < ActionView::TestCase test "full title helper" do assert_equal full_title, "Ruby on Rails Tutorial Sample App" assert_equal full_title("Help"), "Help | Ruby on Rails Tutorial Sample App" end end早速
application_helper_test.rb
に対してrails test
を実行してみます。# rails test test/helpers/application_helper_test.rb ...略 FAIL["test_full_title_helper", ApplicationHelperTest, 0.04009939997922629] test_full_title_helper#ApplicationHelperTest (0.04s) --- expected +++ actual @@ -1 +1 @@ -"Ruby on Rails Tutoial Sample App" +"Ruby on Rails Tutorial Sample App" test/helpers/application_helper_test.rb:5:in `block in <class:ApplicationHelperTest>' 1/1: [=============================================================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.04308s 1 tests, 1 assertions, 1 failures, 0 errors, 0 skips見事(?)テストで不適合が出ました。
2.余談.
base_title
を正しく直したときに、テストが通るかを確認してみます。
base_title
の中身を"Ruby on Rails Tutorial Sample App"に直してみます。app/helpers/application_helper.rbmodule ApplicationHelper # ページごとの完全なタイトルを返します。 def full_title(page_title = '') - base_title = "Ruby on Rails Tutoial Sample App" + base_title = "Ruby on Rails Tutorial Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end end再び
application_helper_test.rb
に対してrails test
を実行してみます。# rails test test/helpers/application_helper_test.rb ...略 1/1: [=============================================================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.02249s 1 tests, 2 assertions, 0 failures, 0 errors, 0 skips今度こそテストが通りました。
- 投稿日:2019-09-27T19:18:08+09:00
Ruby on Rails コンテンツの一覧を昇順/降順 にするメソッドとkaminari ページネーションの実装
初めに
Ruby on Railsで作成しているブログのコンテンツ一覧などの昇順/降順にするメソッドとページネーションを実装するためのgem kaminariの実装メモです。
降順/昇順にするにはorderメソッドを使う
order
メソッドは、テーブルから取得してきたインスタンスたちを並び替えるメソッドである。昇順の場合
contents_controller.rb# 省略 def index @contents = Contents.all.order("id ASC") end降順の場合
contents_controller.rb# 省略 def index @contents = Contents.all.order("id DESC") end
all
メソッドを利用した場合、通常であればレコードはid順に取得されるが、上記のようにorder
メソッドの引数として(“id DESC”)とすれば、レコードを逆順に並び替えられる。また、
order
メソッドを利用する場合は、以下のようにall
メソッドを省略することができる。contents_controller.rb# 省略 def index @contents = Contents.order("id DESC") # allメソッドを省略できる endページネーション(kaminari)の実装
- kaminariをいうGemの一種をインストールすることによって実装可能。
1. Gemfileの最後の行にkaminariを追記
Gemfile# (省略) gem 'kaminari'2. bundle installを実行
ターミナルbundle install
- 必ずローカルサーバーを再起動することを忘れずに。
kaminari:pageメソッド
- kaminariを導入すると、モデルクラスにpageメソッドが定義される。
- このメソッドは、ページネーションにおけるページ数を指定する。
- ビューのリクエストの際に
params
中にpageというキーが追加されて、その値がビューで指定した番号になる。よってpageの引数はparams[:page]
となる。kaminari:perメソッド
per
メソッドもpageメソッドと同様に、kaminariを導入することで使えるメソッドである。- 1ページあたりに表示する件数を指定する。
per
メソッドに引数をして渡した数字が、ページネーションが実装されたビューで1ページあたりに表示する件数になる。上記2つのメソッドは以下のような記述方法になる。
sample_controller.rb変数名 = クラスを利用して取得したレコードのインスタンス.page(params[:page].per(ここに1ページで表示したい件数を入力)contentsコントローラーのindexアクションに今度は投稿日時の降順で記述し、さらにページネーションを導入した記述をしてみると以下のようになる。
contents_controller.rb# 省略 def index @contents = Contents.order("created_at DESC").params[:page].per(5) endkaminari:paginateメソッド
- ページネーションのリンクを表示したいときに使用するメソッド。
- kaminariをインストールすると利用できます。paginateメソッドの引数は、コントローラで定義した変数を指定する。
viewには
<%= paginate(@contents) %>
と記述すればよい。contents.html.erb<div> <% @contents.each do |content| %> # 省略 <% end %> <%= paginate(@contents) %> </div>最後に
gemを入れると使える便利なメソッドがどんどん増えてとても便利。
積極的に使っていこうと思った。
- 投稿日:2019-09-27T13:45:55+09:00
rails webアプリケーション管理者権限でユーザーを削除する(delete)方法,deviseなしバージョン(初心者)
(delete)ユーザー削除機能を実装
ネット情報ではログイン機能 gem deviseをを前提としたのが多く当方初心者な為、まぁまぁ時間を食った。
後々の私の様な初心者の為にRailsチュートリアルを中心に改めて自分なりにまとめて見た。⒈管理者権限用カラムを作成
一般ユーザーと分ける為に管理者専用カラム作成。
$ rails generate migration add_admin_to_users admin:boolean
user.rbclass AddAdminToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :admin, :boolean, default: false end end
$ rails db:migrate
⒉管理者権限を1人に与える
admin を true に。
db/seeds.rbUser.create!(name: "Exaple User", email: "example@railstutorial.org", password: "foobar", password_confirmation: "foobar", admin: true )
$ rails db:migrate:reset
$ rails db:seed
③ログインユーザーが管理者ならパーシャルの横にdelete ボタンを表示
この!current_user?(user)は自分自身の2つ目のアカウントでは管理者権限は無効に。
db/app/views/users/_user.html.erb<div> <%= gravatar_for user, size: 50 %> <%= link_to user.name, user %> <% if current_user.admin? && !current_user?(user) %> | <% link_to "delete" user, method: :delete, data: {confirm: "You sure?"} %> <% end %> </div>current_user? が使えるように定義
db/sessions/helper.rbdef current_user?(user) user == current_user end⒈ログインが前提なので、require_user_logged_inにdestroyを追加。
⒉管理者だけが削除できるように、admin_user, only: :destroyをbefore_actionに新たに作成。
⒊動作用destroyアクションを追加db/user_controller.rbbefore_action :require_user_logged_in, only: [:index, :show, :edit, :update, :destroy, :followings, :followers] before_action :admin_user, only: :destroy #アクション前に事前確認用。 def destroy User.find(params[:id]).destroy flash[:success] = "ユーザー削除完了" redirect_to users_path end private #管理者か確認。 def admin_user redirect_to(root_path) unless current_user.admin? endあとは実際に動作確認するだけ。
もしエラーが出たら、こちらも参照。
ユーザー削除するってことは、関連するデータの処理もどうするのか?
https://qiita.com/shutyan/items/05977055b2ce32c2e322
- 投稿日:2019-09-27T13:22:55+09:00
[初学者]よく起きるエラー 〜syntax error, unexpected end-of-input, expecting keyword_end〜
目的
学習の備忘録と初学者の参考資料として投稿
syntax error, unexpected end-of-input, expecting keyword_endというエラー
コードのエラーってよく赤字で英語表記だから、読む気にならねぇ〜っ
しかも焦っちゃう、、、、、
実際に単語を訳して見ると単語訳 syntax・・・構文 unexpected・・・予想外・予期しない expecting・・・期待して ...etcつまり
endがねぇよ!!ってこと
ハイハイ、書きます書きます
まとめ
エラーはパソコンが親切に教えてくれてることなので、恐れず・焦らずに対処したほうがいい。
まずはエラー文を読んでみて、何が書いてるのか理解すれば、簡単に解決することも多い。
今後も学習で気づきや参考になるものがあれば、アップしていきます。
もし参考になったらいいね!!よろしくお願いします
- 投稿日:2019-09-27T13:02:54+09:00
text_fieldのplaceholderを改行する方法
はじめに
railsのtext_fieldで作成したテキストボックスに改行ありのplaceholderを書きたかった。
ただ調べると文字列に改行コードをいれたり、cssでいじったりと、
個人的にはあまりやりたくない方法しかでてこなかったので、なにか完結にできる方法がないかを模索していた。実装
helperでヒアドキュメントを使って文字列を定義する。
それをテンプレート側(haml)で呼び出すだけ。ソース
= f.text_area :text, class: 'form-control', placeholder: text_placeholderdef text_placeholder <<-"EOS".strip_heredoc 行1 行2 行3 EOS endキャプチャ
- 投稿日:2019-09-27T12:34:02+09:00
[初学者]シングルクォーテーションとダブルクォーテーションの違い(ruby)
目的
学習の備忘録と初学者の参考資料として投稿
シングルクォーテーションとダブルクォーテーション
ネットや参考書などでよく見られる’’や””の違いについての話。特にプログラミングを初めて勉強する方にとって、どちらにするかで非常に悩ましい場面に遭遇することがあります。
sample.rb'Hello world' =>Hello world "Hello world" =>Hello worldこんな感じの
ダブルクォーテーションの役割
式展開や改行文字列を入れたい場合はダブルクォーテーションで囲む必要がある。
sample.rbputs "Hello \n world" **改行文字列** =>Hello world name = Taro puts "Hello #{name}" **式展開** =>Hello Taroまとめ
基本はダブルクォーテーションでいいのでは
いろんな記事を見てるとダブルクォーテーションだと読み込み速度が遅くなるやシングルクォーテーションを書いて、視認性をよくするなどなど書いてあるが、特に気にしなくてもいい今後も学習で気づきや参考になるものがあれば、アップしていきます。
もし参考になったらいいね!!よろしくお願いします
- 投稿日:2019-09-27T12:30:26+09:00
Rails Webアプリケーション管理者権限でユーザー削除出来なかったエラー(初心者)
動作確認で管理者としてユーザーを削除しようとしたらエラー発生。
忘れないために自分用忘備録
以下エラー文
ActiveRecord::StatementInvalid in UsersController#destroyMysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails・・・・・
原因
dependent: :destroy書き忘れていた為に発生したエラー
要するに、削除しようとしているユーザーの投稿、follower、お気に入り等どうするのって話。対策
UserテーブルとPostテーブルを関連付けするために以下のコードを追加し、
Userのデータが削除されたときに、関連するPostのデータを削除するように設定。dependent: :destroy
app/models/user.rbclass User < ApplicationRecord has_many :posts, dependent: :destroy has_many :follows, dependent: :destroy has_many :favorites, dependent: :destroy endこれでユーザーに関連付けられたデータは削除完了。
- 投稿日:2019-09-27T10:06:08+09:00
Railsでデバッグ用のログを出すときの簡易なスニペット
自分用メモ。
こんな感じでlibの下に作っておくとログを出すときに便利。
ログのレベルも一括で変えたりとかできるようにしておくとなお良いかも。class DebugLogger # 一番シンプルな形がこれ def self.info_log(log_msg, prefix = '') Rails.logger.info("[dev]-[#{Time.current}]-[#{prefix}] #{log_msg}") end # こんな感じで機能を持ったログを吐かせるようにしとくと、使う側のコードがスッキリする。 def self.user_params(user, prefix = '') columns = ['id', 'name', 'updated_at'] log_msg = columns.map{|column_name| user.attributes[column_name] }.join(", ") Rails.logger.info("[debug]-[#{Time.current}]-[#{prefix}] #{log_msg}") end end使う時はこう。インスタンス化せずに1行で書けるのがいい。
user = User.find(modified_user_id) DebugLogger.user_params(user, '編集後')ちなみに例外を拾ってスタックトレースを出すときはこんな感じで書くと良い
rescue StandardError => e Rails.logger.info("[#{Time.current}] - #{e.backtrace.join("\n")}") # render plain: e.message end
- 投稿日:2019-09-27T09:00:43+09:00
【aws】LoadError: No such file to load -- aws-sdk-s3.rb【自動デプロイ中】
はじめに
自動デプロイをかけたところ、タイトルのエラーに遭遇。
結論、解決はしたものの原因不明のエラーの為、今後色々調べたいので備忘録として記載します。
OSが関係してるのかなと推測してますが、分かる方いらっしゃればご教示下さい。開発環境
Rails 5.2.1
ruby 2.5.1
capistrano 3.11.1
AWS(EC2)
Web Server Nginx
Application Server Unicorn前提
上記導入済み、aws接続して自動デプロイまでは問題無し。S3設定後初めての自動デプロイ中に
unicorn start
で止まったエラー。ファイルのLoadErrorが発生
unicorn startで止まり、エラー文にはstderr.logの詳細をチェックするようにという記述。
current/log/unicorn.stderr.log# 本番環境でlog表示(less or cat) $ less log/unicorn.stderr.log # エラー抜粋 I, [2019-09-20T08:48:07.350883 #12335] INFO -- : Refreshing Gem list bundler: failed to load command: unicorn (/var/www/appname/shared/bundle/ruby/2.5.0/bin/unicorn) LoadError: No such file to load -- aws-sdk-s3.rb原因(不明)
今回導入予定の無い
aws-sdk
というパッケージに関連するエラー。
確認したところ、公式
http://aws.amazon.com/jp/sdkforruby/
参考
https://qiita.com/w650/items/50449fe162bc0f8425e4というようなgemでした。
検証計画
- aws-sdk-s3.rbの存在→なし
- 本番・ローカルでgemfile、gemfile lockの確認→記述なし
- 再起動・bundleinstallのし直し→変化なし
- S3導入時に変更した設定関連のファイルを確認→問題無さそう (
database.yml
/deploy.rb
/image_uploder
/unicorn.rb
/carrierwave.rb
/enviroment.rb
/capfile
など)- コミットを辿り自動デプロイできていたところまで遡る→できていたはずの段階でも同じエラー
- ★一度、 aws-sdk-s3を導入してみる
- git cloneをやり直し、再度環境構築
- EC2インスタンスから作成のし直し
解決
結局、gemを導入することで解決。
Gemfileにaws-sdk
を追加、bundleinstall、merge、(念の為本番環境でもpullとbundle install)、自動デプロイで本番環境にページ表示されるように。さいごに
自分の中では最終手段のひとつと思っていた導入をすることに。
理由や原因が分からないまま、使わなければいいとは言えgemを追加するのが抵抗あったからですが、かなり時間をかけてしまいました。
コードに特に違和感が無ければ、もっと早くに試しても良かったというのが反省点。
- 投稿日:2019-09-27T07:13:47+09:00
Railsチュートリアル 第5章(余談) - Rails5環境で学習を進めているのに、なぜRails5で非推奨のassert_templateが正常動作するのか
Rails5では、
assert_template
は非推奨Railsチュートリアルで使われているコードのうち、
assert_template
は、Rails5では非推奨となりました。Ruby on Rails - issue #18950に、非推奨とするに至った経緯の説明、および関連するやり取りが残っています(英語)。Deprecation of assigns and assert_template in controller tests - BigBinary Blogにも、この変更についての説明が記述されています(英語)。
今Rails5でRailsチュートリアルを進めている
# rails version Rails 5.1.6…ちょっと待って下さい。現在Railsチュートリアルを進めているRailsのバージョンは5.1.6ですよね。なぜ
assert_template
が動いているのです?Railsチュートリアルで
assert_template
が正常動作する理由答えは
/Gemfile
にありました。group :test do gem 'rails-controller-testing', '1.0.2' gem 'minitest', '5.10.3' gem 'minitest-reporters', '1.1.14' gem 'guard', '2.13.0' gem 'guard-minitest', '2.4.4' endgem 'rails-controller-testing', '1.0.2'
rails-controller-testing
というGemがインストールされていれば、Rails5でもassert_template
を動かすことができます。Railsチュートリアルでは、第3章のリスト 3.2時点でrails-controller-testing
を/Gemfile
に追加していますね。
- 投稿日:2019-09-27T06:33:00+09:00
Railsでシンプルなグラフを扱うならchart-js-rails よりchartkickを使うべし
Railsでグラフを扱う方法として chart-js-rails を使った方法を こちらの記事で解説しました。
一方、シンプルなグラフを埋め込む場合においては chartkick を使ったほうが簡単なことがわかったので本記事で解説します。chartkickとは
chart.jsをいい感じにラッピングして、ワンラインでグラフを埋め込めるようにするプロジェクトです。
Rubyに限らず様々な言語で実装されています。また、chart.jsだけでなくGoogle ChartsやHighchartsもサポートしています。
chartkickを使ったほうが良い理由
RubyとJavascript間のデータの受け渡しを意識しなくて良い
こちらの記事の方法だと、Ruby側で扱う数値を何らかの方法でJavascriptで受け渡す必要がありました。
chartkickの場合はデータを配列にしてRubyのメソッドへ渡すだけなので、Ruby側のみで実装が完結します。
githubのスターの数が多い
あくまでgemで比較した場合ですが、chartkickの方が断然スターが多いです。
直近のコミット日時もchartkickの方が新しいです。Rails6にも対応している
Ruby on Rails6からデフォルトのJavascriptコンパイラがWebpackerとなります。
おそらく、chart-js-rails
をRails6で使おうとすると若干の手間がかかると推測されます。実際に使ってみる
サンプルアプリケーションを作る
今回は時事ネタとして「ラグビーワールドカップ開催国」に関する情報をグラフ化します。
以下コマンドを実行してアプリケーションを作成します。$ rails new chartkick_sample $ rails g scaffold rugby_world_cup_host_country name:string total_attendance:integer matches:integer stadium_capacity:integer held_at:datetime
db/seeds.rb
へ Wikipediaを参考にラグビーワールドカップの開催国を入力します。
※厳密には1991年と1987年にも開催されているのですが、開催国が複数あってDB構造が複雑化するので今回は1995年以降のみ入力しました。db/seeds.rb[ [1995, 'South Africa', 1100000, 32, 1423850], [1999, 'Wales', 1750000, 41, 2104500], [2003, 'Australia', 1837547, 48, 2208529], [2007, 'France', 2263223, 48, 2470660], [2011, 'New Zealand', 1477294, 48, 1732000], [2015, 'England', 2477805, 48, 2600741], [2019, 'Japan', nil, 48, nil], [2023, 'France', nil, 48, nil], ].each do |record| RugbyWorldCupHostCountry.create( held_at: Time.zone.local(record[0]), name: record[1], total_attendance: record[2], matches: record[3], stadium_capacity: record[4] ) end入力が終わったらDBの作成・マイグレーション・seedを実行します。
rails db:create db:migrate db:seedhttp://localhost:3000/rugby_world_cup_host_countries へアクセスすると、一覧が表示されました。
グラフを表示する
chartkickのインストール
Gemfileに追記して、
bundle install
します。
続けてyarn add
も実行してしまいましょう。Gemfile・・・省略・・・ gem 'bootsnap', '>= 1.4.2', require: false gem "chartkick" # 追記 group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end ・・・省略・・・$ bundle installここからはRails6以降かRails5以前かで手順が分かれます。
Rails6以降
yarn
でchart.js
をインストールします。$ yarn add chartkick chart.js
application.js
でchart.js
とchartkick
をrequireします。app/javascript/packs/application.jsrequire("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("chartkick") // 追記 require("chart.js" // 追記Rails5以前
application.js
へ以下を追記します。app/assets/javascripts/application.js//= require chartkick //= require Chart.bundleグラフの実装
開催国一覧ページにグラフを埋め込みます。
app/views/rugby_world_cup_host_countries/index.html.erb<p id="notice"><%= notice %></p> <h1>Rugby World Cup Host Countries</h1> <%= column_chart RugbyWorldCupHostCountry.pluck(:held_at, :total_attendance) %><!-- 追記 --> <table> <thead> <tr> <th>Name</th> <th>Total attendance</th> <th>Matches</th> <th>Stadium capacity</th> <th>Held at</th> <th colspan="3"></th> </tr> </thead> ・・・省略・・・http://localhost:3000/rugby_world_cup_host_countries へアクセスするとグラフが表示されます。
オプションを指定すれば色やサイズも変えられます。
app/views/rugby_world_cup_host_countries/index.html.erb<p id="notice"><%= notice %></p> <h1>Rugby World Cup Host Countries</h1><!-- 追記 --> <%= column_chart RugbyWorldCupHostCountry.pluck(:held_at, :total_attendance), id: 'total-attendance-chart', width: '400px', height: '500px', colors: ['#b00'] %><!-- 追記 --> <table> <thead> <tr> <th>Name</th> <th>Total attendance</th> <th>Matches</th> <th>Stadium capacity</th> <th>Held at</th> <th colspan="3"></th> </tr> </thead> ・・・省略・・・chartkickでできそうにないこと
現時点で実現方法がわからなかったことを記載しておきます。
実現方法をご存知な方はぜひコメントをお願いします。
- 2軸のグラフを表示する
- 2種類のグラフ(折れ線グラフと棒グラフなど)を1つのグラフとして表示する
まとめ
RubyとJavascript間の値の受け渡しを意識しなくて良いですし、なにより1行でグラフを埋め込めるのが素晴らしいです。
Railsでシンプルなグラフを出力したい方はぜひ使ってみてください。