ひよっこエンジニアの雑多な日記

とあるプログラミングスクールで働くひよっこエンジニアが覚えたことや悲しみを記すブログ

bootstrap datepickerで土日の色を変えるよ!

気がついたら、また前回更新から一ヶ月の時が過ぎてしまっていました…
この一ヶ月間は転職したり引っ越ししたりとかなりバタバターっとしていて割と人生的に変換期に入ってきている感があります…!

今回はbootstrap datepickerについて書いていきたいと思いますー!
ちょっと開発をしていてカレンダーから日付入力したいなーと考えた時にお手軽に実装できると噂のbootstrap datepickerを導入しました。
それで土日だけ色を違う色にしたいなーと思ったりして色々調べたものの日本語記事であまり情報がなかったので今回の題材にしようかなーと思いました。

(今回もRails開発の中での導入なのでGemを使って導入していたりしますがご愛嬌ということで…)

bootstrap datepickerを導入するよ!

1. Gemfileに追記

gem 'bootstrap-datepicker-rails'

2. bundle install
3. application.jsに追記

//= require bootstrap-datepicker/core
//= require bootstrap-datepicker/locales/bootstrap-datepicker.ja.js

とりあえず言語は日本語だけで良いのこんな感じ

4. application.cssに追記

 *= require_self
 *= require bootstrap-datepicker3
 *= require_tree .

これで一通り準備は完了!

bootstrap datepicker使ってみるよ!

javascript
$(document).ready(function(){
  $('.datepicker').datepicker();
});
HTML
<div class="input-group">
    <!-- erbの書き方なので注意 -->
    <%= f.text_field :input_date, class: "form-control datepicker" %>
</div>

とりあえずちょいちょいっとこんな感じで書くと下のような感じになります!
f:id:kimuraysp:20160716235227p:plain

とはいえかなり質素感が半端ないですね…
せめて土日だけでもわかりやすくなってほしい…

土日に色をつけるよ!

ということで土日に色をつけましょう!
まずはjavascriptを改良

javascript
$(document).ready(function(){
  $('.datepicker').datepicker({
    beforeShowDay: function(date) {
      var myDate = new Object();
      if (date.getDay() == 0) {
        myDate.enabled = true;
        myDate.classes = 'class-sunday';
        myDate.tooltip  = '日曜日';
      } else if (date.getDay() == 6) {
        myDate.enabled = true;
        myDate.classes = 'class-saturday';
        myDate.tooltip  = '土曜日';
      } else {
        myDate.enabled = true;
        myDate.classes = 'class-weekday';
        myDate.tooltip  = '平日';
      }
      return myDate;
    }
  });
});

beforeShowDayを設定することで日付表示の前に処理を行ってくれる。
ここの感じだとobjectにenabled, classes, tooltipを設定できるとのこと。
今回大事なのはclassesです!
これを使うことで色を変えたい曜日を判別します。

まずカレンダーに表示される日にちは1日単位でtd要素と成っているということを念頭に置いておいてください。
一日ごとにbeforeShowDayをみて処理を行います。
そこでif (date.getDay() == 0)みたいな感じで日にちと謎の数字を判定しています! この謎の数字は日〜土を表していて0〜6で表現されます。 なので0だったら日曜日の処理、6だったら土曜日の処理、それ以外は平日の処理をやってね!ということになります。

で次に設定する内容についてです。
enabletooltipは本質じゃないので割愛で…
大事なのはclassesこれに設定した値は一日単位で生成されるtd要素のclassとして追加されます。

そしてclassが追記されることでスタイルを当てることができるようになります!

.class-sunday {
  color: red !important;
}
.class-saturday {
  color: blue !important;
}

これをcssで記入してやることで

f:id:kimuraysp:20160717001759p:plain

こんな感じで土日に色がつきちょっとおしゃれな感じになりました!

おまけ

入力フォームをちょっとおしゃれにする

<div class="input-group">
  <%= f.text_field :input_date, id: "date-area", class: "form-control datepicker" %>
  <label for="date-area" class="input-group-btn">
    <span class="btn btn-success"><i class="fa fa-calendar"></i></span>
  </label>
</div>

f:id:kimuraysp:20160717002233p:plain

bootstrapと合わせることでちょっとおしゃれなdatepickerの入力フォームができてしまいます!
bootstrap datepickerというだけありbootstrapとの親和性は高いですね!

