- 投稿日:2020-09-22T23:40:23+09:00
each_with_indexメソッドを使ってみる
表題の通りです。
each_with_indexメソッドを使ってコードを書いてみます。each_with_indexメソッドとは
要素の繰り返し処理と同時に、その要素が何番目に処理されたのか表すことができるメソッド。
要素の繰り返し処理であればeachメソッドもできるが、
each_with_indexメソッドは、何番目に処理されたものかも表示できるのが特徴。
下記のように書く。配列名.each_with_index do |item, i| # 処理 end使ってみた(その1)
fruits = ["みかん", "パイナップル", "りんご"] fruits.each_with_index do |fruit, i| puts "#{i}番目のフルーツは、#{fruit}です。" end出力結果は下記のようになる。
0番目のフルーツは、みかんです。 1番目のフルーツは、パイナップルです。 2番目のフルーツは、りんごです。使ってみた(その2)
配列に、ある値が含まれているかを検索して、結果を出力するメソッドを
each_with_indexメソッドを使って書いてみます。
わざわざeach_with_indexメソッド使わなくてもバイナリーサーチ使えば良いのでは配列は
input = [3, 6, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93]
検索したい値は変数target_num
に代入するとします。
下記のような検索結果が出るようにします。# 検索したい値が配列の中にあったとき。たとえば検索したい値が12だった場合 左から4番目にあります # 検索したい値が配列の中にないとき その数は含まれていません回答例
def search(target_num, input) input.each_with_index do |num, index| if num == target_num puts "左から#{index + 1}番目にあります" return end end puts "その数は含まれていません" end input = [3, 6, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93] target_num = gets.to_i search(target_num, input)解説
今回は検索する処理を行うメソッドをsearchメソッドと定義しました。
そのsearchメソッドの中で、each_with_indexメソッドを使い、
配列input
に格納されている値をnum
として1つずつ取り出し、取り出された順で番号付けをします。その番号はindex
に入ります。
num
と、target_num
(検索したい値が入っている)が同じか比べます。
もし同じであれば、その値が配列の左から数えて何番目にあるのかを出力したいので、
puts "左から#{index + 1}番目にあります"
とします。
そして、もう検索結果が出てしまって、これ以上の検索が必要なくなるのでreturn
で終わりにします。配列に格納されているすべての値と、検索したい値が同じにならなかった場合は、
puts "その数は含まれていません"
とします。おわり
以上です。
本題から少しずれますが解説部分を書くのがなかなか難しい・・。
最後まで読んでくださった方ありがとうございました。
- 投稿日:2020-09-22T23:32:02+09:00
未経験からエンジニア就職するために一生懸命作ったクソアプリを晒していく。
はじめに
未経験からエンジニアになる方法として、プログラミングスクールに通う人は多いと思います。
ただスクールで作った物を面接で見せても、採用サイドからすると「またそれね。」って感じらしいです。某有名スクールのなんとかエキスパートだと、メルカリのクローン作りますよね。
僕は実際の面接で、「それの説明はいいよ」って言われました。就職するためには
プラスアルファでどれだけ勉強したか
が重要となってきます。
ただ、どんなものを作ればよいか悩む人は多いのではないかなと思います。当時、僕が就職するために作ったクソアプリたちが出てきたので、参考にして頂ければ幸いです。
「あ、こんなのでいいんだ」と就職活動の緊張から開放されることを願っております。経歴
2018年9月〜12月 プログラミングスクール
2019年1月 クソアプリ作り始める
2019年2月 内定が出る
2019年4月 東京で就職作ったクソアプリたち
※ スマホで動くか微妙です。
神になって惑星を消すアプリ
惑星の画像が並んでいて、クリックすると惑星が消える。
JavaScriptでクリックした要素をRemoveしてるだけだと思います。
このアプリのURL目指せホームラン王!!江越大賀の挑戦
江越大賀という当たれば飛ぶと言われている選手が阪神タイガースにいて、
その選手がホームラン王になれるのは何年後かを計算するアプリ。おそらくif文でぐちゃーっと計算してる。
あいあい傘メーカー
二人分の名前を入力したら相合い傘に名前が書かれる。
たしか、全部Railsで作っていて、POSTとか使いたいなと思って作った。全部上書きで一番上のレコード取得してくるだけの仕様。
文字を縦にするのが難しかった記憶もある。
このアプリのURLジョブスの発表会
ジョブスが発表したっぽい画像を作るアプリ。
入力した文字の画像を生成できる。
Canvasに文字を入れるだけなんだけど、今見たら日本語に対応してなかった。
テキストエリアの文字消してもCanvasから消せなかったから、「TextReset」っていうボタンを付けたみたい。
「CreateImage」押したら下に、画像が貼付けされる。おのののかを探せ
「おのののか」っていう文字列を探すアプリ。
本当のランダムに生成していなくて、一列分の「おのののか」っぽい文字列を配列で登録しておいて、
それを並び替えているっぽい。その他
他にも色々ありましたのでよかったら見てみてください。
結論
誰がどう見てもクソアプリでした。
でも、コレ見せて就職できたのだから作ることって大事なんだと思います。質はともかくとりあえず作ってみるのが良いです。
自己紹介
今更ツイッタはじめました。
よかったらフォローしたり、質問してください。ツイッター←こちら
- 投稿日:2020-09-22T23:11:10+09:00
link_toで遷移した時に1度リロードしないとJSが上手く表示されない問題を解決したい
目次
はじめに
現在、ポートフォリオを作成中であり、
その中でJavaScriptを用いたカレンダー機能を実装していたのですが、link_toからカレンダーページに遷移した際に、
1度リロードしないとJavaScriptが適用されない問題が生じたので、この辺の原因を明確にして、記録を残そうということでこの記事を執筆しています。
本記事は、プログラミング初心者によるものであることをご了承ください。JSの表示を邪魔するのは・・・
ずばり、Turbolinksです。
コイツのせいで、頑張って書いたJSが遷移先で表示されないのです。このように考えると何だか憎たらしいヤツに思えてきちゃいますが、
色々調べていると、実は良いヤツらしい。良いヤツなんだけど、たまに余計なお節介をして、
うっかり我々の大事な大事なJSを隠してしまうようです。Turbolinks、意外と憎めないやつかもしれない。
ってことでもっと知りたくなってきましたね(無理矢理)。次項で詳しく見ていきます。
Turbolinksって何者?
Turbolinksとは、リンククリックに対して、ページ遷移を自動的にAjax化してくれるライブラリです。
①aタグをクリックすると、遷移先のページをAjaxで取得する
②その取得したページが要求するCSSやJSが、現在のページのものと同じである場合、titleやbodyのみ(画面の一部だけ)を置き換える
というのがTurbolinksの主な仕組みです。
Turbolinks導入のメリット
Turbolinks導入の最大のメリットは、画面表示を高速化してくれることです。
通常であれば、リンクをクリックすると、指定されたページに遷移するため、その分時間がかかりますが、Turbolinksを導入している場合、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する仕組みになっているので、結果的に読み込みに時間がかからず、ページの表示が高速化するというわけです。
サイトを見ている側からすると、少しでも待たされる時間をなくしたいわけですから、
TurbolinksはUXを向上させる救世主って感じですね。何だ、やっぱり良いヤツじゃん
最初から導入されているってことは、それなりに便利で使えるからなんですよね。とは言いながらも、どんなに良いヤツでも、時と場合によっては煩わしく感じてしまうものです。
Turbolinks導入のデメリット
Turbolinks導入のデメリットは、JSやCSSの適切な表示を妨げてしまう場合があることです。
メリットのところで、Turbolinksは画面の一部だけを更新させると説明しましたが、
この仕組みこそが、JavaScriptが上手く反映しない問題を生じさせていると言えます。
上記で、Turbolinksを用いると、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する事になると述べました。ブラウザのページ遷移が行われていないので、JavaScriptのイベントが発火せず、結果的にJavaScriptが上手く反映しないというわけです。
これが噂のTurbolinksの余計なお節介ってやつか。なるほど。
解決策
Turbolinksを導入しつつ、必要なところではそれを無効化し、
何とかJavaScriptの動作を上手く反映させたい。そういう人向けに、どうやら2つの解決策があるみたいです。
① Turbolinksが提供するイベントを追加
JSファイルに、Turbolinks専用のイベント
turbolinks:load
を追加することでJSを正常に動作させます。test.jsdocument.addEventListner('turbolinks:load', function() { console.log('Loaded'); });② 特定のリンクでTurbolinksを無効化
aタグ(link_to)に
data: {"turbolinks" => false}
を加えることによって、
Turbolinksを無効化し、遷移先に飛ぶようにすることでJSを正常に動作させます。test.html.slim=link_to "テスト", tests_path, data: {"turbolinks" => false}最後に
ポートフォリオ作成をきっかけにたまたまTurbolinksを調べることになったわけですが、
調べれば調べる程、まだまだ奥が深そうな感じがしました。
今回は、基本の部分で留めておこうと思いますが、また機会があれば調べてみたいところです。尚、本記事の内容で間違っている点や不足している点などがありましたら、ご教示いただけると幸いです。
- 投稿日:2020-09-22T23:11:10+09:00
Turbolinks : link_toで遷移した時に1度リロードしないとJSが上手く表示されない問題を解決したい
目次
はじめに
現在、ポートフォリオを作成中であり、
その中でJavaScriptを用いたカレンダー機能を実装していたのですが、link_toからカレンダーページに遷移した際に、
1度リロードしないとJavaScriptが適用されない問題が生じたので、この辺の原因を明確にして、記録を残そうということでこの記事を執筆しています。
本記事は、プログラミング初心者によるものであることをご了承ください。JSの表示を邪魔するのは・・・
ずばり、Turbolinksです。
コイツのせいで、頑張って書いたJSが遷移先で表示されないのです。このように考えると何だか憎たらしいヤツに思えてきちゃいますが、
色々調べていると、実は良いヤツらしい。良いヤツなんだけど、たまに余計なお節介をして、
うっかり我々の大事な大事なJSを隠してしまうようです。Turbolinks、意外と憎めないやつかもしれない。
ってことでもっと知りたくなってきましたね(無理矢理)。次項で詳しく見ていきます。
Turbolinksって何者?
Turbolinksとは、リンククリックに対して、ページ遷移を自動的にAjax化してくれるライブラリです。
①aタグをクリックすると、遷移先のページをAjaxで取得する
②その取得したページが要求するCSSやJSが、現在のページのものと同じである場合、titleやbodyのみ(画面の一部だけ)を置き換える
というのがTurbolinksの主な仕組みです。
Turbolinks導入のメリット
Turbolinks導入の最大のメリットは、画面表示を高速化してくれることです。
通常であれば、リンクをクリックすると、指定されたページに遷移するため、その分時間がかかりますが、Turbolinksを導入している場合、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する仕組みになっているので、結果的に読み込みに時間がかからず、ページの表示が高速化するというわけです。
サイトを見ている側からすると、少しでも待たされる時間をなくしたいわけですから、
TurbolinksはUXを向上させる救世主って感じですね。何だ、やっぱり良いヤツじゃん
最初から導入されているってことは、それなりに便利で使えるからなんですよね。とは言いながらも、どんなに良いヤツでも、時と場合によっては煩わしく感じてしまうものです。
Turbolinks導入のデメリット
Turbolinks導入のデメリットは、JSやCSSの適切な表示を妨げてしまう場合があることです。
メリットのところで、Turbolinksは画面の一部だけを更新させると説明しましたが、
この仕組みこそが、JavaScriptが上手く反映しない問題を生じさせていると言えます。
上記で、Turbolinksを用いると、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する事になると述べました。ブラウザのページ遷移が行われていないので、JavaScriptのイベントが発火せず、結果的にJavaScriptが上手く反映しないというわけです。
これが噂のTurbolinksの余計なお節介ってやつか。なるほど。
解決策
Turbolinksを導入しつつ、必要なところではそれを無効化し、
何とかJavaScriptの動作を上手く反映させたい。そういう人向けに、どうやら2つの解決策があるみたいです。
① Turbolinksが提供するイベントを追加
JSファイルに、Turbolinks専用のイベント
turbolinks:load
を追加することでJSを正常に動作させます。test.jsdocument.addEventListner('turbolinks:load', function() { console.log('Loaded'); });② 特定のリンクでTurbolinksを無効化
aタグ(link_to)に
data: {"turbolinks" => false}
を加えることによって、
Turbolinksを無効化し、遷移先に飛ぶようにすることでJSを正常に動作させます。test.html.slim=link_to "テスト", tests_path, data: {"turbolinks" => false}最後に
ポートフォリオ作成をきっかけにたまたまTurbolinksを調べることになったわけですが、
調べれば調べる程、まだまだ奥が深そうな感じがしました。
今回は、基本の部分で留めておこうと思いますが、また機会があれば調べてみたいところです。尚、本記事の内容で間違っている点や不足している点などがありましたら、ご教示いただけると幸いです。
- 投稿日:2020-09-22T23:11:10+09:00
Turbolinks : link_toで遷移した時にリロードしないとJSが上手く表示されない問題を解決したい
目次
はじめに
現在、ポートフォリオを作成中であり、
その中でJavaScriptを用いたカレンダー機能を実装していたのですが、link_toからカレンダーページに遷移した際に、
1度リロードしないとJavaScriptが適用されない問題が生じたので、この辺の原因を明確にして、記録を残そうということでこの記事を執筆しています。
本記事は、プログラミング初心者によるものであることをご了承ください。JSの表示を邪魔するのは・・・
ずばり、Turbolinksです。
コイツのせいで、頑張って書いたJSが遷移先で表示されないのです。このように考えると何だか憎たらしいヤツに思えてきちゃいますが、
色々調べていると、実は良いヤツらしい。良いヤツなんだけど、たまに余計なお節介をして、
うっかり我々の大事な大事なJSを隠してしまうようです。Turbolinks、意外と憎めないやつかもしれない。
ってことでもっと知りたくなってきましたね(無理矢理)。てなわけでもう少し詳しく見ていきます。
Turbolinksって何者?
Turbolinksとは、リンククリックに対して、ページ遷移を自動的にAjax化してくれるライブラリです。
①aタグをクリックすると、遷移先のページをAjaxで取得する
②その取得したページが要求するCSSやJSが、現在のページのものと同じである場合、titleやbodyのみ(画面の一部だけ)を置き換える
というのがTurbolinksの主な仕組みです。
Turbolinks導入のメリット
Turbolinks導入の最大のメリットは、画面表示を高速化してくれることです。
通常であれば、リンクをクリックすると、指定されたページに遷移するため、その分時間がかかりますが、Turbolinksを導入している場合、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する仕組みになっているので、結果的に読み込みに時間がかからず、ページの表示が高速化するというわけです。
サイトを見ている側からすると、少しでも待たされる時間をなくしたいわけですから、
TurbolinksはUXを向上させる救世主って感じですね。何だ、やっぱり良いヤツじゃん
最初から導入されているってことは、それなりに便利で使えるからなんですよね。とは言いながらも、どんなに良いヤツでも、時と場合によっては煩わしく感じてしまうものです。
Turbolinks導入のデメリット
Turbolinks導入のデメリットは、JSやCSSの適切な表示を妨げてしまう場合があることです。
メリットのところで、Turbolinksは画面の一部だけを更新させると説明しましたが、
この仕組みこそが、JavaScriptが上手く反映しない問題を生じさせていると言えます。
上記で、Turbolinksを用いると、ページの遷移は行われず、Ajaxにより画面の一部だけを更新する事になると述べました。ブラウザのページ遷移が行われていないので、JavaScriptのイベントが発火せず、結果的にJavaScriptが上手く反映しないというわけです。
これが噂のTurbolinksの余計なお節介ってやつか。なるほど。
解決策
Turbolinksを導入しつつ、必要なところではそれを無効化し、
何とかJavaScriptの動作を上手く反映させたい。そういう人向けに、どうやら2つの解決策があるみたいです。
① Turbolinksが提供するイベントを追加
JSファイルに、Turbolinks専用のイベント
turbolinks:load
を追加することでJSを正常に動作させます。test.jsdocument.addEventListner('turbolinks:load', function() { console.log('Loaded'); });② 特定のリンクでTurbolinksを無効化
aタグ(link_to)に
data: {"turbolinks" => false}
を加えることによって、
Turbolinksを無効化し、遷移先に飛ぶようにすることでJSを正常に動作させます。test.html.slim=link_to "テスト", tests_path, data: {"turbolinks" => false}最後に
ポートフォリオ作成をきっかけにたまたまTurbolinksを調べることになったわけですが、
調べれば調べる程、まだまだ奥が深そうな感じがしました。
今回は、基本の部分で留めておこうと思いますが、また機会があれば調べてみたいところです。尚、本記事の内容で間違っている点や不足している点などがありましたら、ご教示いただけると幸いです。
- 投稿日:2020-09-22T22:10:11+09:00
ruby Hash#==の挙動について
ふとrubyにてHash同士の比較についてどこまで比較してるのか?と気になったので検証してみた。
結果としては公式のドキュメントに記載があるが
自身と other が同じ数のキーを保持し、キーが eql? メソッドで比較して全て等しく、値が == メソッドで比較して全て等しい場合に真を返します。という動作をするようだ、検証してみる。
require 'securerandom' keys = Array.new values = Array.new # 1000個のテストデータを作成 1000.times do keys << SecureRandom.alphanumeric end 1000.times do values << SecureRandom.base64(100) end # valueの中身がばらばらの比較 a = Hash.new b = Hash.new keys.each do |key| a[key] = values.sample b[key] = values.sample end puts a == b ? "match!!" : "unmatch..." # valueの中身を同一にした比較 a = Hash.new b = Hash.new keys.each_with_index do |key, i| a[key] = values[i] b[key] = values[i] end puts a == b ? "match!!" : "unmatch..."結果
$ >> ruby test.rb unmatch... match!!ちゃんと比較してる模様、ちゃんと公式に書いてある通りだった。
- 投稿日:2020-09-22T22:01:44+09:00
Ruby on Rails 環境構築(Windows10)で苦労したこと(SQLite3)
Railsのインストールがやっとできました。
苦労した点1つめをまとめます。RubyとRailsをインストール後
Railsアプリを新規作成したところ、こんな感じのエラーがたくさんでました。エラー: mingw32: キー "AD351C50AE085775EB59333B5F92EFC1A47D45A1" は不明です ・・・ エラー: データベース 'mingw64' は無効です (無効または破損したデータベース (PGP 鍵))Gemfileで
gem 'sqlite3', '~> 1.4'
となっていたので
いろいろ修正してみたけれどだめ。そこで
sqlite.dllをRubyのbinへ配置
node.jsのインストール
yarnのインストール
を行いgem install sqlite3を実施。それでもエラーが出てしまい・・
エラー内容をよく見てみると、こんな記述が・・checking for sqlite3.h... no sqlite3.h is missing. Install SQLite3 from http://www.sqlite.org/ first.sqlite3.hのチェックで引っかかっている?!
そこで、sqlite3.hをRubyのbinへ置き、引数にsqlite.hの場所、sqlite3.dllの場所を指定してgem install sqlite3を実行。
それでもエラーが出て・・
引数にsqlite3のバージョンまで指定するとうまくいきました。こんな感じです。
gem install sqlite3 --version 1.3.13 --platform=ruby -- --with-sqlite3-include=C:\Ruby27-x64\bin --with-sqlite3-lib=C:\Ruby27-x64\bin最後に
Gemfileを
gem 'sqlite3', '~> 1.3.13'
に修正して
bundle install
を実行。
Railsアプリの新規作成を再度行うと無事に作成できました。環境
Windows10 home
- 投稿日:2020-09-22T21:38:41+09:00
Ruby学習6
演習で間違えた問題復習
現在、Ruby技術者認定試験silverを取得するべく勉強中です。
そろそろ試験なので、模擬試験で引っかかった部分を復習します。定数への破壊的メソッド
HOGE = "hoge" HOGE.gsub!("hoge", "piyo") print HOGEこの出力結果に警告が出る(定数の変更が行われる為)と思ってが、違った。
警告文は、定数の「再代入」に対して行われるので、破壊的メソッドの呼び出しには警告なく値を変更できる。?を用いた文法
p ?A短いけど、なんだこれな文法。?の後の1文字を文字列で出力する。2文字以上書くとエラー。僕の脳では何に使うか全くわからない。
クラスの継承
class Hoge < Kernel ; endこれを実行するとエラーを吐くんですが、理由は、Kernelがモジュールだから。モジュールを継承するにはincludeを使う。
to_iメソッド
p "123abc".to_i => 123文字列を整数にする、は理解してたものの、整数以外がある場合を把握していなかった。
整数以外が混ざっている場合、整数以外の1文字手前までを整数として出力する。
なので、"123abc123"to_iの場合でも出力するのは123。演算子||
a = [1,2,3,4] b = [1,3,5,7] p a || b => [1,2,3,4]出力が[1,2,3,4,5,7]だと思ったんですが、それは a | b の場合。
a || b の場合、aがtrueである時、bを評価せずにaを吐いて終わり。正規表現
puts "0123456789-".delete("^13-56-") => 13456-「deleteでマッチするのが1と、3から5と6と-だから、これを消せば良いのね、完全に理解した」←分かってない
正規表現に^が含まれたら「〇〇以外」になるので、上記にマッチした文字列「以外」を削除する。chrメソッド ordメソッド
p 75.chr p "K".ord => "K" 75chrメソッドは整数を文字コードとして文字変換
ordメソッドは逆に文字を文字コード対応の整数に変換To_Be_Continued...
- 投稿日:2020-09-22T21:26:15+09:00
本番環境で画像が表示されない
デプロイ後画像が表示されなくて困ったのでまとめてみました。
どなたかのお役に立てたら嬉しいです。
ruby '2.6.5'
rails '6.0.0'オリジナルアプリケーションを作っています。
本番環境はHerokuを使用します。
Herokuにデプロイして動作確認したらエラーログの出現しました。エラーログから本番環境ですと画像へのパスが変わってしまうのが原因と考えました。
ローカルだとassets/images/直下におけばそのまま表示されますが
本番環境だとプリコンパイルされ表示されない為エラーになりました。調べると、
productionでは、Railsはプリコンパイルされたファイルをpublic/assetsに置きます。プリコンパイルされたファイルは、Webサーバーによって静的なアセットとして扱われます。
app/assetsに置かれたファイルがそのままの形でproduction環境で使用されることは決してありません。上記の記事を参考に本番環境のパスを作ります。
_medicine.html.erb<%= image_tag asset_path('medicine3.jpeg'), class:"med-pic" %>このようにしてパス再指定しましたが私の場合はまたエラーになりました。
本番環境でのasset precompileの設定がされていないのではないかとご指摘いただき確認。
変更前
production.rbconfig.assets.compile = false変更後
production.rbconfig.assets.compile = trueこちらを有効にしてみたら無事画像表示できました。
まとめ
<<初心者向け>>本番環境で画像表示されない場合のやってみて損はない対処法
①本番環境でのasset precompileの設定が有効か確認
29config.assets.compile = true②プリコンパイルでPublic内にAssetsファイルを作成
% rails assets:precompile③assets_pathを記述する
<%= image_tag asset_path('medicine3.jpeg'), class:"med-pic" %>
④デプロイ完了して無事、画像表示完了!!
asset precompileのやってることとか軽く調べておくとより良いかもです!
- 投稿日:2020-09-22T21:07:09+09:00
(ギリ)20代の地方公務員がRailsチュートリアルに取り組みます【第13章】
前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。
さてさて第13章。早いもので残り2章となりました。今回はマイクロポストというユーザーのメッセージ投稿機能を実装していきます。要はTwitterよね。気張っていきましょう!!
本日のBGMはこちら。
ARIA vocal collection
ネオ・ヴェネツィアでカフェラテ飲みながらリモートワークできる時代が来ねえかなあ…
【13.1.1 Micropostモデル 演習】
1. RailsコンソールでMicropost.newを実行し、インスタンスを変数micropostに代入してください。その後、user_idに最初のユーザーのidを、contentに "Lorem ipsum" をそれぞれ代入してみてください。この時点では、 micropostオブジェクトのマジックカラム (created_atとupdated_at) には何が入っているでしょうか?
2. 表 12.1の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: アカウント有効化で行った演習 (11.1.1.1) と同じ理由です。
3. 先ほど作ったmicropostオブジェクトをデータベースに保存してみましょう。この時点でもう一度マジックカラム
→ まとめてドン>> micropost = Micropost.new(user_id: 1, content: "Lorem ipsum") => #<Micropost id: nil, content: "Lorem ipsum", user_id: 1, created_at: nil, updated_at: nil> >> micropost.created_at => nil >> micropost.updated_at => nil >> micropost.user User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil> >> micropost.user.name => "Example User" >> micropost.save (0.1ms) SAVEPOINT active_record_1 SQL (1.3ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2020-09-21 02:33:40.343372"], ["updated_at", "2020-09-21 02:33:40.343372"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true >> micropost.created_at => Mon, 21 Sep 2020 02:33:40 UTC +00:00 >> micropost.updated_at => Mon, 21 Sep 2020 02:33:40 UTC +00:00
【13.1.2 Micropostのバリデーション 演習】
1. Railsコンソールを開き、user_idとcontentが空になっているmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?
→ 下記>> micropost = Micropost.new(user_id: " ", content: " ") => #<Micropost id: nil, content: " ", user_id: nil, created_at: nil, updated_at: nil> >> micropost.valid? => false >> micropost.errors.messages => {:user=>["must exist"], :user_id=>["can't be blank"], :content=>["can't be blank"]}
2. コンソールを開き、今度はuser_idが空でcontentが141文字以上のmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?
→ 下記>> micropost = Micropost.new(user_id: " ", content: "a" * 141) => #<Micropost id: nil, content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", user_id: nil, created_at: nil, updated_at: nil> >> micropost.valid?=> false >> micropost.errors.messages=> {:user=>["must exist"], :user_id=>["can't be blank"], :content=>["is too long (maximum is 140 characters)"]}
【13.1.3 User/Micropostの関連付け メモと演習】
belongs_to: to以下に属する。これを設定した側はメソッド的に使えるようになる。
has_many: 紐付きの親になる側に設定。1. データベースにいる最初のユーザーを変数userに代入してください。そのuserオブジェクトを使ってmicropost = user.microposts.create(content: "Lorem ipsum")を実行すると、どのような結果が得られるでしょうか?
→ 下記>> user = User.first User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil> >> micropost = user.microposts.create(content: "Lorem ipsum") (0.1ms) SAVEPOINT active_record_1 SQL (1.8ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2020-09-21 03:35:41.329488"], ["updated_at", "2020-09-21 03:35:41.329488"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2020-09-21 03:35:41", updated_at: "2020-09-21 03:35:41">
2. 先ほどの演習課題で、データベース上に新しいマイクロポストが追加されたはずです。user.microposts.find(micropost.id)を実行して、本当に追加されたのかを確かめてみましょう。また、先ほど実行したmicropost.idの部分をmicropostに変更すると、結果はどうなるでしょうか?
→ 下記>> user.microposts.find(micropost.id) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? LIMIT ? [["user_id", 1], ["id", 1], ["LIMIT", 1]] => #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2020-09-21 03:35:41", updated_at: "2020-09-21 03:35:41"> >> user.microposts.find(micropost) Traceback (most recent call last): 1: from (irb):6 ArgumentError (You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.)
3. user == micropost.userを実行した結果はどうなるでしょうか? また、user.microposts.first == micropost を実行した結果はどうなるでしょうか? それぞれ確認してみてください。
→ 下記>> user == micropost.user => true >> user.microposts.first == micropost Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ? [["user_id", 1], ["LIMIT", 1]] => true
【13.1.4 マイクロポストを改良する メモと演習】
default_scope: あるスコープをモデルのすべてのクエリに適用したい場合に使用。
dependent: :destroy:has_manyに設定。親が削除されたときに、紐づいている子側のデータベースがすべて削除される。
その他、いろいろ出てきたので用語集にまとめています。1. Micropost.first.created_atの実行結果と、Micropost.last.created_atの実行結果を比べてみましょう。
→ 適当にmicropostを2つ作ってやってから下記(なんとなくsandbox使ってるので以前の演習データが残ってません。)>> Micropost.first.created_at Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] => Mon, 21 Sep 2020 04:56:04 UTC +00:00 >> Micropost.last.created_at Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]] => Mon, 21 Sep 2020 04:55:35 UTC +00:00
2. Micropost.firstを実行したときに発行されるSQL文はどうなっているでしょうか? 同様にして、Micropost.lastの場合はどうなっているでしょうか? ヒント: それぞれをコンソール上で実行したときに表示される文字列が、SQL文になります。
→ 下記>> Micropost.first Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] => #<Micropost id: 2, content: "jaoivhiua", user_id: 2, created_at: "2020-09-21 04:56:04", updated_at: "2020-09-21 04:56:04"> >> Micropost.last Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]] => #<Micropost id: 1, content: "krutinb", user_id: 1, created_at: "2020-09-21 04:55:35", updated_at: "2020-09-21 04:55:35">
3. データベース上の最初のユーザーを変数userに代入してください。そのuserオブジェクトが最初に投稿したマイクロポストのidはいくつでしょうか? 次に、destroyメソッドを使ってそのuserオブジェクトを削除してみてください。削除すると、そのuserに紐付いていたマイクロポストも削除されていることをMicropost.findで確認してみましょう。
→ 下記>> user = User.first User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil> >> user.microposts.first.id Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["user_id", 1], ["LIMIT", 1]] => 1 >> user.destroy (0.1ms) SAVEPOINT active_record_1 Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 1]] SQL (0.7ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]] (0.1ms) RELEASE SAVEPOINT active_record_1 => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil> >> Micropost.find(1) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["id", 1], ["LIMIT", 1]] Traceback (most recent call last): 1: from (irb):13 ActiveRecord::RecordNotFound (Couldn't find Micropost with 'id'=1)
【13.2.1 マイクロポストの描画 演習】
1. 7.3.3で軽く説明したように、今回ヘルパーメソッドとして使ったtime_ago_in_wordsメソッドは、Railsコンソールのhelperオブジェクトから呼び出すことができます。このhelperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみましょう。
2. helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?
→ まとめて下記>> helper.time_ago_in_words(3.weeks.ago) => "21 days" >> helper.time_ago_in_words(6.months.ago) => "6 months" >> helper.time_ago_in_words(1.year.ago) => "about 1 year"
3. micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードにあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。
→ 下記s' for #<Class:0x0000000005968db8>) >> user = User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil> >> microposts = user.microposts.paginate(page: nil) Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 1], ["LIMIT", 11], ["OFFSET", 0]] => #<ActiveRecord::AssociationRelation []> >> microposts.class => Micropost::ActiveRecord_AssociationRelation
【13.2.2 マイクロポストのサンプル 演習】
1. (1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。
→ [1,2,3,4,5,6]という配列ができる。予想どおり。>> (1..10).to_a.take(6) => [1, 2, 3, 4, 5, 6]
2. 先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。
→ なくてもできるね。>> (1..10).take(6) => [1, 2, 3, 4, 5, 6]
3. Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント (英語) を眺めながら画面に出力する方法を学び、実際に大学名や電話番号、Hipster IpsumやChuck Norris facts (参考: チャック・ノリスの真実) を画面に出力してみましょう。(訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。)
→ 下記(そのままfaker-okinawaをbudleするとエラーが出るので、通常のfakergemをコメントアウトしてからbundleしましょう。)>> Faker::University.name => "East Abshire University" >> Faker::PhoneNumber.cell_phone => "1-315-982-9239" >> Faker::Hipster.sentence => "Mumblecore pug tilde marfa drinking 8-bit." >> Faker::ChuckNorris.fact => "Chuck Norris can binary search unsorted data." >> Faker::Okinawa::Awamori.name => "瑞穂"
【13.2.3 プロフィール画面のマイクロポストをテストする メモと演習】
再び登場response.body。完全なHTMLが含まれる。assert_match でそのHTMLから該当の要素を探し出す。assert_selectよりも抽象的なメソッド。
'h1>img.gravatar'⇨h1タグの内側にあるgravatarクラス付きのimgタグ というネスト。1. リスト 13.28にある2つの'h1'のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から redに変わることを確認してみてください。
→ showビューのh1の中身をコメントアウトしよう。show.thml.erb<h1> <%#= gravatar_for @user %> <%#= @user.name %> </h1>
2. リスト 13.28にあるテストを変更して、will_paginateが1度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。
→ count; 1 を追加するだけ。users_profile_test.rbtest "profile display" do get user_path(@user) assert_template 'users/show' assert_select 'title', full_title(@user.name) assert_select 'h1', text: @user.name assert_select 'h1>img.gravatar' assert_match @user.microposts.count.to_s, response.body assert_select 'div.pagination', count: 1 @user.microposts.paginate(page: 1).each do |micropost| assert_match micropost.content, response.body end end
【13.3.1 マイクロポストのアクセス制御 演習】
1. なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイのでしょうか? 考えてみてください。
→ 重複をなくすためですね。DRYの原則に反する。
【13.3.2 マイクロポストを作成する 演習】
1. Homeページをリファクタリングして、if-else文の分岐のそれぞれに対してパーシャルを作ってみましょう。
→ こんなかんじでしょうか。home.thml.erb<% if logged_in? %> <%= render 'logged_in' %> <% else %> <%= render 'not_logged_in' %> <% end %>_logged_in.html.erb<div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> </div>_not_logged_in.html.erb<div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails logo"), 'http://rubyonrails.org/' %>
【13.3.3 フィードの原型 演習】
1. 新しく実装したマイクロポストの投稿フォームを使って、実際にマイクロポストを投稿してみましょう。Railsサーバーのログ内にあるINSERT文では、どういった内容をデータベースに送っているでしょうか? 確認してみてください。
→ INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "good morning."], ["user_id", 1], ["created_at", "2020-09-22 01:20:32.232820"], ["updated_at", "2020-09-22 01:20:32.232820"]]
2. コンソールを開き、user変数にデータベース上の最初のユーザーを代入してみましょう。その後、Micropost.where("user_id = ?", user.id)とuser.microposts、そしてuser.feedをそれぞれ実行してみて、実行結果がすべて同じであることを確認してみてください。ヒント: ==で比較すると結果が同じかどうか簡単に判別できます。
→ 全部一緒ですね。>> Micropost.where("user_id = ?", user.id) == user.microposts Micropost Load (0.9ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]] Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC => true >> Micropost.where("user_id = ?", user.id) == user.feed => true >> user.microposts == user.feed Micropost Load (0.3ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC => true
【13.3.4 マイクロポストを削除する メモと演習】
request.referrerメソッド: 一つ前のURLを返す。
1. マイクロポストを作成し、その後、作成したマイクロポストを削除してみましょう。次に、Railsサーバーのログを見てみて、DELETE文の内容を確認してみてください。
→ DELETE FROM "microposts" WHERE "microposts"."id" = ? ["id", 302] commit transaction
2. redirect_to request.referrer || root_urlの行をredirect_back(fallback_location: root_url)と置き換えてもうまく動くことを、ブラウザを使って確認してみましょう (このメソッドはRails 5から新たに導入されました)。
→ 動きます。
【13.3.5 フィード画面のマイクロポストをテストする 演習】
1. リスト 13.55で示した4つのコメント (「無効な送信」など) のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが redになることを確認し、元に戻すと greenになることを確認してみましょう。
→ Yes, GREEN.
2. サイドバーにあるマイクロポストの合計投稿数をテストしてみましょう。このとき、単数形 (micropost) と複数形 (microposts) が正しく表示されているかどうかもテストしてください。ヒント: リスト 13.57を参考にしてみてください。
→ これでよかろうか。microposts_interface_test.rbtest "micropost sidebar count" do log_in_as(@user) get root_path assert_match "#{@user.microposts.count} microposts", response.body other_user = users(:malory) log_in_as(other_user) get root_path assert_match "0 microposts", response.body other_user.microposts.create!(content: "A micropost") get root_path assert_match "1 micropost", response.body end
【13.4.1 基本的な画像アップロード 演習】
1. 画像付きのマイクロポストを投稿してみましょう。もしかして、大きすぎる画像が表示されてしまいましたか? (心配しないでください、次の13.4.3でこの問題を直します)。
→ まあまあでした。
2. リスト 13.63に示すテンプレートを参考に、13.4で実装した画像アップローダーをテストしてください。テストの準備として、まずはサンプル画像をfixtureディレクトリに追加してください (コマンド例: cp app/assets/images/rails.png test/fixtures/)。リスト 13.63で追加したテストでは、Homeページにあるファイルアップロードと、投稿に成功した時に画像が表示されているかどうかをチェックしています。なお、テスト内にあるfixture_file_uploadというメソッドは、fixtureで定義されたファイルをアップロードする特別なメソッドです18 。ヒント: picture属性が有効かどうかを確かめるときは、11.3.3で紹介したassignsメソッドを使ってください。このメソッドを使うと、投稿に成功した後にcreateアクション内のマイクロポストにアクセスするようになります。
→ 下記。ここでテストがGREENにならない。間違ってそうな箇所が見当たらないので、調べたら解決。こちらの記事を参考にspringを再起動したらGREENになりました。microposts_interface_test.rbtest "micropost interface" do log_in_as(@user) get root_path assert_select 'div.pagination' assert_select 'input[type="file"]' # 無効な送信 post microposts_path, params: { micropost: { content: "" } } assert_select 'div#error_explanation' # 有効な送信 content = "This micropost really ties the room together" picture = fixture_file_upload('test/fixtures/rails.png', 'image/png') assert_difference 'Micropost.count', 1 do post microposts_path, params: { micropost: { content: content, picture: picture } } end assert assigns(:micropost).picture? follow_redirect! assert_match content, response.body # 投稿を削除する assert_select 'a', text: 'delete' first_micropost = @user.microposts.paginate(page: 1).first assert_difference 'Micropost.count', -1 do delete micropost_path(first_micropost) end # 違うユーザーのプロフィールにアクセス (削除リンクがないことを確認) get user_path(users(:archer)) assert_select 'a', text: 'delete', count: 0 end
【13.4.2 画像の検証 演習】
1. 5MB以上の画像ファイルを送信しようとした場合、どうなりますか?
2. 無効な拡張子のファイルを送信しようとした場合、どうなりますか?
→ 両方とも、該当するファイルがあらへんよ…。多分エラーが出るでしょう。
【13.4.3 画像のリサイズ 演習】
1. 解像度の高い画像をアップロードし、リサイズされているかどうか確認してみましょう。画像が長方形だった場合、リサイズはうまく行われているでしょうか?
→ OK
2. 既にリスト 13.63のテストを追加していた場合、この時点でテストスイートを走らせると紛らわしいエラーメッセージが表示されることがあります。このエラーを取り除いてみましょう。ヒント: リスト 13.68にある設定ファイルを修正し、テスト時はCarrierWaveに画像のリサイズをさせないようにしてみましょう。
→ エラー吐いてなかったけど一応やっときました。
【13.4.4 本番環境での画像アップロード 演習】
1. 本番環境で解像度の高い画像をアップロードし、適切にリサイズされているか確認してみましょう。長方形の画像であっても、適切にリサイズされていますか?
→ よっしゃ!いけた!
第13章まとめ
・マイクロポストもリソース化。
・belongs_toとhas_manyでデータテーブル同士を紐付け。子側がメソッド的に使えるようになる。
・デフォルトスコープで表示順序を変更。使い方は気をつけた方がいいっぽい記事が調べると出てきた。
・dependent: :destroyオプションを使うと、関連付けされたオブジェクトと自分自身を同時に削除する。
・ActiveRecordの使用により、生のSQLを使うことはほとんどない。
・CarrierWaveで画像アップロードを実装。(今は標準がActive Storage?)
マイクロポスト実装終わり〜〜。ここはけっこう分量ありました。特にテスト、やってること自体は復習が多いけど、量がありましたね。これでsample_appもそれらしくなってきました。
さあいよいよラスト、第14章に入ります。ユーザーフォローからステータスフィードの実装まで、ラストスパートです!
⇦ 第12章はこちら
学習にあたっての前提・著者ステータスはこちら
なんとなくイメージを掴む用語集
・Proc
ブロック( { }とかdo ~ end)をオブジェクト化したもの。ブロックはオブジェクトではないので、procでオブジェクト化する必要がある。こちらを参照・scope(メソッド)
特定のクエリをまとめたもの。何回も使用するものをまとめてしまって、繰り返し長ったらしいコードを書くことを避ける。・ラムダ式
無名関数。その名のとおり、名前付けされていない関数のこと。・SQLインジェクション
アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。また、その攻撃を可能とする脆弱性のこと。・MIME(Multipurpose Internet Mail Extension(多目的インターネットメール拡張))
規格上US-ASCIIのテキストしか使用できないインターネットの電子メールでさまざまなフォーマット(書式)を扱えるようにする規格。
- 投稿日:2020-09-22T20:26:30+09:00
【Ruby】エラー文に出てくる例外クラスまとめ【NameErrorとかの意味 】
エラー名 エラー内容 NameError 未定義のローカル変数/定数(又はprivateメソッド)とかを呼び出しちゃってる NoMethodError 存在しないメソッドを呼び出しちゃった時に発生 TypeError 期待してたのと違う型(クラス)がメソッドの引数になってるよ! ArgumentError 引数の数・型が違う ZeroDivisionError 整数を0で割り算しようとしちゃった SystemStackError システムスタックがあふれちゃてる。間違ってメソッド再帰呼び出しした場合など LoadError requireやloadの実行に失敗したときに発生 SyntaxError 構文エラー。 endない時とか
- 投稿日:2020-09-22T18:37:31+09:00
テーブルのデータ型とコマンドについての備忘録(Rails)
はじめに
テーブルを作る際、この場合データ型なんだったっけ?コマンドなんだったっけ?と調べ直してしまうことがままあるのでまとめることにしました。
データ型
データ型 種類 integer 数値(整数) decimal 数値(精度の高い小数) float 数値(浮動小数) string 文字(短い文字列) text 文字(長い文字列 date 日付 datetime 日時 time 時刻 timestamp タイムスタンプ binary バイナリ boolean 真偽 テーブル、カラム作成の際に使うコマンド
テーブル、カラムを作る時
$ rails g model モデル名 カラム名:データ型使用例:Userモデルの作成と名前(name)と自己紹介文(introduction)のためのカラムを作成したいとき
$ rails g model User name:string introduction:textテーブルを削除
$ rails d model モデル名使用例
$ rails d model User既存のテーブルに対しカラムの追加/削除するコマンド
カラムの追加
$ rails g migration Addカラム名Toテーブル名 カラム名:型名使用例:Userテーブルにtitleカラムを追加したいとき
$ rails g migration AddTitleToUsers title:stringカラムの削除
$ rails g migration Removeカラム名Fromテーブル名 カラム名:型名使用例:Userテーブルにtitleカラムを削除したいとき
$ rails g migration RemoveTitleFromUsers title:stringマイグレーション実行
作成、変更、削除が出来たらDBには反映させるため、db:migrateコマンドを実行する。
$ rails db:migrate
- 投稿日:2020-09-22T18:15:25+09:00
投稿に対していいね機能を実装する
概要
今回、twitterのような投稿アプリに対して
いいねを押したり、取り消したりできる機能と、
いいねをカウントして件数を表示できる機能と、
いいねするリンクをハートにする実装についてまとめておきます。ちなみに、投稿にいいねをする機能に関しては、progateにカリキュラムがありますが、自分はそのまま実装しても思うような機能にならなかったので、qiitaをはじめとするサイトをたくさん検索しました。
それも踏まえてまとめようと思います。
機能の見た目
実装
likeモデルを用意する
rails g model likeマイグレーションファイルに
t.references, foreign_key: trueとして、user_idとpost_idのカラムを設定します。models/like.rbclass Like < ApplicationRecord validates :user_id, presence: true validates :post_id, presence: true endルーティングを設定する
routes.rbpost "likes/:post_id/create" => "likes#create" post "likes/:post_id/destroy" => "likes#destroy"これによってrails routesをすると次の画像のようになります。
これは、ビューファイルにリンクを指定するときに必要になるのであとで間違えないようにしましょう。コントローラーを作成する
controllers/likes_controller.rbclass LikesController < ApplicationController before_action :authenticate_user! def create @like = Like.new(user_id: current_user.id, post_id: params[:post_id]) @like.save redirect_to "/posts/#{@like.post_id}" end def destroy @like = Like.find_by(user_id: current_user.id, post_id: params[:post_id]) @like.destroy redirect_to("/posts/#{params[:post_id]}") end end保存や削除をした後にその投稿の詳細ページにリダイレクトされるようになっています。
ビューファイルを用意しよう
views/posts/show.html.erb<div class="like-btn"> <% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %> <%=link_to("/likes/#{@post.id}/destroy", {method: :post}) do %> <span class="fa fa-heart like-btn-unlike"></span> <% end %> <% else %> <%= link_to("/likes/#{@post.id}/create", {method: :post}) do %> <span class="fa fa-heart like-btn"></span> <% end %> <% end %> </div>まずは条件分岐でユーザーがいいねしているかしていないかを分岐し、
ボタンを押したときlikeをcreateするか、destroyするかを指定します。そして、次に書きますが、link_to ~~~ doとする事で間にHTML文を挟むことができます。(理解が浅ければprogateのrailsカリキュラムを参照)
link_toのURLは間違えないようにしましょう。
先ほどルーティングでのせた画像の通り、/likes/post_id/create(またはdestroy)と記述します。
いいねをハートアイコンのボタンにする
この部分はprogateを参考にしたのでこれ以外知らないんですけど、
font-awesomeというものを使って、いいねするリンクをボタンにしていきます。まずはfont-awesomeを使えるようにhead部分にリンクを読み込みます。
views/layouts/application.html.erb---省略--- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> </head>ビューファイルはもう適した形式で記述しているので、
最後にいいねしていたらピンク、していなかったらグレーとなるようにcssを記述します。先ほど記述したviews/posts/show.html.erbを確認すればわかりますが、いいねするボタンと取り消すボタンでlike-btnとlike-btn-unlikeが分かれています。
assets/stylesheets/likes.scss.like-btn { color: #8899a6; } .like-btn-unlike { color: #ff2581; } .posts-show-item .fa { font-size: 16px; margin-right: 3px; }これでハートボタンでいいねしたり取り消したりすることが可能になっているはずです。
最後に、いいねの件数をカウントして表示しましょう。
件数をカウントして表示する
postsコントローラーのshowメソッドに@like_countを定義してあげて、ビューファイルで表示するような手順になります。
controllers/posts_controller.rbdef show @post = Post.find(params[:id]) @comment = Comment.new @comments = @post.comments.includes(:user) @like_count = Like.where(post_id: @post.id).count endwhereでその投稿についてるいいねをデータベースから検索して、countで数えていくように定義しています。
views/posts/show.html.erb<div class="like-btn"> <h3>いいね件数:<%= @like_count %></h3> <% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %> <%=link_to("/likes/#{@post.id}/destroy", {method: :post}) do %> ---省略---以上で完成です。
- 投稿日:2020-09-22T17:51:01+09:00
SQLite3からPostgreSQLに変更
背景
プロジェクトの作成時に
rails new sample_app -d postgresqlとするところを
rails new sample_app -D postgresqlとしていたので、ポスグレが設定されていたなかった。
Gemfileで
# Use postgresql as the database for Active Record gem 'pg'となっていてほしいところが、
# Use sqlite3 as the database for Active Record gem 'sqlite3'となっていた。
sqlite3ってのがデフォルトのやつなのかな。
SQLite3からPostgreSQLに変更
https://qiita.com/rubys8arks/items/0749d6fa73e88d3d381c
これを参考に変更していこう。ポスグレ自体はインストールされているので、
Gemfileのsqlite3
のところを# Use postgresql as the database for Active Record gem 'pg', '>= 0.18', '< 2.0'に変更して、
bundle install
する。この状態だと、
rails db:create
できないbin/rails db:create rails aborted! LoadError: Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? sqlite3 is not part of the bundle. Add it to your Gemfile.database.ymlの編集
before
# SQLite version 3.x # gem install sqlite3 # # Ensure the SQLite 3 gem is defined in your Gemfile # gem 'sqlite3' # default: &default adapter: sqlite3 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 development: <<: *default database: db/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default database: db/test.sqlite3 production: <<: *default database: db/production.sqlite3after
default: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development: <<: *default database: sample_app_development test: <<: *default database: sample_app_test production: <<: *default database: sample_app_production username: sample_app password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %>.envにパスワード
ここは省略
データベースの作成
-> % bin/rails db:create Created database 'sample_app_development' Created database 'sample_app_test'
- 投稿日:2020-09-22T17:19:27+09:00
[Rails6]devise + paranoia + MySQL8系で実現するユーザー論理削除と一意制約の両立
はじめに
タイトルの環境にて、ユーザーの論理削除および一意制約の両立にかなり苦戦したので
解決方法を軽くまとめます。
deviseの一意制約をオーバーライドして独自の制約を持たせる方法などが出ていましたが
どれも面倒でもっと簡単にできないかと思い調べた結果です。やりたいこと
先の環境にて(特にMySQL)、deviseデフォルトのユーザー物理削除ではなく論理削除を行い、
かつ退会したユーザーが同じアドレスやIDで再登録できるよう一意制約を担保したい。PostgreSQL, SQLite
こちら2つのDBでは部分indexを活用することで簡単に実装できるそうです。
・実装記事
・add_index仕様1
・add_index仕様2この、部分indexをMySQLは採用しておらず簡単に扱えない模様。
(情報が5.7ばかりだから8系では採用しているのか?どちらにしろRailsのmigrateからは無理そう)
軽くアンチMySQLになるかも…結論(MySQLでの実現方法)
この記事みたらいけると思います。
https://vanhuyz.com/how-to-apply-unique-restriction-with-soft-delete-in-rails/
1. deleted_atカラムを追加後、
2. deleted_atにNullではなくアクティブユーザーにも特定の値をもたせることで、
2カラムでのUNIQUEを実現し(dleted_atがNULLだと実現しない)
3. シンプルに論理削除と一意制約を両立最高に優良な記事をどうもありがとう!!
実装
[環境]
- Ruby:2.6.6p146
- Rails:6.0.3.3
- MySQL:8.0.21
- mysql2:0.5.3
- devise:4.7.2
- paranoia:2.4.2準備
環境構築やdevise, paranoia導入等の基本部分の実装は他に沢山の記事が出ているので割愛します。
devise標準の会員登録・退会機能等が動作する前提です。1. 設定ファイルparanoia.rbを作る
config/initializers/paranoia.rb# ユーザー退会後の論理削除・一意制約を両立させるための処理 # 非削除レコードはdeleted_at = '0000-01-01 00:00:00' # 削除済みレコードはdeleted_at != '0000-01-01 00:00:00' Paranoia.default_sentinel_value = DateTime.new(0)2. migrationでのindex変更
デフォルトのUsersテーブルのindexを変更するマイグレーションファイルを作成
rails g Change_Index_To_Users作成したマイグレーションファイルを以下のように変更
一度デフォルトで作成されたemailインデックスを削除し
新たに[email, deleted_at]でのuniqueインデックスを作成db/migrate/202009220000000.rbclass ChangeIndexUniuqueToUsers < ActiveRecord::Migration[6.0] def change remove_index :users, :email add_index :users, [:email,:deleted_at], unique: true end endmigration
rails db:migrate3. Validation追加
アプリケーションレベルでのバリデーションを追加
app/models/user.rbclass User < ActiveRecord::Base acts_as_paranoid validates :email, uniqueness: { scope: :deleted_at }4. 動作確認
railsコンソールもしくはrailsサーバーを立ち上げ実際にユーザー作成、退会、同アドレスでの再登録を実施してDBを確認します。
無事同じアドレスでの再登録に成功しました!!
他何かいい方法などあれば是非教えてくださいませ~!
- 投稿日:2020-09-22T16:58:48+09:00
Railsで時刻だけを比較する方法(何時〜何時までの間は、みたいなやつ)
問題の背景
深夜営業で23時〜翌5時までは15%増し、みたいなやつをどう書くか?という話。
「23時〜翌5時」という条件を上手く判定したい。解決方法
Time型で比較しようとすると日付情報まで入ってきて面倒くさいので、
strftime
とto_i
で時刻部分だけ数値化してあげてから比較すれば良い。def late_time?(time) num = time.strftime('%H%M').to_i [*0..459, *2300..2359].include?(num) end target_time = Time.zone.parse('2020/9/1 23:00') p late_time?(target_time) #=> true target_time = Time.zone.parse('2020/9/2 0:00') p late_time?(target_time) #=> true target_time = Time.zone.parse('2020/9/2 4:59') p late_time?(target_time) #=> true target_time = Time.zone.parse('2020/9/2 5:00') p late_time?(target_time) #=> false target_time = Time.zone.parse('2020/9/2 6:00') p late_time?(target_time) #=> false target_time = Time.zone.parse('2020/9/2 22:59') p late_time?(target_time) #=> false
- 投稿日:2020-09-22T16:26:58+09:00
Ruby学習5
メソッドとか色々5
現在、Ruby技術者認定試験silverを取得するべく勉強中です。
言語に対する理解がまだまだなので、基本的な事からアウトプットしていきます。小数の切り捨て、切り上げ、四捨五入
ceil = 小数点以下切り上げ
irb(main):001:0> 1.9.ceil => 2floor = 小数点以下切り捨て
irb(main):004:0> 1.9.floor => 1round =小数点以下四捨五入(厳密には最も近い整数を返す)
irb(main):006:0> 1.5.round => 2 irb(main):007:0> 1.4.round => 1joinメソッド
配列の要素同士を、引数指定した文字で区切り、結合した文字列を返す。
arr = ["無駄", "無駄", "無駄", "無駄ァ", "ッ!"] p arr.join("!") => "無駄!無駄!無駄!無駄ァ!!"injectメソッドと条件演算子
模擬問題で詰まったところ。まずはコードから。
numbers = [3,89,40,39,29,10,50,59,69] num = numbers.inject do |i,j| i > j ? i : j endinjectメソッドは、引数をつけた場合は引数を一つ目のブロック変数(i)に、配列の最初の要素を二つ目のブロック変数(j)に代入。do以降の式を実行。実行が終わったら計算結果をiに、配列の次の要素をjに代入。式を実行。以降繰り返して、配列の要素が最後までいったら結果を返す。引数をつけなかった場合は配列の最初と2番目の要素を代入してから開始する。
条件演算子
i > j ? i : j真偽を問う左辺(?から左側)の式を実行後、trueの時に返す値を(:)の左に、falseの時に返す値を(:)の右に置く。なのでこの式は、「iがjより大きい場合はiを、そうでない場合はjを返す」という意味。
で、
これをinjectメソッドで実行するので、「配列の要素を比較して、大きい数値を残し、最終的に配列内で最も大きい数値を返すコード」になる。stepメソッド
1.step(100, 2) do |n| puts n endレシーバの数値を起点として、第二引数の数値刻みで、数値が第一引数になるまで、式を実行する。ブロック変数nにはレシーバの数値が初期値として入る。コードを翻訳すると、
「レシーバの数値「1」を出力(puts n)した後、数値に2を足し再度実行(3を出力)。この数値が100に到達するまで実行する。」
今回は奇数が出力されていき、最後は99。2を足すと100を超えてしまうため、ここで終了。やってる事自体は、1から100までで奇数の出力。
- 投稿日:2020-09-22T15:52:17+09:00
sum と inject(:+) の結果が違うだと?
まあ知ってる人には驚く話ではないのだけれども。
Ruby で数値の総和を求める
Ruby で数値の配列が与えられていて,その要素の総和を求めたいとき,Array#sum を使うのが鉄則である。
以下のコードを比較してみよう。
numbers = Array.new(1000){ rand } # (1) numbers.inject(&:+) # (2) numbers.inject(:+) # (3) numbers.sum速度は (1) → (2) → (3) の順に速くなる。
これらの中で (3) は最も高速かつ簡素であり,それだけでもsum
を採用すべき理由になる。
しかし,それだけではない。精度
数値が Integer や Rational の場合はそもそも演算の誤差が生じないのでよいが,数値が Float の場合,演算の誤差を考えなければならない。
浮動小数点数の演算には誤差を伴うことが多い。それ自体は仕方がないのだが,2 回以上演算を繰り返す場合は,誤差の累積を問題にしなければならない。
結論を先に書くと,
sum
を採用すべき三つ目の理由は,sum
には誤差の累積を抑える「Kahan の加算アルゴリズム」が採用されていることである。
参考:カハンの加算アルゴリズム - Wikipedia誤差の例
Float が表す数について
本題に入る前に,少し予備知識の整理をしておきたい。
2 進法に基づく浮動小数点数で 0.1 という数値が表せないことはよく知られている。簡単に言えば,0.1 という数値は 2 進法では無限小数になるので,それを有限桁で表すからである。
では,Ruby で
puts 0.1を実行すると端末に
0.1
と表示されるのはどういうことか。まず Ruby の処理系は
0.1
という浮動小数点数リテラルを見て,Float
オブジェクトを生成する。
これは「0.1 に極めて近いが同じではない」浮動小数点数を表わしている。
大雑把に言って,(10 進で)15 桁くらいの精度をもった数値である。いや,実は Ruby の Float オブジェクトがどんなビット列で表されているかは環境依存なのだが,多くの環境で C 言語の double という数値型に基づいているらしく,その場合はそうなる。本記事の内容はすべてこの前提で書かれており,該当しない環境の場合は結果が異なりうる。0.1 ではない数値を表示させているのになぜ
0.1
と表示されるのか。
それは,puts
に Float オブジェクトを与えたとき,まずto_s
メソッドによって「10 進法に基づく数字列」という String オブジェクトに変換されるのだが,その結果が"0.1"
という文字列だからである。
10 進 → 2 進と違って,2 進 → 10 進の変換だと,有限小数が無限小数になったりはしない1。実際,浮動小数点数リテラル0.1
が表す Float オブジェクト(が表す数値)は,10 進法で0.1000000000000000055511151231257827021181583404541015625という,小数点以下 55 桁の有限小数になるようだ(私がなんかどこかでミスしてなければ)。
しかし,Ruby の Float#to_s メソッドはそんな文字列を返さない。
Float オブジェクトが持ちうる精度に見合った桁数だけを取り,"0.1"
という文字列を返すのである。これはto_s
の仕様である。
(このあたり,あまり詳しくないので,誤りの指摘や補足があればお願いします)誤差の累積
では本題である
sum
とinject(:+)
で結果が異なる例を見よう。numbers = [0.1] * 6 puts numbers.sum == numbers.inject(:+) # => false6 個の
0.1
の総和が一致していない(繰り返すが,環境によっては結果が異なりうる)。
それぞれの結果をputs
で表示させてみよう。numbers = [0.1] * 6 puts numbers.sum # => 0.6000000000000001 puts numbers.inject(:+) # => 0.6これを見て,「えっ?
sum
よりinject(:+)
のほうが精度が高いじゃん! 嘘つき!」と早合点してはいけない2。既に見たように Ruby の浮動小数点数リテラル
0.1
によって生成される Float オブジェクトが表している数値は 0.1 ではないのである。
ちなみに,inject
版の結果が0.6
と表示されているが,返り値が 0.6(という数を表す Float オブジェクト)であることを意味しない。あくまで返り値をto_s
した結果が"0.6"
という文字列だというだけである。誤差の評価
では,誤差がどうなっているかを正確に知るにはどうすればいいか。
それには Float#to_r を用いて Float オブジェクトを Rational オブジェクトに変換する。
このメソッドは,Float が表している数値に厳密に一致する有理数オブジェクトを返す。こんな具合である。
puts 0.1.to_r # => 3602879701896397/36028797018963968なんかスゴイ分数が表示されたが,何かの間違いではない。
0.1
というリテラルに基づくFloat
オブジェクトはこのような数値を表しているのである。
一見わけが分からない数字列に見えるが,よくよく見えば分子と分母は末尾以外は数字が一致しており,分子のほうが一桁少ない。つまり,0.1 という数値に非常に近いことが見て取れる。Rational に持ち込めば加減乗除で演算誤差は生じない。
こんなふうにしよう。
Rational にしてから取った総和を「真の和」と呼ぶことにする。
sum
とinject
で作った和と真の和の差の絶対値を「誤差」と呼ぶことにする。
どちらが誤差が小さいか比べるため,sum
版の誤差からinject
版の誤差を引いたものを計算する。これが正ならsum
版の誤差が大きく,負ならinject
版の誤差が大きい。# 足すべき数 x = 0.1 # 足し合わせる個数 n = 6 # 真の和(Rational) exact_sum = x.to_r * n # n 個の x からなる配列 numbers = [x] * n # sum による和(Rational) sum_by_sum = numbers.sum.to_r # その誤差 error_by_sum = (exact_sum - sum_by_sum).abs # inject による和(Rational) sum_by_inject = numbers.inject(:+).to_r # その誤差 error_by_inject = (exact_sum - sum_by_inject).abs # 比較 puts sum_by_sum - sum_by_inject # => 1/9007199254740992 puts error_by_sum - error_by_inject # => 0/1あれれ?
sum
版の和とinject
版の和は確かに微妙に違っているのだが,それぞれの誤差は完全に一致している?
一見不可解だが,何も不思議なことはない。「誤差」を計算するときに絶対値を取っているからこうなる。
sum
版とinject
版の和は,真の和の左右に同じ距離だけ離れて存在しているのだ。和そのものの値は違っているが真の和とのズレ量は同じだったということ。「ん? じゃあ,
sum
がinject
より精度が高いとか言ってたのは嘘だったのかよ」
話を最後まで聞いてほしい3。今の場合,つまり,
0.1
(というリテラルによる Float)を 6 個足し合わせた場合は,精度に違いがなかった。
これはinject
版にとって幸運なケースだったということだ。
では,実際にsum
版のほうが真の和に近いケースはあるのか。あるいは逆のケースは無いのか。
以下のようにして調べてみよう。
足すべき数は0.1
(というリテラルによる Float)とし,足し合わせる個数を変えていく。x = 0.1 1.upto(100) do |n| exact_sum = x.to_r * n numbers = [x] * n sum_by_sum = numbers.sum.to_r error_by_sum = (exact_sum - sum_by_sum).abs sum_by_inject = numbers.inject(:+).to_r error_by_inject = (exact_sum - sum_by_inject).abs puts "%4d %2d" % [n, (error_by_sum <=> error_by_inject)] end
error_by_sum <=> error_by_inject
は,もしsum
版の誤差のほうが大きければ1
を,その逆であれば-1
を,同じ誤差の大きさなら0
を返すはずである。結果は以下のようになった。
1 0 2 0 3 0 4 0 5 0 6 0 7 -1 8 -1 9 -1 10 -1 11 -1 12 0 13 0 14 0 15 -1 16 -1 17 -1 18 -1 19 -1 20 -1 21 -1 22 -1 23 -1 24 -1 25 -1 26 -1 27 -1 28 -1 29 -1 30 -1 31 -1 32 -1 33 -1 34 -1 35 -1 36 -1 37 -1 38 -1 39 -1 40 -1 41 -1 42 -1 43 -1 44 0 45 0 46 -1 47 -1 48 -1 49 -1 50 -1 51 -1 52 -1 53 -1 54 -1 55 -1 56 -1 57 -1 58 -1 59 -1 60 -1 61 -1 62 -1 63 -1 64 -1 65 -1 66 -1 67 -1 68 -1 69 -1 70 -1 71 -1 72 -1 73 -1 74 -1 75 -1 76 -1 77 -1 78 -1 79 -1 80 -1 81 -1 82 -1 83 -1 84 -1 85 -1 86 -1 87 -1 88 -1 89 -1 90 -1 91 -1 92 -1 93 -1 94 -1 95 -1 96 -1 97 -1 98 -1 99 -1 100 -1見てのとおり,1 個から 100 個までの中に,誤差の大きさが同じだったものが 11 あった。
それ以外はすべてsum
版のほうが誤差が小さい。
結局,この実験の範囲では,
sum
版とinject
版の誤差が同じケース → 少数sum
版のほうが誤差が小さいケース → 多数sum
版のほうが誤差が大きいケース → 無しであった。
しかし,もちろんこんな簡単な実験の結果を一般化するわけにはいかない。今の私にできるのは,Kahan のアルゴリズムを信じることと,上記の実験の範囲内では期待される結果が得られたことを確認することだけである。ちなみに,
sum
版が真の和と一致したものは 7 個あった。このうち,3 個はinject
版も一致していた。まとめ
総和を得るなら
inject(:+)
ではなくsum
を使おう。
そのほうが簡素で高速。そして,数値が浮動小数点数の場合は誤差の累積も抑えられる。
- 投稿日:2020-09-22T15:25:35+09:00
Ruby on Railsにて、Blocked Host:"ホスト名"が出てしまう時の対処法
背景
Ruby on Railsチュートリアルの1章の演習で、ずっと「Blocked Host:自分のホスト名」というエラーが出てしまい、長時間色々なことを試した結果、ある根本的なことをしていなかったことに気づき解決できたため、この投稿をさせてもらいました。
試したこと
Web上でよく書かれていることとしては、
1.environment/development.rbにて、「config.hosts.clear」を入れることです。
しかし、これを試しても、エラーが発生したままでした。
2.environment/development.rbにて「config.hosts<<"自分のホスト名"」を入れることです。
しかし、これを試しても、またしてもエラーが発生したままでした。原因
(Ⅰ).違うアプリケーションの中の、development.rbに1と2のプログラムを追加していた。
例えば、自分が作っているアプリケーションがhello_appだとすると、違うアプリケーション(例えば、a_app)の中のdevelopment.rbに1と2のプログラムを追加していたということです。
これでは、a_appのホストが許可されただけで、hello_appのホストは許可されません。
これを間違えた理由としましては、階層構造がenvironment/hello_app/config/environment/development.rb、とenvironmentが2つあることにより、同じ環境内であれば、一つのdevelopment.rbを書き直せば、全てに反映されると勘違いしたことによるものでした。
development.rbの階層は上の通りであり、1つのアプリケーションにつき一つのファイルとなります。
決して、共通ではありません。
自分が作成しているアプリケーション内の、config/environment内のdevelopment.rbを1と2のように書き直すようにしましょう。(Ⅱ).development.rbを書き直した後に、「rails server」を実行しなかった。
順序としては、「development.rb内のプログラムを書き直す→rails serverを実行する」という手順を踏まないと、いくら(Ⅰ)の手順を踏んだとしましても、エラーが発生したままとなります。
- 投稿日:2020-09-22T15:18:50+09:00
プロジェクトのrubyとrailsのバージョンアップ
参考
https://qiita.com/_kanacan_/items/c1499f6c13b1c41da982
現状
Gemfile
ruby '2.6.3' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.2.4', '>= 5.2.4.3'最新調査
リストの中確認,2.7.1が最新?
-> % rbenv install --list 2.5.8 2.6.6 2.7.1 jruby-9.2.12.0 maglev-1.0.0 mruby-2.1.1 rbx-5.0 truffleruby-20.1.0 truffleruby+graalvm-20.1.0rbenvをアップデート?
-> % brew upgrade rbenv ruby-build Updating Homebrew... ==> Auto-updated Homebrew! Updated 4 taps (heroku/brew, homebrew/core, homebrew/cask and homebrew/services). ==> New Formulae最新版2.7.1だったわ
1個上の手順意味なかったかも
https://www.ruby-lang.org/ja/downloads/
うん、2.7.1はいってるやん。だから、N(->No)。-> % rbenv install 2.7.1 rbenv: /Users/(username)/.rbenv/versions/2.7.1 already exists continue with installation? (y/N) Nでも、プロジェクトは2.6.3になってる。。。
-> % rbenv versions system 2.3.7 2.3.8 2.5.1 2.5.3 * 2.6.3 (set by /Users/(username)/projects/import_agent_app/.ruby-version) 2.6.5 2.6.6 2.7.1-> % git checkout -b feature/version_up Switched to a new branch 'feature/version_up'-> % rbenv local 2.7.1[feature/version_up *] -> % rbenv versions system 2.3.7 2.3.8 2.5.1 2.5.3 2.6.3 2.6.5 2.6.6 * 2.7.1 (set by /Users/(username)/projects/import_agent_app/.ruby-version)-> % bundle install Traceback (most recent call last): 2: from /Users/(username)/.rbenv/versions/2.7.1/bin/bundle:23:in `<main>' 1: from /Users/(username)/.rbenv/versions/2.7.1/lib/ruby/2.7.0/rubygems.rb:294:in `activate_bin_path' /Users/(username)/.rbenv/versions/2.7.1/lib/ruby/2.7.0/rubygems.rb:275:in `find_spec_for_exe': Could not find 'bundler' (1.17.2) required by your /Users/(username)/projects/import_agent_app/Gemfile.lock. (Gem::GemNotFoundException) To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:1.17.2`これでたから、下記のどちらか打った。
bundle update --bundler gem install bundler:1.17.2なんか色々やってるうちにこれが邪魔しだした。
-> % bundle install /Users/(username)/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/bundler-1.17.2/lib/bundler/rubygems_integration.rb:200: warning: constant Gem::ConfigMap is deprecated Your Ruby version is 2.7.1, but your Gemfile specified 2.6.3いろいろ、調べて参照してるバージョンが違うからみたいな記事が多かったけど、私の場合は、
Gemfileをruby '2.7.1'に書き換えたらいいだけだったぽい。
bundle install
は通るようになった。次はRailsのバージョンアップ
これもGemfileをgem 'rails', '~> 6.0.3', '>= 6.0.3.3'にして
budle install
したら行けるでしょ。-> % bundle install /Users/(username)/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/bundler-1.17.2/lib/bundler/rubygems_integration.rb:200: warning: constant Gem::ConfigMap is deprecated The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Bundler could not find compatible versions for gem "activesupport": In snapshot (Gemfile.lock): activesupport (= 5.2.4.3) In Gemfile: rails (~> 6.0.3, >= 6.0.3.3) was resolved to 6.0.3.3, which depends on activesupport (= 6.0.3.3) web-console (>= 3.3.0) was resolved to 3.7.0, which depends on railties (>= 5.0) was resolved to 5.2.4.3, which depends on activesupport (= 5.2.4.3) Running `bundle update` will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict.怒られた。
bundle updateして
bundle installしたら行けた。
- 投稿日:2020-09-22T14:14:57+09:00
めっちゃ便利なRubyのStructクラスのお話
きっかけ
とちぎRuby会議09にリモートで参加し、そこで見たLTに今更聞けない! Struct の使い方と今後の可能性についてでRubyのStructクラスについて初めて知った。
なにこれめっちゃ便利じゃん!となったので啓蒙も兼ねて記事を書こう、となったのがきっかけ。
Rubyはかれこれ休み休み10年間は触っているけれど未だに発見がある。素敵!学んでいないだけでは??Structクラスとは
Ruby 2.7.0 リファレンスマニュアル Structクラス
構造体クラス。Struct.new はこのクラスのサブクラスを新たに生成します。
個々の構造体はサブクラスから Struct.new を使って生成します。個々の構造体サブクラスでは構造体のメンバに対するアクセスメソッドが定義されています。自分の解釈で説明すると
任意の名前のプロパティ(とメソッド)を持つオブジェクトをお手軽に作成できる便利構造体クラス
使い方
# 4つのパラメーターを持ったStructのサブクラスを生成する BreakwaterClub = Struct.new(:id, :name, :grade, :age) # 生成したサブクラスのインスタンスを作成 bucho = BreakwaterClub.new(1, 'kuroiwa', 3) # 作成したインスタンスはパラメーター名のアクセサメソッドを持っている p bucho.name #=> "kuroiwa" bucho.age = 17 # 初期化時にセットしていなかったageがセットされている p bucho #=>#<struct BreakwaterClub id=1, name="kuroiwa", grade=3, age=17># keyword_init: true を指定することで初期化時にキーワード引数を渡せるようになる # キーワード引数のほうがわかりやすいけど文字数は多くなるのでどちらを使うかはお好みで BreakwaterClub = Struct.new(:id, :name, :grade, :age, keyword_init: true) hina = BreakwaterClub.new(name: 'tsurugi', grade: 1)Hashや配列と比べて何が嬉しいの?
Rubyでちょっとした処理を書く時によく使いがちな配列やHash。
まだ頭の中で整理できてない処理とかをアウトプットしながら整理する時とかにも使ったりする。# 配列 bucho = [1, 'kuroiwa', 3] bucho[0] #=>1 bucho[1] #=>'kuroiwa' bucho[2] #=>3 # Hash bucho = {id: 1, name: 'kuroiwa', grade: 3} bucho[:id] #=>1 bucho[:name] #=>'kuroiwa' bucho[:grade] #=>3定義してないパラメーターを指定するとエラーになる
Hashで定義してたりするとTypoしているのにそれに気付かず「なぜ動かない…合っているはずなのに…」とかあるからこれはありがたい。
senpai = BreakwaterClub.new(name: 'ohno', grade: 2) senpai[:height] #=>NameError (no member 'height' in struct) senpai.height #=>NoMethodError # Hashだと定義してなくても参照できてしまう senpai[:height] #=>nilStructクラスは配列、Hashと同じようにアクセス可能で型変換も可能
つまり上位互換って考えていいと思う。
natsumi = BreakwaterClub.new(name: 'hodaka', grade: 1) # 配列のようにアクセスできる。indexの順番は`Struct.new`で定義した順番になる natsumi[0] #=>nil natsumi[1] #=>'hodaka' # Hashのようにもアクセスできる natsumi[:id] #=>nil natsumi[:grade] #=>1 # 配列にもHashにも変換できる natsumi.to_a #=>[nil, "hodaka", 1, nil] natsumi.to_h #=>{:id=>nil, :name=>"hodaka", :grade=>1, :age=>nil}メソッド定義が可能
Struct.new
の際ににブロックを指定することでメソッドを定義可能BreakwaterClub = Struct.new(:id, :name, :grade, :age, keyword_init: true) do def gakunen "#{grade}年生" end end# `Struct.new`で生成したStructクラスを継承したサブクラスを作成することでも可能 Class BreakwaterClub < Struct.new(:id, :name, :grade, :age, keyword_init: true) def gakunen "#{grade}年生" end endStructクラス自体を継承したサブクラスを定義することは非推奨らしい。ここまだちょっとよく理解できてない。
継承元となるStructクラスが動的に生成した無名クラスなので不定なことに起因していると思う。
参考:
- 無名クラスから継承すると、何が問題なのか(Ruby)
- irbで2回以上loadすると失敗する
- 以下ドキュメント引用ブロックを指定した場合
Struct.new にブロックを指定した場合は定義した Struct をコンテキストにブロックを評価します。また、定義した Struct はブロックパラメータにも渡されます。
Customer = Struct.new(:name, :address) do def greeting "Hello #{name}!" end end Customer.new("Dave", "123 Main").greeting # => "Hello Dave!"Structをカスタマイズする場合はこの方法が推奨されます。無名クラスのサブクラスを作成する方法でカスタマイズする場合は無名クラスが使用されなくなってしまうことがあるためです。
[SEE_ALSO] Class.newまとめ
Structクラスは便利。多種多様な記述方法があるRubyらしいクラスだと思う。
Structクラスまとめ
- 任意のパラメーター、メソッドを定義できる構造体クラス
- 配列やHashのようにアクセスでき、型変換も可能
- 初期化時に指定していないパラメーター名だとエラーになる(Hashだとnil
になる)どういう時に使うと便利?
- 配列やHashですませちゃってるけどClassとして定義したほうがいいよなってとき
- ちょっとコードを書いて検証とかしたいとき
- Classを定義するほど考えがまとまってないとき
- おいそれと叩けないAPIを介したテストをやりたいときまだ試してませんが、RailsのRspecでのテスト時などでも使えそうだと感じました。
irbとかでも気軽に試せるので興味が出た人は是非試してみてください。
- 投稿日:2020-09-22T12:56:46+09:00
マルチスケールシミュレーション特論
- 投稿日:2020-09-22T11:40:29+09:00
【Ruby on Rails】エラーの日本語表記
目標
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina前提
※ ▶◯◯ を選択すると、説明等が出てきますので、
よくわからない場合の参考にしていただければと思います。【Ruby on Rails】エラーメッセージの個別表示
こちらの記事と対比させながら見るとわかりやすいです。流れ
1 gemの導入
2 config/application.rbの編集
3 config/locales/ja.ymlの作成、編集gemの導入
Gemfilegem 'rails-i18n'ターミナル$ bundle install
補足【rails-i18n】
rails g devise:install は deviseの初期設定を行います。config/application.rbの編集
下記2行を追加。
config/application.rbclass Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 config.i18n.default_locale = :ja # ←追加 config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # ←追加 # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. endconfig/locales/ja.yml
config/locales/ja.ymlja: activerecord: attributes: user: email: 'メールアドレス' password: 'パスワード' password_confirmation: 'パスワード(確認用)' name: '名前'
補足【user:】
モデル名となります。同じインデントで記述すれば、
他のモデルの日本語表記も可能です。
- 投稿日:2020-09-22T11:15:07+09:00
【Rails】[0日目]分からないところを無視しないオリジナルサービス制作
始めに
初めまして。名古屋在住のしんやです。転職で東京に行きたいです。
未経験からの転職のためにテックアカデミーのWebアプリケーションコースを受講し、2020年9月20日に受講終了しました。僕としてはスクールのカリキュラムは非常に分かりやすく、サポート体制も整っていたので快適に学習を進めることができました。
プログラミング勉強してる時、完全には納得できてないことがあっても、とりあえず「これはこういうものなんだ」として先に進めると、意外とあとから「そういうことだったのか」って納得できることありませんか??#プログラミング初心者 #駆け出しエンジニアと繋がりたい
— しんや@転職で東京行きたい (@shinyaeng) August 11, 2020唐突に自分の過去ツイートを引用しましたが、このスタンスで進めて受講終了に至りました。
受講終了した今気付いたのが、上記スタンスで進めるのはいいが、絶対に理解してから進めないといけないこともあるという当然のことでした。
簡単に言うと、とりあえずで進めたとこが多数あるが故に、カリキュラムは終えたけど全然理解できてねーなってとこが多数あるってことです。もちろんスクールの受講を終了した程度で全部理解できるとは思っていなかったですが、悔しいです。
今後の内容
悔しがっていてもしょうがないので、復習を兼ねたアウトプットをここでします。
一番重要視するのは、「分からないところを無視しない」という点です。ただただ自分のためのアウトプットであり、カリキュラムがあるわけではありません。
分からないところは徹底的に調べ、調べた内容はここにアウトプットします。自分だけのメモでやってろって話かもしれませんが、自分の認識などに誤りがある場合、ここでアウトプットすることで指摘をいただけるという可能性があります。
淡い期待です。最後に
現状、オリジナルサービスのワイヤーフレーム、サイトマップ、ER図の作成中です。
次回投稿を1回目とし、ここから進めていこうと思っています。よろしくお願いします!
- 投稿日:2020-09-22T11:15:07+09:00
【Rails】[0日目]分からないところを無視せずオリジナルサービス制作
始めに
初めまして。名古屋在住のしんやです。転職で東京に行きたいです。
未経験からの転職のためにテックアカデミーのWebアプリケーションコースを受講し、2020年9月20日に受講終了しました。僕としてはスクールのカリキュラムは非常に分かりやすく、サポート体制も整っていたので快適に学習を進めることができました。
プログラミング勉強してる時、完全には納得できてないことがあっても、とりあえず「これはこういうものなんだ」として先に進めると、意外とあとから「そういうことだったのか」って納得できることありませんか??#プログラミング初心者 #駆け出しエンジニアと繋がりたい
— しんや@転職で東京行きたい (@shinyaeng) August 11, 2020唐突に自分の過去ツイートを引用しましたが、このスタンスで進めて受講終了に至りました。
受講終了した今気付いたのが、上記スタンスで進めるのはいいが、絶対に理解してから進めないといけないこともあるという当然のことでした。
簡単に言うと、とりあえずで進めたとこが多数あるが故に、カリキュラムは終えたけど全然理解できてねーなってとこが多数あるってことです。もちろんスクールの受講を終了した程度で全部理解できるとは思っていなかったですが、悔しいです。
今後の内容
悔しがっていてもしょうがないので、復習を兼ねたアウトプットをここでします。
一番重要視するのは、「分からないところを無視しない」という点です。ただただ自分のためのアウトプットであり、カリキュラムがあるわけではありません。
分からないところは徹底的に調べ、調べた内容はここにアウトプットします。自分だけのメモでやってろって話かもしれませんが、自分の認識などに誤りがある場合、ここでアウトプットすることで指摘をいただけるという可能性があります。
淡い期待です。最後に
現状、オリジナルサービスのワイヤーフレーム、サイトマップ、ER図の作成中です。
次回投稿を1回目とし、ここから進めていこうと思っています。よろしくお願いします!
- 投稿日:2020-09-22T11:15:07+09:00
【Rails】[第0回]分からないところを無視しないオリジナルサービス制作
始めに
初めまして。名古屋在住のしんやです。転職で東京に行きたいです。
未経験からの転職のためにテックアカデミーのWebアプリケーションコースを受講し、2020年9月20日に受講終了しました。僕としてはスクールのカリキュラムは非常に分かりやすく、サポート体制も整っていたので快適に学習を進めることができました。
プログラミング勉強してる時、完全には納得できてないことがあっても、とりあえず「これはこういうものなんだ」として先に進めると、意外とあとから「そういうことだったのか」って納得できることありませんか??#プログラミング初心者 #駆け出しエンジニアと繋がりたい
— しんや@転職で東京行きたい (@shinyaeng) August 11, 2020唐突に自分の過去ツイートを引用しましたが、このスタンスで進めて受講終了に至りました。
受講終了した今気付いたのが、上記スタンスで進めるのはいいが、絶対に理解してから進めないといけないこともあるという当然のことでした。
簡単に言うと、とりあえずで進めたとこが多数あるが故に、カリキュラムは終えたけど全然理解できてねーなってとこが多数あるってことです。もちろんスクールの受講を終了した程度で全部理解できるとは思っていなかったですが、悔しいです。
今後の内容
悔しがっていてもしょうがないので、復習を兼ねたアウトプットをここでします。
一番重要視するのは、「分からないところを無視しない」という点です。ただただ自分のためのアウトプットであり、カリキュラムがあるわけではありません。
分からないところは徹底的に調べ、調べた内容はここにアウトプットします。自分だけのメモでやってろって話かもしれませんが、自分の認識などに誤りがある場合、ここでアウトプットすることで指摘をいただけるという可能性があります。
淡い期待です。最後に
現状、オリジナルサービスのワイヤーフレーム、サイトマップ、ER図の作成中です。
次回投稿を第1回とし、ここから進めていこうと思っています。よろしくお願いします!
- 投稿日:2020-09-22T10:53:18+09:00
Rails 6で認証認可入り掲示板APIを構築する #17 管理者権限の追加
←Rails 6で認証認可入り掲示板APIを構築する #16 policyの設定
管理者権限を用意する
前回までの実装で、投稿の編集や削除は投稿者本人だけできるようになりました。
ですがそこに拡張し、管理者としてログインしている時は誰の投稿でも編集・削除できるようにしてみます。そのためにはuserモデルにadminカラムを追加しましょう。
$ rails g migration AddAdminToUsers admin:booleandb/migrate/xxxxxxxxxxxxxx_add_admin_to_users.rb# frozen_string_literal: true class AddAdminToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :admin, :boolean, default: false, null: false end endさて、今までは機能を実装してからテストを書いていましたが、punditの基本挙動は理解できたと思うので、先にテストから書いてみます。
管理者権限のない別ユーザーでログインしていた際に編集削除できないテストはすでに書かれているので、
- 管理者権限のある際に別ユーザーの投稿を編集できること
- 管理者権限のある際に別ユーザーの投稿を削除できること
を実装してみます。
厳密にはpolicyテストとrequestテスト両方実装すると万全ですが、ここはpolicyだけ実装してみます。もし良ければここから書いてあるサンプルを見ずに、まずは自力で実装してみて見比べてみると、コードを書く練習になるのでやってみてください。
post_policy_spec.rbの編集
spec/policies/post_policy_spec.rb... RSpec.describe PostPolicy, type: :policy do let(:user) { create(:user) } + let(:admin_user) { create(:user, admin: true) } let(:post) { create(:post) } ... it "ログインしているが別ユーザーの時に不許可" do expect(subject).not_to permit(user, post) end + it "adminユーザーでログインしている時に許可" do + expect(subject).to permit(admin_user, post) + end ...spec/factories/posts.rb... remember_created_at { nil } name { "MyString" } tokens { nil } + admin { false } end ...ここまで実装したらrubocopとrspecを動かして確認。
まだpolicyファイルを実装していないのでテストはコケます。policyの実装
まずは管理者であることを判定するprivateメソッドをapplication_policy.rbに生やします。
app/policies/application_policy.rb... private def mine? @record.user == @user end + + def admin? + @user.present? && @user.admin? + end +あとはpost_policy.rbのupdate?destroy?の2つの判定を変えるだけですね。
app/policies/post_policy.rbdef update? - mine? + mine? || admin? end def destroy? - mine? + mine? || admin? endなんとこれだけです。
traitを使ってadminを簡単に作る
これだけだと少し中身の薄い記事なので、factoryBotを触ってadminユーザーをもっと簡単に作れるようにします。
spec/factories/users.rbname { "MyString" } tokens { nil } admin { false } + + trait :admin do + admin { true } + end end
spec/policies/post_policy_spec.rbRSpec.describe PostPolicy, type: :policy do let(:user) { create(:user) } - let(:admin_user) { create(:user, admin: true) } + let(:admin_user) { create(:user, :admin) } let(:post) { create(:post) }これでrspecを動かしてみるとどうでしょうか。
ミスなく書けていればテスト通過するはずです。traitはご覧の通り、factoryBotの
create
等の第2引数に別名として渡すことで使うことができます。
adminフラグを立てるだけなら恩恵も少なく意味もあまりないのですが、例えばadminユーザーの場合に必ずセットで初期値を持っておきたいカラムがあったりすれば、いちいちcreate
のたびに複数カラムの初期値セットをしなくて済みます。ぜひご活用ください。
続き
→
【連載目次へ】
- 投稿日:2020-09-22T10:49:04+09:00
Ruby による2021年の週カレンダー(システム手帳用リフィル)の作成
目的:
週カレンダーの作成です。
背景:
システム手帳のリフィルのサイズ(82mmW x 140mmL)が特殊で、使い始めたものの、入手困難であったため、いっそ手作りしてしまおうと考えたのは過去のことです。その際、リフィル用紙を特注で作ったことがありまして、その時の白紙が余っているので、来年は、久しぶりに手作りしようと考えたこと。
年に一度のことですので、手作りでも良いのですが、せっかくのプログラミング環境があるので、毎年、自動でカレンダーが出力できるような仕組みを作成してみようじゃあないか。という理由で、プログラミングしました。印刷:
ワープロソフトの「差し込み印刷」の機能を用います。従って、差し込み印刷用のカレンダーデータを作れば良い。「差し込み印刷」するテンプレートは、下図のような、見開きで1週間表示となるテンプレートとしました。能率手帳のように片側に1週間を詰め込むというテンプレートでも同様です。
データ:1週間が1レコードになりますので、下図のようなカレンダーデータベースができれば、差し込み印刷に使用できます。この表を、ruby で作ってみました。
ruby のコードは、下記になります。
weekly_calendar.rb# weekly require 'date' day = Date.new(2020,12,28) # 月曜日始まりの12月最終月日 last = Date.new(day.year+1, day.month, day.mday) weekdays = {0 => '日', 1 => '月', 2 => '火', 3 => '水', 4 => '木', 5 => '金', 6 => '土'} while day < last do records=Array.new records.push(day.strftime("%Y%W,%Y,%W")) dday = day while dday < day+7 do wday = weekdays[dday.wday] records.push(dday.strftime("%b,%-m,%d,%w,#{wday},%a,")) dday = dday+1 end print records.join(',') print "\n" day = day+7 endヘッダーのファイルは下記コードで出力し、上の出力の上に貼り付けました。csvを 出力して、libreoffice calc に読み込ませれば、「差し込み印刷」のデータとなります。
header.rb# weekly header printf "%s,%s,%s,","yrwk","yr","wkwk" printf "%s,%s,%s,%s,%s,%s,%s,","mte1","mtn1","dyn1","wkn1","wkj1","wke1","s1" printf "%s,%s,%s,%s,%s,%s,%s,","mte2","mtn2","dyn2","wkn2","wkj2","wke2","s2" printf "%s,%s,%s,%s,%s,%s,%s,","mte3","mtn3","dyn3","wkn3","wkj3","wke3","s3" printf "%s,%s,%s,%s,%s,%s,%s,","mte4","mtn4","dyn4","wkn4","wkj4","wke4","s4" printf "%s,%s,%s,%s,%s,%s,%s,","mte5","mtn5","dyn5","wkn5","wkj5","wke5","s5" printf "%s,%s,%s,%s,%s,%s,%s,","mte6","mtn6","dyn6","wkn6","wkj6","wke6","s6" printf "%s,%s,%s,%s,%s,%s,%s\n","mte7","mtn7","dyn7","wkn7","wkj7","wke7","s7"祝日情報は、手入力で修正になります。データを揃えたら自動的に差し込めるように改良したいですが、年に一回の作業ですので予定は未定です。
- 投稿日:2020-09-22T09:54:14+09:00
剰余とべき乗(冪乗)の演算子
【概要】
1.結論
2.○○になるのはどういう時か
3.ここから学んだこと
1.結論
剰余:%
べき乗:**
を使う!
2.どのように使用するか
今回は2桁の整数(桁数制限するプログラムはしていません。)入力「の10の位と1の位を足した合計を求めています。
def addition(a, b) a + b end def calculation(num) #10の位 no1 = (num / 10) % 10 #1の位 no2 = (num / 1) % 10 return no1,no2 end puts "2桁の整数を入力" num = gets.to_i a, b = calculation(num) add_sum = addition(a, b) puts "10の位と1の位の合計は#{add_sum}"10の位を出すために10で割った数値をさらに10で割った余りを出しています
1の位も同様に1で割った数値をさらに10で割った余りを出しています。その際に"%"を使ってあまりを出しています。(例)28の場合(num / 10)・・・・28 / 10 = 2.8 2.8 % 10 ・・・・0余り2 の"2"を返す。 (num / 1)・・・・28 / 1 = 28 28 % 10・・・・2余り8 の"8"を返す。
3.ここから学んだこと(エラーの時に使用)
算数要素が混ざったプログラムは、プログラムの知識はもちろんのこと、工夫して計算する発想が求められることを学びました。工夫した計算にプログラムの知識も必要ですし、「このメソッドは一体どこで使うんだろう?」と思っているとピンポイントで使うこともあるので非常に大事です。
- 投稿日:2020-09-22T01:28:04+09:00
Rubyで割り算! なぜ0になる?
割り算(除算)の落とし穴をはじめから丁寧に。Rubyを例に扱います。前置きも説明も非常に長いです。結論はこちら。
答えが少数になるときは要注意
たとえば「80÷100」をしたい、とします。
期待する答えは「0.8」(ないしはそれと等しい数)ですが…80 / 100 => 00が返ってきますね。なぜでしょうか?
プログラムとはそういうもの
先ほどの計算が"整数同士の計算"とみなされているからです。「整数同士であってるじゃん」と思うのですが、整数と整数の計算は整数でしか返ってきません。
たとえば「101÷100」もそうです。答えとして「1.01」を返してほしいところですが、次のようになってしまいます。
101 / 100 => 1どうやって計算するの?
期待する答えを得るためには、少数の桁まで計算に含める必要があります。「整数としてではなく、少数として計算してね!」と伝えなくてはいけないわけです。日常的には特段意識してやっていないような計算も、プログラムではこれらが違うことを意識して書いてあげる必要があります。
たとえば、次のように書けば、少数の桁も計算のうちであることを伝えることができます。
80.0 / 100 => 0.8と書いても良いし、
80 / 100.0 => 0.8と書いても良いし、もちろん
80.0 / 100.0 => 0.8でも良いですね。
なるほど。でも、これが変数だったら? そういった状況に対応するためにも、次のメソッドを知っておきたいです。
to_fメソッドを使う
Rubyで"少数"として扱いたい時に使えるのが
to_f
メソッドです。「f」はfloatの「f」、"浮動小数点数"を意味します。(こちらもまた独特の概念ですが、参考にできる記事がたくさんあるのでそちらに譲ります。)
さて、使い方ですが、数値(変数でもOK)の後にto_f
をつけるだけです。80.to_f / 100 => 0.8と書いても良いし、
80 / 100.to_f => 0.8と書いても良いし、もちろん
80.to_f / 100.to_f => 0.8でも良いですね。
この方法なら変数がきても大丈夫です。
n = 80 n.to_f / 100 => 0.8これで期待する結果が得られましたね。