20200818のRubyに関する記事は13件です。

【Rails 】マイグレーションファイルについて

マイグレーションファイルとは

マイグレーションファイルとはデータベースに加える変更内容を記載するファイルです。

基本のコマンド

command
# モデル作成
$ rails g model モデル名

# マイグレーションファイル作成コマンド
$ rails g migration クラス名

マイグレーションファイルはモデル作成時に作成されます。
マイグレーションファイル単体でクラス名を指定し作成することも可能です。
クラス名は「アクション+テーブル名」で作成するのがわかりやすくていいですね!
これで/db/migrate/タイムスタンプ_クラス名.rb というファイルが作れる。ここに、スキーマの変更点を記載しましょう!

テーブルの作成

command
$ rails g model モデル名 カラム名:データ型

モデル作成時にマイグレーションファイルに記載する内容を「カラム名:データ型」で指定します。

指定できるデータ型は下記の通りです。

  • string : 文字列
  • text : 長い文字列
  • integer : 整数
  • float : 浮動小数
  • decimal : 精度の高い小数
  • datetime : 日時
  • timestamp : タイムスタンプ
  • time : 時間
  • date : 日付
  • binary : バイナリデータ
  • boolean : Boolean

マイグレーションファイルをDBへ反映させる

command
# マイグレーションファイルの実行
$ rails db:migrate

このコマンドを実行するとDBへマイグレーションファイルの内容が反映される。

マイグレーションファイルのロールバック

command
# マイグレーションファイルをロールバックする
$ rails db:rollback

実行したマイグレーションファイルをロールバックする。

マイグレーションファイルの確認

command
# 実行したマイグレーションファイルの確認
$ rake db:migrate:status

このコマンドで実行したマイグレーションファイルの内容確認ができる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラーが出た! ※インタラクティブリクエストのテンプレートはありません


Ruby on Railsを使ってアプリ実装の学習中、目的のページを表示させようとしたら、こんな画面が:sweat_smile:

error1.png

「Controller」と「index」という言葉がチラついていたので、VSCodeのルーティングとコントローラーとビューファイルの記述をチェック!コマンドパレットでCompare Active File With Clipboardツールを呼び出して、見本のコード比べてみたけど、異なる部分は見つからず…

いよいよメンターさんに質問しようと、VSCodeのスクショを取っていたら、ビューファイルのディレクトリがおかしいことに気づいた!正しい場所にビューファイルを作り直して、祈りながらページの更新ボタンをクリックすると、ロード時間が長い!「これはいけそう!」と思って待っていると、無事表示ができました:joy:



「〜のテンプレートがありません。」って出てきたら、ファイルの所在などをチェックしたら良いのかな?と少し学びました。

最後に問題のビューファイルの位置を作り直した時のスクショです。(正しい場所と間違った場所の両方あります。)

error2.png

間違っているのは3行目で、index.htnl.erbファイルがtweetsディレクトリに入ってなかったですね:sweat_smile:こんな初歩的な所で間違えるなんて…

でも、まあ、自分で解決できて良かったです:grin:
そして、きちんと表示された時、気持ち良かった:laughing:
ああ、こういった目に見える形で達成感をえられるのも、プログラミングの良さやな〜としみじみ思いました:blush:


※まだまだ、プログラミングを始めたばかりで、間違った解釈や記述をしてしまうことがあります。もし、お気づきのことがありましたら、どんどんアドバイス・ご指摘頂けたらと思います。最後まで読んで頂き、ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]eachの入れ子

概要

入れ子でeachメソッドを使用する際の方法を学習用としてまとめます。
※eachメソッドが何かについては本内容では触れません。

環境

Ruby 2.6.5

内容

例として、下記のような配列に対し

vegetables_price = [["tomato", [120, 150, 80]], ["potato", [50, 60, 40]], ["carrot", [120, 150]]]

下記のような出力結果を求めるとします。

tomatoの合計金額は350円です
potatoの合計金額は150円です
carrotの合計金額は270円です

記述内容

vegetables_price = [["tomato", [120, 150, 80]], ["potato", [50, 60, 40]], ["carrot", [120, 150]]]

vegetables_price.each do |vegetable| #配列vegetables_priceにeachメソッドを使用する。
  sum = 0 #合計値を入れておく変数sumを定義
  vegetable[1].each do |price| #変数vegetable[1]に対しeachメソッドを使用。[1]には価格が入っている。
    sum += price #eachを回して価格を足していく。
  end
  puts "#{vegetable[0]}の合計金額は#{sum}円です" #vegetable[0]にはvegetableの名前が入っている。
end

※備考
添字の0には野菜の名前、1には価格が入っていることになる。

vegetables_price = [["tomato", [120, 150, 80]], ["potato", [50, 60, 40]], ["carrot", [120, 150]]]
   (添字)                0              1

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] Project Euler 第8問

問題