さて今回はbootstrap datepickerについて書きました。
探してた時は藁にもすがる思いでしたがわかってみると意外となんてことないコードでした。
こういう微妙にハメられる系を見つけ次第どんどん記事にしていこうと思います(また次は一ヶ月後かなあ…笑)

【Rails】Ajaxでセレクトボックスの内容を動的に変更する

こんばんは!1週に一回記事更新しますわ〜とか言っておきながら1ヶ月ぶりの更新です…笑

今回の内容は2つのセレクトボックスがあった時に片方のセレクトボックスが変更されたら、もう片方のセレクトボックスで選択できる内容が動的に変更されると言った内容です!
概要だけだと何言ってるかわけわからないと思うので例を挙げると、県と市区町村が選択できるセレクトボックスがそれぞれ存在する時に、県を選択したら市区町村のセレクトボックスの方が選択された県に存在する市区町村が選択肢になるようなイメージです。

どう作ればいいかわからない状態から作ったのでかなり時間がかかったので備忘録がてら書いときます

想定

とりあえずテーブル構造はこんな感じ

f:id:kimuraysp:20160616234834p:plain

prefecturesとcitesとマスタテーブルとしていて存在していて、そんでprofilesに住んでいる県と市区町村が登録されるみたいな想定

ビュー

new.html.erb

<%= form_for(@profile) do |f| %>
  <% if @profile.errors.any? %>
    <div id="error_explanation">
      <ul>
      <% @profile.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="prefecture-area">
    <%= label :prefecture_id %><br>
    <%= collection_select :profile, :prefecture_id, Prefecture.all, :id, :name %>
  </div>
  
  <div class="city-area">
    <%= render partial: 'select_city', locals: {prefecture_id: Prefecture.first.id} %>
  </div>
<% end %>

_select_city.html.erb

<%= label :profile, :city_id %><br>
<%= collection_select :profile, :city_id, Prefecture.where(prefecture_id: prefecture_id), :id, :city_name %>

動的にセレクトボックスを入れ替えるcity_idの方はselect_cityという名前でパーシャル化。
パーシャル化することでAjaxで返ってくる結果をレンダリングしやすくしとく。

コントローラ

class ProfilesController < ApplicationController
  def new
    @profile = Profile.new
  end

  # Ajax処理を行う処理
  def get_cities
    render partial: 'select_city', locals: {prefecture_id: params[:prefecture_id]}
  end
end

セレクトボックスを動的に変える処理をget_citiesというアクションで受け取る。
Ajaxでこのアクションを呼ぶ。
ルーティングは以下の感じで

Rails.application.routes.draw do
  resources :profiles, only: :new do
    collection do
      get 'get_cities' # /profiles/get_cities
    end
  end
end

モデル

今回の記事の本質からは外れるので割愛します。
だいたいテーブル構造で想像してください。

Coffee Script

セレクトボックスを動的に変えるのはAjaxを使って実現するためcoffeeやjavascriptが肝になる。
ここがいまいちわかっていなかったので色々詰まっていた…

$(document).on 'change', '#profile_prefecture_id', ->
  $.ajax(
    type: 'GET'
    url: '/profiles/get_cities'
    data: {
      prefecture_id: $(this).val()
    }
  ).done (data) ->
    $('.city-area').html(data)

$(document).on 'change', '#profile_prefecture_id'
この処理でprefecture_idのセレクトボックスが変更されたことを検知する。
profile_prefecture_idはcorrection_selectで自動生成されるid。correction_selectというよりForm系のヘルパーで生成される。

