- 投稿日:2022-03-03T23:51:53+09:00
ボウリングスコア計算問題を解いてみた
はじめに 今回は、ボウリングのスコアを算出するプログラムの作成を行いました。 自分が書いたソースコードと、先輩にいただいた回答例を紹介していきます。 ボウリングスコア計算問題 投球を完了した状態で最終的なスコアを算出する。 10フレーム分の投球を配列とする。 各フレームも配列とする。 ストライクがある場合、フレームの配列は 1つの要素とする。 10フレーム目は、スペアまたはストライクの場合、3つの要素とする。 ガーターの場合、0 とする。 入力例 [ [1, 1], # 1フレーム目 [0, 2], # 2フレーム目 [3, 0], # 3フレーム目 [4, 4], # 4フレーム目 [5, 5], # 5フレーム目 [1, 1], # 6フレーム目 [10], # 7フレーム目 [4, 4], # 8フレーム目 [1, 1], # 9フレーム目 [5, 5, 5] # 10フレーム目 ] 要求仕様 最終スコアを数値(整数)で返す。 入力値が整数ではない、ボウリングのルール上計算出来ない値の場合、例外を返す(例外はカスタム例外を作成する)。 カレントフレームスコアシステムは適用しない。(これまで通りの計算方法) ファールの投球はないものとする。 自分が書いたソースコード class InvalidError < StandardError end class Bowling def initialize(score) @score = score end def result total_score = 0 @score.each.with_index(1) do |points, frame| raise InvalidError unless valid?(points) total_score += calculate_score(points, frame) end total_score end private def calculate_score(points, frame) total = points.sum total += strike_bonus(frame) if strike?(points) total += spare_bonus(frame) if spare?(points) total end def spare_bonus(frame) @score[frame].first end def strike_bonus(frame) if double_or_turkey?(frame) @score[frame].first + @score[frame + 1].first else @score[frame].first + @score[frame].last end end def valid?(points) return false if points.empty? return false if points.count > 3 return false unless @score.count == 10 return false unless points.all? { |point| (0..10).include?(point) && point.integer? } true end def spare?(points) points.count == 2 && points.sum == 10 end def strike?(points) points.count == 1 && points.first == 10 end def double_or_turkey?(frame) @score[frame].count == 1 end end 処理について def result total_score = 0 @score.each.with_index(1) do |points, frame| raise InvalidError unless valid?(points) total_score += calculate_score(points, frame) end total_score end resultメソッドの中で、計算結果を入れるためのtotal_scoreを定義しています。 each.with_index()を使用して、各フレーム数と点数を元にcalculate_scoreで計算した値を足し込む処理を作成しました。 フレーム数を1から始めると、実際のボウリングのスコアと同じ感覚で進められそうと思ったので、each.with_index(1)を使用しています。 また、計算を行う前に、フレーム毎の点数をvalid?メソッドで有効な値かどうか判定しており、不正な値の場合は例外を発生させています。 class InvalidError < StandardError end カスタム例外は、InvalidErrorを作成しています。 def valid?(points) return false if points.empty? return false if points.count > 3 return false unless @score.count == 10 return false unless points.all? { |point| (0..10).include?(point) && point.integer? } true end 不正な値の判定基準は以下としていました。 フレームに対する点数が空の場合 フレームに対する点数が3つより多い場合 フレーム数が10ではない場合 フレームに対する点数の全てが0~10の整数でない場合 def calculate_score(points, frame) total = points.sum total += strike_bonus(frame) if strike?(points) total += spare_bonus(frame) if spare?(points) total end calculate_scoreでは、フレーム毎の点数を合計し、ストライク(ダブルとターキーも含む)やスペアの場合は、それぞれボウリングの計算方法に則って加点をする処理を行っています。 課題 想定される値に考慮漏れがあり、不正な値が弾けていない。 10フレーム目の条件が全く考慮できていない。 resultメソッドで、total_scoreを無駄に宣言している。 回答例 class InvalidScoreError < StandardError; end class Bowling attr_reader :score def initialize(score) @score = score end def result raise InvalidScoreError unless score_valid? calculate_total_score end private def score_valid? return false unless valid_game? return false unless valid_pitching? true end def valid_game? score.is_a?(Array) && score.size == 10 end def valid_pitching? return false unless score.all? { |flame| flame.is_a?(Array) } return false unless score[0..8].all?(&method(:valid_flame_score?)) return false unless valid_final_flame? true end def valid_flame_score?(flame) return false unless flame.all? { |pitching| pitching.is_a?(Integer) && (0..10).cover?(pitching) } flame.size == 1 && flame[0] == 10 || flame.size == 2 && (0..10).cover?(flame.sum) end def valid_final_flame? return true if final_flame_strike_or_spare? return true if valid_final_flame_score? false end def final_flame_strike_or_spare? score[9].size == 3 && (score[9][0] == 10 || score[9][0..1].sum == 10) end def valid_final_flame_score? score[9].size == 2 && score[9][0..1].sum < 10 end def calculate_total_score score.each.with_index(1).inject(0) do |total_score, (flame, index)| total_score + calculate_flame_score(index, flame) end end def calculate_flame_score(index, flame) return flame.sum if index == 10 calculate_score(index, flame) end def calculate_score(index, flame) calculated_score = flame.sum calculated_score += strike_score(index) if strike?(flame) calculated_score += spare_score(index) if spare?(flame) calculated_score end def strike?(flame) flame.size == 1 && flame[0] == 10 end def strike_score(index) return score[index][0] + score[index + 1][0] if score[index].size == 1 score[index][0..1].sum end def spare?(flame) flame.size == 2 && flame.sum == 10 end def spare_score(index) score[index][0] end end 処理について attr_reader :score まず、attr_readerでscoreが定義されています。 (以下と同じ意味) def score @score end (attr_readerをよく知らなかったので、覚えておこうと思いました。) def result raise InvalidScoreError unless score_valid? calculate_total_score end resultメソッドでは、計算処理を行う前に、score_valid?で不正なスコアが弾かれています。 def score_valid? return false unless valid_game? return false unless valid_pitching? true end def valid_game? score.is_a?(Array) && score.size == 10 end def valid_pitching? return false unless score.all? { |flame| flame.is_a?(Array) } return false unless score[0..8].all?(&method(:valid_flame_score?)) return false unless valid_final_flame? true end def valid_flame_score?(flame) return false unless flame.all? { |pitching| pitching.is_a?(Integer) && (0..10).cover?(pitching) } flame.size == 1 && flame[0] == 10 || flame.size == 2 && (0..10).cover?(flame.sum) end def valid_final_flame? return true if final_flame_strike_or_spare? return true if valid_final_flame_score? false end def final_flame_strike_or_spare? score[9].size == 3 && (score[9][0] == 10 || score[9][0..1].sum == 10) end def valid_final_flame_score? score[9].size == 2 && score[9][0..1].sum < 10 end score_valid?では、ボウリングゲーム全体に関する判定(valid_game?)と、投球に関する判定(valid_pitching?)に分かれており、有効な値のみtrueが返されています。 valid_pitching?内では、各フレーム毎のスコアが有効かどうか(valid_flame_score?)、10フレーム目として有効かどうか(valid_final_flame?)を判定しています。 さらに、valid_final_flame?内では、10フレーム目の有効なスコアかどうか(valid_final_flame_score)、10フレーム目のスペアかストライクか(final_flame_strike_or_spare)を判定しています。 これで、ボウリングのスコアとして有効な値のみが抽出されたことになります。 def calculate_total_score score.each.with_index(1).inject(0) do |total_score, (flame, index)| total_score + calculate_flame_score(index, flame) end end 計算処理は、each.with_index(1)にinject(0)がチェインされているので、 事前にtotal_scoreを定義しなくても良くなっています。 学んだこと パターンの洗い出しに漏れがないようにする。 起こり得る全ての場合を想像する。 テストファーストで実装すると、contextの洗い出しのタイミンングで漏れに気付きやすい。 コードの統一感を意識する。 配列の要素の取得方法が混在していた。(@score[frame].first → @score[frame][0]) Enamerableのはメソッドはいくらでもチェインして使える。 with_index + inject等 レシーバが配列であれば、countではなくsizeを使う方が良い。 countは、ActiveRecordの件数を取得する際に使うため、配列の要素数を取得する際は、sizeを使用する方が良い。 sizeの方が処理速度が早い。 クラスやメソッドの中身を書かない場合は、endだけで改行せずに1行で書ける。 class InvalidScoreError < StandardError; end まとめ 今回は、ボウリングのスコア計算問題ということで、想定されるパターンが多く、パターンの洗い出しが不足していたことを感じました。 実際に、どのような値が入るかというイメージを実装前に固めておくことが大切だと実感したので、今後意識して取り組みたいと思います。
- 投稿日:2022-03-03T22:56:29+09:00
ヘッダーを透明にし、body要素の上に配置する
ackground-color: rgba(0, 0, 0, 0);とposition: fixed;を使用してヘッダーを透明にし、body要素の上に配置する 具体的な使い方 header { position: fixed; background-color: rgba(0, 0, 0, 0); } こんな感じになります。 結論 1.position: fixed;はヘッダーを固定するプロパティでbody要素に被せるように配置します。スクロールしてもヘッダーは表示されたままになります。 2.background-color: rgba(0, 0, 0, 0);は4番目の値を0~1で透明度を設定でき、0に近ければより透明になります。1、2、3番目の値はbackground-colorを設定する値になります。
- 投稿日:2022-03-03T22:04:58+09:00
【Ruby】ある与えられた入力を昇順に並び替えて出力する
こんにちは。 早く駆け出したい修行僧です。 今回、paizaラーニング 「背番号順 Ruby編」にて学びがありましたので、 こちらでアウトプットしていきます。 まだ問題を解いていない方は、ここで退出することをお勧めします。 尚、「それは違うよ」など訂正箇所がございましたら、 愛のあるご指摘を頂けたら幸いです。 問題 最初にある数値nが与えられます。 その後、n行の背番号と選手名の文字列が与えられます。 背番号が小さい順に並び替えて出力してください。 コード考察 ここからは一つ一つコードがどのような処理になっていくのか見ていきます。 2-1. 最初に数値nを入力できるようにする main.rb n = gets.to_i #=> 5 まずは、getsメソッドを使用してサンプルコードを入力できるようにして、変数nに格納します。また、後に繰り返し処理を行うので、この変数nを数値として扱えるようにto_iを加えます。getsメソッドは1行を読み込んで成功したら文字列を返すようになっているからですね。[公式リファレンス] 上記の「5」という結果は適当に割り当てた数値です。ここでは「5」が入力された仮定で進めていきます。 尚、今回はn回入力されることが分かっていて、それを並び替えることを考えると空の配列を用意しておくと良いかもしれません。ですので、以下を追加していきます。配列を作るということは要素は複数であると考えられるので変数も複数形で定義しております。 main.rb n = gets.to_i players = [] 2-2. n行の文字列を入力できるようにする 続いて、最初に与えられた数値を基にその数値行背番号と選手名が与えられるようにしていきます。ここで、登場するのが繰り返し文ですね。 main.rb n.times do # 繰り返したい処理 end 上記で数値を取得することができたので、timesメソッドを活用していけば良いですね。 timesメソッドはself回繰り返します。また、正の数値以外が与えられた場合は何もしません。[公式リファレンス] ですので、今回は「5」が入力されたと仮定しているので、上記のコードは実際には以下のようなコードを意味しています。 main.rb 5.times do # 繰り返したい処理 end そして、この節ではn回入力できるようにしたいので、2-1でも使用したgetsメソッドを使っていきましょう。 main.rb 5.times do str = gets.split end splitメソッドは第1引数に与えられたセパレータによって文字列の配列を返します。[公式リファレンス] なので、例えば変数strに10 Hondaが入力されたとすると、["10", "Honda"]のような文字列の配列が格納されます。 2-3. 格納した値を配列に加えていく 変数strに格納しただけでは、繰り返し処理の中で更新されていってしまうので、どこかに格納する必要がありそうです。ここで登場するのが2-1で定義した空の配列playersです。では、コードを見ていきましょう。 main.rb 5.times do str = gets.split players << [str[0].to_i, str[1]] end まず空の配列に格納するための<<は省略した書き方です。これはpushメソッドを使った場合と同じ機能を実現してくれます。 irb s = ["野球", "サッカー"] s.push("バレー") #=> ["野球", "サッカー", "バレー"] では、右辺を見ていきましょう。先ほどまでの処理で["10", "Honda"]が変数strに格納されていました。ですので、ここでいうstr[0]は配列のインデックス番号0の値を指します。すなわち"10"ですね。そして、to_iによって数値としての10が空の配列であるplayersに格納されるということですね。もうお分かりだと思いますが、str[1]は"Honda"ですね。 [str[0].to_i, str[1]] str[0].to_i #=> 10 str[1] #=> "Honda" 2回目以降も同様に格納されていくので、2回目時点では[[10, "Honda"], [26, "Kagawa"]]のような結果が得られるかと思います。 2-4. 配列playersを並び替える 続いて、今回の目玉とも言える並び替える処理を見ていきます。昇順で並び替えるときは、sortもしくはsort_byを使用します。どちらも配列の内容を比較して、昇順に並び替えをしてくれます。両者の違いとしては、比較する条件が複雑な場合における実行速度が上げられます。sort_byは複雑な条件においてもメソッドの実行回数は要素数と同じであり実行速度としては速いです。[公式リファレンス] main.rb players.sort_by!(&:first) sort_by!とすることで破壊的に配列playersを上書きします。カッコ()の中身ですが、“symbol-to-proc”と呼ばれる省略記法です。実際には以下のようなコードと同じになります。 # 省略した書き方 players.sort_by!(&:first) # 上記の書き方と意味は同じ players.sort_by!{ |x| x.first } これによって仮に3回目が[1, "Kawashima"]、4回目が[5, "Nagatomo"]、5回目が[14, "Ito"]が入力されていたとすると、以下の順番にソートされることになるかと思います。 players.sort_by!(&:first) # [1, "Kawashima"] [5, "Nagatomo"] [10, "Honda"] [14, "Ito"] [26, "Kagawa"] #=> 5 2-5. 出力するための形に整える では、最後に期待される出力にするため整えていきます。期待される出力とは"10 Honda"のような一つの文字列にまとめて出力することです。繰り返し処理を使ってまとめていけそうですね。 main.rb players.each { |s| puts "#{s[0]} #{s[1]}" } playersには前節で破壊的にソートを行ったので、背番号が若い順に格納されていました。 それをeach文を利用して、一つ一つの要素(例えば[5, "Nagatomo"])をブロック内の引数sに代入していきます。変数展開で期待される出力と同じ形で記述していきます。s[0]はお分かりのとおり一つの要素内のインデックス番号[0]を指します。ですので、背番号に当たる部分です。後半のs[1]は選手名ということになります。 まとめ 出てきたのは基本的な文法ばかりでしたが、それを複数組み合わせることで様々な出力をすることができます。目指すゴールをイメージして、それまでの過程をどのように記述していくか言語化することが大事だと実感しました。これからも励んでいきたいと思います。 最後までお読みいただきありがとうございました! 参考 公式リファレンス Array#sort &:って??? - @soma_sekimoto さん 【Ruby】array.map(&:method)を理解する - @k-penguin-sato さん
- 投稿日:2022-03-03T21:03:06+09:00
Ruby で 数独 の3
はじめに 前回の記事で初期値の設定が終わりましたので、判定処理に移ります。 入力例は、wikiの数独の問題例を使用します。 5 3 0 0 7 0 0 0 0 6 0 0 1 9 5 0 0 0 0 9 8 0 0 0 0 6 0 8 0 0 0 6 0 0 0 3 4 0 0 8 0 3 0 0 1 7 0 0 0 2 0 0 0 6 0 6 0 0 0 0 2 8 0 0 0 0 4 1 9 0 0 5 0 0 0 0 8 0 0 7 9 初期値設定後の状態 require 'numo/narray' include Numo h = w = 9 @banmen = UInt32.zeros(h, w) @uramen = NArray.cast(Array.new(h){ Array.new(w){ (1..9).to_a } }) def mask(h, w, num) return if num == 0 h2 = (h / 3) * 3 w2 = (w / 3) * 3 maskarray = @uramen[h, w, 0..].eq(num) @uramen[h, w, 0..] *= maskarray # 数値の設置用マスク maskarray = @uramen[h, 0.., 0..].ne(num) @uramen[h, 0.., 0..] *= maskarray # 行のマスク maskarray = @uramen[0.., w, 0..].ne(num) @uramen[0.., w, 0..] *= maskarray # 列のマスク maskarray = @uramen[h2..h2+2, w2..w2+2, 0..].ne(num) @uramen[h2..h2+2, w2..w2+2, 0..] *= maskarray # ブロックのマスク end h.times do |i| a = gets.chomp.split('').map(&:to_i) a.each_with_index do |x, j| @banmen[i, j] = x mask(i, j, x) end endi p @uramen[0.., 4, 0..] # output Numo::Int32(view)#shape=[9,9] [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 4, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 5, 0, 0, 0, 0], # 可能性のある数値が1つだけ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 0, 5, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]] ここでは初期値設定後の5列目(ソース上はw=4)を取り出しますが、5行5列のマスの可能性のある数値が5のみとなっています。 よって、このマスに5の数値が入り、それによって、7行5列のマスが3に決まり、次に3行5列のマスが4に決まります。 今回のソース narray01.rb require 'numo/narray' include Numo def mask(h, w, num) h2 = (h / 3) * 3 w2 = (w / 3) * 3 @uramen[h, w, 0..] *= @uramen[h, w, 0..].eq(num) @uramen[h, 0.., 0..] *= @uramen[h, 0.., 0..].ne(num) @uramen[0.., w, 0..] *= @uramen[0.., w, 0..].ne(num) @uramen[h2..h2+2, w2..w2+2, 0..] *= @uramen[h2..h2+2, w2..w2+2, 0..].ne(num) end def input(h, w) h.times do |i| a = gets.chomp.split(' ').map(&:to_i) a.each_with_index do |x, j| @banmen[i, j] = x mask(i, j, x) if x != 0 end end end def check1(h, w) if @uramen[h, w, 0..].ne(0).count_true == 1 num = @uramen[h, w, 0..].sum @banmen[h, w] = num mask(h, w, num) true else false end end def loop_func(h, w) loop_flg = true while loop_flg loop_flg = false h.times do |i| w.times do |j| if @banmen[i, j] == 0 loop_flg ||= check1(i, j) end end end end end h = w = 9 @banmen = UInt32.zeros(h, w) @uramen = NArray.cast(Array.new(h){ Array.new(w){ (1..9).to_a } }) input(h, w) loop_func(h, w) p @banmen # output Numo::UInt32#shape=[9,9] [[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7], [8, 5, 9, 7, 6, 1, 4, 2, 3], [4, 2, 6, 8, 5, 3, 7, 9, 1], [7, 1, 3, 9, 2, 4, 8, 5, 6], [9, 6, 1, 5, 3, 7, 2, 8, 4], [2, 8, 7, 4, 1, 9, 6, 3, 5], [3, 4, 5, 2, 8, 6, 1, 7, 9]] 今回のポイントは判定処理1です。 if @uramen[h, w, 0..].ne(0).count_true == 1 あるh行w列で0ではない数値が1個の場合、そのマスに当てはまる可能性のある数値が1個となりますので、そのマスを埋めることができます。 wikiの数独の問題例は、なんとかcheck1で解くことができました。 としますと、前々記事の試作1号と同機能になりますが、今回のソースはメソッド単位で確認することができますので、見通しがいいと思われます。 まとめ 今回は、判定処理1を追加し、簡単な問題を解きました。
- 投稿日:2022-03-03T19:26:31+09:00
【Heroku】herokuでRailsアプリをデプロイする際の基本的な手順のまとめ
本記事の前提 使用環境 Rails 5.2.6.2 ruby 2.6.6 git 2.18.5 AWS Cloud9 省略作業 Herokuアカウントの登録 Gitのインストール デプロイするアプリの作成 デプロイする手順 1.Heroku CLIのインストール Herokuの操作をコマンドラインから行えるようにするために、Heroku CLIをインストールしていきます。 以下の公式サイトからご自身のOSに合ったものをインストールして下さい。 AWS Cloud9の場合 以下のコマンドを全てコピーし、ターミナルに貼り付けて実行して下さい。 terminar $ curl -OL https://cli-assets.heroku.com/heroku-linux-x64.tar.gz tar zxf heroku-linux-x64.tar.gz && rm -f heroku-linux-x64.tar.gz sudo mv heroku /usr/local echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.bash_profile source $HOME/.bash_profile > /dev/null 2.デプロイするアプリのディレクトリに移動しherokuにログインする terminar $ cd アプリ名 $ heroku login --interactive //herokuにログインするためのコマンド メールアドレスとパスワードが求められるので、それぞれ入力しエンターを押して、無事にログインに成功すると、Logged in as Eメールアドレスと表示されますので確認して下さい。 3. Railsアプリをデプロイするための流れ 大きく以下のような項目に分けてRailsアプリの設定を変更していきます 3-1. rails_12factorの導入 静的アセットファイルをHeroku上でうまく動作するように調整してくれるGemになります。以下のようにGemfileに下記の記述を追加して下さい。 Gemfile group :production do gem 'rails_12factor' end 3-2. Gemfileの各項目の設定変更 次にGemfileの内容をデプロイできる状態に変更していきます。 まず、gem 'sqlite3'と記述がある部分をコメントアウトにします。 Gemfile # gem 'sqlite3' 次に、group :development, :test do - endの中にgem 'sqlite3'を追記します。 Gemfile group :development, :test do gem 'sqlite3' end そして本番環境でPostgreSQLを使っていくために、以下のような設定を追記します。 Gemfile group :production do gem 'pg' end 最後にGemfileの変更を適用するためbundle installを実行します。このとき本番環境に関係するproduction以外の部分を対象とするため、以下のようなオプションをつけてコマンドを実行していきます。 terminar $ bundle install --without production 3-3. config/datebase.ymlの設定変更 本番環境で実際にデータベースと接続するための設定を変更するためdatebase.ymlのproduction:以降を以下のように変更していきます。このときproduction:直下の記述が半角スペース2個分空いていることを確認して下さい。 config/datebase.yml production: <<: *default adapter: postgresql encoding: unicode pool: 5 3-4. config/environments/production.rbの設定 続いて本番環境でassets以下のフォルダから動的な画像の表示していくためにproduction.rbの次の記述をtrueに変更していきます。 config/environments/production.rb config.assets.compile = true 3-5. Heroku上にアプリを作成する ここからHeroku上にアプリを作成していきます。ここでHerokuではGitコマンドでアプリを登録していく関係で、ローカルリポジトリを作成されていない場合はgit initコマンドから作成しましょう。それからheroku createコマンドで新しいアプリを作成していきます。このときアプリ名を指定したい場合は、heroku create アプリ名とすると指定できますが、既に使用されている名前の場合は指定できないので注意して下さい。 terminar $ git init // ローカルリポジトリ作成後 $ heroku create ここで、ターミナルに表示されたアプリのURLにアクセスしてアプリが作成されていることを確認しましょう。 そして、Gitコマンドからファイルの管理が済んでいない方は以下のようにGitコマンドを実行して下さい。 terminar $ git add . $ git commit -m "upload to heroku" そしていよいよアプリのデプロイになります。git push heroku masterからアプリのプッシュを行い、それが終わったら本番環境でのデータベースの作成をheroku run rails db:migrateコマンドで実行します。 terminar $ git push heroku master // 終わり次第以下のコマンドでデータベースを作成する $ heroku run rails db:migrate 補足情報 デプロイしたアプリのURLや情報は以下はheroku apps:infoコマンドから確認できます。 terminar $ heroku apps:info 本番環境にdb/seeds.rbに記述した初期データを登録するためには、heroku run rake db:seedを実行して反映させていきます。 terminar $ heroku run rake db:seed デプロイ後のアプリの更新については、先程と同様にGitコマンドから変更したファイルを反映させます。 terminar $ git add . $ git commit -m "update to heroku" $ git push heroku master こんなエラーが発生したとき こちらの記事の内容を参考にしてみて下さい。 参考記事 【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】 RailsアプリをHerokuにデプロイする手順 herokuで本番環境にデータ投入
- 投稿日:2022-03-03T14:03:34+09:00
【Heroku】Rubyバージョンの差異によるエラーの解決
発生したエラー git commitしたの後にgit push heroku masterを実行し、アプリをデプロイしようとしたところ、Rubyバージョンが2.6.3のためにheroku-20にデプロイできないという旨のエラー発生した。。 解決方法 解決策としてherokuのstackを20から18に下げる方法もあるが、今回はRubyのバージョンをheroku-20に対応している2.6.6に変更することで解決していく。Rubyのバージョンを2.6.6に変更する際の作業の流れは以下のようになる。 1.Rubyバージョンの確認し、Ruby2.6.6をインストール terminal $ rvm -v //rvmのバージョンとインストールされていることを確認 $ rvm list //インストール済みのRubyバージョンを確認 $ rvm install 2.6.6 //Rubyのバージョン2.6.6をインストール 2.Rubyの使用バージョンを2.6.6に変更し、デフォルトのバージョンも変更 terminal $ rvm use 2.6.6 //使用するRubyバージョンを2.6.6に切り替える $ rvm --default use 2.6.6 //デフォルトで使用するRubyバージョンを2.6.6に切り替える $ rvm list //念のためRubyバージョンを確認し、2.6.6がcurrent && defaultなのを確認する 3.GemfileのRubyのバージョンを2.6.6に変更 Gemfile ruby '2.6.6' 4.Gemfile.lockを削除し、再度bundle installを実行 terminal $ rm Gemfile.lock //Gemfile.lockファイルを削除する $ bundle install 5.変更したファイルの差分を反映するためGitコマンドを実行 terminal $ git add . $ git commit -m 'change ruby version' $ git push heroku master 終わりに heroku-20に対応していないRubyバージョンを使用してデプロイしようとするとエラーが発生し、stackのバージョンを下げるよう提案される。 herokuのstackバージョンを下げることで解決しようとすると、アプリのパフォーマンスが低下したり、stackのサポート終了日が早くやってきてしまうといったデメリットがある。そのためHerokuも推奨しているようになるべく最新のサポートバージョンのRubyを使用するのが無難みたいですね。。 参考記事 【解決】heroku-20 You are trying to install ruby-2.6.5 on heroku-20. The Ruby version you are trying to install does not exist on this stack. AWS Cloud9でRubyのバージョンをアップデートする方法 AWS Cloud9 でRuby on Rails の開発環境を構築する
- 投稿日:2022-03-03T13:24:52+09:00
【Rails】ページネーション機能の導入
#gem kaminariによるページネーション機能の実装 実装の流れ Gemfileにgem 'kaminari'を追加する。 bundle installを実行してkaminariをインストールする。 rails g kaminari:configを実行してkaminariの設定ファイルを作成する。 rails g kaminari:views defaultを実行してページャで利用するテンプレートを作成する。 該当するコントローラー,ビューにページャを実装していく。 1.Gemfileにgem 'kaminari'を追加する。 Gemfile gem 'kaminari' 2.bundle installを実行してkaminariをインストールする。 terminal $ bundle install 3.rails g kaminari:configを実行してkaminariの設定ファイルを作成する。 terminal $ rails g kaminari:config 4.rails g kaminari:views defaultを実行してページャで利用するテンプレートを作成する。 terminal $ rails g kaminari:views default 5.該当するビューにヘルパーでページャを実装していく。 app/views/posts/index.html.erb <%= paginate @posts %>
- 投稿日:2022-03-03T11:16:07+09:00
Ruby問題 特定の数字を検知するプログラムの実装
はじめに プログラミングスクールでRubyの問題を毎日解いています。 これから平日は毎日何かしらアウトプットのため投稿を続ける予定です。 今回は特定の数字が存在するかどうかを判定するプログラムをinclude?メソッドを使用して実装していきます。 問題 以下の要件を満たすarray123メソッドを実装。 ・配列内に1,2,3が全て入っている場合は、「True」と出力する ・配列内に1,2,3の全てが入っていない場合は、「False」と出力する 雛形 def array123(nums) # 処理を記述 end # 呼び出し例 array123([1, 1, 2, 3, 1]) include?メソッドについて Ruby 3.1 リファレンスマニュアルinstance method String#include? Ruby 3.1 リファレンスマニュアルinstance method Array#include? まずはif文で1,2,3が全て入っている場合は、「True」、1,2,3の全てが入っていない場合は、「False」を記述しました。 def array123(nums) if #条件式 puts "True" else puts "False" end end 条件式は公式マニュアルを参考にし、下記のように記述しました。 &&で1かつ、2かつ、3という記述をしています。(とっても単純ですね) if nums.include?(1) && nums.include?(2) && nums.include?(3) あとはこれをガッチャンコしてこう def array123(nums) if nums.include?(1) && nums.include?(2) && nums.include?(3) puts "True" else puts "False" end end これで1,2,3全てが含まれているかを判別することが出来ました
- 投稿日:2022-03-03T11:16:07+09:00
Rubyで特定の数字を検知するプログラムを実装する
はじめに プログラミングスクールでRubyについて学習しています。 これから平日は毎日何かしらアウトプットのため投稿を続ける予定です。 今回は特定の数字が存在するかどうかを判定するプログラムをinclude?メソッドを使用して実装していきます。 問題 以下の要件を満たすarray123メソッドを実装します。 ・配列内に1,2,3が全て入っている場合は、「True」と出力する ・配列内に1,2,3の全てが入っていない場合は、「False」と出力する 雛形 def array123(nums) # 処理を記述 end # 呼び出し例 array123([1, 1, 2, 3, 1]) include?メソッドについて Ruby 3.1 リファレンスマニュアルinstance method String#include? Ruby 3.1 リファレンスマニュアルinstance method Array#include? まずはif文で1,2,3が全て入っている場合は、「True」、1,2,3の全てが入っていない場合は、「False」を記述しました。 def array123(nums) if #条件式 puts "True" else puts "False" end end 条件式は公式マニュアルを参考にし、下記のように記述しました。 &&で1かつ、2かつ、3という記述をしています。(とっても単純ですね) if nums.include?(1) && nums.include?(2) && nums.include?(3) あとはこれをガッチャンコしてこう def array123(nums) if nums.include?(1) && nums.include?(2) && nums.include?(3) puts "True" else puts "False" end end これで1,2,3全てが含まれているかを判別することが出来ました
- 投稿日:2022-03-03T11:01:54+09:00
テキストエリアの高さを行数に合わせて自動で変更する
cssでheightプロパティー、 値autoを使ってテキストエリアの高さを中身の行数によって自動で変更 具体的な使用例 .css{ height: auto; } 結果 こんな感じで長い文章でも表示できます。
- 投稿日:2022-03-03T09:01:12+09:00
rubyにおけるcount,size,lengthメソッドの違い
結論からいうと、配列、ハッシュ、文字列によって異なります。 配列のとき ある条件を満たす要素数を知りたいとき→count 単に配列の要素数を知りたいとき→length, sizecountも使える ハッシュのとき ある条件を満たすkeyとvalueの要素数を知りたいとき→count 単にKeyとvalueの要素数を知りたいとき→length, sizecountも使える 文字列のとき ある条件を満たす要素数を知りたいとき→count 単に文字数をしりたいとき→length, sizecountは使えないです この結論をもとに話を進めていきます。 lengthとsize 先ほどの説明からも分かる通り、同じメソッドなんです。やってることが同じです。 実際にコードを打っても同じ結果が返ってきます。 count countは文字列以外を除いては、lengthとsizeと同じ働きをします。書き方に多少の差はありますが、同じ動きをしてくれます。 では何が違うのか?length/sizeとcountだと処理速度が違ってきます 実際に見ていきましょう 今回は、benchmark_driver という gem を使って処理速度を測ってみます ※以下は@scivola様のコードと結果を拝借しております。 gem "benchmark_driver" require "benchmark_driver" Benchmark.driver do |r| r.prelude "a = Array.new(1000){ rand }" r.report "a.count" r.report "a.length" end すると結果は、 Comparison: a.length: 161043915.9 i/s a.count: 33329644.2 i/s - 4.83x slower lengthは1秒間で1B回処理なされ、countだと1秒間に10M回の処理がなされている。 なぜこのような結果が生まれてしまうのか。countというメソッドについて深堀りしていきたいとおもいます arrayやhashでのcountというメソッドは、 Enumerableというモジュールのメソッドなのです。参考資料 そして、Enumeriableはeachを使ってメソッドが定義されているんです。 eachはN+1問題からもわかるように、不必要な値を取得してしまうことが多々あるんですね。 そのため、countはlength/sizeメソッドに比べて処理が遅いんです。 今回は、length/sizeメソッドとcountメソッドの違い、そしてcountメソッドはなぜ処理が遅いのかという部分を解説させていただきました 以上です。何か間違いがございましたら、ご教示いただけますと幸いです。 【参考文献】
- 投稿日:2022-03-03T06:24:23+09:00
ridgepoleを導入したのに、" Migrations are pendding "とはドユコト? のときに見る記事
はじめに 今更ながらrailsアプリケーションにridgepoleを導入してみました。 その際にrails migrateとは別れたはずなのですが、railsのエラー画面で"Migrations are pendding"というmigrationファイルと付き合っていたころの見慣れたエラーに出くわしてしまいました。 別れた次の日にばったりいつもの公園で出会ってしまったカップルのように・・・ どうやら復縁を迫られているみたいです。 そこで、そんな人がそんなときに見る用の記事(migrationと未練を残さずお別れする方法)として、やったことをまとめました。 「そもそもridgepoleとはなんぞ?」 という方は、偉大な先人たちがたくさん知恵を残してくれているのでそちらをご参照ください! (導入まで簡単にできます) 原因調査 1. railsの見ているDBと実際のDBが異なるのではと疑う これまではmigrationファイルでの管理だったため、railsの参照先としているDBが古い方のDBを参照したままなのではないかという、憶測を立てたので確認。 まずは、ActiveRecordで使用しているDBの接続情報を調べてみる。 [1] pry(main)> ActiveRecord::Base.connection_config # => 接続しているDBの情報 {:adapter=>"XXX", :encoding=>"XXX", :charset=>"XXX", :collation=>"XXX", :reconnect=>false, :pool=>5, :database=>"XXX", :username=>"XXX", :password=>"XXX", :host=>"XXX" 次に、環境ごとに設定している、railsのDB情報は、アプリ名/config/database.ymlに記載されているので、確認。 config/database.yml development: <<: *default database: <%= XXX %> username: <%= XXX %> password: <%= XXX %> host: <%= XXX %> socket: <%= XXX %> staging: ~~~ production: ~~~ ちゃんと同じDBを参照していた。 結論: これではない 2. migrationファイル消してなくない? 当たり前のことだったが、migrationファイルを消し忘れていた? (正確には消さなくてもいけるんじゃないかという、migrationに対する未練があった) ridgepoleはアプリ名/db/Schemafileを参照しているので、当たり前だが、ここにmigrationファイルが残っていたらそれはお別れできないですな! # 未練残さずきっぱりと削除 $ rm -rf db/migrate $ rm db/schema.rb これで無事エラーは解決! だがこれで終わってしまうのも、情けないので追加情報 おまけ ridgepoleコマンドをrakeタスク化 通常ridgepoleは以下のようなコマンドで流す -cはconfigファイルの指定。(※ただしコマンドを打つ場所からの相対パスで指定) -Eはdatabase.ymlに記載のどの環境のDB情報か。 ridgepoleコマンド $ bundle exec ridgepole -c ./config/database.yml -E development --apply --file ./db/Schemafile たが長いし、覚えられないし、ということでrakeタスクファイルを作成し、アプリ内(/lib/tasks)に配置。 ridgepole.rake namespace :ridgepole do schema = Rails.root.join('db', 'Schemafile') config = Rails.root.join('config', 'database.yml') environment = ENV['RAILS_ENV'] || 'development' desc 'apply schema migration' task apply: :environment do result = `bundle exec ridgepole --apply --env #{environment} --file #{schema} --config #{config}` puts result end desc 'dry run' task dry_run: :environment do result = `bundle exec ridgepole --apply --env #{environment} --file #{schema} --config #{config} --dry-run` puts result end end これで次回以降は、↓だけで済む! $ bundle exec rake ridgepole:apply メリットでもありデメリットでもあるが、 ridgepoleではSchemafileを直接編集するので、CREATE TABLE ~の文を削除するだけで、DBを簡単にDROPできてしまう。 そんな時のためにも、テーブルDROPのリスク管理を行うようなrakeタスクを作成しておくとより安心だと思う。(↓参考記事) 運用方法 今回のようなことがまた起きないためにも確認してみる。 例えば、rails g コマンドでモデルを新規作成したときを見ると・・・ $ rails c $ rails g model Test => db/migrate ファイルが作られてしまう なのでrails g modelを使用する際にはオプションで --skip-migration を指定 $ rails g model Test --skip-migration ちなみに・・・↓で rails g model で作られてしまったファイルを全て削除できる $ rails d model Test おそらくrailsコマンドの使用した際には、migrationファイルが自動で作られるようになっている。 なので、モデルを作成するときには、--skip-migrationをつけるか、手動でモデルファイルを作成しましょう! という結論も一つあるが、 もしくは↓記事にあるように、application.rbに書くことによって解決できそうです。 さいごに ridgepoleに変えてみて、migrationファイルの管理(束縛)から解放されすごく楽になりました。 男ならいさぎよく未練なくきっぱりとお別れすることが大事だと学びました。 「今までありがとうmigration、これからよろしくrigepole!」
- 投稿日:2022-03-03T01:29:45+09:00
limitとoffsetについて
activerecordはrailsを使う際にはよく使われるとおもいますが、コマンドを打ったりコードを書いたりする際にlimitとoffsetをちゃんと知らんかったのでまとめようとおもいます。 limit これは名前から分かるかもしれませんね。 引数に数値を渡すことで、指定したレコード数を取得できます。 例えば、 User.limit(2) # 最初のレコードから2件レコードを獲得される offset これは、特定の位置からそれ以降のレコード数を取得できるメソッドです。 User.offset(3) # 最初から3件目のレコードから最後までのレコードを獲得する 最後にこの2つを合わせた書き方があるんです。 途中から指定した件数を取得 以下のようなコードになります。 User.limit(3).offset(7) # 最初から7番目のレコードから3件値を取得する 次回は、link_toのソースコードをアウトプットしようと思っていますので重めの内容になるかと思います 以上です。何か間違いがございましたら、ご教示いただけますと幸いです。 【参考資料】