次の1000桁の数字のうち、隣接する4つの数字の総乗の中で、最大となる値は、9 × 9 × 8 × 9 = 5832である。

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450

この1000桁の数字から13個の連続する数字を取り出して、それらの総乗を計算する。では、それら総乗のうち、最大となる値はいくらか。

参考: Project Euler Problem8

problem008.rb
num = 
'73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450'

numbers = num.gsub(/[\r\n]/, "").chars.map(&:to_i)

array = []

numbers.each_cons(13) do |elements|
  total = elements.inject(:*)
  array.push(total)
end

p array.max
#=> 23514624000

解説

numbers = num.gsub(/[\r\n]/, "").chars.map(&:to_i)
p numbers
#=> [7, 3, 1, 6, 7, 1, 7, 6, 5, 3, 1, 3, 3, 0, 6, 2, 4, 9, 1, 9, 2, 2, 5, 1, 1, 9, 6, 7, 4, 4, 2, 6, 5, 7, 4, 7, 4, 2, 3, 5, 5, 3, 4, 9, 1, 9, 4, 9, 3, 4, 9, 6, 9, 8, 3, 5, 2, 0, 3, 1, 2, 7, 7, 4, 5, 0, 6, 3, 2, 6, 2, 3, 9, 5, 7, 8, 3, 1, 8, 0, 1, 6, 9, 8, 4, 8, 0, 1, 8, 6, 9, 4, 7, 8, 8, 5, 1, 8, 4, 3, 8, 5, 8, 6, 1, 5, 6, 0, 7, 8, 9, 1, 1, 2, 9, 4, 9, 4, 9, 5, 4, 5, 9, 5, 0, 1, 7, 3, 7, 9, 5, 8, 3, 3, 1, 9, 5, 2, 8, 5, 3, 2, 0, 8, 8, 0, 5, 5, 1, 1, 1, 2, 5, 4, 0, 6, 9, 8, 7, 4, 7, 1, 5, 8, 5, 2, 3, 8, 6, 3, 0, 5, 0, 7, 1, 5, 6, 9, 3, 2, 9, 0, 9, 6, 3, 2, 9, 5, 2, 2, 7, 4, 4, 3, 0, 4, 3, 5, 5, 7, 6, 6, 8, 9, 6, 6, 4, 8, 9, 5, 0, 4, 4, 5, 2, 4, 4, 5, 2, 3, 1, 6, 1, 7, 3, 1, 8, 5, 6, 4, 0, 3, 0, 9, 8, 7, 1, 1, 1, 2, 1, 7, 2, 2, 3, 8, 3, 1, 1, 3, 6, 2, 2, 2, 9, 8, 9, 3, 4, 2, 3, 3, 8, 0, 3, 0, 8, 1, 3, 5, 3, 3, 6, 2, 7, 6, 6, 1, 4, 2, 8, 2, 8, 0, 6, 4, 4, 4, 4, 8, 6, 6, 4, 5, 2, 3, 8, 7, 4, 9, 3, 0, 3, 5, 8, 9, 0, 7, 2, 9, 6, 2, 9, 0, 4, 9, 1, 5, 6, 0, 4, 4, 0, 7, 7, 2, 3, 9, 0, 7, 1, 3, 8, 1, 0, 5, 1, 5, 8, 5, 9, 3, 0, 7, 9, 6, 0, 8, 6, 6, 7, 0, 1, 7, 2, 4, 2, 7, 1, 2, 1, 8, 8, 3, 9, 9, 8, 7, 9, 7, 9, 0, 8, 7, 9, 2, 2, 7, 4, 9, 2, 1, 9, 0, 1, 6, 9, 9, 7, 2, 0, 8, 8, 8, 0, 9, 3, 7, 7, 6, 6, 5, 7, 2, 7, 3, 3, 3, 0, 0, 1, 0, 5, 3, 3, 6, 7, 8, 8, 1, 2, 2, 0, 2, 3, 5, 4, 2, 1, 8, 0, 9, 7, 5, 1, 2, 5, 4, 5, 4, 0, 5, 9, 4, 7, 5, 2, 2, 4, 3, 5, 2, 5, 8, 4, 9, 0, 7, 7, 1, 1, 6, 7, 0, 5, 5, 6, 0, 1, 3, 6, 0, 4, 8, 3, 9, 5, 8, 6, 4, 4, 6, 7, 0, 6, 3, 2, 4, 4, 1, 5, 7, 2, 2, 1, 5, 5, 3, 9, 7, 5, 3, 6, 9, 7, 8, 1, 7, 9, 7, 7, 8, 4, 6, 1, 7, 4, 0, 6, 4, 9, 5, 5, 1, 4, 9, 2, 9, 0, 8, 6, 2, 5, 6, 9, 3, 2, 1, 9, 7, 8, 4, 6, 8, 6, 2, 2, 4, 8, 2, 8, 3, 9, 7, 2, 2, 4, 1, 3, 7, 5, 6, 5, 7, 0, 5, 6, 0, 5, 7, 4, 9, 0, 2, 6, 1, 4, 0, 7, 9, 7, 2, 9, 6, 8, 6, 5, 2, 4, 1, 4, 5, 3, 5, 1, 0, 0, 4, 7, 4, 8, 2, 1, 6, 6, 3, 7, 0, 4, 8, 4, 4, 0, 3, 1, 9, 9, 8, 9, 0, 0, 0, 8, 8, 9, 5, 2, 4, 3, 4, 5, 0, 6, 5, 8, 5, 4, 1, 2, 2, 7, 5, 8, 8, 6, 6, 6, 8, 8, 1, 1, 6, 4, 2, 7, 1, 7, 1, 4, 7, 9, 9, 2, 4, 4, 4, 2, 9, 2, 8, 2, 3, 0, 8, 6, 3, 4, 6, 5, 6, 7, 4, 8, 1, 3, 9, 1, 9, 1, 2, 3, 1, 6, 2, 8, 2, 4, 5, 8, 6, 1, 7, 8, 6, 6, 4, 5, 8, 3, 5, 9, 1, 2, 4, 5, 6, 6, 5, 2, 9, 4, 7, 6, 5, 4, 5, 6, 8, 2, 8, 4, 8, 9, 1, 2, 8, 8, 3, 1, 4, 2, 6, 0, 7, 6, 9, 0, 0, 4, 2, 2, 4, 2, 1, 9, 0, 2, 2, 6, 7, 1, 0, 5, 5, 6, 2, 6, 3, 2, 1, 1, 1, 1, 1, 0, 9, 3, 7, 0, 5, 4, 4, 2, 1, 7, 5, 0, 6, 9, 4, 1, 6, 5, 8, 9, 6, 0, 4, 0, 8, 0, 7, 1, 9, 8, 4, 0, 3, 8, 5, 0, 9, 6, 2, 4, 5, 5, 4, 4, 4, 3, 6, 2, 9, 8, 1, 2, 3, 0, 9, 8, 7, 8, 7, 9, 9, 2, 7, 2, 4, 4, 2, 8, 4, 9, 0, 9, 1, 8, 8, 8, 4, 5, 8, 0, 1, 5, 6, 1, 6, 6, 0, 9, 7, 9, 1, 9, 1, 3, 3, 8, 7, 5, 4, 9, 9, 2, 0, 0, 5, 2, 4, 0, 6, 3, 6, 8, 9, 9, 1, 2, 5, 6, 0, 7, 1, 7, 6, 0, 6, 0, 5, 8, 8, 6, 1, 1, 6, 4, 6, 7, 1, 0, 9, 4, 0, 5, 0, 7, 7, 5, 4, 1, 0, 0, 2, 2, 5, 6, 9, 8, 3, 1, 5, 5, 2, 0, 0, 0, 5, 5, 9, 3, 5, 7, 2, 9, 7, 2, 5, 7, 1, 6, 3, 6, 2, 6, 9, 5, 6, 1, 8, 8, 2, 6, 7, 0, 4, 2, 8, 2, 5, 2, 4, 8, 3, 6, 0, 0, 8, 2, 3, 2, 5, 7, 5, 3, 0, 4, 2, 0, 7, 5, 2, 9, 6, 3, 4, 5, 0]