$.ajax(
    type: 'GET'
    url: '/profiles/get_cities'
    data: {
      prefecture_id: $(this).val()
    }

ここでAjaxの通信を開始している。typeにメソッド、urlに呼び出すアクションのパス、dataにコントローラに渡す値を設定する。
今回のdataはセレクトボックスで選択された値をparamsprefecture_idとして乗せる。

done(data)でアクションの結果を受け取る。今回のアクションはprofilesコントローラのget_citiesアクションでget_citiesの中身はrender partial: 'select_city', locals: {prefecture_id: prefecture_id}なので_select_city.html.erbレンダリングした結果を受け取る。
アクションの処理結果はdataに格納されている。

ここのdata$.ajaxでコントローラに渡したdataとは別もんな気がするので注意。

$('.city-area').html(data)レンダリングされたHTMLソースを<div class="city-area">内に差し込むことでセレクトボックスを入れ替える。

ここまでやることでセレクトボックスの入れ替えが実現できました!

いざ記事にしてみると大して難しいことはやっていないのだけど、初めて実装する機能などはやはり難易度が高いように感じてしまう…。
こうやって色々試して、ブログなんかで知識を蓄積して慣れていくしかないですかね…。

うわぁ、がんばろう

【Rails】enumをI18n対応させるenum_helpが便利すぎた

Railsでの開発を通して色々なgemを教えてもらったりして常に目から鱗状態という最近の事情です。

その中でenum_helpというenumI18n化するgemが個人的に簡単かつ便利だなーと思ったのでご紹介です!

まずenumとは何者か

enum0を男性, 1を女性のように数字を何かしらの値と紐付けているようなデータの管理をしている場合に非常に有効なものです。

例えばUserモデルで性別をgenderで管理している場合に男性を0, 女性を1としていると想定

class User < ActiveRecord::Base
  enum gender: { male: 0, female: 1 }
end

このように書くと

# genderに0を設定している場合
user.gender
=> "male"

# genderに1を設定している場合
user.gender
=> "female"

こんな感じでgenderを数字ではなく、文字列で扱うことができるようになって直感的になるし、色々と便利ヘルパーが付加されるので、こんな感じのデータ構造を取っている際は積極的に設定するべき!(ただRails4.1から導入された機能っぽい)

enumI18n対応させたい!

user.genderと書いて"male"や"female"とでるのは素敵だけど、日本語で"男性", "女性"と表示させたいと日本人の僕的には思うわけですよ…
ただ素の状態のenumI18n化するのは若干面倒くさそう…

そこででてくるのがenum_helpという便利gemです!! 使い方も簡単でenum_helpを導入してlocalesを設定するだけ!

enum_helpを導入しよう!

1. まずは例のごとくGemfileに導入するgemを追記する

gem 'enum_help'

2. bundle install

3. localファイルに追記

config/locales/ja.yml

ja:
  enums:
    user:
      gender:
        male: 男性
        female: 女性

4. あとは呼び出すだけ!

呼び出すときはenumを設定している属性を呼び出す際に◯◯_i18nとするだけ!

# genderに0を設定している場合
user.gender_i18n
=> "男性"

# genderに1を設定している場合
user.gender_i18n
=> "女性"

これでenumI18n化は完了なんです!
本当にgem入れて、locales設定するだけでenumI18n化ができちゃうので簡単ですね!

おまけ

enum_helpを設定すると便利ヘルパーが付与される

# 設定しているenumをハッシュで展開
user.genders_i18n.invert
=> {"男性"=>"male", "女性"=>"female"}

ビューでselect_boxを設定する際などに重宝します!

もっといろんなgemとか覚えて、効率的にRails開発をできるように精進したい今日この頃。

RSpecの基本的な部分を勉強してみた

ブログだいぶサボってました…。
いよいよまずいと思ったので久々に更新です。

今回はRSpecについてだらっと書きます。
Ruby on RailsでWebアプリを作る営みをしているのですが、テストをかなりおざなりにしていた+テストの書き方がイマイチわからない状態というブログ更新を滞ったよりもまずい状況だったので勉強しました。

ざっくり学んだことをもはや個人的なメモとしてだらだら書いていきます。
がっつり理解できているわけではないので内容的に曖昧なところもあるので悪しからずです…。

RSpecとはなんぞや?

Rubyのテストフレームワークの一つで、テストを書くのに特化したDSLとかいうやつみたいです。

RSpecを使うために

  • rspec --initで.rspecとspecディレクトリが作成される
  • テストを記述したファイルはspecフォルダに格納
    • xxxx_spec.rbみたいなファイル名にする
  • テスト実行はrspecコマンドを使用する
    • -fdオプションをつけるとテスト毎の成否が見れるようになる

RSpecの基本的な書き方

RSpec.describe "テスト名(クラス名とか)" do
  before do
  end
  
  it "テストケース(メソッドとか)" do
  end

  it "テストケース" 
end

beforeメソッド

  • テスト実施前の初期処理を書く(インスタンス生成とか)
  • beforeで生成した変数は変数の前に@をつけてインスタンス変数のように宣言する必要あり
  • beforeの引数には:exampleと:contextがある
    • :exampleは各exampleの前に実行される(引数省略で:exampleとなる)
    • :contextはdescribeの中の最初の一回目に実行される

example

  • it 〜 endのこと(1テストケースを指す?)
  • itはexample / specifyと書くことも可能
    • テストケースのところに書く文と文脈があうようなものを適宜選択する
  • ブロックを記載しないとpending(棚上げ)となる

describe / context

  • テスト対象を指定する(exampleのグループ化ができる)
  • describeもdescribeの中で入れ子にできる
  • トップレベルのdescribeはクラス名を書く
  • describeと同様に使うことができる
    • describeが物、contextが状況という使い分けをする

matcher

期待する振る舞いを指定する記号を指す

matcher一覧

  • eq(x):xと等しいか
  • not_eq(x):xと等しくないか
  • be [true/false]:trueかfalseか
  • be < x:xより小さいか
  • be_between(x, y).inclusive:数値がxとyの範囲内か(xとyも範囲に含む)
  • respond_to(:{メソッド名}):そのメソッドが存在しているか
  • Rubyの特徴を生かした書き方
    • 例として結果がintegerかどうか調べるテスト を書く expect(obj.add(2,3).integer?).to be true
      上記の書き方を
      expect(obj.add(2,3)).to be_integer
      と書くことができる

subject

RSpec.describe Sample do
  it {
    sample = Sample.new
    expect(sample.add(2, 3)).to eq(5)
  }
end

これを

RSpec.describe Sample do
  it {
    expect(subject.add(2, 3)).to eq(5)
  }
end

にできる。
しかし、このままだとテストケースが長くなった時にsubjectってなんだっけとなってしまうので以下のように書く。

RSpec.describe Sample do
  subject(:sample) { Sample.new }
  it {
    expect(sample.add(2, 3)).to eq(5)
  }
end

subjectにシンボルを渡して{}内でnewしてやるとsampleインスタンスとして使えるようになる。
beforeを使えば良いじゃないかという話もあるがbeforeだとexample内でインスタンス変数を使う必要が出るためsubjectの方がスッキリする。(好みの問題的な側面もあるらしいが…)

method stub

メソッドをまだ実装していないけどテストに使用したいよーという場合に使う。
ex)Userモデルから名前を取得するnameメソッドを使いたいけどまだできてないよーっていう場合

