- 投稿日:2019-09-27T23:42:57+09:00
4&5/100日目 #100DaysOfCode #qiita #駆け出しエンジニアと繋がりたい #プログラミング初心者
やる予定だったもの
progate rails9-11 読み込み
progate rails2章 手を動かすやったこと
progate rails9-11章 読み込み
progate ruby 1-3章 読み込み
html&Css 初級と中級 読み込み学習時間
2h, 1.5h
反省・改善点、気づき
朝方にしてシフトして来た。
なお課題の実機操作については0-7時の睡眠なので、23-6時に来週以降は固定して対応する。
progateの活用方法をシェアしたところ意外と反響があったため、情報のアウトプットは重要だと実感。
https://twitter.com/kakedashi_555/status/1176120891849068545?s=21明日やること
atomインストール等環境構築
rails2-3章 実機
予定見直し
- 投稿日: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-27T19:36:30+09:00
AWS IoT EnterpriseでJobcanの打刻ができる出勤ボタンをつくった
背景
- AWS LambdaがRubyの対応したので、自分で作成して使ってみたかった
- Amazonがダッシュボタン事業から撤退してしまうので、その弔いとして
- ジョブカンで打刻するのが億劫だなと感じていた
構成
構成としては以下の図のような構成になっています。
参考にしたのは、このブログのリンクでpythomで自分が実現したいことを達成していたので、90%くらいはここから真似してきました。
こちらのブログだとpythonで書かれていたのですが、今回使いたかったのはRubyなので、Rubyで書きました。
手順
- Amazon IoT Enterprise をAmazonで購入
- 2500円します。
- 実験としてはいいけど、高いな。。。
AWS IoT エンタープライズボタン – IoT をシンプルに。
- Amazon IoT Enterpriseをセットアップ
- アプリをダウンロード
- バーコードを読み込んで、デバイスを登録する
- デバイスとWifiを繋げる
- Amazon IoTをセットアップ
- プロジェクトを作成
- デバイステンプレートを作成
- Labda のセットアップ
- 関数を作成
- SlackのAPI token とチャンネルのIDの獲得
- ここから獲得
- コードの記述
- slack-ruby-clientというgemを使う
- 以下のようなコードを書きます
require 'slack-ruby-client' def hello(event:, context:) Slack.configure do |config| config.token = ENV['SLACK_API_TOKEN'] end client = Slack::Web::Client.new client.chat_command(channel: ENV['SLACK_CHANNEL'], command:'/jobcan_touch') end
- 詳しいコードは以下のGithubから確認してください。
https://github.com/knsg16/slack-jobcan
- コードをAWS Lambdaにアップする
- zip形式のフォルダを作成
- 当該ファイルをLambdaにアップする
- Lambdaにテストイベントを作成して、テスト
- 完成
- お疲れ様でした!!
トラブルシューティング
- ステルスの社内WIFIにAmazon IoT Enterpriseを接続できない。。。
- rubyのversionを2.5.0にきっちり揃えないといけなかった。。。
- 普通にwebhookでは、コマンドをAPIで叩くことができなかった。かつ、非推奨のレガシートークンを使用しなければならなかった。。。
- lamdaにアップするためにファイルをzipにする時のファイル構成
- 投稿日:2019-09-27T19:02:56+09:00
Ruby 2.7 で in 演算子が使えるようになる
ブログ記事からの転載になります。
Ruby 2.7 で
in
演算子が使えるようになります。
そもそもin
演算子ってなんやねんって話なんですが、同じく Ruby 2.7 で入る予定のパターンマッチ構文のin
になります。
例えばパターンマッチを使用することで次のような判定を行うことが出来ます。def check user # name に m が含まれていて age が 14以下 case user in { name: /m/, age: (..14) } "OK" else "NG" end end p check(id: 1, name: "Homu", age: 13) # => OK p check(id: 2, name: "mami", age: 15) # => NGこのようにパターンマッチを使用する事でより細かく条件を指定する事が出来ます。
in
演算子を使う上記の例だと
case <expr>; in <pattern>; ... end
みたいな構文を書く必要があるのでやや冗長に見えますね。
そこでin
演算子です。
in
演算子を使うことでパターンマッチ式を1行で書くことが出来ます。def check user # name に m が含まれていて age が 14以下 # in 演算子は true / false を返す (user in { name: /m/, age: (..14) }) ? "OK" : "NG" end p check(id: 1, name: "Homu", age: 13) # => OK p check(id: 2, name: "mami", age: 15) # => NG
in
演算子はパターンマッチと同様に<expr> in <pattern>
と書くことが出来ます。
また戻り値はtrue / false
になります。
これは次のようなselect
などと組み合わせるとより強力になります。users = [ { id: 1, name: "Homu", age: 13 }, { id: 2, name: "mami", age: 14 }, { id: 3, name: "Mado", age: 21 }, { id: 4, name: "saya", age: 14 }, ] # @1 から _1 に変わったナンパラを使うとより簡潔にかける p users.select { _1 in { name: /m/, age: (..14) } } # => [{:id=>1, :name=>"Homu", :age=>13}, {:id=>2, :name=>"mami", :age=>14}]これめっちゃ便利そうですね!!!
ちなみにin
演算子なのかin
式なのかどっちが正しいんですかね?
matz がin
operaetor と書いていたのでin
演算子と明記しましたがin
式のほうが正しいのかな。
- 投稿日:2019-09-27T13:47:31+09:00
VRChat APIで二段階認証する
VRChat APIを利用する際にAPI経由でVRChatにログインしてトークンを取得することが多いのですが、二段階認証を利用している場合については記事が無かったのでメモします
ざっくり概要を書くと、
まず通常ログインでトークンを取得。ただし二段階認証が有効になっている場合、トークンは使えない
トークン取得後に二段階認証する(Google Authenticatorから取得した番号をPOSTする)とトークンが有効化される
といった流れになります。注意
この記事で使用しているVRChat APIの仕様は非公式なドキュメントに基づくものです。
公式からは悪意のないAPI利用はいいけど他人にPW入力させたらダメと言及されていますので、その点ご注意ください。1. VRChatから認証トークンを取得
まずはVRChatからトークンを取得します。手順は以下の通りです。
1. https://api.vrchat.cloud/api/1/config をGETしてapiKeyを取得
2. https://api.vrchat.cloud/api/1/auth/user をGETしてauthTokenを取得 (VRChatのユーザ名・パスワードでBasic認証を行う)
※Basic認証時、トークンはCookieに返ってくるので注意!rubyで書くとこんな感じです。GETするあたりは最低限必要な分だけ書いてます。
VRChatBasicAuthdef login_vrc username = "VRCのユーザー名" password = "VRCのパスワード" api_base = "https://api.vrchat.cloud/api/1" # 後々二段階認証で使うのでapiKeyとauthTokenはセッションに保管しています # apiKey取得 apikey = get_apiKey(api_base) session[:apiKey] = apiKey # authToken取得 session[:authToken] = get_authToken(api_base, apikey, username, password) end def get_apikey(api_base) endpoint = "/config" url = api_base + endpoint uri = URI.parse(url) req = Net::HTTP::Get.new(uri) Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https'){ |http| response = http.request(req) @json = response.body } apikey_json = JSON.parse(@json) return apikey_json["clientApiKey"] end def get_authToken(api_base, apikey, username, password) endpoint = "/auth/user" url = api_base + endpoint uri = URI.parse(url) uri.query = URI.encode_www_form(apiKey: apikey) req = Net::HTTP::Get.new(uri) req.basic_auth(username, password) Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') { |http| response = http.request(req) @cookie = response.get_fields('Set-Cookie') } #取得したCookieからauthTokenを抽出する(かなり強引なので正直直したい) cookie = @cookie.select { |x| x.include?("auth") } unless cookie[0].nil? return cookie[0] end end2. トークンを有効化
先ほど取得したapiKeyとauthToken、それとGoogle Authenticatorから取得した番号をhttps://api.vrchat.cloud/api/1/auth/twofactorauth/totp/verify にPOSTします。
認証が成功するとverified = tureが返ってきて、authTokenが各種APIで利用できるようになります。
VRChat2FAdef twofa_vrc num = "Google Authenticatorから取得した番号" api_base = "https://api.vrchat.cloud/api/1" endpoint = "/auth/twofactorauth/totp/verify" url = api_base + endpoint uri = URI.parse(url) uri.query = URI.encode_www_form(apiKey: session[:apikey]) req = Net::HTTP::Post.new(uri) req["Cookie"] = session[:auth_token] req.set_form_data('code' => num) Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') { |http| response = http.request(req) @json = response.body } #認証成功するとtwofa_result = trueになります twofa_result = JSON.parse(@json)["verified"] end参考
- 投稿日: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-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:28:21+09:00
#Ruby 2.5 では 1行のBodyだけのCSV ファイルで headers が取得できないっぽい? ( Ruby 2.6 では可能 )
Unable to headers with only one line no body CSV file in Ruby 2.5
Ruby 2.5
require 'csv' CSV.parse("A,B,C", headers: true).headers # => [] CSV.parse("A,B,C\nX,Y,Z", headers: true).headers # => ["A", "B", "C"] CSV.parse("A,B,C", headers: true).to_s # => "\n"以下で動作確認。
ruby 2.5.2p104 (2018-10-18 revision 65133) [x86_64-darwin18]
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin18]
ruby 2.5.6p201 (2019-08-28 revision 67796) [x86_64-linux]
(docker+mac)Ruby 2.6
require 'csv' CSV.parse("A,B,C", headers: true).headers # => ["A", "B", "C"] CSV.parse("A,B,C\nX,Y,Z", headers: true).headers # => ["A", "B", "C"] CSV.parse("A,B,C", headers: true).to_s # => "A,B,C\n"以下で動作確認。
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-darwin18]
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin18]
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
(docker+Mac)Docs
どこに違いがあるのだろう?
https://ruby-doc.org/stdlib-2.5.3/libdoc/csv/rdoc/CSV.html#method-i-headers
https://ruby-doc.org/stdlib-2.6.1/libdoc/csv/rdoc/CSV.html#method-i-headersOriginal by Github issue
- 投稿日:2019-09-27T07:21:44+09:00
これで大抵賄える!AWS SDK for Ruby V3でのS3操作まとめ!!
S3の操作するコードを書くタスクは日常的にやるわけではなくて、なぜかちょうど忘れた頃の絶妙なタイミングでやってきます。そのたびにAPIリファレンスを見ながら頑張って組み立ててるような気がしたので、しゃらくせぇ!と思い、よく使うやつをまとめました。
Aws::S3::Client
とAws::S3::Resource
の2系統を用意しました。お好みに応じて参照してください。
参考までに、この2つの違いがよく分からなくて途方に暮れている人はこちらをご覧ください。
AWS SDK for Ruby V3のAws::S3::ClientとAws::S3::Resourceの違いに正面から向き合うクレデンシャル情報やリージョンは、環境変数やIAMロール等で設定されているものとします。
個別に設定する場合は、Aws::S3::Client.new
やAws::S3::Resource.new
の引数に渡してあげてください。Aws::S3::Client
# 共通 client = Aws::S3::Client.newバケット一覧
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#list_buckets-instance_method
# バケット情報一覧 client.list_buckets => #<struct Aws::S3::Types::ListBucketsOutput buckets= [#<struct Aws::S3::Types::Bucket name="bucket-01", creation_date=2017-09-16 14:07:03 UTC>, #<struct Aws::S3::Types::Bucket name="bucket-02", creation_date=2018-08-19 04:22:41 UTC>,], owner=#<struct Aws::S3::Types::Owner display_name="XXXXXX", id="xxxxxxxxxxxxxxx">> # 各バケットを操作 client.list_buckets.buckets.each do |bucket| # なにかする endバケット作成
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#create_bucket-instance_method
client.create_bucket(bucket: "bucket-03") => #<struct Aws::S3::Types::CreateBucketOutput location="http://bucket-03.s3.amazonaws.com/">バケット削除
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#delete_bucket-instance_method
client.delete_bucket(bucket: "bucket-03") => #<struct Aws::EmptyStructure>オブジェクト一覧
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#list_objects_v2-instance_method
# オブジェクト情報一覧 # `list_objects`は使わない client.list_objects_v2(bucket: "bucket-01") => #<struct Aws::S3::Types::ListObjectsV2Output is_truncated=true, contents= [#<struct Aws::S3::Types::Object key="example_01.txt", last_modified=2019-09-24 21:42:10 UTC, etag="\"xxxxxxxxxxxxxx\"", size=7, storage_class="STANDARD", owner=nil>, #<struct Aws::S3::Types::Object key="example_02.txt", last_modified=2019-09-24 21:42:10 UTC, etag="\"xxxxxxxxxxxxxx\"", size=7, storage_class="STANDARD", owner=nil>], name="bucket-01", prefix="", delimiter=nil, max_keys=1000, common_prefixes=[], encoding_type=nil, key_count=1000, continuation_token=nil, next_continuation_token="1I+AdtAyjlXhJ1jEKpdpR3fqz2bDnfJFEfZ+wgn9vmpXjHNGcLzL8bg==", start_after=nil> # 各オブジェクトを操作(1000個以下の場合) client.list_objects_v2(bucket: "bucket-01").contents.each do |object| # なにかする end # 各オブジェクトを操作(1000個より多いの場合) options = {bucket: "bucket-01"} loop do object_list = client.list_objects_v2(options) object_list.contents.each do |object| # なにかする end options[:continuation_token] = object_list.next_continuation_token break unless object_list.next_continuation_token end # キー名のプレフィクスによる絞り込み client.list_objects_v2(bucket: "bucket-01", prefix: "hoge/").contents.each do |object| # なにかする endオブジェクトアップロード
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_object-instance_method
client.put_object(bucket: "bucket-01", key: "example_01.txt", body: "example") => #<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"xxxxxxxxxxxxxx\"", server_side_encryption=nil, version_id="JlDwEydDxlVNoyEaChzromzkjPo5FwVl", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id=nil, ssekms_encryption_context=nil, request_charged=nil>オブジェクト読み込み
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#get_object-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/GetObjectOutput.htmlclient.get_object(bucket: "bucket-01", :key => 'example_01.txt').body.read => "example"オブジェクト削除
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#delete_object-instance_method
client.delete_object(bucket: "bucket-01", key: "example_01.txt") => #<struct Aws::S3::Types::DeleteObjectOutput delete_marker=true, version_id="skCZWAXC0.vhxjK6zKTF0dBkj4H0gEe8", request_charged=nil>オブジェクトのバージョン操作
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#list_object_versions-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#delete_object-instance_method# バージョン情報 client.list_object_versions(bucket: "bucket-01", prefix: "example_01.txt") => #<struct Aws::S3::Types::ListObjectVersionsOutput is_truncated=false, key_marker="", version_id_marker="", next_key_marker=nil, next_version_id_marker=nil, versions= [#<struct Aws::S3::Types::ObjectVersion etag="\"xxxxxxxxxx\"", size=7, storage_class="STANDARD", key="example_01.txt", version_id="BcczH4ZRVunnKyBuzk3cBOQgrh1gtGpR", is_latest=true, last_modified=2019-09-24 21:47:54 UTC, owner=#<struct Aws::S3::Types::Owner display_name="xxxxxx", id="xxxxxxxxxxxxxxxxxx">>, #<struct Aws::S3::Types::ObjectVersion etag="\"xxxxxxxxxx\"", size=7, storage_class="STANDARD", key="example_01.txt", version_id="null", is_latest=false, last_modified=2019-09-24 21:42:10 UTC, owner=#<struct Aws::S3::Types::Owner display_name="xxxxxx", id="xxxxxxxxxxxxxxxxxx">>], name="bucket-01", prefix="example_01.txt", delimiter=nil, max_keys=1000, common_prefixes=[], encoding_type="url"> # 特定のバージョンを削除 client.delete_object(bucket: "bucket-01", prefix: "example_01.txt", version_id: "JlDwEydDxlVNoyEaChzromzkjPo5FwVl") => #<struct Aws::S3::Types::DeleteObjectOutput delete_marker=nil, version_id="JlDwEydDxlVNoyEaChzromzkjPo5FwVl", request_charged=nil>マルチパートアップロード
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#create_multipart_upload-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#upload_part-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#complete_multipart_upload-instance_methodmultipart = client.create_multipart_upload(bucket: "bucket-01", key: "multipart") => #<struct Aws::S3::Types::CreateMultipartUploadOutput abort_date=nil, abort_rule_id=nil, bucket="bucket-01", key="multipart", upload_id="xxxxxxxxxxxxxxxxxxxx", server_side_encryption=nil, sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id=nil, ssekms_encryption_context=nil, request_charged=nil> part_01 = client.upload_part(bucket: "bucket-01", key: "multipart", body: File.open("/path/to/part_01"), part_number: 1, upload_id: multipart.upload_id) => #<struct Aws::S3::Types::UploadPartOutput server_side_encryption=nil, etag="\"xxxxxxxxxxxxxxxxxxxxx\"", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id=nil, request_charged=nil> part_02 = client.upload_part(bucket: "bucket-01", key: "multipart", body: File.open("/path/to/part_02"), part_number: 2, upload_id: multipart.upload_id) => #<struct Aws::S3::Types::UploadPartOutput server_side_encryption=nil, etag="\"yyyyyyyyyyyyyyyyyyyyy\"", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id=nil, request_charged=nil> client.complete_multipart_upload( bucket: "bucket-name-0001", key: "multipart", multipart_upload: { parts: [ {etag: part_01.etag, part_number: 1}, {etag: part_02.etag, part_number: 2} ] }, upload_id: multipart.upload_id ) => #<struct Aws::S3::Types::CompleteMultipartUploadOutput location="https://bucket-01.s3.ap-northeast-1.amazonaws.com/multipart", bucket="bucket-01", key="multipart", expiration=nil, etag="\"zzzzzzzzzzzzzzzzzzzzz\"", server_side_encryption=nil, version_id="CSbIGreIivX2CTbIxj9Qz1JbePVQb2Qo", ssekms_key_id=nil, request_charged=nil>署名付きURL作成
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Presigner.html
# clientではできない signer = Aws::S3::Presigner.new signer.presigned_url(:get_object, bucket: "bucket-01", key: "example_01.txt", expires_in: 3600) => "https://bucket-01.s3.ap-northeast-1.amazonaws.com/example_0001?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-...Aws::S3::Resource
# 共通 resource = Aws::S3::Resource.newバケット一覧
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Resource.html#buckets-instance_method
# バケット情報一覧 resource.buckets => #<Aws::S3::Bucket::Collection:0x00007fb1e3ccb6a8 @batches=#<Enumerator: ...>, @limit=nil, @size=nil> # 各バケットを操作 resource.buckets.each do |bucket| # なにかする endバケット作成
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Resource.html#create_bucket-instance_method
resource.create_bucket(bucket: "bucket-03") => #<Aws::S3::Bucket:0x00007fb1e3d3f2d8 @client=#<Aws::S3::Client>, @data=nil, @name="bucket-03"> # こちらでもできるけど、Client系統で扱うbucket情報が返ってくる resource.bucket("bucket-03").create # 存在確認もできる resource.bucket("bucket-03").exists? => trueバケット削除
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#delete-instance_method
resource.bucket("bucket-03").delete => #<struct Aws::EmptyStructure>オブジェクト一覧
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#objects-instance_method
bucket = resource.bucket("bucket-01") # オブジェクト情報 bucket.objects => #<Aws::S3::ObjectSummary::Collection:0x00007fb1e3e47a68 @batches=#<Enumerator: ...>, @limit=nil, @size=nil> # 各オブジェクトを操作(1000個より多くてもOK!) bucket.objects.each do |object| # なにかする end # キー名のプレフィクスによる絞り込み bucket.objects(prefix: "hoge/").each do |object| # なにかする endオブジェクトアップロード
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#put_object-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#put-instance_methodbucket = resource.bucket("bucket-01") # 文字列から bucket.put_object(key: "example_01.txt", body: "example") => #<Aws::S3::Object:0x00007f859e9625c8 @bucket_name="bucket-01", @client=#<Aws::S3::Client>, @data=nil, @key="example_01.txt"> # ファイルから bucket.put_object(key: "example_01.txt", body: File.open("example_01.txt")) => #<Aws::S3::Object:0x00007f859e9625c8 @bucket_name="bucket-01", @client=#<Aws::S3::Client>, @data=nil, @key="example_01.txt"> # こちらでもできるけど、Client系統で扱うbucket情報が返ってくる bucket.object("example_01.txt").put(body: "example") => #<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"xxxxxxxxxxxxxxxxxxxxxxxxxxxx\"", server_side_encryption=nil, version_id=nil, sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id=nil, ssekms_encryption_context=nil, request_charged=nil>オブジェクト読み込み
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#get-instance_method
bucket = resource.bucket("bucket-01") bucket.object("example_01.txt").get.body.read => "example" # 存在確認もできる bucket.object("example_01.txt").exists? => trueオブジェクト削除
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#delete-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/ObjectSummary/Collection.html#batch_delete!-instance_methodbucket = resource.bucket("bucket-01") bucket.object("example_01.txt").delete => #<struct Aws::S3::Types::DeleteObjectOutput delete_marker=nil, version_id=nil, request_charged=nil> # 一括削除 bucket.objects(prefix: "hoge/").batch_delete! => nilオブジェクトのバージョン操作
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#object_versions-instance_method
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#delete-instance_methodbucket = resource.bucket("bucket-01") bucket.object_versions(prefix: "example_01.txt").map(&:data) => [#<struct Aws::S3::Types::ObjectVersion etag="\"xxxxxxxxxx\"", size=7, storage_class="STANDARD", key="example_01.txt", version_id="BcczH4ZRVunnKyBuzk3cBOQgrh1gtGpR", is_latest=true, last_modified=2019-09-24 21:47:54 UTC, owner=#<struct Aws::S3::Types::Owner display_name="xxxxxx", id="xxxxxxxxxxxxxxxxxx">>, #<struct Aws::S3::Types::ObjectVersion etag="\"xxxxxxxxxx\"", size=7, storage_class="STANDARD", key="example_01.txt", version_id="null", is_latest=false, last_modified=2019-09-24 21:42:10 UTC, owner=#<struct Aws::S3::Types::Owner display_name="xxxxxx", id="xxxxxxxxxxxxxxxxxx">>] # 特定のバージョンを削除 bucket.object("example_01.txt").delete(version_id: "BcczH4ZRVunnKyBuzk3cBOQgrh1gtGpR") => #<struct Aws::S3::Types::DeleteObjectOutput delete_marker=nil, version_id="BcczH4ZRVunnKyBuzk3cBOQgrh1gtGpR", request_charged=nil>マルチパートアップロード
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#upload_file-instance_method
bucket = resource.bucket("bucket-01") # デフォルトだと15MBより大きければマルチパートアップロードになる # しきい値を変えたい場合は、`multipart_threshold`オプションを与える bucket.object("very_large_file.txt").upload_file("/path/to/very_large_file") => true署名付きURL作成
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
bucket = resource.bucket("bucket-01") # `:get`/`put`/`head`/`delete`が指定できる # `expires_in`はデフォルト900秒 bucket.object("example_01.txt").presigned_url(:get, expires_in: 3600) => "https://bucket-01.s3.ap-northeast-1.amazonaws.com/example_0001?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-...
- 投稿日: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でシンプルなグラフを出力したい方はぜひ使ってみてください。
- 投稿日:2019-09-27T01:40:33+09:00
Ruby gem を調べる
Ruby gem を調べる
業務でRubyの簡単な改修をするようになり、当然のように既存のgemを利用していますが、改めてgemについて簡単にまとめてみました。
gemとは何か
RubyにおけるgemとはRubyのライブラリのことです。
gemを使用することで1からコードを書かなくても機能を実装できるため便利です。
しかし、注意点としてフレームワークのバージョンによる相性やgemのバージョン同士の組み合わせによっては動作しない可能性があります。その為インストールやアンインストールにはRubyGemsやbundlerといったパッケージ管理ツールを使うことが多いです。
Rubyのバージョン1.9以降はRubyGemsは標準添付となっていますが、それ以前のバージョンの場合は自分でインストールする必要があります。gemを探す
gemは主にRubyGems.orgに置いてあります。その他にはgemコマンドを使用したりWebサイトを直接閲覧したりして探します。
またruby関連のリポジトリとしてGitHubが広く使われている為、gemのソースコードのほとんどがGitHubで見ることができます。gemコマンド
ターミナルでgemコマンドを使うことでgemをインストールしたり一覧を見ることができます。
$gem search -r #入手可能な全てのgemの一覧を調べます
--local(-r)オプションを使うと、インストール済みのgemに対してローカルで検索をすることが出来ます
$gem install [gem名] #gemをインストールします
--version/-vフラグを使うと、バージョンを指定してインストールすることができます。
$gem update #gemをアップデートします
$gem install bundler #未インストールのバンドルがある場合インストールします。
$gem list #インストール済みのgemを見ることができます。
$gem help #ドキュメントをターミナルで参照することができます。bundleコマンド
$gem install bundler #bundlerをインストールします。
$bundle install #これだけで使用するとRuby共通フォルダにgemをインストールします。
$bundle install --path #プロジェクトフォルダにgemをインストールします。
以降--pathオプションを付けなくてもプロジェクトフォルダにインストールされます。
$bundle install --without #デプロイする際に余計なgemを入れない為に使用します。
$bundle list #プロジェクトで使用しているgemのバージョンを確認します。よく使われるgem
devise
IDとメールアドレスを使用して登録、ログインをする一般的なログイン機能を実装することが可能になります。
登録処理、仮登録、ログイン処理、認証機能、パスワードの変更/再発行などログイン機能として十分な機能を備えている為広く使用されています。active admin
管理画面を自動で作成します。
権限付与・管理を行えるcancancanというgemなどをセットに使用することがよくあります。
また、管理画面を作成するのに使用されるgemは他にもrails adminやAdministrateなど有名なものがありますが、それぞれ特性がある為調べて使用しましょう。i18n
Ruby on Rails 2.2以降からRailsに同梱されている多言語化に用いるgemです。
多くの言語ファイルが用意されていて、様々な言語を翻訳してくれる為サイトの国際化などに便利です。Kaminari
長い文章の記事をページ分割して各ページへのリンクを表示してくれるページネーション機能を実装するGemです。
表示件数やデザインも簡単に設定変更できます。better errors
エラーメッセージをわかりやすく表示してくれるgemです。
ブラウザからのirbによるdebugが可能で、どうなっているかわからない箇所ターミナルの表示をせずにすぐ確認できます。
デバッグ作業が捗る人気なgemです。最後に
今回はRubyのgemについて簡単な説明とよく使われるgemを少し紹介しました。
脱Ruby初心者に向けて既存のgemをただ使用するだけでなくソースを確認し一歩踏み込んだ読み方をして自分でもgemを作れるようになるとよりRubyを知ることができると思います。