まず、gsubメソッドを使って改行文字を空文字に置換します。
次に、charsメソッドを使って文字列の各文字を文字列の配列で返します。
最後に、mapメソッドを使って配列の中身の文字列を整数の値に変換していきます。

numbers.each_cons(13) do |elements|
  total = elements.inject(:*)
  array.push(total)
end

p array.max

each_consメソッドを使って、13個の連続する数字を取り出します。
その後、injectメソッドを使ってたたみこみ演算を行なっています。
参考: Ruby 2.7.0 リファレンスマニュアル
injectメソッドで総乗したものを配列に入れていき、その中の最大値を表示します。

以上になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【独学】Railsチュートリアル終えた後の、ポートフォリオ(オリジナルアプリ)作成

はじめに

今回は

Railsチュートリアル学習したのに、ポートフォリオ(オリジナルアプリ)の作成に苦戦している方に向けて書いていこうと思います。

(スクールに通われている方にも参考になるかと思います。)

自己紹介

・24歳
・2020年3月 プログラミング学習開始
・6月 ポートフォリオ作成開始
 ポートフォリオ
 Github
・7月~現在 受託開発に取り組みながら、エンジニアへの転職活動をしています。

Railsチュートリアル学習したのにポートフォリオが作れない理由

それは、単純にRailsチュートリアルはポートフォリオ(オリジナルアプリ)を作り方を学べるものではないからです。

そもそも、RailsチュートリアルはRailsの構造を理解することに適した教材です。(おそらくスクール教材も)

実際に私自身もRailsチュートリアルを終えた後にすぐに、ポートフォリオ作成に取り組みましたがすぐに手が止まりました。