RSpec.describe Sample do
  it {
    #test doubleの呼び出し
    #doubleの引数はなくても良いが失敗した時などに便利なので書いておく
    user = double('user') 
    #nameメソッドが呼び出されたら強制的にtanakaを返すようにする
    allow(user).to receive(:name).and_return('tanaka')
  
    sample = Sample.new
    expect(sample.add(5, user.name)).to eq('5 by tanaka')
  }
end

message expectation

呼ばれなかったらテストが失敗する。
ある処理の前に確実にある処理が走っていることを保証したい場合に使用する。
ex)ログの処理を書いていなけどログを書く処理が各区実に呼ばれたかテストしたいよーっていう場合

RSpec.describe Sample do
  it {
    #ロガーのtest doubleを作成する
    logger = double('logger')
    #loggerのlogメソッドが呼ばれていることを確認する
    expect(logger).to receive(:log)
    #sampleクラスでloggerインスタンスを使用する想定
    sample = Sample.new(logger)
    expect(sample.add(5, user.name)).to eq('5 by tanaka')
  }
end

あー、久々に記事書きましたー。
文章書くのは訓練しないとどんどん書けなくなりますね…
これからは週に最低1記事を目標にブログ頑張ります!

ドットインストールで快適に学習するために『Dotinstall Pane』を使ってみた

プログラミング初心者の強い味方といえば『ドットインストール - 3分動画でマスターする初心者向けプログラミング学習サイト』がありますね。

動画を見ながらプログラミングを学習するサイトで、学べる内容が豊富でどれから手をつければいいのかしら?となるうれしい悲鳴が上げられる感じのサービスです。
学習の方法としては動画でやっていることを写経して、実行してを繰り返す感じ。
ただ写経して、実行のためにブラウザを立ち上げて〜という動作を繰り返すのは割とだるい印象。

ただ最近ドットインストールのメルマガで『dotinstall-pane』なるものを知り、取り入れてみたところ結構便利だったので紹介していこうかと思った次第。

