- 投稿日:2020-03-01T23:51:19+09:00
2桁から十の位と一の位を取得する
数字の正規表現
def slice_num(input) ten = (input/10) % 10 one = (input/1) % 10 return ten, one end puts "2桁の整数を入力してください" input = gets.to_i ten, one = slice_num(input) puts "足し算結果と掛算結果の合計値は#{(ten+one)+(ten*one)}です"ユーザーが整数2桁を入力するとその数字を
(十の位+一の位)+(十の位*一の位)を計算し
その計算結果を出力します。to_iメソッドで入力した内容を数字型にします
slice_numの返り値をten, oneに入れます
ten = (input/10) % 10の記述にて2桁の整数を10で割り、その値を10で割った際の余をだすことで十の位を取り出すことができる
one = (input/1) % 10の記述にて2桁の整数を1で割り、その値を10で割った際の余をだすことで一の位を取り出すことだできる
その返り値をreturnで戻すことで計算に用いることができる追記
コメント欄にてご教授いただいた内容も記載します。
divmodメソッド
def slice_num(input) ten, one = (input % 100).divmod(10) return ten, one end puts "2桁の整数を入力してください" input = gets.to_i ten, one = slice_num(input) puts "足し算結果と掛算結果の合計値は#{(ten+one)+(ten*one)}です"x.divmod(y)
divmodメソッドを使用するとxをyで割った商と余を取得することができます。
この場合xがtenにyがoneに代入されていますね。digitメソッド
def slice_num(input) one, ten = input.digits.take(2) return ten, one end puts "2桁の整数を入力してください" input = gets.to_i ten, one = slice_num(input) puts "足し算結果と掛算結果の合計値は#{(one+ten)+(one*ten)}です"input.digits.take(2)にて引数で渡されたinputに対しdigitsを使用しtakeで取得します。
take(2)では一の位、十の位までを取得します。
take(4)にした場合には、一、十、百、千までを取得します。
返り値は一、十の順番で代入されるため、今までと異なり、one、tenと変数を置いていく必要があります。
- 投稿日:2020-03-01T23:45:56+09:00
プログラミングスクール卒業後3ヶ月たった所感
2019年10月に最近youtubeでよく広告を見る某プログラミングスクールに通い、11月より機械学習エンジニア・データサイエンティストとして現職につきました。
卒業後3ヶ月経った今、スクールで学んだことで役立ったこと、今大変なことなどをつらつらと書いていきたいと思います。経歴
2015年4月 食品会社の研究・開発職として4年間務める
2019年7月 プログラミングスクール学習開始
2019年10月 プログラミングスクール卒業
2019年11月 機械学習エンジニア・データサイエンティストとして転職スキルセット
言語の使用状況
スクール 転職後 HTML ○ ○ CSS ○ ○ JavaScript ○ ○ SQL ○ △ Linux ○ ○ Ruby ○ - Python - ○ フレームワークの使用状況
スクール 転職後 SCSS ○ ○ jQuery ○ △ Vue - ○ Ruby on Rails ○ - Flask - ○ Django - ○ インフラ等
スクール 転職後 Git/Github ○ ○ Nginx ○ - EC2(AWS) ○ ○ S3(AWS) ○ ○ スクールについて
役立ったこと
- HTML/CSS/JavaScript/Linuxについて基礎が学べ、今でもシステムを作成する際に役立っている
- Ruby→Pythonに言語が変わったがどちらも動的型付けなため、基礎的な文法を学ぶことへの学習コストはかからなかった
- EC2およびS3の作成・運用の基礎が学べ、現職ではシステムのデプロイは全てAWSを用いているため、すんなり業務に入ることができた
- 現職では頻繁には使用しないが、Gitを利用した複数人の開発の経験は業務の随所で役立つ
- メンターへの質問シートの書き方を守ることで、先輩エンジニアなどに質問する際に、何が問題で・どこで詰まっているのかが明確に伝えられ、回答を頂きやすい
役立たなかったこと
基本的には学んだことはある程度役立つと思いますが、下記点は仕方ないと考えていました。
- スクールでRubyを学んだが、現職では全く使用しないこと(機械学習案件を志望していたため、仕方ないと割り切ってはいました)
転職前にやるべきだったこと
- 簡単でもいいので、新しい職場で使用するであろう言語で簡易的なシステムやアプリをデプロイまで持っていく
理由
コードの書き方や、フレームワークを予め触っておいて、素早く職場に馴染めるようにもっと努力をすべきと思いました。
- AWSのサービスをより理解しておく
理由
最近のサービスのデプロイのほとんどはAWSを利用していると思います。無料枠内で学ぶのもいいですが、実際には有料で数万円数十万円をかけてシステムを運用します。そのため少しの金額でも身銭を切って、ドメインの取り方を学んだり、アクセスの分散方法を学ぶなど、AWSというサービスを少しでも多く理解しておいた方がよかったと思いました。
- 綺麗なコードを書く意識をつける・練習する
理由
シンプルにレビュアーが大変なのと、あとで改修する際に自分でもわからなくなってしまうので、綺麗なコードを意識することは非常に大事です。(作者はめっちゃ冗長に書きすぎて、最初はレビューの時間が非常にかかってしまいました)
今大変なこと
- 想像以上にJavaScriptを多用するため、様々な機能を覚える必要がある
- 基本的に少人数でシステムを作成したり、データ分析を行うため分からない部分が多く、仕事が止まりやすい
他にもいろいろ大変なこともあるのですが、結論としては悩んだらわかりそうな人に聞く!!これが仕事をうまく進める方法だと思います。私自身人に聞くということが苦手で、どうしても1人でやろうとしがちだったのでした。しかし、先輩エンジニアに聞いたら数分で解決してくれたり、アドバイスしてくれるため、1時間もかけて調べるよりも、わかる人に聞いた方が100%早いです!!
- 投稿日:2020-03-01T23:25:21+09:00
レシーバがnilかもしれないときの単純なメソッドの書き方
概要
下記のような単純なメソッドがあります。
def user_name user.name end上記メソッド内に登場する
userという変数には、
nameというメソッドを実行できるオブジェクトが格納されていると仮定します。上記メソッド内の
userがnilかもしれない場合は、NoMethodErrorが発生する可能性があります。
なので、 nilの場合の処理 を書く必要があります。どのように書くのが1番読みやすいか悩んでいた時に4人の方に相談したのですが、
メソッドの内容によって、どれを読みやすいと思うかが変わる結果になりました。同じような単純なメソッドだけど結果が変わってくるのが面白く思ったので、その時の結果を記事にします。
- 注意
- この場合はこの書き方が絶対いい!!ということを主張する記事ではないです。
- そもそも上記のような単純なメソッドの場合は、わざわざメソッドを書かずに他の方法が使えるケースも多いと思いますが、今回はその辺りには踏み込まないです。
ケース1: メソッドで処理さえできればいい時
下記のような条件のメソッドの場合は、どのように書くのが読みやすいでしょうか。
メソッドの条件
- メソッドの名前は
delete_user!- メソッド内では変数
userが使える- メソッド内の変数
userがnilではない場合は、userをレシーバーとしてdestroy!メソッドを実行する- メソッドの戻り値は利用しないので気にしなくていい
考えられるバターン
下記の2パターンを作成してみました。
パターン1: 早期リターン
def delete_user! return unless user user.destroy! end「やることないならとにかく早期リターンやろ」という考えを元に作成しました。
パターン2:
&.を使うdef delete_user! user&.destroy! end「1行で書けるんやから
&.を使うべきやろ」という考えを元に作成しました。相談の結果
4人とも、 「パターン2:
&.を使う」 が読みやすいとのことでした。やはり、ここまで単純だとわざわざ早期リターンしてメソッド内を3行にするより、
1行で書けるメリットの方が大きく感じられるようでした。ケース2: メソッドの戻り値が必要な時
下記のような条件のメソッドの場合は、どのように書くのが読みやすいでしょうか。
メソッドの条件
- メソッドの名前は
user_name- メソッド内では変数
userが使えるuserがnilではない場合は、userをレシーバーとしてnameメソッドを実行する- メソッドの戻り値を、呼び出し元で利用する
userがnilの場合は、'no name'という文字列を返却する考えられるバターン
下記の2パターンを作成してみました。
(早期リターンは、ケース1で評判が悪かったので最初から選択肢に入れませんでした)
パターン1: 三項演算子
def user_name user ? user.name : 'no name' end「三項演算子がパッと見で読みやすいかな〜」と思って作成しました。
パターン2:
&.を使うdef user_name user&.name || 'no name' endケース1では全員が読みやすいと考えた
&.を使った書き方です。
三項演算子を使うより短く書けます。
三項演算子が好きではない人向けに作成しました。個人的には、今回の場合は
||を書く必要があるので、
ちょっとパッと見では読みづらい気がしました。結果
投票結果は2対2に割れました。
「うーんどうしたものか。好みの問題かな・・・」と思っていると、
「早期リターンを使った書き方がよいのでは?」との提案をいただきました。def user_name return 'no name' unless user user.name end確かに・・・1番分かりやすい気がする・・・!!
ケース1では不評だった早期リターンが、今回は1番読みやすい気がします。今回のケースの場合は早期リターンを使うと、
「userがnilの場合は'no name'を返却する」
というのが1番パッと見で伝わりやすいし、メソッドの意図が伝わりやすい感じがします。メソッドの戻り値が重要なときは、こちらの書き方を採用しようと思いました。
おわりに
Railsは、いろいろ自由な書き方が出来る分、書き方にこだわったり悩んだりする必要が多いと思います。
今回考えた内容は「好みの問題」で片付けられてしまうことも多いですが、
人にも聞いてみると、「こういうとき、1番読みやすいのはこれじゃないか」という傾向が見えてきて面白かったです。一緒に考えてくれた4名のみなさんありがとうございました
- 投稿日:2020-03-01T23:25:21+09:00
nilで場合分けの必要がある単純なメソッドの書き方
概要
下記のような単純なメソッドがあります。
def user_name user.name end上記メソッド内に登場する
userは、 「Userオブジェクトまたはnilを返却するメソッド」だと仮定してください。
Userオブジェクトにはnameというメソッドが実行できます。
nilの可能性があるので、NoMethodErrorが発生する可能性があります。
なので、 nilの場合の処理 を書く必要があります。どのように書くのが1番読みやすいか悩んでいた時に4人の方に相談したのですが、
メソッドの内容によって、どれを読みやすいと思うかが変わる結果になりました。同じような単純なメソッドだけど結果が変わってくるのが面白く思ったので、その時の結果を記事にします。
- 注意
- この場合はこの書き方が絶対いい!!ということを主張する記事ではないです。
- そもそも上記のような単純なメソッドの場合は、わざわざメソッドを書かずに他の方法が使えるケースも多いと思いますが、今回はその辺りには踏み込まないです。
ケース1: メソッドで処理さえできればいい時
下記のような条件のメソッドの場合は、どのように書くのが読みやすいでしょうか。
メソッドの条件
- メソッドの名前は
delete_user!- メソッド内では変数
userが使える- メソッド内の変数
userがnilではない場合は、userをレシーバーとしてdestroy!メソッドを実行する- メソッドの戻り値は利用しないので気にしなくていい
考えられるパターン
下記の2パターンを作成してみました。
パターン1: 早期リターン
def delete_user! return unless user user.destroy! end「やることないならとにかく早期リターンやろ」という考えを元に作成しました。
パターン2:
&.を使うdef delete_user! user&.destroy! end「1行で書けるんやから
&.を使うべきやろ」という考えを元に作成しました。相談の結果
4人とも、 「パターン2:
&.を使う」 が読みやすいとのことでした。やはり、ここまで単純だとわざわざ早期リターンしてメソッド内を3行にするより、
1行で書けるメリットの方が大きく感じられるようでした。ケース2: メソッドの戻り値が必要な時
下記のような条件のメソッドの場合は、どのように書くのが読みやすいでしょうか。
メソッドの条件
- メソッドの名前は
user_name- メソッド内では変数
userが使えるuserがnilではない場合は、userをレシーバーとしてnameメソッドを実行する- メソッドの戻り値を、呼び出し元で利用する
userがnilの場合は、'no name'という文字列を返す考えられるパターン
下記の2パターンを作成してみました。
(早期リターンは、ケース1で評判が悪かったので最初から選択肢に入れませんでした)
パターン1: 三項演算子
def user_name user ? user.name : 'no name' end「三項演算子がパッと見で読みやすいかな〜」と思って作成しました。
パターン2:
&.を使うdef user_name user&.name || 'no name' endケース1では全員が読みやすいと考えた
&.を使った書き方です。
三項演算子を使うより短く書けます。
三項演算子が好きではない人向けに作成しました。個人的には、今回の場合は
||を書く必要があるので、
ちょっとパッと見では読みづらい気がしました。結果
投票結果は2対2に割れました。
「うーんどうしたものか。好みの問題かな・・・」と思っていると、
「早期リターンを使った書き方がよいのでは?」との提案をいただきました。def user_name return 'no name' unless user user.name end確かに・・・1番分かりやすい気がする・・・!!
ケース1では不評だった早期リターンが、今回は1番読みやすい気がします。今回のケースの場合は早期リターンを使うと、
「userがnilの場合は'no name'を返す」
というのが1番パッと見で伝わりやすいし、メソッドの意図が伝わりやすい感じがします。メソッドの戻り値が重要なときは、こちらの書き方を採用しようと思いました。
おわりに
Railsは、いろいろ自由な書き方が出来る分、書き方にこだわったり悩んだりする必要が多いと思います。
今回考えた内容は「好みの問題」で片付けられてしまうことも多いですが、
人にも聞いてみると、「こういうとき、1番読みやすいのはこれじゃないか」という傾向が見えてきて面白かったです。一緒に考えてくれた4名のみなさんありがとうございました
- 投稿日:2020-03-01T23:23:01+09:00
Ruby復習
Uncaught TypeError: Cannot read property 'reset' of undefined
エラーの原因が $('from')[0].reset;なのはわかっているのだが、なぜ、どういう原因なのかかが全くわからない為、明日聞く。カリキュラムが進まない為、Rubyの復習をやった所、初めてみた時と全然違う!!
書いてあることがわかることが多いい。
変数、引数、配列演算子、配列オブジェクト、式展開なんとなくだったことがわかった気がする。
今更だけど、移動時間で読み返そう!!
- 投稿日:2020-03-01T23:21:48+09:00
三項演算子
- 投稿日:2020-03-01T23:09:54+09:00
リファクタリング練習
リファクタリング
リファクタリングは単にコードを読みやすくするだけではない
チーム開発において見やすいコードとは非常に重要で、チーム開発では他人のコードを解読する時間は非常に多くなる
その時間を短くすることは開発効率の促進に繋がるarray = [1, 2, 3, 4, 5].map do |el| if el.odd? el end end.compact!このプログラムは配列に格納されている数字から奇数のみを取り出すプログラムです。
これをワンライナーで書き直します。array = [1, 2, 3, 4, 5].map { |el| el if el.odd? }.compact! array = (1..5).to_a.delete_if { |el| el.even? } array = (1..5).to_a.delete_if(&:even?) array = [1, 2, 3, 4, 5].select{ |el| el.odd?}mapメソッドは配列から値を一つずつ取り出して変数に格納するメソッド
odd?は奇数を判別するメソッド
delete_ifは条件に合わない値を削除するメソッド
to_aメソッドは対象を配列としてまとめて返すメソッド
even?は偶数を判別するメソッド
- 投稿日:2020-03-01T22:56:03+09:00
配列の中にハッシュがある場合の取り出し
配列の中にハッシュ
以下の配列からnameの値を取り出して取得する
user_data = [ { user: { profile: { name: 'Tanaka' } } }, { user: { profile: { name: 'Sato' } } }, { user: { profile: { name: 'Saito' } } } ] user_data.each do |u| puts u[:user][:profile][:name] endeachでuに値を入れて出力
user_data.each { |u| puts u.dig(:user, profile, name)}でも出力することができる
digメソッドは引数で渡したキーから値を取り出すことができる
- 投稿日:2020-03-01T22:45:44+09:00
引数の種類
引数の種類
一言に引数といっても様々な種類の引数が存在します。
その中から3種類について書きますデフォルト引数
代入するオブジェクトがなかった場合はデフォルトで指定した値が代わりに代入される。
記述は以下def initialize(name="Tanaka") end可変長引数
可変長引数は引数の数に制限を設けていない引数のこと。
その引数は配列として認識される。
記述は以下def initialize(*name) endキーワード引数
引数にキーを設定して置くことで、誤った値が代入されないように設定することができる。
キーに値が入っていないと
ArgumentErrorが発生するdef initialize(name: "Taro") end引数の特性について知ることで値を扱いやすくなりますね
- 投稿日:2020-03-01T22:12:13+09:00
何でbelongs_to :userになるか?(忘備録)
はじめに
「ActiveRecord::AssociationNotFoundError in Tweet#index」エラー向けの記事になります。
エラー勉強会の復習した時に、疑問に思ったのでアウトプットしてみました。
Associationと出てる時点で、DBの問題であると考える事ができますが、今回は掘り下げてみたいと思います。エラーの意味
エラーの通りで、tweet_controllerのindexアクションでAssociationが見つからないエラーです。
Associationとは
DBに構築されているテーブルの関連付ける事を指しています。
今回は、下図のようなAssciationを組んでいる時に起こるエラーとなります。
まずは、tweet_controllerのindexアクションをみてみます。
app/controllers/tweet_controller.rb
記述に問題はありませんが、SQLを読み込むincludeメソッドにエラーが起きている事が分かります。
:userはUserモデルの事を指しているので、 Userモデルを見てみます。記述に問題はなさそうです。
has_manyの復習すると、 has_many モデル名(複数) という記述をします。
userはtweetとcommentの1対多の関係になります。そうすると、他のtweetモデルかcommentモデルに問題があると考えます。
ようやくエラー元を見つけました。 belongs_to :usersが悪さをしていました。
belongs_to モデル名(単数) と定義されます。
ここで :users ⇨ :user に直すとエラーが解消されます。tweetモデルとuserモデルが、associationができていなかった為にエラーが出てたようです。
参照文献:railsガイド
- 投稿日:2020-03-01T21:31:57+09:00
【初心者向け】AWS EC2デプロイ時にでるエラーを解決する
自作したアプリケーションをAWS上にデプロイする際に、いくつかエラーが出てつまづいたので、どうやって解決したかをまとめ的な感じでつらつらと書いていきます。
各エラーの内容にはそこまで踏み込まず、解決するという部分にフォーカスしてます。なお、デプロイ時は、下記のQiita記事を参考に行いました。
題の通り、世界一丁寧でした。。。誠に有難うございます。。。世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
環境
macOS:10.14.6
Ruby:2.5.7
Rails:5.2.4.1①ssh: connect to host [作成したEC2のElasticIP] port 22: Operation timed out
これは、EC2インスタンスにsshログインした時に出たエラー。
$ ssh -i [app名].pem ec2-user@[作成したEC2のElasticIP] ssh: connect to host [作成したEC2のElasticIP] port 22: Operation timed out解決方法
1.AWSコンソールに入り、AWSのサービスからEC2を選択
2.左のメニューバーからセキュリティグループを選択
3.インバウンドを選択
4.編集
5.タイプ:SSH、ソース:マイIPのルールを追加
6.保存これでsshログインできるようになります。
ちなみに自分のIPアドレスはwi-fi環境により異なったりするので注意が必要です。②ERROR: Ruby install aborted due to missing extensions
これは、EC2インスタンスにrubyをインストールする際に出たエラー。
$ rbenv install -v 2.5.7 ~ #省略 ~ BUILD FAILED (Amazon Linux AMI 2018.03 using ruby-build 20200224) Inspect or clean up the working tree at /tmp/ruby-build.20200229053400.25770.gpU4NA Results logged to /tmp/ruby-build.20200229053400.25770.log Last 10 log lines: installing capi-docs: /home/irikawa/.rbenv/versions/2.5.7/share/doc/ruby The Ruby readline extension was not compiled. ERROR: Ruby install aborted due to missing extensions Try running `yum install -y readline-devel` to fetch missing dependencies.解決方法
これはエラー文に書かれている以下のコマンドを実行し、
$ sudo yum install -y readline-develその後、rubyをインストール
$ rbenv install -v 2.5.7 ~ #省略 ~ Installed ruby-2.5.7 to /home/user/.rbenv/versions/2.5.7無事入りました。
③EC2インスタンスにmaster.keyを作成する
これは特別エラーが出たわけではないのですが、考え方の整理として。
rails5.2以上では、rails newをしたタイミングで、「credentials.yml.enc」と「master.key」が生成されます。
それぞれの役割は、超ざっくり説明すると、「credentials.yml.enc」が鍵穴で、「master.key」が鍵という位置付けになり、「credentials.yml.enc」を複合する際には「master.key」が必要になるとのこと。ローカル環境で開発している段階では特別意識しなくても良いのですが、EC2にあげるタイミングで.gitignoreに書かれている「master.key」はEC2側には渡ってくれません。
なので、以下の方法でEC2側に「master.key」を手動で作成してあげる必要があります。
まずはローカル環境でmaster.keyの中身をコピーします。
以下のコマンドを打って出てくる英数字の暗号みたいなものをコピーする。local.~/myapp$ vi config/master.key次にEC2側にmaster.keyを以下のコマンドで作成し、先程コピーしたものをペーストします。
server.[user|myapp]$ vim config/master.key保存して終了。これでOKです。
④bundle install時、mysqlが入らないエラー:Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
EC2インスタンス内でbundle install時に以下のエラー。主要部分のみ抜粋してます。
server.[user|myapp]$ bundle install ~ #省略 ~ Gem::Ext::BuildError: ERROR: Failed to build gem native extension. ----- mysql client is missing. You may need to 'sudo apt-get install libmariadb-dev', 'sudo apt-get install libmysqlclient-dev' or 'sudo yum install mysql-devel', and try again. ----- An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.mysqlクライアントがないというエラーみたいですね。
エラー文に書いている以下のコマンドを実行。
server.[user|myapp]$ sudo yum install mysql-develその後、bundle installしたら無事mysqlが入りました。
一旦、以上になります。
今後随時更新していきます。
- 投稿日:2020-03-01T21:19:17+09:00
特定パスのファイルをまとめて require するワンライナー
1. はじめに
はじめに結論!
Dir.glob(Rails.root.join('lib', 'tasks', 'module', '*')).each(&method(:require))2. そもそも
Array#mapなどに、ブロックではなく、&:メソッド名を渡すショートハンドは、Ruby に慣れた方なら割と使っているかもしれませんこういうやつirb(main):001:0> [1, 2, 3].map(&:to_s) => ["1", "2", "3"]ところで、この配列の要素を、レシーバー側ではなく、引数側に取りたい場合は、
#methodメソッドを用いて以下のような書き方ができますこんな感じirb(main):002:0> [1, 2, 3].map(&2.method(:eql?)) => [false, true, false]ただこの書き方は、ややテクニカル過ぎてかえってコードが読みづらくなるため、あまり使うことはないだろうと思っていました
3. 何があったのか
ところが、仕事が4つも5つもある、妙に多機能な rake task スクリプトを書かされる悲しい事件が起きたとき
、スクリプトを短く保つために機能ごとに module を分割することにしたのですが、module が増えるたびに rake ファイルに require 行を増やすのが美しくなかったため、読み込みを自動化できるようワンライナーで書いたのが冒頭のコードとなります
4. まとめ
#requireならレシーバーも省略できてややスッキリ書けますし、記述するのはファイルの冒頭なので、お決まりの書き方的な感じで、他のコーダーにも受け入れてもらいやすいかもしれません![]()
みなさんもぜひ使ってみてくださいね
![]()
これらのショートハンドが動作する仕組みについては、以下の記事が詳しいです
参考: &演算子と、procと、Object#method について理解しなおす
- 投稿日:2020-03-01T20:36:14+09:00
【Rails】5ステップでイケてる enum を作る(翻訳)
※ この記事は Ruby on Rails - How to Create Perfect Enum in 5 Steps を許可をとって翻訳したものです。日本語で読みやすいように一部意訳しております。
はじめに
エンジニアの皆さんなら客先からの仕様変更なんてものは日常茶飯事でしょう。度々来る仕様変更に対して柔軟に対応できるモデルを設計する必要があります。大抵のモデルはその属性を表すカラムを持っているでしょう。例えば宅配モデルなら「通常配送」「お急ぎ便」「時間指定」などの属性を持つことが想定されます。このような属性を定義する方法の一つが列挙型、すなわち enum です。
Rails では4.1以降で enum をサポートしています。
この記事は下記のような構成になっています。
- 基本的な使い方 ー
ActiveRecord::Enumを出来るだけ簡単に導入する- 5ステップで enum を改善する
- 5ステップ全部載せ
わかりやすくするために実際の例を用いていきます。ここでは作品モデル
Artworksとそれを収録するカタログモデルCatalogsを考えます。また、Catalogsが持つ属性の中でも以下の4つを扱うことにしてみます。
state: ["incoming", "in_progress", "finished"]auction_type: ["traditional", "live", "internet"]status: ["published", "unpublished", "not_set"]localization: ["home", "foreign", "none"]基本的な使い方
既存のモデルに enum を追加するのは簡単です。まず最初にマイグレーションファイルを作成します。Rails では enum を用いるのに integer 型のカラムを定義します。
rails g migration add_status_to_catalogs status:integerclass AddStatusToCatalogs < ActiveRecord::Migration[5.1] def change add_column :catalogs, :status, :integer end endモデルに enum を宣言します。
class Catalog < ActiveRecord::Base enum status: [:published, :unpublished, :not_set] endそして、マイグレーションを実行しましょう。これで便利な追加メソッドをたくさん使えるようになりました。
例えば、次のようにして現在のステータスをチェックすることができます。
catalog.published? # false属性の値の変更は次のようにします。
catalog.status = "published" # published catalog.published! # published値が published のカタログ一覧は次のようにすれば取得できます。
Catalog.published全てのメソッドを見るには ActiveRecord::Enum を参照してください。
以上の方法は非常にシンプルで便利ですが、プロジェクトが進むといくつかの問題に直面するでしょう。備えあれば憂いなし、いくつかの準備をすることでメンテナンス性の高い enum を作ることができます。
5ステップで enum を改善する
STEP1 enum を配列ではなくハッシュで定義する
enum を配列で定義すると、DBで保持される整数値は、配列の順番に依存します。
class Catalog < ActiveRecord::Base enum localization: [:home, :foreign, :none] end0 -> home 1 -> foreign 2 -> noneこの方法は柔軟性が全くありません。例えば、 "foreign" が "America" と "Asia" に分割された時、古い値を削除して新しい値を追加する必要があります。しかし、このケースでは "foreign" を削除すると "foreign" に割り当てられていた整数値 1 が他の属性に割り当てられ、過去の紐付けとの食い違いが発生してしまいます。
ハッシュで定義することでこの問題を回避することができます。
class Catalog < ActiveRecord::Base enum localization: { home: 0, foreign: 1, none: 2 } endこの方法なら宣言の順番に依存することなく整数値を割り当てることができるため、属性の削除や追加が可能になります。
STEP2 ActiveRecord::Enum と PostgreSQL enum を紐付ける
rails 側で属性と整数値を紐付けた enum を定義した時、DB側が保持するのは整数値のみです。当然、整数値自体は意味を持たない値なのでDB上の値を見ても「1」が何を指しているのかは分かりません。
rails consoleで次のように where メソッドを使うとエラーになってしまいます。> Catalog.where.not(“state = ?”, “finished”) ActiveRecord::StatementInvalid: PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: "finished"このエラーはDB側では "finished" という値ではなく整数値を保持しているために起こってしまいます。
ActiveRecordを介さない SQL クエリを実行するときにも同様の問題が発生します。単純にDBに保存されている情報を見ようとしただけでも、どの整数値がどの属性に対応するかを逐一確認する必要があるため、非常に手間がかかります。このように、整数型の enum を用いることで情報が失われてしまうことを理解しておく必要があります。
さらに、データの安全性に関する問題もあります。例えば、DB側では整数値であれば insert 可能ですので、enum で宣言した値以外の値が insert されてしまうことも起こり得ます。この問題は
PostgreSQL enumによりデータベースレベルで制約を設けることで解決可能です。どの程度の信頼性を確保するかは設計者自身が決める必要があります。PostgreSQL は Ruby on Rail のプロダクトで標準的に使用されているデータベースです。
PostgreSQLはテーブルの中で属性値を扱うのに適しています。それでは、早速実装してみましょう。
rails g migration add_status_to_catalogs status:catalog_statusデータ型を変更する時、 "status" のような命名はおすすめできません。近い将来別の "status" という属性が必要になる可能性が大いにあるからです。
次は、マイグレーションファイルを編集します。マイグレーションファイルは基本的に可逆的で SQL を実行できる必要があります。
class AddStatusToCatalogs < ActiveRecord::Migration[5.1] def up execute <<-SQL CREATE TYPE catalog_status AS ENUM ('published', 'unpublished', 'not_set'); SQL add_column :catalogs, :status, :catalog_status end def down remove_column :catalogs, :status execute <<-SQL DROP TYPE catalog_status; SQL end endenum の宣言部分は前回のものに少し変更が必要です。
class Catalog < ActiveRecord::Base enum status: { published: "published", unpublished: "unpublished", not_set: "not_set" } endSTEP3 index を enum で定義した属性に追加する
この変更はシンプルなものです。enum 属性はモデル内の特定のオブジェクトを抽出するときによく使われます。例えば、カタログモデルの中で "published" のものと そうでないものをリスト化する時などです。このようなフィルタリングの処理は非常に頻繁に行われるので index を追加しておくことはパフォーマンスの工場に繋がります。
次のようにマイグレーションファイルを修正しましょう。
class Catalog < ActiveRecord::Base enum status: { published: "published", unpublished: "unpublished", not_set: "not_set" } endSTEP4 prefix または suffix オプションを使う
今一度
Catalogモデルを見てみましょう。
state: ["incoming", "in_progress", "finished"]auction_type: ["traditional", "live", "internet"]status: ["published", "unpublished", "not_set"]localization: ["home", "foreign", "none"]prefix または suffix は次のようにして enum に追加します。
class Catalog < ActiveRecord::Base enum status: { published: "published", unpublished: "unpublished", not_set: "not_set" }, _prefix: :status enum auction_type: { traditional: "traditional", live: "live", internet: "internet" }, _suffix: true endなぜこれが役立つのでしょうか。
Catalogモデルを見てみると、4つの enum と 12 の属性値を持っていることが分かります。すなわち、12のスコープを持つことになり、直感的には非常に分かりづらいものになっています。Catalog.not_set Catalog.live Catalog.unpublished Catalog.in_progress上記のメソッドがどんな値が返すかすぐ答えるためには、全ての enum 属性をを常に覚えておく必要があります。とても大変なことです。
Catalog.status_not_set Catalog.live_auction_type Catalog.status_unpublished Catalog.state_in_progressだいぶ分かりやすくなりました。
ここでもう一つ
Catalogに enum を加えることになったとしましょう。グローバルカタログ内の各カタログの順序に関する情報を保持する enum です。一部のカタログの順序は指定されていない場合があります。最も重要なのは、どのカタログが最初でどれが最後かが分かることです。次のように作成します。class Catalog < ActiveRecord::Base enum order: { first: "first", last: "last", other: "other", none: "none" } endでは、
rails consoleで作成した enum をチェックしてみましょう。> Catalog.order ArgumentError: You tried to define an enum named "order" on the model "Catalog", but this will generate a class method "first", which is already defined by Active Record."first" は既に ActiveRecord で定義されているというエラーが表示されました。そこで次のように修正します。
class Catalog < ActiveRecord::Base enum order: { first_catalog: "first_catalog", last_catalog: "last_catalog", other: "other", none: "none" } end再度チェックしてみます。
> Catalog.order ArgumentError (You tried to define an enum named "order" on the model "Catalog", but this will generate an instance method "none?", which is already defined by another enum.)先程と違うエラーです。"none" が別の enum で使われていることを指摘されています。
prefix または suffixs オプションはこの問題を解決するのに最適です。"first" "last" のような属性値自体はシンプルなまま残すことができます。また、スコープは直感的で分かりやすいものとなります。変更後のコードは次のようになります。
class Catalog < ActiveRecord::Base enum order: { first: "first", last: "last", other: "other", none: "none" }, _prefix: :order endSTEP5 enum を Value Object として切り出す
次のような状態の場合は enum 属性をValue Object として切り出すことを推奨します。
- enum 属性が2つ以上のモデルで使われている場合
- enum 属性がモデルを複雑にする特定のロジックを持っている場合
それでは例を用いて説明します。我々のプロジェクトではアートワークを販売するオークションハウスを全国に配置しています。ポーランドは voivodeships(日本で言うところの県)と呼ばれる16の地域に分かれています。各
AuctionHouseモデルはVoivodeship属性を含むAddressモデルを持っています。なんらかの理由で下記のメソッドを実装することになったとします。
- 北部のオークションハウスをリスト化するメソッド
- 人口の多いいくつかの県のオークションハウスをリスト化するメソッド
これらのメソッドを
Addressモデルに実装すると、モデルが肥大化してしまいます。そこで、別のクラスに切り出すことで再利用可能かつよりクリーンな状態を実現します。class Voivodeship VOIVODESHIPS = %w(dolnoslaskie kujawsko-pomorskie lubelskie lubuskie lodzkie malopolskie mazowieckie opolskie podkarpackie podlaskie pomorskie slaskie swietokrzyskie warminsko-mazurskie wielkopolskie zachodnio-pomorskie).freeze NORTHERN_VOIVODESHIPS = %w(warminsko-mazurskie pomorskie zachodnio-pomorskie podlaskie).freeze MOST_POPULAR_VOIVODESHIPS = %w(dolnoslaskie mazowieckie slaskie malopolskie).freeze def initialize(voivodeship) @voivodeship = voivodeship end def northern? NORTHERN_VOIVODESHIPS.include? @voivodeship end def popular? MOST_POPULAR_VOIVODESHIPS.include? @voivodeship end def eql?(other) to_s.eql?(other.to_s) end def to_s @voivodeship.to_s end end次に
Addressモデルから切り出したVoivodeshipを呼び出す部分を記述します。array_to_enum_hashは配列で定義された enum をハッシュに変換するメソッドです。class Address < ApplicationRecord enum voivodeship: array_to_enum_hash(Voivodeship::VOIVODESHIPS), _sufix: true def voivodeship @voivodeship ||= Voivodeship.new(read_attribute(:voivodeship)) end endこれで
Voivodeshipsに関連するロジック全体が単一のクラスにカプセル化されました。必要に応じて拡張可能で、Addressが肥大化することもありません。voivodeships 属性を取得したいときは、
Voivodeshipsクラスが返されます。これはまさに Value Object です。※Value Object はデザインパターンの一つです。こちら等が参考になります。
voivodeship_a = Address.first.voivodeship # #<Voivodeship:0x000000000651eef0 @voivodeship="pomorskie"> voivodeship_b = Address.second.voivodeship # #<Voivodeship:0x00000000064e9cf0 @voivodeship="pomorskie"> voivodeship_c = Address.third.voivodeship # #<Voivodeship:0x000000000641ef00 @voivodeship="lodzkie">voivodeship_a と voivodeship_b は同じ voivodeship の値を持っていますが、オブジェクトとしてはイコールではありません。幸いなことに、我々が作ったメソッドは値が等しいかをチェックすることができます。
voivodeship_a.eql? voivodeship_b # true voivodeship_a.eql? voivodeship_c # falseさらに、先程定義したメソッドを使用して次のように記述できるのも非常に強力なメリットです。
voivodeship_a.northern? # true voivodeship_a.popular? # false voivodeship_c.northern? # false voivodeship_c.popular? # false5ステップ全部載せ
ここまで5ステップに渡って enum の改善方法を示してきました。それでは、ここまでの振り返りとして、これら全てを実装した究極の enum を作っていきましょう。例として、
Catalogモデルのstatus属性を考えます。マイグレーションファイルの作成
rails g migration add_status_to_catalogs status:catalog_statusマイグレーションファイルの編集
class AddStatusToCatalogs < ActiveRecord::Migration[5.1] def up execute <<-SQL CREATE TYPE catalog_status AS ENUM ('published', 'unpublished', 'not_set'); SQL add_column :catalogs, :status, :catalog_status add_index :catalogs, :status end def down remove_column :catalogs, :status execute <<-SQL DROP TYPE catalog_status; SQL end endValue Object の作成
class CatalogStatus STATUSES = %w(published unpublished not_set).freeze def initialize(status) @status = status end # what you need here endCatalog モデルと enum の定義
class Catalog enum status: array_to_enum_hash(CatalogStatus::STATUSES), _sufix: true def status @status ||= CatalogStatus.new(read_attribute(:status)) end end結論
以上がイケてる enum を作る5つのステップです。
これら全てが必要になることもあるし、一部だけ使うこともあります。自分のプロジェクトのニーズに合わせて調整してください。
最後に、この記事が誰かの訳に立つことを願っています。より良い改善方法があればコメントをよろしくお願いします。
この記事について
冒頭でも述べたとおり、下記を翻訳したものです。
Ruby on Rails - How to Create Perfect Enum in 5 Steps著者に掲載の許可を取って公開しています。
enum 以外にもマイグレーションの可逆性、Value Objectによるリファクタリングなど、多くの学びがある非常に良質な内容だと感じました。また、日本語で同様の情報を見つけることができなかったので自分で翻訳してみました。より良い改善方法があればこちらでもコメントしてもらえると助かります。
最後までご覧いただきありがとうございました。
- 投稿日:2020-03-01T20:29:46+09:00
Ruby/GTK3 - 表示するだけのTreeView
gem install gtk3なるべくシンプルにしてみたらこんな感じになった。
改良の余地があるはず。require 'gtk3' require 'csv' require 'open-uri' url = "https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv" iris = CSV.parse(URI.open(url)) ls = Gtk::ListStore.new(*([String] * 5)) mf = ls.create_filter treeview = Gtk::TreeView.new(mf) iris.shift.each_with_index do |header, i| column = Gtk::TreeViewColumn.new(header, Gtk::CellRendererText.new, text: i) treeview.append_column(column) end iris.each do |val| iter = ls.append iter.values = val end win = Gtk::Window.new win.title = 'Gtk::TreeModelFilter sample' win.set_size_request 500, 400 sw = Gtk::ScrolledWindow.new sw.add_with_viewport(treeview) win.add sw win.signal_connect('destroy') { Gtk.main_quit } win.show_all Gtk.main
- 投稿日:2020-03-01T20:06:49+09:00
Debianシステム全体にrbenvを導入する
Debianシステム全体にrbenvを導入する
概要
Windows10のWSL2上に構築したDebianに
rbenvを導入したときのメモ。
インストールマニュアル通りの手順を行うと、ホームディレクトリ配下にRubyバイナリが配置される。しかし個人的にホームディレクトリMac-Windows間でクラウド同期しているので、バイナリをホーム配下に配置したくなかった。
そこで/usr/local/lib配下にrbenvをインストールした。環境
- OS: Debian(WSL2で構築)
- Shell: zsh
手順
- githubより
rbenvをclonesudo git clone https://github.com/rbenv/rbenv.git /usr/local/lib/rbenv
- またgithubより
ruby-buildをclonesudo git clone https://github.com/rbenv/rbenv.git /usr/local/lib/rbenv/plugins/ruby-build
.zshrcに環境変数を設定する.zshrcexport RBENV_ROOT=/usr/local/lib/rbenv export PATH=${RBENV_ROOT}/bin:${PATH}
.zshrcを再読み込みsource .zshrc
- Rubyをインストールする(執筆時点で最新の2.7.0の場合)
sudo -E /usr/local/lib/rbenv/bin/rbenv install -v 2.7.0インストールが完了すると
Downloading ruby-2.7.0.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0.tar.bz2 Installing ruby-2.7.0... Installed ruby-2.7.0 to /usr/local/lib/rbenv/versions/2.7.0しっかり
/usr/local/lib配下にインストールされている
- バージョン設定
sudo -E /usr/local/lib/rbenv/bin/rbenv global 2.7.0
- 初期化する
sudo -E /usr/local/lib/rbenv/bin/rbenv rehashさいごに
インストールマニュアルでは、
.zshrcにeval "$(rbenv init -)"を追記するように記述があり、ログインごとにrehashする設定にしているが、この手順はインストール先がホームディレクトリ配下でないためeval "$(rbenv init -)"を実行するのにsudoが必要となる。このため.zshrcには記述しない。よって、新バージョンをインストールした際など必要に応じて手動でrehashする。
- 投稿日:2020-03-01T19:32:59+09:00
bootstrap4導入
注意点 gemfileに記入するgemを
bootstrapにする。bootstrap-sassはbootstrap2,3に対応していたらしいこれにより、.scssファイルの
@import bootstrap-sprocketsを削除する。
また次のようなエラーが出るので、gemfilegem 'sqlite3', '~> 1.3.6'としてsqliteのバージョンを管理する。
gem変更の際は一度railsを再起動しなければgemが更新されないことを忘れずに
- 投稿日:2020-03-01T19:31:19+09:00
実務3ヶ月で学んだ開発便利ツールを紹介する
はじめに
ふとしたことからプログラミングに出会い、異業種から転職して早3ヶ月が経過しました。
先輩方の開発姿を観察していると、何やら 知らないコマンドやツール を使い倒していました。
尋ねてみたところ、全く知らなかった開発便利ツールがゴロゴロと出てきました!
それら現場で学んだ、 開発がグッと便利になる!! ツールたちの一部を紹介します。対象者
- プログラミング初学者
- 開発効率化にあまり励んでこなかった方
※効率化好きの先輩方、他の開発便利ツール教えてください
![]()
筆者
実務経験
- 約3ヶ月
使用言語
- Ruby / ROR / JS / React
開発環境
- MacOS Catalina 10.15.3
- VSCode 1.42.1
- tmux 2.9 ← 後ほど紹介
さっそく紹介していこう
1. VSCode設定 Rubyでメソッド定義元にジャンプする
Rubyのメソッド定義に素早くジャンプする為の設定です。(ClassやModuleもジャンプ可)
RubyMineにはデフォルトで備わっている機能ですが、VScodeでも簡単な設定で使用できます。
VSCode拡張機能のRubyの中の設定を少し触るだけです!タスクバーからVScodeの[基本設定]=>[設定]をクリック。
画面上部[設定の検索]に「Ruby」と入力します。
Ruby:Intellsense の設定を[false]から[rubyLocate]に変更して設定完了です。引用元: 【Visual Studio Code】Rubyで開発時にメソッドの定義に移動する方法【簡単です】
2. Clipy
クリップボードへのコピー履歴を最大50まで残せて、さっと呼び出せる便利ツール
カスタマイズも出来て、スニペットも保存できます。コピペッパー君には手放せないツールです
Before) コピー → 貼り付け → コピー → 貼り付け → コピー → 貼り付け → コピー → 貼り付け
After) コピー → コピー → コピー → コピー → 貼り付け →貼り付け →貼り付け → 貼り付け
↓↓コピー連打 選択貼り付け
公式: https://clipy-app.com/
Github: https://github.com/Clipy/Clipy/
参考: クリップボード拡張Macアプリ「Clipy」を公開しました3. tmux
端末多重化ソフトウェア
一つのターミナル上に複数のターミナルを立ち上げて並行作業が可能
画面分割、キーバインド、その他カスタマイズが出来ます。.tmux.confに設定を突っ込み、
オラオラ仕様にして純粋にカッコイイ〜〜テンションあがる〜〜
Github: https://github.com/tmux/tmux
参考: tmuxを必要最低限で入門して使う4. tldr
CLIコマンドのオプションの付け方どうだったかな〜〜〜???
エンジニアにとって頻発する悩みではないでしょうか。manやhelpでコマンドを調べようと頑張っても長ったらい出力が返ってきて、
目的のオプションを探し当てるために相当時間が掛かってしまった。。。そんなあなたの悩みを解決してくれるかもしれない便利ツールです。
実用的なコマンドに焦点を当ててマニュアルを教えてくれます。
↓↓ man git から tldr git / tldr git add / tldr git commit
公式: https://tldr.sh/
Github: https://github.com/tldr-pages/tldr5. Gyazo
Gyazoは、デスクトップ上で撮ったキャプチャ画像やGIF動画を、パソコン越しの相手とその場で共有できるツールです。パソコンを使ったチャットや音声通話では、その場で画像を共有しながら会話・会議・打ち合わせを行うことで意思疎通がよりスムーズになります。
Gyazoは、撮った画像やGIF動画をPCに保存することなく、誰でも簡単かつリアルタイムに相手と画像共有できるのが特徴です。引用元: Gyazoとは?撮った画像をその場で相手と共有できるツールの使い方を解説
スクリーンショットを撮って、加工して、テキスト入れて、添付して・・・
これらの手間を省略してくれる優れもの。GIFも撮れます!!
また、Google Chrome拡張機能を使うと一層便利です。
公式: https://gyazo.com/tour?lang=ja6.BetterTouchTool
皆さんご存知BTTを紹介します!!
それ開発便利ツール??そんなん既知過ぎるわ!!
という方もいらっしゃると思いますが、かまわず紹介します!キーボード、トラックパッド、マウス・・・
あらゆる操作にショートカットが設定できる神ツールです!!(少額ですが有料です)例えば??が私のGoogle Chrome上でのトラックパッド設定
公式: https://folivora.ai/
参考: 【Macユーザー必見】Better Touch Toolのおすすめの設定7. VSCodevim
初めてVimmerメンターの開発画面を見た時「なんだこれは・・・」という衝撃を受けました。
キーバインドを使いこなして思考スピードでコードを書いていました...「自分もあんな風になりたい」とは思うものの、
Vimは癖が強く(個人的見解です、Vimmerへの敬意です)学習コストを高く感じていました。
そこで、VSCodevimたるものの存在を教えて貰い使ってみたところ、イケてます!!もちろん、好みのキーバインドを設定することができます。
VSCodeの慣れ親しんだショートカットキーは残しつつ徐々にVimを取り入れてる状態です。
INSERTとNORMALモードを日々往復しながら習得に向けて頑張っています。
Github: https://github.com/VSCodeVim/Vim
参考: 割と突き詰めてやったvim→vscode移行終わりに
もっともっとエンジニアリングを快適に効率よく進める為に
開発便利ツールとの出会いを楽しんでいきたいと思います!!紹介させて頂いた開発便利ツールの開発者様、
それらの使い方を分かりやすくQiitaなどで紹介頂いた投稿者様本当に本当に感謝しております!!
参考
- 投稿日:2020-03-01T19:03:21+09:00
chartkickに計算したデータを渡す
chartkickに計算したデータを渡す
参考にした記事
https://qiita.com/withelmo/items/1eb02f784eea414fc723
グラフを作ってくれるgem chartkick に同じハッシュ形式のデータ同士を計算して渡したかった。
ハッシュに対してmergeを使うことで解決しました。こんな感じのデータと
@group_month_income_sum = current_user.accounts.where(income_check: true).group_by_month(:date).sum(:income) @group_month_income_sum => {Wed, 01 Jan 2020=>2917931, Sat, 01 Feb 2020=>996911, Sun, 01 Mar 2020=>1078732, Wed, 01 Apr 2020=>891573, Fri, 01 May 2020=>345790, Mon, 01 Jun 2020=>979213, Wed, 01 Jul 2020=>62281, Sat, 01 Aug 2020=>1660182, Tue, 01 Sep 2020=>155942, Thu, 01 Oct 2020=>64652, Sun, 01 Nov 2020=>926668, Tue, 01 Dec 2020=>895074}こんな感じのデータ(keyは同じだけどvalueの中の数字が違う)を計算してchartkickに渡したかった。
@group_month_spend_sum = current_user.accounts.where(spend_check: true).group_by_month(:date).sum(:spend) @group_month_spend_sum => {Wed, 01 Jan 2020=>323982, Sat, 01 Feb 2020=>685464, Sun, 01 Mar 2020=>47592, Wed, 01 Apr 2020=>1693776, Fri, 01 May 2020=>465287, Mon, 01 Jun 2020=>1445360, Wed, 01 Jul 2020=>1288774, Sat, 01 Aug 2020=>1804126, Tue, 01 Sep 2020=>1200592, Thu, 01 Oct 2020=>1660155, Sun, 01 Nov 2020=>727937, Tue, 01 Dec 2020=>287939}mergeメソッドをつかって解決しました。
@group_month_total = @group_month_income_sum.merge(@group_month_spend_sum){|k, v1, v2| v1 - v2} @group_month_total => {Wed, 01 Jan 2020=>2593949, Sat, 01 Feb 2020=>311447, Sun, 01 Mar 2020=>1031140, Wed, 01 Apr 2020=>-802203, Fri, 01 May 2020=>-119497, Mon, 01 Jun 2020=>-466147, Wed, 01 Jul 2020=>-1226493, Sat, 01 Aug 2020=>-143944, Tue, 01 Sep 2020=>-1044650, Thu, 01 Oct 2020=>-1595503, Sun, 01 Nov 2020=>198731, Tue, 01 Dec 2020=>607135}chartkickのコード
<%= line_chart [ { name: "収入", data: @group_month_income_sum, curve: false }, { name: "支出", data: @group_month_spend_sum, curve: false }, { name: "収支", data: @group_month_total, curve: false } ], colors: ["blue", "red", "green"], thousands: ",", messages: {empty: "データが登録されていません"} %>実際のグラフ
- 投稿日:2020-03-01T18:21:28+09:00
Rails 5.2~ credentials.yml.enc/master.key の扱いについて(備忘録)
credentials.yml.enc / master.keyとは何か
Rails 5.2 ~ からはsecret.ymlが廃止され、credentials.yml.enc / master.keyが導入されました。credentials.ymlはmaster.keyによって暗号化、復号化されます。secret.ymlは暗号化されておりませんでしたので、セキュリティ的にはより強固なものとなったようです。
特徴
- Rails 5.2 ~
- credentials.yml.encはmaster.keyとペアであり、master.keyによって暗号化されている。
- credentials.yml.enc, master.key共にデフォルトでgitignoreに追加されている。
編集及び取得方法
編集方法
$ cd [アプリケーションのディレクトリ] $ sudo EDITOR=vim bin/rails credentials:edit初めてこのコマンドを叩くとcredentials.yml.encとmaster.keyのペアが自動生成されます。
編集コマンドを叩いて次のような画面が出てきたらOK# aws: # access_key_id: 123 # secret_access_key: 345 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: *******************************************************ここにAWSやその他のサービスを使う際に必要となるaccess_key_id, secret_access_keyの情報を入力する。
aws: access_key_id: *** secret_access_key: *** # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: *******************************************************取得方法
credentials.yml.encに書き込んだ情報はmaster.keyによって復号化され取得されます。その際の取り出し方について。
Rails.application.credentials.***例 AWSの場合
Rails.application.credentials.aws[:access_key_id] # アクセスキーID Rails.application.credentials.aws[:secret_access_key] # シークレットアクセスキー実際に取得できるか試してみる
credentials.yml.encに入力した情報の取得が成功しているかを判定する際に非常に便利なコマンドがあります。
$ rails c $ [1] pry(main) > Rails.application.credentials.aws $ => { :access_key_id=>"123", :secret_access_key=>"456" }このように表示されていればOK!
チーム開発におけるcredentials.yml.encとmaster.keyの扱いについて
チーム開発においては、master.keyを信頼できる開発メンバーにファイル共有ソフト等を用いて共有します。credentials.yml.encは本番環境にデプロイしてもmaster.keyによって暗号化されているため問題ありません。
本番環境ではmaster.keyを絶対にアップロードしないで下さい。本番環境でのmaster.keyの値を取得方法
本番環境にはmaster.keyは当然アップロードすることは出来ません。代わりに環境変数に設定してcredentials.yml.encを復号化します。
$ cd ~ #本番環境 $ sudo vim /etc/environment #環境変数の設定環境変数は必ず、RAILS_MASTER_KEYとして設定して下さい。
また、デフォルトでcredentials.yml.encはgitignoreに追加されてしまっているので、この記述をコメントアウトします。.gitignore # config/credentials.yml.enc config/master.key #マスターキーは絶対にgit addしない。
- 投稿日:2020-03-01T18:18:58+09:00
Railsで既存のテーブルのカラムを追加・編集・削除する方法
今回は「users」というモデルのカラムを色々いじくりたいとします
マイグレーションファイルを作成
rails generate EditColumndbディレクトリ配下にできたマイグレーションファイルをいじっていきます
「name」カラムを追加するとき
20200301090906_edit_column.rbclass EditColumn < ActiveRecord::Migration[5.2] def change add_column :users, :name, :string end end既存の「age」カラムを「nenrei」に変更するとき
20200301090906_edit_column.rbclass EditColumn < ActiveRecord::Migration[5.2] def change rename_column :users, :age, :nenrei end end既存の「age」カラムを削除
20200301090906_edit_column.rbclass EditColumn < ActiveRecord::Migration[5.2] def change remove_column :users, :age, :string end end
- 投稿日:2020-03-01T17:31:56+09:00
Railsとrubyの感嘆符!は意味が違う件について
はじめに
rubyのメソッドに対して使う!と、Railsのメソッドに対して使う!では、意味が異なるので簡単にまとめてみました。めちゃくちゃ簡潔に言うと...
●
rubyの場合
注意喚起。今からオブジェクトに対して何らかの変化を加えますよ〜とういう合図。
例:uniq! reverse!●
Railsの場合
メソッドを実行した結果の返却値がnilの場合に例外を発生させる。
例:save! update!● rubyの場合
hoge = [1,2,2,3,3] hoge.uniq! #たまに見かけるこれ!●
uniqとuniq!何が違うの?
uniq配列から重複した要素を取り除いた新しい配列を返す。
uniq!削除を破壊的に行い元の配列をの値を操作する。hoge = [1,2,2,3,3] hoge.uniq p hoge #-> [1,2,2,3,3] hoge.uniq! p hoge #-> [1,2,3]●まとめると...
!は配列を破壊的に操作しています。
!をつけることで、配列そのものに変更を加えています。● Railsの場合
def update @hoge = Hoge.find(params[:id]) @hoge.save! #よく見かけるこれ! end●
saveとsave!何が違うの?
saveレコードの保存に失敗 → nilを返す
save!レコードの保存に失敗 → 例外を発生させる●まとめると...
!はメソッドの処理が失敗した場合に、例外処理を行います。番外編
● 例外処理とは
例えば、「入力された2つの数を足し合わせて結果を返す」コードがあるとき、利用者が入力欄に「あ」と書き入れると数値の足し算の処理は実行不可能となる。
このようなプログラムが通常の処理では想定していない事態や事象を「例外」(exception)と呼び、例外が生じた時の対応を記述したコードを例外処理という。つまり、何らかの予期せぬエラーが発生した際に、別の処理を行うこと。
- 投稿日:2020-03-01T17:12:01+09:00
中学2年生がRailsでyoutube動画共有サービスを作った
1 おれtubeというサービスをつくった
おれtubeっていうのは自分の好きなyoutube動画を投稿して。 それをユーザー同士で共有しあうみたいなサイトです。
https://oretube.herokuapp.com/
↑ おれtubeのリンク
以前に遊びサーチというサービスを公開しましたが、
やはりあのサイトはデザインが。。wなのでちょっとデザインもシンプルにしてみました。
それとGoogleのみログインできるようにしました。
2 どのように作ったか
youtubeAPIを使う
これがないと何もできないですね!
youtubeの動画埋め込みにはyoutubeAPIは使ってないんですが
youtubeの視聴回数や高評価 タイトルなどを取得するためにyoutubeAPIをつかっています
'https://www.googleapis.com/youtube/v3/videos?id=' + url + '&key=' + ENV['YOUTUBEAPIKEY'] + '&part=snippet,contentDetails,statistics' json = open(jsonURL).readこれでjsonを取得してここからタイトルや本文を取ってくるって感じです。
最後に
遊びサーチに比べてあまり時間がかかりませんでした。
遊びサーチはあんなに時間かけてデザインひどいし、
最悪でしたね。。。
ですが、その反省をしっかりいかすことができました。
本当に良かったです。ツイッターやっています。 よかったら見ていってください
https://twitter.com/Kuro_on_Rails
- 投稿日:2020-03-01T17:03:30+09:00
CSVファイルを作成してrails db:seedで大量のデータを投入する
栄養価を意識したレシピ投稿サイトを作っています
大量の食材を一つ一つデータに投入するのは面倒です
そこで食品成分表2015のデータを使って(そこらへんにあります)、
自分の使いたいデータを抽出してcsvファイルにまとめてrails db:seed で一気にデータを入れます僕のPCはMacbookなのでGoogleスプレッドシートを使います
手順
2.左上の「ファイル」→「ダウンロード」→「カンマ区切りの値(.csv、現在のシート)」でcsvファイルを保存
3.保存したファイルをrailsのdb/csvのなかに保存
4.db/seedsrbに書き込み
db/seeds.rbrequire "csv" CSV.foreach('CSVファイルのパス',headers: true) do |row| モデル名.create( カラム名: row['csvファイルの列'], 〜 〜 ) end5.rails db:migrate → rails db:seedでデータ呼び込み
以上
初めてcsvファイルを触りましたが思ってたよりカンタンに導入できましたいつものことですが、もし間違ってたらご指摘いただけると助かります
- 投稿日:2020-03-01T16:25:48+09:00
【Rails】rails6 + docker + nuxt ssr で connect ECONNREFUSED ERROR socket hang up
rails + docker + nuxtでなにか作ろうとしていた際にハマった部分
時間があるときに詳細を書きたい。docker-compose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3306:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql backend: build: ./backend/ command: bash -c "rm -f tmp/pids/server.pid && bundle install && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/app ports: - "3000:3000" depends_on: - db stdin_open: true tty: true command: bundle exec rails server -b 0.0.0.0 frontend: build: ./frontend/ command: npm run dev volumes: - .:/app ports: - 8080:3000 volumes: mysql-data: driver: localfrontend/pages/users/_id.vue<template> <h1>Hello, {{ id }}</h1> </template> <script> export default { asyncData ({ $axios, params }) { return $axios.$get(`http://localhost:3000/users/${params.id}`).then((res) => { return { id: res.id } }) } } </script>ブラウザからapi(http://localhost:3000/users/1) を叩くと
{ id: 1, email: "test@example.com", created_at: "2020-02-29T13:39:03.638Z", updated_at: "2020-02-29T13:39:12.055Z", url: "http://localhost:3000/users/1" }vueのfront(http://localhost:8080/users/1) からアクセスすると
frontend_1 | ERROR socket hang up frontend_1 | frontend_1 | at connResetException (internal/errors.js:604:14) frontend_1 | at Socket.socketOnEnd (_http_client.js:460:23) frontend_1 | at Socket.emit (events.js:323:22) frontend_1 | at Socket.EventEmitter.emit (domain.js:482:12) frontend_1 | at endReadableNT (_stream_readable.js:1204:12) frontend_1 | at processTicksAndRejections (internal/process/task_queues.js:84:21)以下に書き換える
frontend/pages/users/_id.vue<template> <h1>Hello, {{ id }}</h1> </template> <script> export default { asyncData ({ $axios, params }) { return $axios.$get(`http://backend:3000/users/${params.id}`).then((res) => { return { id: res.id } }) } } </script>Request failed with status code 403
railsを以下を追加
backend/config/environments/development.rbconfig.hosts << "backend"解決した。
- 投稿日:2020-03-01T15:05:43+09:00
ドリルTX7 (200301実施)
①コードは基本上から順番に実行される
なんとなく「上からざっと読まれてメソットじゃないところから読まれる。」
捉えていた②コードが読まれる順番について
はじめに読まれる、配列に全てのレビューの情報が格納される。
・スコープに違いによってメソット内から直接アクセスできない。
・そのため、各メソッドに引数としてpostsを渡して、その返り値をpostsに再代入しています。
・各メソッドでは、仮引数がa_postsという名前で定義されています。そのため、渡されたpostsをa_postsという変数にコピーして、メソッド内で使用することができます。。。。えっと、、、日本語話してもらっても大丈夫ですか?
③メソットが何をしているのか
・65行目でpost_review(posts)という記述があります。
そのため、今までのレビュー全件が入ったpostsという配列が引数で渡されてpost_reviewが呼び出されます。・仮引数の名前はa_postsなので、post_reviewのメソッド内ではa_postsという配列にレビュー全件が入っていることになります。
・メソッドの中で、postというハッシュを定義し、その中に最新のレビュー情報を格納しcwます。
・最後にa_postsにpostを追加し、その結果を返り値として返しています。
キーワード
スコープ、ハッシュ、本引数、仮引数、代入、返り値
- 投稿日:2020-03-01T14:55:14+09:00
renderでビューを出す時の注意点
- 投稿日:2020-03-01T14:49:21+09:00
配列における計算と出力
問題
八百屋さんのシステムで以下の配列から名前と合計金額を出力するプログラムを描いてください
fruits_price = [["banana", [200, 250, 220]], ["grape", [100, 120, 80]], ["watermelon", [1200, 1500]]]解答
fruits_price.each do |name, price| puts "#{name}は合計金額は#{price.sum}円です" endeach文により配列から順に値を取り出してnameとpriceに入れます。
それを出力する際にprice.sumで値を全て合計してから出力をしています。
これにより出力結果が以下のようになります。ターミナルbananaの合計金額は670円です grapeの合計金額は300円です watermelonの合計金額は2700円です
- 投稿日:2020-03-01T14:33:18+09:00
曜日の取得
曜日の取得方と曜日による条件分岐
曜日によって表示が変わるプログラムがあったとします。
例)
金曜日なら 今日は華金だ!
他の曜日なら 今日は●曜日だ上記のように曜日を取得して、取得した曜日によって条件分岐を行う方法はどのようなものでしょうか?
Timeクラスを使用する方法
day = Time.now if day.friday? puts "今日は華金だ!" else puts "今日は" + %w(日 月 火 水 木 金 土 )[day.wday] + "曜日だ" end日付に関する値を取得するTimeクラスがもともと存在します。
Time.nowで今日の日付情報を取得できます。
試しにTime.nowを出力するとday = Time.now puts dayターミナル2020-03-01 14:25:39 +0900上記のように日付情報を取得できます。この情報は自分のPCに依存します。
Time.nowに取得した値に対してwdayメソッドを使用することで曜日の値を取得できます。
曜日の値は0~6の数字で返され、0が日曜日、6が土曜日を表します。
これを利用して条件分岐を行なって行きます。
まず2行目のif文ではfriday?メソッドにより
今日が金曜日かを判断しています。
もし金曜日(値が5)であれば”今日は華金だ!”と出力され、筆者は夜の歓楽街へと消えることでしょう
それ以外の値であった場合5行目の%w(日..土)に対して[day.wday]により引数の中の何番目に当たる値を代入して表示するかの文で今日は●曜日だを出力しています。このようにすることで曜日の取得と曜日による条件分岐を行うことだできます。
参考記事
- 投稿日:2020-03-01T13:38:12+09:00
Google Cloud RunでRubyを使ってFirestoreにアクセスする(サンプル付き)
背景
ちょっとした処理をGCEのf1-microインスタンスでdockerを使って定期実行していたのですが、なぜか昨年11月頃から動作が重くなり処理が完了できなくなりました。そもそもspec不足だろうとは思ったので、最近知ったCloud RunならRubyのまま移行できるのではないか、と考えました。
Cloud Runではボリュームに相当する機能が使えないので、ファイルに保存していたデータを別の方法で保存する必要が発生しました。試行錯誤の結果、今回はFirestoreに保存するのが良さそうという結論になったので、この記事を書いています。
サンプル
https://github.com/skuroki/hello_google_run_with_firestore
Ruby2.6なのは、現時点ではRuby2.7でgrpcのgemがbundleできないからです。あと数日で出来そうな気配はあります。
要点
Cloud Runは、処理の内容が何であっても、HTTPリクエストを受け取って実行する仕組みです。なので、既存の処理を、リクエストが来たら実行するwebアプリケーションに書き換える必要があります。
Cloud Runは、同じプロジェクトであれば、設定を全く行わずに、Firestoreにアクセスできるようになっています。
動かしかた
1つ1つのプロセスは各サービスの基本的なものなので、公式ドキュメントやGoogleなどで調べられると思います。
Firestoreのデータ
GCPの同じプロジェクトでFirebaseを動くようにして、Firestoreも動くようにして、以下のようにデータを入れます。
コンテナのビルド
gcloud builds submit . --project=your-awesome-project --config=cloudbuild.yamlプロジェクト名は置き換えましょう。
サービスの設定
サービスが設定されたら自動的に最初のデプロイが行われるので、すぐ実行できるようになります。
実行
Firestoreのデータを引っ張ってこれていることがわかります。
ここまででCloud Runを動かす話は終わりです。ついでに開発時に手元で動作させる方法について書きます。
手元での動かしかた
サービスアカウントの鍵を発行
発行時にJSONを手元にダウンロードして保存します。
鍵の配置
レポジトリの下に
dataディレクトリを作って鍵ファイルを置いて、env.sample# Replace file name GOOGLE_APPLICATION_CREDENTIALS=/data/your-awesome-project-deadbeef1234.json鍵ファイルの名前に書き換えて、
.envとして保存します。この環境変数はFirestore用のgemが読み取ります。起動
docker-compose upで、 http://localhost:8080 にアクセスして、
{:foo=>"bar", :hoge=>"piyo"}と表示されればOKです。





