そこで、私が実践した3つのステップをご紹介します。

1. 転職活動している未経験者のポートフォリオを参考にする
2. とにかく作る
3. 現役のエンジニアにみてもらう

1. 転職活動している未経験者のポートフォリオを参考にする

転職活動しているほとんどの方がポートフォリオとGithubのリポジトリを公開しているので両方見ましょう。

コードが単純で書かれているものが多いので、Railsチュートリアルを十分に理解しているレベルなら、ほぼ理解できるかと思います。

ここでRailsチュートリアルで出てこない機能(gem)がたくさん出てくるので、Railsでどのような機能を実装できるのかがわかると思います。

私は、実際に検索機能(Ransack)ログイン機能(Devise)は他のポートフォリオを通じて知りました。

2. とにかく作る

簡単なものでもいいのでとにかく作りましょう!

私が最初に作ったサービスは「どのジムでどこの部位の筋トレをするかを投稿できるサービス」です。
正直、出来はひどいです。(見たい方は私のGithubから探してくださいw)

ですが、自分が理解していないところがわかったり、様々なエラーを自力で解決させたりすることは、実際に手を動かさないと経験できません。

一つポイントとして、作る前にモックアップを書きましょう!

なのでとりあえず作る!これに限ります。

3.現役のエンジニアにみてもらう

これが何よりも大事です!

「お前独学の人を対象に書いてるのに何いってんだ!」って言われても仕方ないですが、本当に現役のエンジニアにみてもらうのは大事なんです

2.であげた自分が理解していないところがわかったり、様々なエラーを自力で解決させたりすることは一人では限界がありますし、かなり時間がかかります。

冒頭で挙げたポートフォリオは独学で一人で作成しましたが、現役のエンジニアの方から頂いたフィードバックを元に修正をしたところ、カジュアル面談でかなり評価されたので、独学で頑張ってる方、初学者の方々には是非現役のエンジニアからレビューをもらいましょう!

私はオンラインサロンに入ってレビューをもらいましたが、Twitterなどに上げてみるのもいいですね!

最後に

本当に初学者のために書いたので、あまり参考になる人は少ないかもしれませんね汗

私自身、独学でやってた時に何から手をつけてわからなかったので、この記事を見て少しでも参考になれば幸いです!

次はポートフォリオ作りで技術的に苦労したところをあげようと考えてます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2でRailsを起動する(手動デプロイ)

日記がてら。

EC2でのRails起動までの準備を着々と進めていき、
いざ起動コマンド!と思いきや

image.png

master failed to start, check stderr log for details
というエラー分が出て起動失敗。

stderrとはなんぞ?と思い調べてみるとconfig/unicorn.rbに
image.png
という部分が。
なるほどエラーログというものがあるのか。

lessコマンドを使用することでファイルの中身を除けるらしいのでターミナルで打ち込んでみることに。
すると
image.png
この青ラインが何度も出ていることに気づいた。

まずcredentialなるものについてファイルを覗くも、全く触ったことのないファイルだったので一旦後回し。
その上のaccess_key、secret_access_keyに何かありそうだ。
調べていくうちに起動までの手順を一つ抜かしていたようで、ターミナルにsudo vim /etc/environmentと打ち込み、
image.png
こちら2点を追記することであっさり解決。

ここまで調べたり試したりで所要時間3時間。

もしEC2でRails起動しないことでお悩みの方は上記の点の確認と、
・ローカル→GiuHubへのpushのし忘れはないか(mergeまで確認)
・GithubからEC2への反映(git pull origin master)のし忘れは無いか
・EC2サーバー側でエラーログの内容を確認し、原因を見つけたか
・カリキュラム通りの記載ができているか
・データベースは正しく起動しているか
・EC2サーバー側の環境変数は正しく設定できているか
・EC2インスタンスの再起動を行ってみる
を確認してみることをお勧めします。

またインスタンスの再起動は最初よくわからず全く別のインスタンスを作成したりとかしちゃったので、その場合はいらないインスタンスで右クリック→インスタンスの状態→終了で問題なく終わらせられます。
すぐには消えないけど24時間程度で消えるそうです。

誰かの参考になれば幸いです。

master failed to start, check stderr log for details
bundler: failed to load command: unicorn_rails
Aws::Sigv4::Errors::MissingCredentialsError: Cannot load Rails.config.active_storage.service:
missing credentials, provide credentials with one of the following options:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

productとvariantの違い

productは表示用の商品のまとまりで、variant在庫単位の商品
variantの方がより細分化されている

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今更ですが、Groongaを使ってみた

全文検索とかどうなの?ということだったので、Groongaを使ってみました。
 → Groonga

全文検索

「全文検索」とはとっても簡単に言うと 「Google検索」 のようなものです。
キーワードなどを入力してたくさんの文書の中から目的のものを見つけだすあれです。

いろいろあるみたい