Atomをインストールする

まず『dotinstall-pane』はテキストエディタAtom』のプラグインなので『Atom』を導入する。

自分はMacを使用しているのでMacのインストール方法を記載する。

  1. 公式サイトにアクセスする。http://atom.io/
  2. 公式サイトにある『Download For Mac』ボタンをクリックする。
  3. ダウンロードしたファイルを解凍する。
  4. 解凍したファイルをアプリケーションフォルダに配置する。

これでインストールは完了!

windowsの場合は以下のサイトが参考になります。
Windows - テキストエディタ「Atom」のインストール - 開発メモ - Webkaru

また『Atom』はデフォルト英語なので、日本語化したい場合は以下を参考にすると良さげ。
Atom の日本語化パッケージ "Japanese Menu" を作りました - syonx

『Dotinstall Pane』を導入する

Atom』のインストールが完了したら本題の『dotinstall-pane』を導入する。
※日本語化を行っているので操作項目がデフォルトの状態とは若干異なります。

まずAtomを起動し、ツールバーの『Atom > 環境設定』を選択する。
f:id:kimuraysp:20160214123051p:plain

環境設定画面に遷移したら、サイドバーから『インストール』をクリックする
f:id:kimuraysp:20160214123351p:plain

その後パッケージの検索バーに『dotinstall』と入力しエンターを押すことで検索をかける。
検索をかけると『dotinstall-pane』が出てくるので『install』ボタンをクリックしてインストールする。

これで『Dotinstall Pane』の導入完了です!

『Dotinstall Pane』を使用する

さて導入が終わったらいよいよ使ってみる!

使用方法はすごく簡単で

Macなら『Option + Shift + D』
Windowsなら『Alt + Shift + D』

上記のキーをエディタ画面で押すだけ!!
f:id:kimuraysp:20160214124457p:plain

するとエディタの横にドットインストールの画面が出てきちゃいます!
これで動画みながら学習が効率よくできるようになります!

さらに便利にするために

公式ブログ『http://blog.dotinstall.com/post/139045656660/dotinstallpane』にもありますが、『atom-html-preview』を導入すると学習効率がかなり上がります。

atom-html-preview』ってなんぞやとなりますが、これはエディタ上でショートカットキーを押すことでブラウザでの見え方を簡単に確認できるプラグインです。

