- 投稿日:2019-03-30T23:02:28+09:00
hidden_fieldの使い方
Railsで
hidden_fieldを使ったので、内容整理のためメモ。
まず、hidden_fieldとhidden_field_tagは用途は似ているが使い方が違う。
hidden_fieldはform_forやform_withの中で使用するに対し、hidden_field_tagは一つだけ単体でパラメーターを渡したいときに使用する。
hidden_field_tagもform_for内で使用できないわけではない。
今回はhidden_fieldに焦点を当てることとする。○実装の背景
ログインしているユーザーが残業申請フォームから一日分の残業申請できるようにする。
usersテーブルとattendancesテーブルが存在し、2つのテーブルは1対多の関係である。/users/show.html.erb<!-- 残業申請フォーム --> <%= @date.each do |date| %> <div class ="test" id="<%= date.attendance_day %>"> <h3>残業申請</h3> <%= form_for(date, url: sample_path, method: :post) do |f| %> <%= f.hidden_field :id, :value => date.id %> <!-- attendancesテーブルのidを渡す --> <%= f.hidden_field :user_id, :value => @user.id %> <table class="txt1 table table-bordered table-striped table-condensed"> <thead> <tr> <th>日付</th> <th>曜日</th> <th>終了時間(必須)</th> <th>業務内容</th> <th>確認(必須)</th> </tr> </thead> <tbody> <tr> <!-- 日付 --> <td><%= date.attendance_day.month %>/<%= date.attendance_day.day %></td> <!-- 曜日 --> <td><%= @week[date.attendance_day.wday] %></td> <!-- 終了時間 --> <td><%= f.time_field :scheduled_end_time, class: 'form-control' %> </td> <!-- 業務内容 --> <td><%= f.text_field :business_outline, class: 'form-controll' %></td> <!-- 確認 --> <td> <% if current_user.superior? %> <%= f.select :instructor_test, [@superior], :include_blank => true %> <% else %> <%= f.select :instructor_test, @superior, :include_blank => true %> <% end %> </td> </tr> </tbody> </table> <%= f.submit "残業を申請する", class: "btn btn-sm btn-primary" %> <% end %> </div> <% end %>@dateにはログインしているユーザーの月初から月末までの勤怠データが格納されており、each文で繰り返し処理している。
レコードはattendancesテーブルの内容。
form_forでブロック変数fを定義し、f.hidden_fieldを2つ指定している。
第一引数にはパラメータで渡すname属性を指定するので、何でも良いが、わかりやすい名前にする。今回はidとuser_idとする。
第二引数はvalue属性を指定するので、idの場所にdate.id、user_idに@user.idとする。
date.idはattendancesテーブルのid(つまり月初から月末までの1日ごとのid)を、@user.idはusersテーブルのid(つまりログインしているユーザーのid)をvalueに指定している。
hidden_fieldで値をパラメーターで渡すときは上記のようにname属性とvalue属性を指定する。attendances_controller.rb# 一日分の残業申請 # users/show.html.erbのhidden_fieldのパラメータ取得 def sample # 取得できるものは以下と同じ @user = User.find(params[:id]) @user = User.find(params[:attendance][:user_id]) @attendance = @user.attendances.find(params[:attendance][:id]) # binding.pry if params[:attendance][:scheduled_end_time].blank? || params[:attendance][:instructor_test].blank? flash[:warning] = "必須箇所が空欄です。" redirect_to @user else @attendance.update_attributes(overtime_params) flash[:success] = "残業申請が完了しました。" redirect_to @user and return end endアクションでhidden_fieldで渡されたパラメーターを受け取ることができる。
受け取る時の決まりとしてparams[:モデル名][:渡したname属性]である必要がある。# 今回は @user = User.find(params[:id]) と、取得できるものは実質同じ @user = User.find(params[:attendance][:user_id])上記の@userはhidden_fieldで指定したname属性であるuser_idを受け取っている。
user_idの中身(value属性)は@user.idなので、ログインしているユーザーのidを取得している。@attendance = @user.attendances.find(params[:attendance][:id])上記の@attendanceもhidden_fieldで指定したname属性であるidを受け取っている。
idの中身(value属性)はdate.idなので、月初から月末までの残業申請したい日付のidを取得している。params[:attendance][:scheduled_end_time] params[:attendance][:instructor_test] <!-- users/show.html.erb の一部 --> <td><%= f.time_field :scheduled_end_time, class: 'form-control' %> </td> <td><%= f.select :instructor_test, [@superior], :include_blank => true %></td>上記はhidden_fieldでパラメーターとして値を渡していないが、値が受け取れる。
なぜなら、form_for内でf.time_fieldのようにしてscheduled_end_timeを指定しているから。以下は失敗例
params[:attendance][:scheduled_end_time].blank? # のところを↓に変更すると... @attendance.scheduled_end_time.blank?上記の
@attendance.scheduled_end_timeでは値がnilになり取得できない。
これはscheduled_end_timeがDBにすでに保存されていれば取得できるが、今回は申請前の残業申請フォームの「終了時間」を入力時点での値を取得したいので、params[:attendance][:scheduled_end_time]とする必要がある。
- 投稿日:2019-03-30T22:00:30+09:00
「.gitignore」をコピーし損ねてGithub desktopに膨大な変更履歴があがった話
初投稿です。
初学者の私がハマった不具合の内容と原因を共有します。[内容]
Gitでリポジトリを作成しRailsファイルを編集していたところ、viewファイルを2~3ついじっただけでキャッシュやログのデータが1,000件以上Github desktop上にあがってしまった。
[経緯]
使用するRailsアプリケーション(仮にAとする)を
rails new Aコマンドで作成した。そのディレクトリとは別に空のディレクトリを作成(仮にA'とする)、そのディレクトリに
git initをしてローカルリポジトリとした。Railsファイル(A)をカットアンドペーストしA’に貼り付けてA’内で作業した。
上の画像の様にfinderで切り取りしてAからA'にそのままRailsファイルを貼り付けた。
[原因]
![]()
実際は隠しファイルの.gitnoreは選択されていませんでした...。
.gitignore内にログやキャッシュを追跡対象から外す記述がされているので、このファイルがないとGitは全ての変更を記録してしまい、私の様に1,000件以上の変更履歴を残すことになってしまいます...。
(※因みに隠しファイルは「⌘ + shift + .(ドット)」で表示できます)[対応]
とりあえず、Railsディレクトリ内に「.gitignore」を作成。何を追跡対象から外すのが良いかは全く見当が付かなかったので、下記リンクからRails.gitignoreのテンプレートを使用しました。
以下、色々な.gitignoreのテンプレートが載っているリポジトリです。
https://github.com/github/gitignoreまた、.gitignoreを編集しても、既にGitが記録してしまっているものに対しては反映されません。ファイルを指定して追跡対象から外すこともできますが、私の場合キャッシュデータが膨大だった為、以下のコードでまとめて追跡対象から除外しました。
git rm --cached `git ls-files -i --full-name --exclude-from=.gitignore`以下、参照させて頂いた記事です。
[参考記事]
https://qiita.com/yuuAn/items/b1d1df2e810fd6b92574
https://qiita.com/koppayashi/items/fa971b641770266af9a7
- 投稿日:2019-03-30T21:58:37+09:00
【まつもとゆきひろ氏 特別講演】20代エンジニアのためのプログラマー勉強法のまとめ 2019/3/30
【まつもとゆきひろ氏 特別講演】20代エンジニアのためのプログラマー勉強法のまとめ
予想していた技術的な勉強法というより、エンジニア、ビジネスマンとしての生き方や、成功するための方法論を20代に向けてMatzさんが伝えてくれたのでまとめます。(自分なりの解釈も少し入ってます)
とてもためになる講演でした。個人的には特に前談2、3、4、5、6がためになりました!!
Matzさんありがとうございました!講演内容
前談1. テクノロジーとは人を幸せにするためのもの
前談2. 若いうちから頑張ろう
1.学生と社会人の"勉強"の違い
2.なぜ勉強するのか?
3.勉強についてのTips(what, where, when, how)
4.とにかくアウトプット
5.成功するためのTips
6.最後に3つのアドバイス前談1. テクノロジーとは人を幸せにするためのもの
本来人を幸せにするためのテクノロジーが人を不幸にしてしまっていることがある。
もっと幸せになるために使われるべき。
EX)エントリーシート
・人事担当はチェックするのめんどい
・就活生は競争力のある就活生しか”人”をみてもらえない前談2. 若いうちから頑張ろう
30代は遅すぎる訳ではないが、身体的にも無理がきく20代で頑張った方がいい。
20代をモラトリアムにしてしまうのはもったいない。
→故に特に若い時は戦略が必要だし、若いうちが頑張りが効くし成果も出やすい。
Ex)アインシュタインがノーベル賞をとったのは比較的若いうち
Ex)Matzは27歳でRuby開発確かに前みたTEDで35歳までの出来事で人生の80%が決まると聞いて頑張ろうと思ったのを思い出した。
20代の人は絶対見た方がいい。そして社会人の勉強法としてこの前談以降を参考にしてほしいとのこと
1. 学生と社会人の"勉強"の違い
まず、勉強というと学生時代にやった勉強をイメージしがちであるが学生と社会人の勉強は異なる。
こういった"勉強"という言葉の抽象化は時に便利だが、誤解や二元論に繋がったりもするので抽象的な言葉を使うときは注意が必要。共通点
- 知識と技術の獲得
違い
- 満点 VS 満点なし:学校では満点あるが社会にでてから会社では満点はない。
- 苦手克服 VS 得意を伸ばす:学生では苦手教科を克服した方が、いい成績を取る上で効率がいいが社会では得意なことを伸ばした方が効果的。
- 暗記 VS 把握:学生のテストでは覚えてなければいけないが、会社ではわからなければではググればいい。
- 知識 VS インデックス:同上。
- 試験 VS 常在戦場:学校では基本テストの時の結果が大事、会社では常に仕事での成果が見られる。
- メイン VS サブ:学ぶことは学生にとってはメイン、社会人は学ぶうこと自体はサブ、学びを使って成果を出す必要がある。
2.なぜ勉強するのか?
※以降、勉強の定義は”お勉強”ではなく”成長するための方法”
正直Matzも勉強したくない、
でも成功したい、高収入が欲しい、良好な人間関係が欲しい、嫌なことはしたくない、好きなことでいきていきたいから勉強する。
そのために必要なのは「高評価」を得ること。
→つまり勉強の目的は「尊敬」または「尊重」を得ることと言える。3.勉強についてのTips(what, where, when, how)
what
好きなことを学ぼう。すきそこものの上手なれ。
好きなものを見つけるためには、インベントリ(棚卸し)、つまり自己分析をするべき。
得意、趣味、興味、思考、背景といった自分の特性について理解を深めることが大切。そういえば最近読んだメモの魔力に書いてあったなー
あの本についてた自己分析の質問をやるのがいいかもしれない。where
上でも書いたが、
成功=高評価、尊敬、尊重されること。いい環境で学ぶ→成長する→尊敬される→モチベーションUP→もっと勉強する
といった好循環ループが入ることができる環境に自分を置くべき。
ループになることを妨げる環境からは離れていい。
win-winの好循環ループか No Dealであるべきで、そうならないのなら逃げていい。when
まとまった時間をつくるのは意外に難しい。
だからこそ優先順位をつけるべき。
だから無意識の優先順位と意識(理想の)の優先順位を一致させる必要がある。
Ex)twitterのフォローワーをしぼるとか、情報収拾のフォーカスをしぼるとか、ただ音楽を聴いている時間をAudibleで本を聞くとか。あとは生産性を高める。
Ex)早く仕事を終わらせて、終わったと上司にあえて伝えず、自分が得たい技術を身につけるための時間にするとか。how
好きなことをやって稼げるのがベストだが、お金にならない可能性もある。
だから妥協と打算が大事。
どこまで妥協して、どこまでは譲れないのかを決めるということ。Ex) Matzが就職するタイミングで言語を開発したかったけど、そんなことをしている会社はなかったので、とりあえずUnixでソフトウェア開発で食えるというところまで、妥協した。
ただ一回決めた自分の判断を永遠に信用したらいけない。
上の例の10年後、言語を開発して食っていけるようになったみたいなことが起こるから、一回妥協しても、常に可能性を模索すべき。4.とにかくアウトプット
知識を得ることはググればいいので比較的楽である昨今、アウトプットに価値がある。
一見簡単だが、アウトプットすることで失敗したらどうしようとか、めんどくさかったりと意外にできなかったり心理的障壁がある。
Ex)Youtuberとか。「スライム風呂はいるとかだれでもできるでしょ。」的なことを思うけど、意外にみんなやらない。でもアウトプットは人を成長させる。
ハードルを下げるためにアウトプットの質はとりあえず棚上げしても良い。
→アウトプットしていくと、アウトプットを習慣にしていくうちに楽になっていく。
→繰り返すことによって心理的障壁を下げる。繰り返すことで変われるという人間の可塑性にかけるべき。
Ex)マークザッカーバーグは彼女に振られて大学のデータベースから女子の写真を盗み、美人コンテストしてた悪ガキ。でも今は大企業で社長を立派にやっていけてる。
5.成功するためのTips
成功するためには知名度を上げる。
→基本的にみんなミーハー、有名人を見るとそんな好きでなくても写真を撮りたくなる、サイン欲しいとかなる。ということで知名度はそれだけで価値がある。
そのためには認知され、知られる必要がある。
故にアウトプットして、自分の価値を示す必要がある。
俺はここにいるぞ!、こんな人だぞって。でもマタイによる福音書第13章に
「おおよそ、持っている人は与えられて、いよいよ豊かになるが、持っていない人は、持っているものまでも取り上げられるであろう。」
とあるように成功するためには成功していなければならない。
じゃあはじめに成功するには?
会社でのマーケティングなら金つかってCM打てばいいかもが、個人の場合はキャズム理論が役に立つ。
キャズム理論
5つの顧客群
イノベーター
アーリーアダプター
------------------ここにキャズム(溝)がある
アーリーマジョリティ
レートマジョリティ
ラガードキャズム理論とは簡単にいうと、このキャズムを超えるのがむずいからターゲットを絞り、まずニッチを攻めなさいというもの。
個人でやるなら、ユニークさ、埋没しないのが大切。
ターゲットを絞り、まずニッチを攻め認知度を広げその後、横展開するのがいい戦略なのではないか。Ex)Matzの例
言語開発者という局所的なところで認知度を高める。
→他にも講演や執筆等の横展開。6.最後に3つのアドバイス
コンピューターサイエンス・アーキテクチャの基礎を押さえる
CSの基礎知識それ自体が差別化になりうる。
いいプログラムを書く人はコンピューターアーキテクチャをしっかり理解してる。英語を学ぶ
まず英語があれば18億人と繋がれる。
プログラミングに限らず、最新の技術や発見は英語で発信される。
英語があればそこで一時情報を取れるが、できないと日本語に訳されるのを待つしかない。ぺらぺらでなくても、最低、英語で情報を取る能力は身につけるべき。
またタイムマシン経営とかできる。
欧米の流行りが5年の時差で日本に来るやつ。日本というガラパゴス諸島に囚われているのは得策ではない。
コンフォートゾーンから出る
とりあえずやってみよう!以上!
個人的な感想
まず、若いうちから頑張ろうというテーマで、35までで人生80%決まるという恐ろしい事実思い出したので、とりあえずそれまでは自分のリソースほぼ投資に使おうと改めて思った。
アウトプットの重要性についても最近認識してたので、ちょうど今日から情報発信前提のtwitterも始めた。この記事でもQiitaで初めてアウトプットしてるし継続する。
自己分析についてはもっと深掘りする必要があると最近感じて、前田さんのメモの魔力を買ったが、全然やってないので、やるべき。
環境については今のところ、問題なしかなーと思う。
時間の使い方は改善する必要ある。人生は自分の時間をどう使ったかの結果だと思うので、ここはすごい大事。
キャズム理論は勉強になった。これももっと考えるべき。
英語とコンフォートゾーンでるについては今のとこそこまで問題なしだが、CSの基礎はやはり、今のうちに固めとくべきと痛感した。とりあえず、Matzさんの講演いってよかった!写真撮れたし!サインもらえたし!
3/30にtwitterを始めたので、よかったら!
@mokkey19930617
- 投稿日:2019-03-30T19:10:11+09:00
ブラックジャックゲームを作ってみた[Ruby]
はじめに
プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべしという記事に影響されて、Rubyで作ってみました!
参考にさせていただいた記事の「開発開始!」まで読み進めて作成したものとなります。作成者のスペック
- 実務経験なし
- 学習歴 約3ヶ月
- Ruby, Railsを学習中(Javaもいつかやりたいな・・・)。
未熟な部分しかございませんが、よろしくお願いします!
ルールの整理(実際には様々なルールが存在しますが、今回は以下のようにしました。)
- カードは52枚, 同じカードは存在しない。
- Aは1点として扱う。(得点に応じて、1か11にする例外処理が実装できませんでした。)
- プレイヤーがバーストした時点で即敗北。(ディーラーのターンは来ない。)
- スコアが同じであれば、引き分け(両者ブラックジャックの場合も)
- プレイヤーのドロー終了後、ディーラーのターン開始
- プレイヤーのみ、特定条件および一定確率でデスティニードロー(イカサマ)発生。 ・・・ オリジナル
工夫したところ
一定確率で確実にブラックジャックになるカードを引くことのできる特殊演出を実装しました。
ゲームとしては、崩壊していますが遊び心として大目にみてください。発生条件
- プレイヤーの得点が21未満11以上
- 手札が3枚以上
- ランダムで生成される数字がif文の条件と一致。(10%の確率)
コードとしては、「Playerクラスのdestiny_drawメソッド」と「Deckクラスのdestiny_draw_cardメソッド」になります。特定条件の判定はblackjack.rbにて行なっています。
blackjack.rb
blackjack.rbは主にゲームの進行を取り扱うところです。
run_gameが実行されるとゲームが始まり、プレイヤーとディーラーお互いの手札が配分され、プレイヤーのターン開始となります。
putsの空行が至る所にありますが、これは出力結果を見やすくするために出力しています。blackjack.rbrequire './card' require './deck' require './player' # ゲームの進行 def run_game # 初期手札2枚の準備,手札公開,得点表示 deck = Deck.new deck.shuffle player = Player.new player.first_draw(deck) player.score_count dealer = Dealer.new dealer.first_draw(deck) dealer.score_count #ユーザーのターン while true do number = rand(11) if number == 3 && (player.instance_variable_get :@hands).length >= 3 && player.score_count >= 11 && player.score_count != 21 puts "山札が輝きだした!\n山札を信じて引きますか?[Y/N]" puts "あなたの得点: #{player.score_count}" answer = gets.chomp.to_s if answer == "Y" || answer == "y" player.destiny_draw(deck, player) unless blackjack?(player) break end elsif answer == "N" || answer == "n" puts puts "さらにカードを引きますか?[Y/N]" puts "あなたの得点: #{player.score_count}" answer = gets.chomp.to_s puts "Answer: #{answer}" if answer == "Y" || answer == "y" player.draw(deck, player) unless burst?(player) exit end unless blackjack?(player) break end elsif answer == "N" || answer == "n" break else puts "無効な値です。もう一度入力してください。" end else puts "無効な値です。もう一度入力してください。" end else # 通常時の処理 puts puts "さらにカードを引きますか?[Y/N]" puts "あなたの得点: #{player.score_count}" answer = gets.chomp.to_s puts "Answer: #{answer}" if answer == "Y" || answer == "y" player.draw(deck, player) unless burst?(player) exit end unless blackjack?(player) break end elsif answer == "N" || answer == "n" break else puts "無効な値です。もう一度入力してください。" end end end #ディーラーのターン while true do if dealer.score_count < 17 dealer.draw(deck, dealer) else break end end judge(player, dealer) end # 勝敗判定 def judge(player, dealer) player_score = player.score_count dealer_score = dealer.score_count puts puts "----- あなたの得点 -----" puts "#{player_score}" puts "--- ディーラーの得点 ---" puts "#{dealer_score}" puts if dealer.score_count == player_score puts "引き分け" elsif player_score == 21 puts "ブラックジャック!\nあなたの勝ちです!" elsif dealer_score == 21 puts "ディーラーのブラックジャック!\nあなたの負けです..." elsif dealer_score > 21 puts "ディーラーはバーストしました。" puts "あなたの勝ちです!" elsif dealer_score > player_score puts "あなたの負けです..." elsif dealer_score < player_score puts "あなたの勝ちです!" end end # バーストしたか? def burst?(player) if player.score_count <= 21 return true else puts puts "あなたの得点: #{player.score_count}" puts "バーストしました。あなたの負けです。" return false end end # ブラックジャックかどうか def blackjack?(player) if player.score_count == 21 return false else return true end end puts "---------------------" puts "ブラックジャックへようこそ!" puts "---------------------" puts run_gamePlayerクラス
プレイヤークラスには手札を持たせており、手札に関係する処理を行うメソッドを定義しました。
ディーラーとプレイヤーで処理を分けています。player.rb# プレイヤークラス class Player def initialize @hands = [] end # 一枚引く def draw(deck, player) card = deck.draw_card @hands.push(card) puts puts "あなたが引いたカードは#{card.show}です" puts "----- あなたの手札 -----" @hands.each_with_index do |hand, i| puts "[ #{hand.show} ]" end puts "---------------------" end # 得点計算 def score_count score = 0 @hands.each do |hand| score += hand.count end return score end # 最初のドローおよび手札公開 def first_draw(deck) 2.times do card = deck.draw_card @hands.push(card) end puts "----- あなたの手札 -----" @hands.each_with_index do |hand, i| puts "#{i+1}枚目: #{hand.show}" end puts "---------------------" end # デスティニードロー def destiny_draw(deck, player) card = deck.destiny_draw_card(player) @hands.push(card) puts puts "あなたが引いたカードは#{card.show}です" puts "----- あなたの手札 -----" @hands.each_with_index do |hand, i| puts "[ #{hand.show} ]" end puts "---------------------" end end # ディーラークラス class Dealer def initialize @dealer_hands = [] end # 一枚引く def draw(deck, player) card = deck.draw_card @dealer_hands.push(card) puts "ディーラーはカードを引きました" end # 得点計算 def score_count score = 0 @dealer_hands.each do |hand| score += hand.count end return score end # 最初のドローおよび手札公開 def first_draw(deck) 2.times do card = deck.draw_card @dealer_hands.push(card) end puts "--- ディーラーの手札 ---" puts "1枚目: #{@dealer_hands.first.show}" puts "2枚目: 伏せられている" puts "---------------------" end endDeckクラス
デッキクラスでは、山札を作成する役割を与えています。
Deck.newと同時にbuildメソッドが動き、52枚のカードを配列内に作成します。
その後、shuffleメソッドを動かすことで配列内の順番をランダムにします。deck.rb# デッキクラス class Deck def initialize @cards = [] build end # 山札作成 def build for suit in ["♡", "♠", "♦︎", "♣︎"] do for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] do card = Card.new(suit, number) @cards << card end end end # 山札シャッフル def shuffle cards_length =(@cards.count) - 1 cards_length.step(1,-1) do |i| r = rand(i) @cards[i] , @cards[r] = @cards[r], @cards[i] end end # 山札の上から一枚引く def draw_card @cards.pop end # 得点が21になるようなカードを探して引く def destiny_draw_card(player) target = (21 - player.score_count) @cards.find { |card| (card.instance_variable_get :@number) == target.to_s } end endCardクラス
ここでは、カードを定義しています。カードにスートに関するパラメータと数字に関するパラメータを持たせることでトランプとして再現しています。countメソッドでは、J, Q, Kを点数計算時のみ「10」に置き換えています。
card.rb# カードクラス class Card def initialize(suit, number) @suit = suit @number = number end def show return "#{@suit} of #{@number}" end # J,Q,Kの処理 def count if @number == "J" || @number == "Q" || @number == "K" return @number = 10 else return @number.to_i end end end実行結果
こちらが実行結果になります。
通常時
--------------------- ブラックジャックへようこそ! --------------------- ----- あなたの手札 ----- 1枚目: ♦︎ of 6 2枚目: ♣︎ of 4 --------------------- --- ディーラーの手札 --- 1枚目: ♣︎ of K 2枚目: 伏せられている --------------------- さらにカードを引きますか?[Y/N] あなたの得点: 10 y Answer: y あなたが引いたカードは♦︎ of 8です ----- あなたの手札 ----- [ ♦︎ of 6 ] [ ♣︎ of 4 ] [ ♦︎ of 8 ] --------------------- さらにカードを引きますか?[Y/N] あなたの得点: 18 n Answer: n ディーラーはカードを引きました ----- あなたの得点 ----- 18 --- ディーラーの得点 --- 24 ディーラーはバーストしました。 あなたの勝ちです!特殊演出
「山札を信じて引きますか?」で「N」を選択すると、通常処理に戻り、カードを引くか再び聞くようにしています。
以下の例では、「Y」を選択しています。--------------------- ブラックジャックへようこそ! --------------------- ----- あなたの手札 ----- 1枚目: ♦︎ of 5 2枚目: ♣︎ of 3 --------------------- --- ディーラーの手札 --- 1枚目: ♡ of 4 2枚目: 伏せられている --------------------- さらにカードを引きますか?[Y/N] あなたの得点: 8 y Answer: y あなたが引いたカードは♠ of 3です ----- あなたの手札 ----- [ ♦︎ of 5 ] [ ♣︎ of 3 ] [ ♠ of 3 ] --------------------- 山札が輝きだした! 山札を信じて引きますか?[Y/N] あなたの得点: 11 y あなたが引いたカードは♡ of 10です ----- あなたの手札 ----- [ ♦︎ of 5 ] [ ♣︎ of 3 ] [ ♠ of 3 ] [ ♡ of 10 ] --------------------- ディーラーはカードを引きました ----- あなたの得点 ----- 21 --- ディーラーの得点 --- 22 ブラックジャック! あなたの勝ちです!終わりに
思った以上にコード量が増えすぎてしまい、まだまだ可読性を上げる訓練が必要だなと感じました。
変数名やクラス名の命名や処理のメソッド化など改善の余地はたくさんありますね...。
プログラミング入門者の卒業試験!とのことでしたが、個人的には留年判定かと思います。
しかし、形はどうであれ、0から試行錯誤を繰り返して作り上げることはとても楽しかったです。
実装する機能も細分化してどういった順番で書いていけば、スムーズに開発できるか考えることもいい訓練になりました!もし、僕と同じような初心者の方がいたら、ぜひ挑戦してみてください!
今回作成したコードのサンプルは以下にあります。
コードサンプル最後までお読みいただきありがとうございました。
- 投稿日:2019-03-30T17:51:03+09:00
Railsチュートリアルのサンプルアプリにlike(いいね)機能を拡張する
Railsチュートリアルで作成したサンプルアプリに、like(いいね)機能を拡張します。
環境
・Rails 5.1.6
・Railsチュートリアル第4版(Rails5.1) 14章まで実装済み仕様
・プロフィールにユーザーのlike数が表示される
・like数をクリックすると、likeしたマイクロポスト一覧が表示される
・マイクロポストにlikeボタンとlikeされた回数が表示される
・likeボタンをクリックすると、like数とボタンのlike/unlikeが非同期で更新されるFavoriteRelationshipModel
テーブルの作成
ユーザーは複数のマイクロポストをlikeでき、また、マイクロポストは複数のユーザーからlikeされます。そのため、フォロー機能のときと同じように、中間テーブルを作成して多対多の関係を作っていきます。モデル名は、チュートリアルにならってFavoriteRelationshipとします。このFavoriteRelationshipモデルは、ユーザとマイクロポストを結びつけるため、user_idとmicropost_idを属性として持たせます。まずは以下のコマンドを実行して、マイグレーションを生成します。
rails generate model FavoriteRelationship user_id:integer micropost_id:integer次に、インデックスを追加します。さらに、user_idとmicropost_idの組み合わせがユニークであるという制約も追加します。
/sample_app/db/migrate/[timestamp]_create_favorite_relationships.rbclass CreateFavoriteRelationships < ActiveRecord::Migration[5.1] def change create_table :favorite_relationships do |t| t.integer :user_id t.integer :micropost_id t.timestamps end #indexを追加 add_index :favorite_relationships, :user_id add_index :favorite_relationships, :micropost_id add_index :favorite_relationships, [:user_id, :micropost_id], unique: true end end最後に以下のコマンドを実行して、FavoriteRelationshipテーブルを作成します。
rails db:migrateUser/Micropostの関連付け
ユーザーは複数のマイクロポストをlikeでき、マイクロポストは複数のユーザーからlikeされるという多対多の関係を、FavoriteRelationshipモデルを用いて定義します。
まず、ユーザーが、likeしたマイクロポストを複数持つことを、FavoriteRelationshipモデルを用いて以下のように定義します。これにより、user.likesでそのユーザーがlikeしたマイクロポスト一覧を取得可能になります。/sample_app/app/models/user.rbclass User < ApplicationRecord : : has_many :favorite_relationships, dependent: :destroy has_many :likes, through: :favorite_relationships, source: :micropost : : end同様にして、マイクロポストが、likeしたユーザーを複数持つことを以下のように定義します。これにより、
micropost.liked_byで、そのマイクロポストをlikeしたユーザー一覧が取得できるようになります。/sample_app/app/models/micropost.rbclass Micropost < ApplicationRecord : : has_many :favorite_relationships, dependent: :destroy has_many :liked_by, through: :favorite_relationships, source: :user : : end最後に、FavoriteRelationshipをユーザーとマイクロポストの両方に属するように定義します。
/sample_app/app/models/favorite_relationship.rbclass FavoriteRelationship < ApplicationRecord belongs_to :user belongs_to :micropost validates :user_id, presence: true validates :micropost_id, presence: true endlikes関連のメソッドの追加
マイクロポストのlikeやlike解除を簡単に行えるようにするために、便利なメソッドを追加します。
/sample_app/app/models/user.rbclass User < ApplicationRecord class UserTest < ActiveSupport::TestCase : : # マイクロポストをライクする def like(micropost) likes << micropost end # マイクロポストをライク解除する def unlike(micropost) favorite_relationships.find_by(micropost_id: micropost.id).destroy end # 現在のユーザーがライクしていたらtrueを返す def likes?(micropost) likes.include?(micropost) end private : : endこれにより、
user.like(micropost)でマイクロポストをlikeし、user.unlike(micropost)でマイクロポストのlikeを解除することができるようになりました。likeページの作成
ユーザーのステータス部分にlike数を表示し、そこをクリックするとlikeしたマイクロポスト一覧を表示できるようにします。完成イメージは以下の通りです。
like数の表示
ユーザーのステータス部分に、そのユーザーがlikeしたマイクロポストの総数を表示します。
まずは、以下のようにルーティングを追加します。/sample_app/config/routes.rb: : resources :users do member do get :following, :followers , :likes end end : :これにより、以下のようなルーティングテーブルが生成されます。
HTTPリクエスト URL アクション 名前付きルート GET /users/1/likes likes likes_user_path(1) 次に、統計情報パーシャルに以下を追記することによって、プロフィールページとHomeページのステータス部分にlike数を表示します。
/sample_app/app/views/shared/_stats.html.erb. . <a href="<%= likes_user_path(@user) %>"> <strong id="likes" class="stat"> <%= @user.likes.count %> </strong> likes </a> </div>これで、ユーザーのステータスにlike数が追加されました。
likeしたマイクロポスト一覧の表示
ユーザーのプロフィール内のlike数をクリックすると、そのユーザーがこれまでにlikeしたマイクロポスト一覧が表示されるようにします。
まず、users_controller.rbにlikesアクションを追加します。/sample_app/app/controllers/users_controller.rbclass UsersController < ApplicationController before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers, :likes] : : def likes @title = "Likes" @user = User.find(params[:id]) @microposts = @user.likes.paginate(page: params[:page]) render 'show_like' end private : :likesアクションでは、
@micropostsにユーザーがlikeしているマイクロポストを代入し、show_likeビューを描画しています。このビューは以下の通り作成します。/sample_app/app/views/users/show_like.html.erb<% provide(:title, @user.name) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= gravatar_for @user %> <h1><%= @user.name %></h1> <span><%= link_to "view my profile", @user %></span> <span><b>Microposts:</b> <%= @user.microposts.count %></span> </section> <section class="stats"> <%= render 'shared/stats' %> </section> </aside> <div class="col-md-8"> <h3><%= @title %></h3> <% if @user.likes.any? %> <ol class="microposts"> <%= render @microposts %> </ol> <%= will_paginate @microposts %> <% end %> </div> </div>以上で、likeしたマイクロポスト一覧を表示できるようになりました。
likeボタンの作成
マイクロポストにlikeボタンを表示し、クリックでlike/unlikeの切り替えができるようにします。
likeボタンの表示
likeページのときと同様に、まずはルーティングの追加から始めます。以下のように、FavoriteRelationshipリソース用のルーティングを追加します。
/sample_app/config/routes.rb: : resources :favorite_relationships, only: [:create, :destroy] end次に、micropostパーシャルに、like/unlikeボタンの表示を追加します。以下では、current_userがそのマイクロポストをlikeしているかどうかで、表示するボタンを切り替えています。また、後でAjaxを実装して非同期でボタンを切り替えるために、ユニークなidをspanタグに割り振っています。
/sample_app/app/views/microposts/_micropost.html.erb: : <span class="like" id="like_form_<%= micropost.id %>"> <% if micropost.liked_by.include?(current_user) %> <%= render "microposts/unlike", micropost: micropost %> <% else %> <%= render "microposts/like", micropost: micropost %> <% end %> </span> </li>likeボタンとunlikeボタンのパーシャルは、それぞれ以下の通りです。ボタンには、Bootstrap3に用意されているglyphiconと呼ばれるアイコン集の中のハートアイコンを用いています。また、likeされた数を
micropost.liked_by.countで取得し、ハートアイコンの隣に表示しています。/sample_app/app/views/microposts/_like.html.erb<%= form_for(current_user.favorite_relationships.build) do |f| %> <div><%= hidden_field_tag :micropost_id, micropost.id %></div> <%= button_tag(class: "btn btn-default" ) do %> <%= content_tag :span, micropost.liked_by.count, class: "glyphicon glyphicon-heart-empty" %> <% end %> <% end %>/sample_app/app/views/microposts/_unlike.html.erb<%= form_for(current_user.favorite_relationships.find_by(micropost_id: micropost.id), html: { method: :delete }) do |f| %> <%= button_tag( class: "btn btn-default") do %> <%= content_tag :span, micropost.liked_by.count, class: "glyphicon glyphicon-heart" %> <% end %> <% end %>最後に、ボタンの配置や色を変更するために、
custom.scssに以下を追記します。/sample_app/app/assets/stylesheets/custom.scss: : /* likes */ .glyphicon-heart { color: red; } .btn-default:hover > .glyphicon-heart-empty { color: red; } .btn-default { position: relative; left: 60px; }ここまでで、マイクロポストにlike/unlikeボタンが表示されました。次は、これを動作させてlike機能を完成させます。
likeボタンの動作
rails generate controller FavoriteRelationshipsでFavoriteRelationshipsコントローラーを作成し、コントローラー内にcreateアクションとdestroyアクションを記述します。/sample_app/app/controllers/favorite_relationships_controller.rbclass FavoriteRelationshipsController < ApplicationController before_action :logged_in_user def create micropost = Micropost.find(params[:micropost_id]) current_user.like(micropost) redirect_back(fallback_location: root_url) end def destroy micropost = FavoriteRelationship.find(params[:id]).micropost current_user.unlike(micropost) redirect_back(fallback_location: root_url) end endここまででlike機能が完成しました。次は、これを非同期で動作するように拡張します。
Ajaxによる非同期化
Ajaxを用いて、like/unlikeボタンをクリックした際に、ページを更新せずにlike数の更新とlike/unlikeボタンの切り替えを行うようにします。
まずは、like_formとunlike_formに、remote:trueを追加します。/sample_app/app/views/microposts/_like_form.html.erb<%= form_for(current_user.favorite_relationships.build, remote: true) do |f| %> : :/sample_app/app/views/microposts/_unlike_form.html.erb<%= form_for(current_user.favorite_relationships.find_by(micropost_id: micropost.id), html: { method: :delete }, remote: true) do |f| %> : :次に、FavoriteRelationshipsコントローラでAjaxリクエストに対応します。
/sample_app/app/controllers/favorite_relationships_controller.rbclass FavoriteRelationshipsController < ApplicationController before_action :logged_in_user def create @user = current_user @micropost = Micropost.find(params[:micropost_id]) current_user.like(@micropost) respond_to do |format| format.html { redirect_back(fallback_location: root_url) } format.js end end def destroy @user = current_user @micropost = FavoriteRelationship.find(params[:id]).micropost current_user.unlike(@micropost) respond_to do |format| format.html { redirect_back(fallback_location: root_url) } format.js end end end最後に、Ajaxリクエストを受信時に呼び出されるJS-ERbファイルを作成します。以下では、likes数の更新と、like/unlikeボタンの切り替えを行なっています。
/sample_app/app/views/favorite_relationships/create.js.erb$("#likes").html('<%= @user.likes.count %>'); $("#like_form_<%= @micropost.id %>").html("<%= escape_javascript(render('microposts/unlike', micropost: @micropost)) %>");/sample_app/app/views/favorite_relationships/destroy.js.erb$("#likes").html('<%= @user.likes.count %>'); $("#like_form_<%= @micropost.id %>").html("<%= escape_javascript(render('microposts/like', micropost: @micropost)) %>");以上で完成です。
内容に不備がございましたら、コメントにてお知らせいただければ幸いです。
- 投稿日:2019-03-30T15:30:15+09:00
配列から繰り返し処理する場合にindexが欲しい場合【each.with_index】
はじめに
本項ではeach.with_indexについて解説
概要
要素にインデックスを添えて繰り返すメソッド。
要素自体に順番を示すインデックスを属性として設定していないが、処理として要素の順番を示したい場合に使用する。使用方法
参考例.rbObjs.each.with_index(offset){|obj,index| 〜繰り返す処理〜 } # Objs => 繰り返す対象の配列。 # offset => 開始するインデックス番号。 # obj => 繰り返し処理中の要素の変数名。 # index => 繰り返し処理中のインデックスの変数名。備考
- offsetを与えない場合、indexは0から始まります。使用例(offsetなし)
使用例(offsetなし).rbanimals = ["cat","dog","mouse"] animals.each.with_index {|animal,i| puts "#{i}番目は#{animal}です。" }出力結果は以下
0番目はcatです 1番目はdogです 2番目はmouseです使用例(offsetあり)
人間の感覚で0から始まると気持ちが悪いのでoffsetに1を与えると良いと思います。
使用例(offsetあり).rbanimals = ["cat","dog","mouse"] animals.each.with_index(1) {|animal,i| puts "#{i}番目は#{animal}です。" }出力結果は以下
1番目はcatです 2番目はdogです 3番目はmouseです参考記事
each_with_index (Enumerable) - Rubyリファレンス
内容のみならず、レイアウトや書き方にアドバイスがある方はお気軽に(優しく)コメントを頂けると助かります。
- 投稿日:2019-03-30T15:30:15+09:00
配列から繰り返し処理する際にindexが欲しい場合【each.with_index】
はじめに
本項ではeach.with_indexについて解説
概要
要素にインデックスを添えて繰り返すメソッド。
要素自体に順番を示すインデックスを属性として設定していないが、処理として要素の順番を示したい場合に使用する。使用方法
参考例.rbObjs.each.with_index(offset){|obj,index| 〜繰り返す処理〜 } # Objs => 繰り返す対象の配列。 # offset => 開始するインデックス番号。 # obj => 繰り返し処理中の要素の変数名。 # index => 繰り返し処理中のインデックスの変数名。備考
- offsetを与えない場合、indexは0から始まります。使用例(offsetなし)
使用例(offsetなし).rbanimals = ["cat","dog","mouse"] animals.each.with_index {|animal,i| puts "#{i}番目は#{animal}です。" }出力結果は以下
0番目はcatです 1番目はdogです 2番目はmouseです使用例(offsetあり)
人間の感覚で0から始まると気持ちが悪いのでoffsetに1を与えると良いと思います。
使用例(offsetあり).rbanimals = ["cat","dog","mouse"] animals.each.with_index(1) {|animal,i| puts "#{i}番目は#{animal}です。" }出力結果は以下
1番目はcatです 2番目はdogです 3番目はmouseです参考記事
each_with_index (Enumerable) - Rubyリファレンス
内容のみならず、レイアウトや書き方にアドバイスがある方はお気軽に(優しく)コメントを頂けると助かります。
- 投稿日:2019-03-30T14:04:28+09:00
bundleのアップデートをするとNo such file or directoryと表示された時の対処法
はじめに
Rubyのlocal開発環境を作ろうとした時にこの「rbenv/1.1.1/libexec/rbenv: No such file or directory」とゆうErrorが発生しました。
対処法
試したこと
まず「No such file or directory」 と言われているのでここで単純に、PATHが通っていなぁと思ったので以下を試しました。
- bashなりzshなりPATHを書いた場所を確認しにいくが記述してあったので問題なし。
- 新たにbundleをインストールしようと試みても反応は同じ。
全くなんで治らないんだと思った時に下記の対処方に書いてあるコマンドを実行しました。
対処
> rbenv rehashこのコマンドがなにをしているかと言いますと、
~/.rbenv/shims:/以下にファイルを移行してコマンドでの実行を可能にするらしい...正直突発的に治っ感が否めないので、「そうゆうもの」として受け入れましたが、あとになって環境構築がまとめてあるサイトを覗きに行ったときに同じように実行していたので問題ないようです。。。
納得いかねぇぇぇぇぇぇ※ 後日この原因を調査するのでこのErrorで手が止まっているより動いてるほうがいいよね!!
- 投稿日:2019-03-30T09:37:05+09:00
redisとruby on railsの連携
環境
macOS Mojava 10.14.4
Docker 18.09.2
Rails 5.2.2
ruby 2.6.1p33Webアプリケーションのセッション管理の方法はいくつかあるのですが、ruby on railsは特に指定しなければローカルのCookieにセッションが保存されます。
この状態でrailsを動作させるとCookieが作成されています。
Safariだと
Safari > 環境設定 > プライバシー > CookieとWebサイトのデータ:
を確認すると127.0.0.1のWebサイトのデータが保存されています。(ローカル開発時)開発だけであればいいんですが、実際の環境でユーザーCookieでセッション情報を管理するのは問題があるのでredisで管理するようにしました。
redisをインストール
$brew install redis ・・・インストールログ・・・ ==> Summary ? /usr/local/Cellar/redis/5.0.4: 13 files, 3.1MBなんかログにビールが出ました。多分成功の証です。
$redis-cli Could not connect to Redis at 127.0.0.1:6379: Connection refusedインストールできていました。
redisサーバーが起動してないので接続できていません。せっかくなんで学習のため、redisサーバーはDockerコンテナを使ってみようかと思います。
Dockerのインストールはこちらを参考にしました。
https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81redisのDockerコンテナーの起動
停止したら削除するために--rmをつけます。
-pでコンテナのポートを公開します。(ホストのポート6379をコンテナのポート6379に接続)$docker container run --name local-redis -d --rm -p 6379:6379 redis Unable to find image 'redis:latest' locally latest: Pulling from library/redis 27833a3ba0a5: Pull complete cde8019a4b43: Pull complete 97a473b37fb2: Pull complete c6fe0dfbb7e3: Pull complete 39c8f5ba1240: Pull complete cfbdd870cf75: Pull complete Digest: sha256:000339fb57e0ddf2d48d72f3341e47a8ca3b1beae9bdcb25a96323095b72a79b Status: Downloaded newer image for redis:latest cbc1a37039dab71ce996cecdae2cd7cbdbbb78cd76e281ff5837eac3113dd403dockerイメージがローカルにない場合はDocker Hubから自動でPullしてくれます。
https://hub.docker.comredisのコンテナが動作しているかの確認
$docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cbc1a37039da redis "docker-entrypoint.s…" 51 seconds ago Up 50 seconds 0.0.0.0:6379->6379/tcp local-redis--nameで指定した名前がNAMESに出力されています。(コンテナの名称)
接続できることを確認します。
redis-cli 127.0.0.1:6379>exitを入力し、終了
redisサーバーが用意できたので、railsのセッションをredisで管理するように変更します。
redisを使うためGemfileにredis-railsを追加します。
Gemfilegem ‘redis-rails’インストールを行います。
bundle installrailsのセッションの設定を行います。
config/initializers/session_store.rbにredisにセッション情報を格納することを記載します。
特に何も設定していない場合はsession_store.rbは存在しないので作成します。session_store.rbRails.application.config.session_store :redis_store, { servers: [ { host: ENV['REDIS_HOST'] || 'localhost', port: ENV['REDIS_PORT'] || 6379, db: 0, namespace: 'session' } ], expire_after: 10.minutes }これでセッション情報はredisに記録されるようになります。
実際にredisコンテナを停止させてからrailsを起動するとredisに接続できないことによるエラーが発生します。
SafariでCookieを確認しても127.0.0.1のWebサイトの情報はなくなります。ローカル開発でこれをやると、毎回redisコンテナを起動させないとエラーで落ちてしまうのでなにか自動化は別途しようと思います。
- 投稿日:2019-03-30T09:10:22+09:00
Ruby on Rails スタートライン〜アプリ立ち上げ〜
開発の道具
- ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin17]
- Rails 5.2.2
アプリ立ち上げ
ターミナルでコマンド入力
$ rails new tweet-appこれでtweet-appというフォルダが作成され、フォルダの中にappやらbinやら色んなフォルダが生成されました。
早速、ブラウザで見てみたいと思います。
まずはtweet-appに移動します。(最初の頃はこれがわからずに苦労した)
次にサーバーを立ち上げます。コマンドは以下の通りです。
$ cd tweet-app $ rails serverブラウザで、http://0.0.0.0:3000/ を開いてください。
あれ、、、
原因について調べてみると、どうやらsqlite3のloadにエラーが出ているみたい。
先ほど生成したtweet-appフォルダの中身(tweet-app/bin)を確認したら、sqlite3が入っておりません。
そこでsplite3は手動で導入します。sqlite3の導入
以下コマンドでsqlite3がインストールしましょう。
$ gem install sqlite3spotliteでsqlite3を検索 。
デベロッパとしてsqlite3がありますので、ドラッグ&ドロップでtweet-appのbinフォルダに移動させます。
こんな感じでtweet-app/binフォルダの中にsqlite3が導入されました。
もう一回ブラウザチャレンジ
さあ、これでブラウザで開けるはずです。
http:/0.0.0.0.3000で更新しましょう。
あれ、、、
まだどこかおかしいようです。原因について
このエラーの原因をめっちゃ調べました。
teratailの記事にも同じようなトラブルに見舞われた人がいたようで・・・
https://teratail.com/questions/174121そこで、tweet-appのGemfileを開き、修正することにします。
これを・・・
で、ターミナルでbundle installします。
ひとつ注意なのが、rails serverを実行しているターミナルではなく、新しくターミナルのウィンドウを開いて、そこから実行してください。
コマンドは以下の通りです。
$ cd tweet-app #tweet-appに移動 $ bundle installこうすると、インストールが完了します。
3度目の正直
できているでしょうか。
Chromeで、http:0.0.0.0:3000にアクセスします。無事にできていることが確認できました。
- 投稿日:2019-03-30T03:41:47+09:00
Ruby on Railsを触ってみる
はじめに
RoRがどんなものか試してみたかった。
雑ですが、また同じことを調べなくて済むようにメモ。
ちなみにsqliteもお初。前提
Ubuntu 16.04
Ruby 2.4.1p111
Rails 5.2.3
Sqlite 2.4.1p111プロジェクト作成
プロジェクト作成からscaffoldingまで。
## プロジェクト作成 $ rails new cafe $ cd cafe/ ## サーバ起動→http://localhost:3000でYay!が出ることを確認。 $ rails s ## 日記機能を生成 $ rails g scaffold diary title:string body:text ## scaffoldingした機能がURLマッピングされていることを確認 $ rails routes ## scaffoldingの結果を元にDBにテーブルを追加する $ rails db:migrateちなみにscaffoldするときのstringは省略可能。デフォルトの型はstringのため。
DBにアクセスしてみる
"rails db"でDBコンソールにログインする。
### DBコンソール(sqlite)にログインする $ rails db ### テーブル一覧を見てみる sqlite> .tables ### テーブル定義を確認する sqlite> .schema diaries CREATE TABLE "diaries" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "body" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL); ### select結果にカラム名も出す sqlite> .headers on sqlite> .mode column ### SELECTしてみる sqlite> SELECT * FROM diaries; ### sqliteコンソールを抜ける sqlite> .quitViewをいじってみる
更新日時を追加してみる。ヘッダとかも日本語にする。
app/views/diaries/index.html.erb<p id="notice"><%= notice %></p> <h1>日記一覧</h1> <table> <thead> <tr> <th>タイトル</th> <th>本文</th> <th>更新日時</th> <!-- 追加 --> <th colspan="3"></th> </tr> </thead> <tbody> <% @diaries.each do |diary| %> <tr> <td><%= diary.title %></td> <td><%= diary.body %></td> <td><%= diary.updated_at %></td> <!-- 追加 --> <td><%= link_to '見る', diary %></td> <td><%= link_to '編集', edit_diary_path(diary) %></td> <td><%= link_to '削除', diary, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to '日記を新規作成する', new_diary_path %>updated_atはscaffoldingの際に指定してないが、Railsが勝手に追加してくれる。
日付形式を変更する
最初はUTCになっているので、JSTに変更&表示形式も変更する。
JSTへ変更
config/application.rb へ
config.time_zone = 'Tokyo' を追加。config/application.rbrequire_relative 'boot' require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Cafe class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 config.time_zone = 'Tokyo' # 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. end endtime_formats.rb の作成
以下を記入したconfig/initializers/time_formats.rbを作成する。
time_formats.rbTime::DATE_FORMATS[:default] = "%Y/%m/%d %H:%M"確認
再起動して更新日時が"%Y/%m/%d %H:%M"で表示されていることを確認。
scaffoldとは
参照・登録・編集・削除の機能を持つアプリの雛形を作成するジェネレータコマンド。
コマンド1つで必要なファイルを一括して作成してくれるのでとても便利だが、
これしか使わないとRailsがいつまでたっても理解できないので乱用はよろしくないってよく言われるらしい。他の言語でプログラミングとかMVCの基礎を学んでいる人は別にいいと思う。もちろんscaffoldを使わない方法も知る必要がある。
以下でコントローラから機能を作成する手順も載せる。コントローラ生成からスタートして機能を作成する
コントローラの生成
usersコントローラとそれが持つindexアクションを生成。
コントローラだけでなくビューなどいろいろ生成されていることがわかる。$ rails g controller users index Running via Spring preloader in process 9407 create app/controllers/users_controller.rb route get 'users/index' invoke erb create app/views/users create app/views/users/index.html.erb invoke test_unit create test/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/users.coffee invoke scss create app/assets/stylesheets/users.scss生成されたコントローラ
app/controllers/users_controller.rbclass UsersController < ApplicationController def index end end以下の通り、処理がない場合は紐づくビューが呼ばれる。
Action Controller の概要 - 3 メソッドとアクションここでご注目いただきたいのは、newメソッドの内容が空であるにもかかわらず正常に動作するという点です。これは、Railsではnewアクションで特に指定のない場合にはnew.html.erbビューを描画するようになっているからです。
よって空のusers/indexアクションはapp/views/users/index.html.erbを返す。
※ちなみにerbは(embedded ruby)の略。ルーティング追加
config/routes.rbにgetの2行を追加。usersとusers/indexにアクセスがあった場合はusersコントローラのindexアクションを実行するという意味。
config/routes.rbRails.application.routes.draw do get 'users', to: 'users#index' get 'users/index', to: 'users#index' resources :diaries # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html endmodelの生成
タイヤ、エンジン、ハンドルを属性に持つ車モデルを定義。
モデルクラスとマイグレーションファイルが生成されている。$ rails g model car tire:string engine:string handle:string Running via Spring preloader in process 12514 invoke active_record create db/migrate/20190329170956_create_cars.rb create app/models/car.rb invoke test_unit create test/models/car_test.rb create test/fixtures/cars.yml作成されたモデルクラスcar.rbを見ると処理は記載されていないがこのクラスがあるだけでcarsテーブルにアクセスできるようになる。
マイグレーションの実行
Rails4まではrakeコマンドで行う必要があったが5からはrailsコマンドに吸収されたため以下のようにマイグレーションやroutesもrailsコマンドで実施できる。
$ rails db:migrate == 20190329170956 CreateCars: migrating ======================================= -- create_table(:cars) -> 0.0024s == 20190329170956 CreateCars: migrated (0.0025s) ==============================このあとrails dbコマンドでDBコンソールを開いてcarsテーブルが作成されていることを確認。
ERBの記述のルール
記号 動作 サンプル <% %> コードとして実行 <% if @test.present? %>Test<% end %> <%= %> 変数の中身を出力 <%= @test %> コントローラからビューへ変数を渡す
app/controllers/users_controller.rbclass UsersController < ApplicationController def index @test='テストだよ!!' render template: 'users/index' end endapp/views/users/index.html.erb<h1>Users#index</h1> <p><%= @test %></p> <p><% if @test.present? %>test test test<% end %></p>結果
Users#index
テストだよ!!
test test testが画面に表示される。
デザインの変更
共通のレイアウトは以下のファイルで定義されている。
app/views/layouts/application.html.erb
ここに共通で適用したいcssとかjavascriptを設定してあげればOK。ページごとにデザインを変えたい場合
assetを追加する
app/assets/javascripts/users.coffee.users-index { font-size: 20px; }app/assets/stylesheets/users.scssalert('Hi user!');のように、coffeeまたはscssを配置する。(ファイル名はassets.rbに指定する)
assets.rbのprecompileのコメントアウトを外す
指定するのはSprocketsによって変換された後のjs、css。
config/initializers/assets.rbRails.application.config.assets.precompile += %w( users.js users.css )サーバを再起動してポップアップが出ることと、文字がおっきくなっていることを確認。
Sprocketsとは
coffeescriptやscssからJavascriptやcssを生成する仕組みを管理する。
ActiveRecordとは
Railsが採用するO/Rマッパー。
SQLを使用せずにDBにアクセスする。
rails g modelコマンドでActiveRecordによるモデルクラスを生成する。
このコマンドを使用するとテーブル作成SQLを実行するためのマイグレーションファイルも作成してくれる。マイグレーションとは
テーブルの新規作成や変更を簡単に行うための仕組み。
DBMSによる違いを吸収して記述することができる。
以下はdiaryをg scaffoldした時のマイグレーションファイル。
timestampsはupdated_atなどのメタ情報。db/migrate/20190328140515_create_diaries.rbclass CreateDiaries < ActiveRecord::Migration[5.2] def change create_table :diaries do |t| t.string :title t.text :body t.timestamps end end endあとがき
ほんとはWindows+VSCodeで試したかったけど8年選手の低スペックマシンでは耐え切れなかったのでUbuntu16+Atomで試しました。
次は簡単なWEBページを作成してみようと思います。
- 投稿日:2019-03-30T01:58:29+09:00
はじめてのCircleCI。Railsプロジェクトで試してみる。
CIツールを入れて生産性バク上げしたい。今回はよく聞くCIツール、CircleCIを使ってみた。
この記事のゴール
RSpecで書いたRuby on RailsのアプリをCircleCI上でテストする
動作環境
ruby 2.5.1 rails 5.2.2 CircleCI 2アプリケーションのセットアップ
まず、Railsアプリのセットアップ。無心でセットアップしていく。
$ rails new circle-ci-test -d mysql $ cd circle-ci-test $ rails db:create $ rails g rspec:install $ rails g scaffold product title:string content:text $ rails db:migrate $ rails s以上でproductリソースをCRUD処理する機能が出来上がった。下記みたいなカンジ。
テストをCIで試してみたいので、RSpecを書いていく。
specを書く
コントローラのspecが自動で生成されているが、モデルのほうがテストを書くのが簡単なので、コントローラのspecはぶち消す。productモデルにバリデーションを実装して、そのテストを書いていく。
product.rb
class Product < ApplicationRecord validates :title, :content, presence: true validates :title, length: { in: 5..20 } endproduct_spec.rb
require 'rails_helper' RSpec.describe Product, type: :model do it 'title, contentが存在するので、バリデーションが通る' do product = Product.new(title: 'こんにちは!', content: 'うあはうあ!') expect(product).to be_valid end it 'titleが存在しないので、バリデーションが弾かれる' do product = Product.new(content: 'しょーもない人生') expect(product).not_to be_valid end it 'contentが存在しないので、バリデーションが弾かれる' do product = Product.new(title: 'スマブラ強くなりてえ!!!!') expect(product).not_to be_valid end it 'titleが5文字未満なので、バリデーションが弾かれる' do product = Product.new(title: 'パクチー') expect(product).not_to be_valid end endテストを実行
$ bundle exec rspec ... Finished in 0.01224 seconds (files took 0.81481 seconds to load) 4 examples, 0 failuresOK、テストアプリはできた。以上をコミットしてGithubリポジトリを作ってpushする。
CircleCIのセットアップ
CircleCIにサインアップして、GirHub連携をするとダッシュボードが表示されて、以下のカードが表示される。
この画像通りに設定をしてみる。circle-ci-testレポジトリ発見。Set Up Projectボタンを押す。
下の方に優しく設定方法が書いてある。これに従えば基本的なセットアップができる親切設計。最高
この順に設定していく。
$ mkdir .circleci && cd .circleci && touch config.yml && cd ..config.ymlを以下の設定にする。
ちょっと試行錯誤した。# Ruby CircleCI 2.0 configuration file # # Check https://circleci.com/docs/2.0/language-ruby/ for more details # version: 2 jobs: build: docker: # specify the version you desire here - image: circleci/ruby:2.5.1-node-browsers environment: RAILS_ENV: test MYSQL_HOST: 127.0.0.1 MYSQL_USERNAME: 'root' MYSQL_PASSWORD: '' MYSQL_PORT: 3306 - image: circleci/mysql:5.7.18 environment: MYSQL_ALLOW_EMPTY_PASSWORD: true MYSQL_ROOT_PASSWORD: '' MYSQL_DATABASE: circle_ci_test-test # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ # - image: circleci/postgres:9.4 working_directory: ~/circle-ci-test steps: - checkout # Download and cache dependencies - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: name: install dependencies command: | bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: paths: - ./vendor/bundle key: v1-dependencies-{{ checksum "Gemfile.lock" }} # Database setup - run: name: waiting for stating database command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m - run: bundle exec rake db:create - run: bundle exec rake db:schema:load # run tests! - run: name: Run rspec in parallel command: | bundle exec rspec --profile 10 \ --format RspecJunitFormatter \ --out test_results/rspec.xml \ --format progress \ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) # collect reports - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-resultsまた、Gemfileに以下を追加する
gem 'rspec_junit_formatter'bundle installしてpushすると、自動でGithubでビルド&テスト実行される!
雑感
めっちゃ簡単!チーム開発する場合はどんどん入れていきたいところ!
また、上のプロジェクトをGithubのパブリックなリポジトリに登録していたらCircleCIからフィードバックアンケートのお願いのメールが来た。ユーザーと共に良いプロダクトを作っていて最高でございます。




