というわけで、まずは Googleさんに「全文検索システム」とかで聞いてみるといろいろ出てきます。

  • Fess
  • Elasticsearch
  • Apache Solr
  • Namazu
  • Groonga

などなど
Namazu とか懐かしいなぁと思いながら、いろいろみてみると、どうやら Groonga が良さげだったので、使ってみることにしました。

インストール

ローカル環境に入れるのはちょっとあれだったので、docker-compose を利用してインストールすることにしました。#公式のドキュメントにも書いてあるし。。。

公式に書いてある方法で試してみた

 → Groonga#with-docker-compose

おお、確かに簡単に立ち上がりました。
確認は、ブラウザを立ち上げて、http://localhost:10041 とすると、なんかページが表示されます。

#画像を貼ろうと思ってアップしてみたのだけれど、何度やっても以下のように怒られたので、皆さん脳内補完(想像してみてください)お願いします。。。
[Something went wrong]()

がしかし。。。

% docker-compose run groonga
だと、ターミナル立ち上げっぱなしにしないと落ちてしまいます。
じゃあ再度立ち上げようといって % docker-compose restart とかすると、こんどは、データファイルはすでに存在するよエラー。。。

#理由は command: ["-n", "/mnt/db/data.db"]-n がデータファイルを新規に作成というオプションなので、毎回新規に作成しようとして、怒られてました。

むむむ。

というわけで、Dockerfile を作って立ち上げるようにしてみた

ソースを Github に上げたので参照してください。

簡単に説明します。
まずは、/groonga の下。

docker-compose.yml に構成設定等を記述しています。
中にある build: ./groonga で、./groonga/Dockerfile から生成するようにしています。
volumes でホストマシンからでもデータファイルが見れるようにします。

あとの xxxx.sh は便利ツール的な感じです。
インストールするときは、 $ ./install.sh で docker-compose build とかをしてくれます。
docker プロセスに入りたい場合は $ ./login.sh で入れます。
この場合は $ exit で抜けましょう。
停止は $ ./stop.sh で、再起動は $ ./restart.sh です。
dockerプロセスがいらなくなったら $ ./remove.shdocker rm container をします。

つづいて、/groonga/groonga の下。
Dockerfile に具体的な作り方が書いてあります。

なんかバグっぽいのがあるらしい

普通に groonga をインストールして、http でアクセスしていると、時々エラーになることがあります。ネットで調べてみると次の記事がありました。
 → https://okamuuu.hatenablog.com/entry/2017/11/13/185903

#okamuuuさん ありがとうございます。
#しかしこの記事が 2017年で、2020年の今でも同じエラー。。。

というわけで、stack-fix.c はパッチ。それを含めたものを Dockerfile に書いています。
それから、先ほどの、「再起動時に新規ファイル作成でエラー」を回避するために、起動スクリプトを書いて、データファイルがなければ作り、あればそれを使用するようにしました。 groonga.sh です。

再度インストール

というわけで、$ ./install.sh っと。

http://localhost:10041 にアクセスして、はい。できました。

Ruby からかまってみる。

groonga を ruby から使うにはいくつかあるらしいです。
rroonga が有名ですが、これは同一サーバ上に groonga が動いている場合のライブラリっぽいです。
Docker とか別のサーバ(仮想含む)で動いてる場合には groonga-client っぽいです。

というわけで、インストール。

インストールとかサンプルとか

groonga-client は $ gem install groonga-client
でインストールできます。

次のサンプルを実行してみました。

test.rb
# -*- coding: utf-8 -*-
require "groonga/client"

host = "127.0.0.1"
port = 10041
Groonga::Client.open(host: host, port: port, protocol: :http) do |client|
  tables = client.table_list
  unless tables.map{|m| m.name}.include?("docs")
    # ---- create normal table ----
    client.table_create(name: "docs",
                        flags: "TABLE_HASH_KEY",
                        key_type: "ShortText")
    client.column_create(table: "docs",
                         name: "body",
                         flags: "COLUMN_SCALAR",
                         type: "Text")

    # ---- data insert to table ----
    values = [
      { "_key" => "/path/to/document/1",
        "body" => "メロスは激怒した。" },
      { "_key" => "/path/to/document/2",
        "body" => "メロスには政治がわからぬ。" },
      { "_key" => "/path/to/document/3",
        "body" => "メロスには竹馬の友があった。" },      
    ]   
    client.load(table: "docs",
                values: values.to_json)
  end

  # ---- data search ----
  query = "激怒"
  response = client.select(table: "docs",
                           query: query,
                           match_columns: "body")
  puts "hits: #{response.n_hits} (query: #{query} -> body)"
  response.records.each do |record|
    p record
  end

  query = "政治"
  response = client.select(table: "docs",
                           query: "body:@#{query}")
  puts "hits: #{response.n_hits} (query: #{query} -> body)"
  response.records.each do |record|
    p record
  end

  filter = "/path/to/document/3"
  response = client.select(table: "docs",
                           filter: "_key == '#{filter}'")
  puts "hits: #{response.n_hits} (filter: #{filter} -> _key)"
  response.records.each do |record|
    p record
  end
  query = "/document"
  response = client.select(table: "docs",
                           query: "_key:@#{query}")
  puts "hits: #{response.n_hits} (query: #{query} -> _key)"
  response.records.each do |record|
    p record
  end