導入方法は『Dotinstall Pane』と同様にツールバーの『Atom > 環境設定』と進み、サイドバーで『インストール』を選択する。
その後検索バーに『atom-html-preview』と検索して、『install』ボタンをクリックしてインストールします。
[f:id:kimuraysp:20160214125708p:plain

使用方法はエディタでHTMLを記述して、
f:id:kimuraysp:20160214130114p:plain
(このソースはjQuery入門第6回授業のものになります)

エディタにフォーカスを当てつつ、ショートカットキー『Ctrl-Shift-H』を入力するだけ!
f:id:kimuraysp:20160214130414p:plain
するとピョロっと右側にブラウザでの見え方が表示されます!

この二つのプラグインを導入することで学習効率がすごい上がった気がします!
特にHTML,CSS,javascript系の学習効率が跳ね上がります。

プログラミングに興味がある人はこれを導入して学習を始めるのもありっぽいかも。

jQueryでアコーディオンパネルを作る

最近javascriptを使えないと世の中的にやばいんじゃないかと思いjQueryを勉強し始めました…。
勉強してる中でアコーディオンパネルを作ったわけなんですが、想像以上に簡単に実装できちゃう感じを知り若干感動したのでブログ化です。
(これで感動しているあたり素人感丸出しなのは内緒。)
今更こんなん記事にします?っていう指摘は言わないであげてください。笑

アコーディオンパネルってなんや

アコーディオンパネルはクリックするとクリックした項目の下に文字なんかがスライドで出てきて、もう一回クリックするとスライドしてもとに戻るやつです。
説明下手すぎてイメージつかないですね。
つまりこんな感じのやつです。

初期表示
f:id:kimuraysp:20160211182428p:plain

でこれの一番上の項目をクリックすると
f:id:kimuraysp:20160211182500p:plain

こんな感じでクリックするとヌルッと出てくるやつです。
もう一回同じ項目をクリックすると初期表示の状態に戻ります

とりあえず作ってみよう

ということでまずはHTMLとCSSを作ります。

HTML
<ul id="accordion mdl-list">
  <li class="accordion-item mdl-list__item">
     <h3 class="question">職業はなんですか?</h3>
     <span>+</span>
     <div class="answer">
        <p>システムエンジニアです。いわゆるSEです。</p>
     </div>
  </li>
  <li class="accordion-item">
     <h3 class="question mdl-list__item">なんのスポーツが好きですか?</h3>
     <span>+</span>
     <div class="answer">
	<p>野球</p>
     </div>
  </li>
  <li class="accordion-item mdl-list__item">
     <h3 class="question">携帯は何使ってますか?</h3>
     <span>+</span>
     <div class="answer">
        <p>iPhone</p>
     </div>
  </li>
</ul>

初期に表示されている箇所がh3要素、出たり隠れたりする箇所が<div class="answer">~</div>の部分になります。

CSS
ul {
  list-style: none;
}

.accordion-item {
  position: relative;
  width: 400px;
  border-bottom:1px solid #ccc;
  cursor: pointer;
  margin-top: 5px;
}
.accordion-item:hover {
  background-color: #E6E6E6;
}

.question {
  font-size: 18px;
  margin: 0;
  padding: 0;
}

.accordion-item span {
  position: absolute;
  top: 5px;
  right: 5px;
  color: #B3B3B3;
  font-size: 20px;
}

.answer {
  display: none;
  font-size: 12px;
}

初期表示の状態では出たり隠れたりする部分をdisplay: none;で画面に表示されないようにしておきます。

javascriptを書くッッ

さてアコーディオンパネルを実現するためのjavascriptを書きます。
とはいってもjQueryを使うとすごくさっくりした記述で処理が書けちゃいます!

$(function() {
  $('.accordion-item').click(function(){
        
    //出隠れする部分を変数に格納しておく
    var $answer = $(this).children('.answer');

    //hasClassメソッドで出隠れする部分にopenが存在するか確認
    if ($answer.hasClass('open')) {

      //出隠れ部分が出ている場合の処理

      //&ltdiv class="answer open"&gtからopenを消す
      $answer.removeClass('open');

      //出ている部分をスライドアップして隠す
      $answer.slideUp();

      //横っちょにある開閉ボタンを+に変える
      $(this).children('span').text('+');
    } else {

      //出隠れ部分が隠れている場合の処理
      
      //&ltdiv class="answer"&gtにopenを追加する
      $answer.addClass('open');

      //出す部分をスライドダウンして表示させる
      $answer.slideDown();

      //横っちょにある開閉ボタンを-に変える
      $(this).children('span').text('-');
    }
  });
});

<div class="answer">にopenを追加することで開いたり、隠したりする感じ。
openを追加することでCSSに記載しているdisplay: none;が無効になったり、有効になったりするイメージかしら。

とはいえたった10行前後で良く見るあのアコーディオンパネルさんを実現できるのはちょっと感動。
Ruby on Railsを極めつつ、javascriptも極めていきたいっすなあ。
とはRailsさんはcoffeescriptだからまた勉強が必要やが…。

そのうちcoffeescript備忘録つくろ

Railsで関連モデルを同時に更新したい(has_oneで関連させていたモデルをbuild_associationで安直に生成しようとしたら痛い目をみた話)

Railsで関連モデルを同時に更新するようなコードを書こうとして、思いの外ハマって抜けられなくなったので備忘録。

関連モデルを同時に更新したい状況

例えばメールアドレスやパスワードのような認証情報と、生年月日や趣味のようなプロフィール情報を分けて管理したい場合など結構ありがちだと思います。 以下のような関連を持つモデルのようなイメージ。

f:id:kimuraysp:20160207000912p:plain

とりあえず書いてみる

ひとまず自分の思うがままにコードを書いていく。

まずモデルから。

app/models/user.rb

class User < ActiveRecord::Base  
  has_one :profile, dependent: :destroy  
  accepts_nested_attributes_for :profile  
end  

app/models/profile.rb

class Profile < ActiveRecord::Base
  belongs_to :user
end  

親モデルのUserにaccepts_nested_attributes_forを設定することで親モデルから子モデルを作成したり、保存したりできるようにする。

次にコントローラ。

app/controllers/users_controller.rb

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    @users = User.all
  end

  def show
  end

  def new
    @user = User.new
  end

  def edit
    @user.build_profile
  end

  def create
    @user = User.new(user_params)
    @user.save
    redirect_to :users
  end

  def update
    @user.update(user_params)
    redirect_to :users 
  end

  def destroy
    @user.destroy
    redirect_to :users
  end

  private
    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:email, :password, profile_attributes: [:birthday, :hobby])
    end
end

viewでfields_forを使うのでprofile_attributesをストロングパラメータに記入しておく。
(fields_for[引数に与えた名前]_attributesというname属性になるためらしい)
仕様としてプロフィール情報を入れるのはユーザ情報編集時のみとするため、登録しているUserモデルからProfileモデルを生成できるようにedit@user.build_profileと何気なく記入。
親から子を生成するときはとりあえずbuildっしょとかいう素人考えでbuild_profileを記入したが 、後にどツボへの道へと誘うことになるとはこの段階では知る由もなかった…。

とりあえず次にビュー。

app/views/_form.html.erb

<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_field :email %>
  </div>
  <div class="field">
    <%= f.label :password %><br>
    <%= f.password_field :password %>
  </div>
  
  <% unless current_page?(new_user_path) %>
    <%= f.fields_for :profile, @user.profile do |pf| %>
      <div class="field">
        <%= pf.label :birthday %><br>
        <%= pf.date_field :birthday %>
      </div>
      <div class="field">
        <%= pf.label :hobby %><br>
        <%= pf.text_field :hobby %>
      </div>
    <% end %>
  <% end %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

プロフィール情報はnewのタイミングでは表示させないようにcurrent_page?を設定。
fields_forのタグを<%= %>ではなく<% %>で記入してプロフィール情報入力欄出ない問題にも軽くハマっているのは内緒の話。
これでとりあえず一通り完成!

てことで動作確認

ユーザを登録

f:id:kimuraysp:20160207111430p:plain

次にプロフィール情報を入力するために編集

f:id:kimuraysp:20160207111542p:plain f:id:kimuraysp:20160207111643p:plain
いい感じにプロフィール情報を入力できるようになっている。
とりあえずプロフィール情報を入力。

登録できたか確認

f:id:kimuraysp:20160207111651p:plain
いい感じに登録できている。

しかし、問題が起きる…

登録までできて、「いやー、ひと仕事終わったよ」と清々しい気分でいましたが、再び編集画面を表示した際に違和感を覚える。
f:id:kimuraysp:20160207111542p:plain
あれ…?プロフィール情報が表示されてなくね…?

いやいや、まさかそんなはずは…と思い、再びshowしてみるとProfileモデルなんて存在してねーけど…と怒られる。
f:id:kimuraysp:20160207113010p:plain

DBも確認してみたが、profilesテーブルがまっさらになっている…

>> Profile.all
  Profile Load (0.5ms)  SELECT "profiles".* FROM "profiles"
=> #<ActiveRecord::Relation []>

さっきまで存在していたプロフィール情報がどこか遠い国に旅立ってしまっている…

登録し直したり、デバッグしてみたりで原因を探ること小一時間。
問題点があらわになる。
@user.build_profileを実行した後にレコードが消えている…!!!!!

サーバログを見てみると

Started GET "/users/8/edit" for ::1 at 2016-02-07 12:12:38 +0900
Processing by UsersController#edit as HTML
  Parameters: {"id"=>"8"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 8]]
  Profile Load (0.2ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT 1  [["user_id", 8]]
   (0.1ms)  BEGIN
  SQL (0.2ms)  DELETE FROM "profiles" WHERE "profiles"."id" = $1  [["id", 7]]
   (2.0ms)  COMMIT
  Rendered users/_form.html.erb (3.0ms)
  Rendered users/edit.html.erb within layouts/application (4.2ms)
Completed 200 OK in 38ms (Views: 20.1ms | ActiveRecord: 2.7ms)

なんかDELETEが走っちゃってますけど!!!!

どうもbuild_profileでProfileモデルを生成の際は一旦存在するレコードを消してから生成みたいなことが起きているっぽい。

ということでbuild_profileを使わないように修正。

def edit
    @user.profile = Profile.new if @user.profile.blank?
end

てな感じで無事プロフィール情報を登録してから編集画面にいっても情報が消えなくなりました。

とりあえず関連モデルを生成するならbuildじゃね?という安易な考えを悔い改めようと思いましたまる