end

では実行。

$ ruby ./test.rb
hits: 1 (query: 激怒 -> body)
{"_id"=>1, "_key"=>"/path/to/document/1", "body"=>"メロスは激怒した。"}
hits: 1 (query: 政治 -> body)
{"_id"=>2, "_key"=>"/path/to/document/2", "body"=>"メロスには政治がわからぬ。"}
hits: 1 (filter: /path/to/document/3 -> _key)
{"_id"=>3, "_key"=>"/path/to/document/3", "body"=>"メロスには竹馬の友があった。"}
hits: 3 (query: /document -> _key)
{"_id"=>1, "_key"=>"/path/to/document/1", "body"=>"メロスは激怒した。"}
{"_id"=>2, "_key"=>"/path/to/document/2", "body"=>"メロスには政治がわからぬ。"}
{"_id"=>3, "_key"=>"/path/to/document/3", "body"=>"メロスには竹馬の友があった。"}

なんか良さそうですね。

'激怒' と '政治' の検索で query の書き方を2通りの方法で書いてみました。
match_columns を使うのと、column:@query のように書く書き方です。
とりあえず、どちらでもいい感じです?

。。。っていうか、テーブル作成時に、トークナイザーとか設定してないし、インデックステーブルも作ってないけど、bodyカラムの中を "%激怒%" 的に検索できてるなぁ。。。 query っていうのがそういうものなのだろうか。。。

っていうか、そういうものっぽいですね。はい。

インデックステーブルを作成してみた

というわけで、インデックステーブルを作成してみました。
これを作ると検索時間がとても早くなるっぽい。

test2.rb
# -*- coding: utf-8 -*-
require "groonga/client"

host = "127.0.0.1"
port = 10041
Groonga::Client.open(host: host, port: port, protocol: :http) do |client|
  tables = client.table_list
  unless tables.map{|m| m.name}.include?("doc_indexes")
    # ---- create indexes ----
    client.table_create(name: "doc_indexes",
                        flags: "TABLE_PAT_KEY",
                        key_type: "ShortText",
                        default_tokenizer: "TokenBigram",
                        normalizer: "NormalizerAuto")
    client.column_create(table: "doc_indexes",
                         name: "body_index",
                         flags: "COLUMN_INDEX|WITH_POSITION",
                         type: "docs",
                         source: "body")
  end

  query = "わからぬ"
  response = client.select(table: "docs",
                           query: query,
                           match_columns: "doc_indexes.body_index")
  puts "hits: #{response.n_hits} (query: #{query} -> doc_indexes.body_index)"
  response.records.each do |record|
    p record
  end  
end

で実行すると

$ ruby ./test2.rb
hits: 1 (query: わからぬ -> doc_indexes.body_index)
{"_id"=>2, "_key"=>"/path/to/document/2", "body"=>"メロスには政治がわからぬ。"}

取れましたね。
これがもしかすると正しいやり方なのかもしれない。。。

うむ。

Groongaについて

そうそう、大事なことを忘れてました。groonga は全文検索に最適化したデータベースシステムです。なので、いつも使っている RDB とか KVS とも少し違うので、慣れるまでが大変です。

RDBとかではテーブルを作成するときにキーにするカラムとかデータを格納するカラムとかいろいろ いっしょくた に作成できるのですが、groonga は最初にテーブルを作るときにキーの構成や全文検索用の機能等を指定しておき、後にカラムをテーブルに追加していく、という作り方をします。
それから、インデックスの作成とか検索の仕方とか、とにかく独特な印象です。
使いこなせたらきっと便利なんだろうと思います。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]曜日を表示するコード

概要

曜日を表示する方法について、学習内容をまとめます。

環境

Ruby 2.6.5

内容

今日の曜日を表示する為には、DateクラスというRubyの標準ライブラリを使用します。

require "date"

Dateクラスを使用して今日の曜日を取得するには以下のように記述します。
wdayはDateクラスに用意されているメソッドで、曜日を整数0(日曜日)~6(土曜日)で取得することができます。
変数dayに代入します。

day = Date.today.wday

wdayで取得した整数に対応させるように、曜日を配列を用意します。

days = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]
#整数      0         1        2        3        4        5        6

下記のように出力してあげます。
daysという配列からday番目の要素を取り出します。

puts "今日は#{days[day]}です"

出力結果(月曜の場合)

今日は月曜日です

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerコンテナ上でのbundle installコマンドがcode: 15でエラー終了する時はBUNDLE_PATHの設定を確認する

事象

Docker Imageをクリアした日からCIがエラー終了するようになった

bundle install --cleanが失敗するようになってしまった。

Bundle complete! 62 Gemfile dependencies, 187 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Cleaning all the gems on your system is dangerous! If you're sure you want to
remove every system gem not in this bundle, run `bundle clean --force`.
Service 'xxxx' failed to build: The command '/bin/sh -c bundle install --clean' returned a non-zero code: 15

一度Bundle complete!と表示されるのだが、その後Cleaning all the gems on your system is dangerous!としてcode: 15を返してエラー終了してしまっている。


問題となったコンテナのDockerfile

FROM ruby:2.6.5
ENV RUBYOPT -EUTF-8
ENV LANG C.UTF-8
RUN gem install rails -v 6.0.2
# 以下略

原因

RubyのDocker Imageに入った下記の変更により、BUNDLE_PATHが自動で設定されなくなっていた。
Stop setting `BUNDLE_PATH` by deivid-rodriguez · Pull Request #306 · docker-library/ruby

Dockerfile上ではBUNDLE_PATHを指定していなかったため、Imageでの設定が削除されたことでBUNDLE_PATHの指定が空になってしまい、対象がグローバルになってしまった。
だから以下のようなメッセージが表示されたと考えられる。

Cleaning all the gems on your system is dangerous! If you're sure you want to
remove every system gem not in this bundle, run `bundle clean --force`.

しばらくはキャッシュで古いイメージを使っていたため問題が発生しなかったが、キャッシュをクリアしてImageを再取得したことで問題が発生した。

対応

ENV BUNDLE_PATH=$GEM_HOMEをDockerfileに追加することで解消した(上記PRで削除された箇所を、自前のDockerfileに記載する形)。
(最初はdocker-composeファイルのenvironmentに追加したのだがエラーが解消されませんでした。急いでいたので詳しく検証はできていないですが…)

参考

Newest version breaks build - 'Cleaning all the gems on your system is dangerous!' · Issue #3271 · rubygems/rubygems

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]カラムを絞るだけの目的でselectメソッドは使うな!

ActiveRecordのselectメソッドについて

ActiveRecordでデータを取得すると基本的に該当するテーブルのすべての項目が取得されます。
発行するSQLを見ていただくと分かる通り、*で全項目取得しています。
全項目取得しているので、以降の処理ではどの項目でも参照することができます。

pry(main)> user = User.first
  User Load (0.7ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, name: "ham", created_at: "2020-03-10 01:03:37", updated_at: "2020-06-16 02:18:39">
pry(main)> user.id
=> 1
pry(main)> user.name
=> "ham"

ただ、全カラム使うわけではないので必要なカラムだけ取得したほうがいいのでは?という考え方もあると思います。
そんなときにselectというメソッドを使うことで取得するカラムを絞ることができます。
selectについて詳しくはRailsガイド をご覧ください。

selectを指定することで必要なカラムだけ取得することができます。
取得していないカラムは当然以降の処理では参照できません。

pry(main)> user = User.select(:id, :created_at).first
  User Load (0.7ms)  SELECT `users`.`id`, `users`.`created_at` FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, created_at: "2020-03-10 01:03:37">
pry(main)> user.id
=> 1
pry(main)> user.name
ActiveModel::MissingAttributeError: missing attribute: name
from /usr/local/bundle/gems/activemodel-6.0.3.2/lib/active_model/attribute.rb:221:in `value'

カラムを絞るだけのselectは使うな!

あくまで私個人の考えではあるのですが、チーム開発など複数人で開発を行っている場合はカラムを絞るだけのselectは使わないほうがいいと考えています。

なぜなのか?

下記のコードを見てください。

def hoge(user_id)
  # selectでid, nameだけ取得
  user = User.select(:id, :name).find(user_id)

  ...(様々な処理)

  generate_response(user)
end

private

def generate_response(user)
  { id: user.id, name: user.name }
end

後にhogeメソッドのレスポンスにemailを追加することになったらどうでしょうか?
Userモデルはemailカラムを持っていることとします。

おそらく該当箇所を見つけて、generate_responseにemail足せばいいだけだな!
と思い、下記のように修正すると思います。

def generate_response(user)
-  { id: current_user.id, name: current_user.name }
+  { id: current_user.id, name: current_user.name, email: current_user.email }
end

よし修正終わり!1行で出来たぜ!!テスト実行!!!

pry(main)> { id: user.id, name: user.name, email: user.email }
ActiveModel::MissingAttributeError: missing attribute: email
from /usr/local/bundle/gems/activemodel-6.0.3.2/lib/active_model/attribute.rb:221:in `value'

あれ??動かないぞ・・・
受け取っているuserがおかしいのか?
たどってたどって、、、

そうです。selectで取得カラムを絞っているのでそちらにemailを足す必要があります。
下記も修正すれば動くようになります。

def hoge(user_id)
  # selectでid, nameだけ取得
-  user = User.select(:id, :name).find(user_id)
+  user = User.select(:id, :name, :email).find(user_id)

テストもうまくい通りました!

pry(main)> { id: user.id, name: user.name, email: user.email }
=> {:id=>1, :name=>"hoge", :email=>"hoge@example.com"}

どう思いますか?

RailsのActiveRecordを使うと基本全カラムを取得すると思うので上記のように一度はハマる人が多いと思います。

1回1回の手間はそこまでではないかもしれませんが、継続的に開発されていくシステムであれば毎回同じ事が起きます。
これは結構なコストです。また最悪の場合、気づかずにバグを生む可能性もあります。

今回のselectは開発コストやバグのリスクを上げてまで実装する必要があるのでしょうか?
私は多少最適ではなかったとしても他の人が勘違いしづらいコードのほうが良いと思っています。
これが私がカラムを絞るだけで使うselectは使わないほうが良いと思っている理由です。

selectの使いどころ

selectの存在を全否定している記事になってしまったのですが、もちろん使いどころもあります。
下記のように集計関数を使ったときです。

users_group_by_name = User.select('name, count(*) AS cnt').group(:name)
users_group_by_name.each do |u|
  p u.name
  # u.cntでカウントが取得できる
  p u.cnt
end

ただ、この場合も変数名をusersなどにしてしまうと勘違いさせてしまう可能性が高いので、それとわかる変数名にしたほうが良いでしょう。

あと、たまにjoinした先のテーブルをselectを使って直接アクセスできるようにしていることがありますが、これもとても分かりづらいのでやめたほうが良いと思います。

review = Review.select('reviews.id, users.name').joins(:user).find_by(id: 1)
# これでuser.nameにアクセスできる
review.name

普通にアソシエーション経由でアクセスするかdelegateを実装しておきましょう。

app/models/review.rb
review = Review.find_by(id: 1)
# アソシエーション経由でアクセス
review.user.name
# もしくはReviewモデルにdelegateを定義しておく (delegate :name, to: :user, prefix: true)
review.user_name

まとめ

この記事ではselectに焦点を当てましたが、複数人が同じコードを触るチーム開発では他人が理解しやすい(勘違いしづらい)コードを書くことが重要だと思います。
読みやすい(勘違いしづらい)コードを書くことで開発スピードが上がり、バグも減ると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[ruby]ドリルアウトプット

内容

image.png

実装

image.png

解説

①ターミナルから数値を入力する書き方
②条件分岐の書き方

まず①からです。ターミナルからの入力をしたいときは、getsを使います。getsだと入力した値が文字列になってしまうのでgetsの後ろに、.to_iをつけてgets.to_iとします。to_iメソッドは数値に変換するメソッドです。

②(条件分岐)は、if文を使います。今回は3つのパターンで条件を分けるため、elsifとelseも使います。
今回の条件は数値の比較です。比較には<や==などの比較演算子を使います。
今回は10以下や0以下という比較なので、<=を使います。=を付けた場合は以下や以上という意味になります。
今回の条件式で難しいのは、どの条件から書くかです。
image.png
このように10以下の数字ですの条件式を先に入れた場合、input = -1 の場合でも input <= 10 に当てはまるため0以下の数字ですと出力してくれません。
なので、10以下でなければ10より大きいため、10より大きい数値の場合の条件はelseのあとに書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Herokuへのデプロイ(2回目以降の編集用)

この記事でわかること

①Herokuでapp作成し、後にローカルで編集した変更点をデプロイする方法。

:thinking:初めてappを作成する人用ではないので注意!

前提として

ディレクトリは作成中のappに移動した状態。
アプリはRailsで作成しています。
  
  
  
  
:hugging:ではここから初めて行きます:hugging:

コマンドの流れ

①Gemfileを変更したり追加したりしている場合バンドルインストール。
私はとにかく行っておきます。

% bundle install 

  
  
②変更のコミット

% git add .   
% git commit -m "Add pg gem for Heroku"                         

  
  
③リポジトリのプッシュをしてアプリのデプロイをする。

% git push heroku master 

  
  
④DBマイグレーションする。
本番環境Heroku用の作業だと思っている。

% heroku run rake db:migrate  

  
これでアプリを開いてみてください!
デプロイ出来ているはずです:v_tone1::crown:
  
お疲れ様でした!  
  
  
  

本記事は、まだまだ初学者のため自分用の記録です。
以下の記事を参考にさせていただきました。
参考記事( [Rails アプリを初めて Heroku にデプロイしてエラーとなったとき]https://qiita.com/hmmrjn/items/e2dff8036fbbd74f049a)
  
  
  

ひとことMemo

自分はですが、
デプロイ時③の工程で良くエラーが出てしまいました。
原因は「% bundle install 」の工程を忘れていたからでした。
  
  
本番環境へ写す際にGemfileを変更をしなければならないので起きていたようです。
  
  
  

  
最後まで読んで頂きありがとうござました!
ではまた!:blush::wave_tone1:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む