新米パパの育児留学

読者です 読者をやめる 読者になる 読者になる

新米パパの育児留学

『育児留学』とは、育児を通して異なる視点を得たり新しいことに挑戦して自己成長に繋げること。育児奮闘中の新米パパが育児を通して得た気づきや感じたこと、育休中に習得したプログラミングに関する話題を発信していきます。

水も電気もない島で育つとこどもはどうなる?

みなさんは、水も電気もない島で育つとこどもはどうなるか想像できますか?

 

f:id:mochikichi321:20170430094110j:plain

 

昨日、ある縁で妻が知り合ったフィリピンに住む日本人女性とフィリピン人のご主人、6歳の男の子が日本に来ているということで会ったときの出来事を紹介します。 

 

 

出会いのきっかけ

約2年前、妻が「ヒト・モノ・トチの良さを活かす未来を創る旅」で世界一周一人旅をしている際にフィリピンのセブ島である日本人女性と出会った。

彼女は、ソーシャルコーディネーションサービスの提供を通してセブ・離島の発展途上エリアの社会課題に向き合う『NGO go share』の代表三浦聖子さん。

短い時間ではあったが、話をしているうちに意気投合し、お互いに将来一緒に事業をしていきたいと思える関係になった。その際、少しではあるがフィリピン人のご主人、息子さんとも会うことができた。

www.goshare-island.com

 

初対面

妻は上記のとおり、ご家族と面識はあったが、私自身はみなさん初対面。妻からはステキな家族だと話を聞いていたので会えるのを楽しみにしていた。

今回は、お互いの夫婦とこどもそれぞれ3人ずつの家族で一緒にランチをすることにした。

待ち合わせ場所で、妻を見つけた時にみんな笑顔で会えたことを喜んでくれた。

私もみなさんと笑顔であいさつ。

少し話をしていると、6歳の男の子が おもむろに背中のリュックから何かを取り出して、笑顔で差し出した。

少年K「ママ、あれ渡してもいい?(渡したくて仕方がない様子)」

女性「いいよ。」

少年K「これ、Sくんのために作ってきたからあげる。」

f:id:mochikichi321:20170430094215j:plain

 

なんと、5か月の息子に会うからと手作りのおもちゃを作って持ってきてくれたのだ。しかも英語と日本語の名前入り!

おもちゃをくれたことももちろんすごく嬉しいのだが、何よりも息子と会うのをそんなに楽しみにしてくれていたんだと思うと感動した。

妻が2年前に訪れた際にはほんの少しの時間しか会えておらず、小さかったこともあり恐らくほとんど記憶にはないはず。お母さんから話を聞いていたんだろうか。それだけ楽しみにしてもらえていたとはすごく幸せだ。

 

こちらは帰宅後息子がそのおもちゃで遊んでいる様子。

すごく楽しそうでお気に入りの様子(^^♪

 

f:id:mochikichi321:20170430094251j:plain

 

このおもちゃ、実は素晴らしい出来なんです。

おそらく、ヨーグルトの空容器のようなものを重ね合わせて、中に貝殻か何かを入れて振ると音が鳴るおもちゃ。

・ほどよい軽さ(振り回しやすい)

・ほどよい大きさ、取っ手付(持ちやすい)

・ほどよい柔らかさ(当たっても痛くない)

・音が鳴る(楽しい)

・原色使い(興味をもつ色)

 

なんと素晴らしいUX(ユーザー体験)なんだ!

彼は優秀なクリエイターになるに違いない!!

と、勝手に私が興奮していましたw

 

 

ネイティブアメリカンフードのお店でランチ

ネイティブアメリカン(インディアン)フードのお店でランチをすることに。

 

ランチ中の会話で、彼らの生活スタイルを聞いていると、仕事の関係でセブ島ボホール島、カオハガン島を行き来しているようだ。

セブ島は有名なリゾート地、ボホール島も最近人気でセブよりも自然が多く残るリゾート地。

ご主人の生まれ育ったカオハガン島は、人口600人の小さな島で、飲み水や電気などのインフラがまだ整っていないような自然と共にある島だそうだ。

 

f:id:mochikichi321:20170430181419p:plain

出展元:JAPPH

 

少年に聞いてみた。

私「Kくんはどの島が好き?」

少年K「カオハガンが一番楽しい。」

意外な答えだった。

私「不便だったりしない?セブの方がいろいろ遊ぶものとか遊ぶところあるんじゃないの?」

少年K「なんにもないけど、思いっきり走り回れるし、おもちゃがなくても自分でいろいろ遊び方を考えるんだ。」

 

少年は幼稚園には行かなかったし、小学校も行かない予定だと聞いた。セブ島に住んでいるので施設としては十分あるので行こうと思えば行ける。しかし、あまり決まった場所にとどまっていないから、まぁ、行かなくてもいっか、という感じ。

日本人の私からすると、「え?幼稚園とか小学校って行かなくてもいいんだ。」と思った。

学校に行っていないから、発達が遅れたりするんじゃないかと思うかもしれないが、そんなことはまったくなかった。上手にコミュニケーションを取っているし、目がイキイキしている。

 

ランチのお店は、民族衣装やアクセサリー、人形、写真などがたくさん飾られているお店。

少年は店内のそれらの置物に大興奮!

「この人たちはどこに住んでるのー?」「なんでこんな服着てるのー?」など怒涛の質問をママに浴びせていた。

自分の好奇心ベースで日常の中で自然と社会の勉強ができている。

 

そのあと、ご家族からお土産にと、ラベンダーの香りのする可愛らしいくまさんのぬいぐるみをいただいた。いい香りで癒される♪

f:id:mochikichi321:20170430095452j:plain

 

さらに、少年がアッと何かを思い付いたように、ママにひそひそ話をしだした。

少年K「これ、Sくんにあげる!」

女性「フィリピンのおやつです。日本でいう卵ボーロみたいなものかな。Sくんにはまだ早いかもしれないけどどうぞ。」

 

こんなにもらってしまっていいんだろうか。Give精神に溢れていてすごく嬉しい。

f:id:mochikichi321:20170430100032j:plain

 

 息子の様子

息子は現在、0歳5か月。当然だが何もしゃべれない。

今回のご家族との面会が彼の目にどう映ったかはまだわからない。

しかし、息子は会っている間終始ニコニコ笑顔でリラックスしていた。ご家族の優しいオーラに安心したのだろう。

また、帰宅後、あやしていると、あるポイントで見たこともないくらい楽しそうにキャッキャッと声を出して笑うことがあった。今回の面会で何かを感じ取ったのかもしれない。

 

まとめ

"こどもの教育ってなんだろう?"

今回の面会で私はすごく大きな気づきを得た。

日本では義務教育だとかで学校に行かなければならない、これをしなければならないといった型にこだわっているし、物質的な豊かさでしか心を満たせない人が多くなってしまっている気がする。教育の本質はなんだろうかということを考える機会が欠如している気がする。

 

Kくんは、いろんな事に好奇心に溢れていて、自分の持っているモノに対するこだわりよりも、息子や私たちと楽しい時間を過ごすためにといろいろと話してくれたり、プレゼントをくれたりする。

素敵なご両親の元で、物質ではないものの豊かさを小さい頃から肌で感じてきたのだろう。

息子にも、Kくんのように本当に心の豊かな人間に育ってほしいと強く感じた。

 

今回、素敵な時間と大きな気づきを与えてくれたご家族に「ありがとう!」。

0歳児の赤ちゃんと初めての旅行体験記 授乳、交通手段、宿、持ち物のおすすめ

こんにちは!育休中新米パパのmochikichiです。

先日、 0歳4か月の息子と妻と3人で初めて東京へ旅行をしてきました。

生後、関西から出たことがなかったので、東京訪問は初の遠出だけど大丈夫かなぁ...と、ドキドキでしたがいろいろと準備したのもあって無事に楽しい旅となりました。

赤ちゃんと一緒に遠方へ出かける際に気を付けておきたいことを実体験を元にまとめてみました。

赤ちゃん_遠征_注意点

 

訪問目的

・転職候補先のオフィス視察

・転居予定場所のエリア、物件視察

・託児付きコワーキングスペース体験

 

スケジュール

スケジューリングのポイント

・授乳、おむつ交換のタイミングと場所を常に想定する

 授乳とおむつ交換はいつでもどこでもできる訳ではないので、事前に念入りに調査と計画を実施しました。

 

・スケジュールには余裕を持ちましょう 

 赤ちゃんと一緒だと、普段よりも歩くスピードがゆっくりになったり、ベビーカーで行くとエレベーターが思いの外遠かったりと予想以上に時間がかかる場合があります。余裕を持ったスケジュールを立てたつもりでしたが、途中でうんちをしておむつ交換したりするとバタバタと慌てるのでもっと時間には余裕を持った方が良かったなと思いました。

実際のスケジュール

1日目

8:00 授乳@自宅

8:30~14:30 関西⇒東京移動(電車+ぷらっとこだま)(11:30 授乳@新幹線

14:30 ホテル荷物預け、授乳@ホテル

15:00~18:00 物件視察

18:30 お風呂

19:00 授乳@ホテル、こども就寝

(21:30,1:30,5:00 授乳@ホテル

2日目

8:00 こども起床、授乳@ホテル

9:00~11:00 吉祥寺エリア視察

11:30~16:30 託児付きコワーキングスペース体験(11:30,15:00 授乳@コワーキングスペース

18:30 お風呂

19:00 授乳@ホテル、こども就寝

(21:30,1:30,5:00 授乳@ホテル

3日目

7:30 こども起床、授乳@ホテル

8:00~9:30 清澄白河モーニングコーヒー、おさんぽ

9:30~10:30 居住エリア視察

10:30 授乳@ホテル

11:00~12:00 オフィス視察

13:00~18:00 東京⇒関西移動(ぷらっとこだま+電車)(15:00 授乳@新幹線

18:30 お風呂

19:00 授乳@自宅、こども就寝

交通手段

飛行機、新幹線、自家用車など様々ですが、授乳とおむつ交換をどうするかが重要です。

今回は、低価格かつ比較的混雑しない「ぷらっとこだま」を選択しました。

ぷらっとこだま|JR東海ツアーズ

ぷらっとこだまの注意点

授乳

多目的室を授乳室として利用できます。ただし、多目的室は普段はカギがかかっており利用できません。乗務員に授乳したいと伝えると開けてくれます。

今回はあまり混雑していなかったのですぐに使えましたが、多目的室はぷらっとこだまに1室しかないので先に授乳中の方がいたり、体調が悪くなった人が休憩するスペースとして利用するなど名前の通り多目的に利用され、利用できない可能性もあるので、念のため授乳ケープを持参しておいた方が良いでしょう。

おむつ交換

おむつ交換台は上記の多目的室または多目的トイレに設置されているため安心です。しかし、こちらも混雑時は他の方とラップする可能性があるので早め早めの対応を心掛けた方が良いでしょう

服装

3月下旬でまだ外は寒かったので温かい服装をしていましたが、車内は暑かったため、途中で服を脱がせて薄着に着替えさせました。すぐに体温調整できるように脱ぎ着しやすい服装が良いでしょう。

おもちゃ 

寝ている間は問題ないですが、起きていて長時間座っていると飽きてきて機嫌が悪くなり泣き出しました。その時にお気に入りのおもちゃであやしてあげるとゴキゲンになりました。それでもダメな時は、デッキに出て気分転換も効果的でした。

宿

赤ちゃんにとって快適な場所を選びましょう。

ファミリー向けのホテルを選ぶと広くて使いやすいのですが、どうしても値段が高くなってしまうので、TripAdvisor (トリップアドバイザー) の口コミを元にビジネスホテルを探しました。

今回利用したホテルはこちら。すごくよかったです。

「ホテルマイステイズ浅草橋」

アクセス

移動に便利な駅近くがいいなと思い、駅から徒歩5分圏内で探しました。飲み屋街など騒がしい場所は避けました。慣れない土地での移動は予想以上に疲れてしまったので、今回利用したホテルは駅から徒歩1分でかなり助かりました。

設備

ベッド

赤ちゃんもできるだけ普段の睡眠と似た環境が安心できると思います。私たちは普段から"クイーンサイズのベッド"で3人で川の字になって寝ているので"クイーンサイズのベッド"があるホテルを選びました。

赤ちゃん_ホテル_クイーン

おふろ

調査時にはあまり意識していませんでしたが、よくあるビジネスホテルの狭いユニットバスではなく、身体を洗う広いスペースがあり、こどもと一緒におふろに入るのがすごく快適でした。ビジネスホテルでも、クイーンなど大きめの部屋にするとおふろの洗い場も広い部屋があるかもしれません。 

持ち物リスト

今回の旅で持参したものを紹介します。

・おむつ 12枚×3日=36枚 

 ⇒不安だったので多めに持って行きました。6枚くらい余りましたが多めに持って行って正解でした。

・おしりふきシート 1袋

・おむつ交換用シート

・使用済みおむつ用ごみ袋 10枚

 ⇒移動中などのおむつ処理は臭いが困るので、以下の臭いが気にならない防臭袋を持っていくと臭いが全然気にならず重宝しました。

【驚異の防臭袋 BOS(ボス)Sサイズ大容量200枚入り 赤ちゃん用おむつ処理袋】

・ハイター、洗剤(小分け)

 ⇒うんち漏れで服が汚れた時用に持っていきました。実際に漏れて汚れたので持って行って良かった。。。

・服 5枚

・肌着 5セット

・パッチ 1枚(寒いとき用)

・靴下 3セット

・ガーゼ 5枚

・スタイ 5枚

・バスチェアー

 ⇒ポンプ内臓で空気で膨らませられるバスチェアーを持っていきました。空気を抜くとかさばらないので便利です。

【リッチェル ふかふかベビーチェア(エアーポンプ内蔵)】

・バスタオル

⇒ベッドで寝かす時に使用しました。

・せっけん

・帽子

・抱っこひも

・授乳ケープ

母子手帳

・保険証

・おもちゃ

⇒息子のお気に入りの「オーボール」を持っていきました。これで遊ぶとゴキゲンなのでかなり助かりました。

【オーボール ベーシック】

まとめ

いろいろと心配になりますが、備えあれば患いなし!

みなさんも事前にシミュレーションをしてかわいい赤ちゃんと一緒にステキな旅をお楽しみください。

Ruby on Rails いいね(like)機能拡張 (railsチュートリアル)

目的

Ruby on Rails チュートリアル 5.0(第4版)で実装したサンプルアプリケーション(+検索機能拡張版)を元に、いいね機能を拡張します。

完成版アプリケーション

実際に検索機能を拡張したアプリケーションを見てみたい方はこちら。

https://fathomless-shore-36670.herokuapp.com/about

開発環境

Rails 5.0

・cloud9

実装

最終的なソースコードと確認画面

[View]

[microposts/_micropost.html.erb]

<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
    <%= micropost.content %>
    <%= image_tag micropost.picture.url if micropost.picture? %>    
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,data: { confirm: "You sure?" } %>
    <% end %>
  </span>
  <!--like拡張機能-->
  <%= render partial: 'likes/like', locals: { micropost: micropost, likes: @likes } %>
</li>

[likes/_like.html.erb]

<% if micropost.like_user(current_user.id) %>
  <%= button_to micropost_like_path(likes, micropost_id: micropost.id), method: :delete, id: "like-button", remote: true do %> 
    <%= image_tag("icon_red_heart.png") %>
    <span>
      <%= micropost.likes_count %>
    </span>
  <% end %>
<% else %>
  <%= button_to micropost_likes_path(micropost),id: "like-button", remote: true do %>  
    <%= image_tag("icon_heart.png") %>
    <span>
      <%= micropost.likes_count %>
    </span>
  <% end %>
<% end %>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def show
    @user = User.find(params[:id])
    # 検索拡張機能として.search(params[:search])を追加    
    @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search])
    # like拡張機能
    @likes = Like.where(micropost_id: params[:micropost_id])
  end
(中略)
end

[static_pages_controller.rb]

class StaticPagesController < ApplicationController
  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      # 検索拡張機能として.search(params[:search])を追加 
      @feed_items = current_user.feed.paginate(page: params[:page]).search(params[:search])
      # like拡張機能
      @likes = Like.where(micropost_id: params[:micropost_id])
    end
  end
(中略)
end

[likes_controller.rb]

class LikesController < ApplicationController
  def create
    @like = Like.create(user_id: current_user.id, micropost_id: params[:micropost_id])
    @likes = Like.where(micropost_id: params[:micropost_id])
  end

  def destroy
    like = Like.find_by(user_id: current_user.id, micropost_id: params[:micropost_id])
    like.destroy
    @likes = Like.where(micropost_id: params[:micropost_id])
  end
end

[Model]

[models/micropost.rb]
class Micropost < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy
  default_scope -> { order(created_at: :desc) }
  mount_uploader :picture, PictureUploader
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
  validate  :picture_size
(中略)
  # like拡張機能
  def like_user(user_id)
    likes.find_by(user_id: user_id)
  end

  private
(中略)
end

[models/like.rb]
class Like < ActiveRecord::Base
  belongs_to :micropost, counter_cache: :likes_count
  belongs_to :user
end

[JS]

[likes/create.js.erb]
$("#like-buttons").html("<%= j(render partial: 'like', locals: { micropost: micropost, likes: @likes, like: @like}) %>");

[likes/destroy.js.erb]
$("#like-buttons").html("<%= j(render partial: 'like', locals: { micropost: micropost, likes: @likes }) %>");

[Routes]

[routes.rb]
Rails.application.routes.draw do
  get 'password_resets/new'

  get 'password_resets/edit'

  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  post   '/signup',  to: 'users#create'  
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  resources :relationships,       only: [:create, :destroy]

#like機能拡張用に指定
  resources :microposts do
    resources :likes, only: [:create, :destroy]
  end

end

【Usersページ】 f:id:mochikichi321:20170408065023p:plain

【Homeページ】 f:id:mochikichi321:20170408065030p:plain

実装ステップ

STEP1 現状把握

まず、現時点での[View],[Controller],[Model]及び[Routes]はこのようになっている。

[View]

[microposts/_micropost.html.erb]

<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
    <%= micropost.content %>
    <%= image_tag micropost.picture.url if micropost.picture? %>    
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,
                                       data: { confirm: "You sure?" } %>
    <% end %>
  </span>

#ここへlike拡張機能追加予定

</li>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def show
    @user = User.find(params[:id])
    # 検索拡張機能として.search(params[:search])を追加    
    @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[static_pages_controller.rb]

class StaticPagesController < ApplicationController
  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      # 検索拡張機能として.search(params[:search])を追加 
      @feed_items = current_user.feed.paginate(page: params[:page]).search(params[:search])
    end
  end
(中略)
end

[Model]

[models/micropost.rb]

class Micropost < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy
  default_scope -> { order(created_at: :desc) }
  mount_uploader :picture, PictureUploader
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
  validate  :picture_size
(中略)
end

[Routes]

[routes.rb]

Rails.application.routes.draw do
  get 'password_resets/new'

  get 'password_resets/edit'

  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  post   '/signup',  to: 'users#create'  
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  resources :microposts,          only: [:create, :destroy]
  resources :relationships,       only: [:create, :destroy]
end

STEP2 likesテーブルを作成

ターミナルでlikesテーブルを作成。カラムはinteger型でuser_idとmicropost_idを追加。

$ rails g model Like user_id:integer micropost_id:integer                                       

作成したLikeモデルに以下を記載。

[models/like.rb]

class Like < ActiveRecord
  belongs_to :micropost, counter_cache: :likes_count
  belongs_to :user
end

counter_cahce: :likes_countは、子モデル(リレーションされているlike)の数を親モデルのカラム(micropostのlikes_count)に保存を意味する。なので、likes_countカラムをMicropostテーブルに追加する。

counter_cahceとは?

STEP3 Micropostテーブルにlikes_countカラムを追加

Micropostテーブルにinteger型のlikes_countというカラムを追加する。

$ rails g migration add_likes_count_to_microposts likes_count:integer

$ rails db:migrate

さらに同モデルファイルに以下を記載する。

[models/micropost.rb]

class Micropost < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy
(中略)
  def like_user(user_id)
   likes.find_by(user_id: user_id)
  end

  private
(中略)
end

like_userメソッドは指定のユーザーが既にマイクロポストにいいねしているかを確認するメソッド。

STEP4:ルーティング設定

resourcesを使いmicropostsとlikesの構造を規定。

[routes.rb]

Rails.application.routes.draw do
  get 'password_resets/new'

  get 'password_resets/edit'

  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  post   '/signup',  to: 'users#create'  
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
#削除
  resources :relationships,       only: [:create, :destroy]

#like機能拡張用に指定
  resources :microposts do
    resources :likes, only: [:create, :destroy]
  end

end

STEP5 likesコントローラーのアクションを定義

likes controllerを作成し、そのファイルに以下を記載する。

[likes_controller.rb]

class LikesController < ApplicationController
  def create
    @like = Like.create(user_id: current_user.id, micropost_id: params[:micropost_id])
    @likes = Like.where(micropost_id: params[:micropost_id])
  end

  def destroy
    like = Like.find_by(user_id: current_user.id, micropost_id: params[:micropost_id])
    like.destroy
    @likes = Like.where(micropost_id: params[:micropost_id])
  end
end

likeを増やしたり消したりするlikeとその後の合計like数を表示するための@likesを定義。

合わせて、usersコントローラーとstatic_pagesコントローラーにも@likesを定義。

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def show
    @user = User.find(params[:id])
    # 検索拡張機能として.search(params[:search])を追加    
    @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search])
    # like拡張機能
    @likes = Like.where(micropost_id: params[:micropost_id])
  end
(中略)
end

[static_pages_controller.rb]

class StaticPagesController < ApplicationController
  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      # 検索拡張機能として.search(params[:search])を追加 
      @feed_items = current_user.feed.paginate(page: params[:page]).search(params[:search])
      # like拡張機能
      @likes = Like.where(micropost_id: params[:micropost_id])
    end
  end
(中略)
end

STEP6 like部分テンプレートを作る

今回はいいねボタンを部分テンプレートを使って作る。

STEP3で定義したlike_userメソッドを使用し、指定ユーザーがマイクロポストにいいねしているかを判断し、表示を切り分ける。いいねしていたら赤色のハート、いいねしていなければ灰色のハートを表示。

likesのモデルファイルでcounter_cacheの記述があるので、micropost.likes_countとするだけでそのマイクロポストに結びつくlike数が表示される。

どちらのアクションのパスもremote: trueの記述をしてajaxを使いアクションを実行する。

icon_red_heard.pngがいいね後の赤色のハート、icon_heart.pngがいいね前の灰色のハート。画像を準備して"assets/images"フォルダの中にそれぞれ保存をしておく。

[画像サンプル]

f:id:mochikichi321:20170408070249p:plain

f:id:mochikichi321:20170408070253p:plain

[likes/_like.html.erb]

<% if micropost.like_user(current_user.id) %>
  <%= button_to micropost_like_path(likes, micropost_id: micropost.id), method: :delete, id: "like-button", remote: true do %> 
    <%= image_tag("icon_red_heart.png") %>
    <span>
      <%= micropost.likes_count %>
    </span>
  <% end %>
<% else %>
  <%= button_to micropost_likes_path(micropost),id: "like-button", remote: true do %>  
    <%= image_tag("icon_heart.png") %>
    <span>
      <%= micropost.likes_count %>
    </span>
  <% end %>
<% end %>

like部分テンプレートを表示したい箇所に挿入する。

[microposts/_micropost.html.erb]

<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
    <%= micropost.content %>
    <%= image_tag micropost.picture.url if micropost.picture? %>    
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,
                                       data: { confirm: "You sure?" } %>
    <% end %>
  </span>
  <!--like拡張機能-->
  <%= render partial: 'likes/like', locals: { micropost: micropost, likes: @likes } %>

</li>

STEP7 JSファイルを作る

ajaxを使ってページ遷移をせずにいいねを増やしたり消したりするjavascriptファイルを作る。

[likes/create.js.erb]

$("#like-buttons").html("<%= j(render partial: 'like', locals: { micropost: micropost, likes: @likes, like: @like}) %>");

[likes/destroy.js.erb]

$("#like-buttons").html("<%= j(render partial: 'like', locals: { micropost: micropost, likes: @likes }) %>");

以上で簡易like機能の実装が完了。

参考

http://qiita.com/YuitoSato/items/94913d6a349a530b2ea2

プログラミング未経験から2か月でエンジニアへ転職したノウハウを知りたい方の相談も受け付けております。

関連記事

mochikichi.hatenablog.com

男性の育休取得に対する周囲の反応あるある まとめ

こんにちは!エンジニアを目指す育休中新米パパのmochikichiです。

男性で育児休業を取っていると周りの方々から様々な反応があるのでご紹介します。

 

男性の育休周囲の反応

反応事例

 

以下は「育休を取ります/育休を取っています」と話した時の反応。 

 

「え?どういうこと!?」 #会社の直属の上司(30代男性)

 ⇒"男性が育休を取る"ということがあまりにも無縁すぎてプチパニック状態。

 

「そんなに休暇取れるっていいね。」 #友人(20代男性)

⇒遊びじゃねーよ。

 

「なんで男性が育休を取る必要があるの?」 #会社の上司など多数

父親だから。

 

「男性が育休取っても育休中ってヒマでしょ?」 #病院の助産師(40代女性)

 ⇒これは結構驚いた一言。男性は育児には力になれないという思い込みから。助産師から聞くとは。。。

 

「里帰りして両親に見てもらったらいいんじゃないの?」 #会社の同僚(40代男性)

 ⇒里帰りをしないという選択もあるんです。

 

「俺も取りたいけど、復帰後会社の椅子がなくなりそう。」 #親戚(30代男性)

 ⇒本当になくなるなら辞めてしまったらいい。

 

「いいな~大企業は。俺の会社なんて育休制度ないし~。」 #友人(20代男性)など多数

⇒法律で定められていてだれでも取れます。(※下記参照)

 

「何か家庭の事情でもあるの?」 #会社の同僚(40代男性)

⇒事情がないと取っちゃダメなの? 

 

「おっ!流行りの "イクメン" だね~!(ひやかし)」 #会社の同僚など多数

 ⇒一番ウザいやつ。無視です、無視。

 

「給料は?生活費大丈夫なの??」 #親

⇒心配するかと思うけど育児休業給付金があります。

 

「素晴らしいね!その経験は絶対将来活きてくるよ!私は育児をやり切ったという経験があったから強くなれた。男性もその経験をできないのは勿体ない!主人にももっと育児をさせてあげれば良かった。」 #コワーキングスペースマネージャー(30代女性)

⇒一番響いた言葉。

 

「俺も取りたかったなぁ~。子供が小さいとき出張ばっかりで成長をほとんど見れなかったし。」 #会社の先輩(30代男性)など多数

⇒意外と多い、後悔系。

 

「うちの夫にもとってほしい」 #友人(30代女性)

⇒ お母さんは頑張ってますよ、お父さん。

 

「主人の周りの訪日外国人はみんな育休取ってて、主人も勧められたって」 #友人(30代女性)

⇒外国人からすると当たり前なんだ。。

 

「ありがとう。一緒に子育てできるのは幸せ。」 #妻

⇒一番成長する時期を一緒に見守れることが幸せ。大変なことも一緒に乗り越えることで夫婦、家族の絆は深まる。

 

育休制度の法律

育児休業とは、子どもを養育する労働者が取得できる休業のことで、法律によって認められた制度であり、就業規則への記載が義務づけられています。

第五条

労働者は、その養育する一歳に満たない子について、その事業主に申し出ることにより、育児休業をすることができる。育児休業、介護休業等育児又は家族介護を行う労働者の福祉に関する法律 第5条より)

万一、就業規則に記載がない場合でも、事業主は労働者からの育休の申し出を拒否することはできません。

 

第六条

事業主は、労働者からの育児休業申出があったときは、当該育児休業申出を拒むことができない。

育児休業、介護休業等育児又は家族介護を行う労働者の福祉に関する法律 第6条より)

 

男性の育休周囲の反応

まとめ

いくつか紹介しましたが、周囲の反応は様々です。残念ながら今の世の中では賛同する声ばかりではないのが実態です。その事実をわかったうえでも、育休を取得することでかけがえのない貴重な経験ができたと思っています。詳細は別途発信します。

重要なのは、夫婦間で育児に対してしっかりと向き合い、話し合いをして自分たちの意思で決めることです。

育休を取りたいけど不安で踏み出せない方がいらっしゃれば、一歩踏み出すことを応援します。

 

男性の育休取得に対して"こんな意見を言われた/聞いた"という皆さんの意見をお待ちしています。お気軽にコメントください。

 

男性の育休取得に対する相談も受け付けております。

 

MediumのPublicationを始めました。交流の場として活用ください。

medium.com

Ruby on Rails 検索機能拡張 (railsチュートリアル)

目的

Ruby on Rails チュートリアル 5.0(第4版)で実装したサンプルアプリケーションを元に、検索機能を拡張します。

完成版アプリケーション

実際に検索機能を拡張したアプリケーションを見てみたい方はこちら。

https://fathomless-shore-36670.herokuapp.com/about

開発環境

Rails 5.0

・cloud9

実装

1.indexページにユーザ検索機能を実装する

最終的なソースコードと確認画面

[View]

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

<!--  検索拡張機能  -->
<p>user search</p>
<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

<ul class="users">
  <%= render @users %>
</ul>
<%= will_paginate %>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true).paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[Model]

[models/user.rb]

class User < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はUser.を意味する
    if search
      where(['name LIKE ?', "%#{search}%"]) #検索とnameの部分一致を表示。User.は省略
    else
      all #全て表示。User.は省略
    end
  end
  
  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326111010p:plain

[検索後] f:id:mochikichi321:20170326111015p:plain

実装ステップ

手順を知りたい方向け。

STEP1

まず、現時点でのindexページ及びusers_controllerはこのようになっている。

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

#ここへ検索拡張機能追加予定

<ul class="users">
  <%= render @users %>
</ul>
<%= will_paginate %>

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true,).paginate(page: params[:page])
  end
(中略)
end

f:id:mochikichi321:20170326111218p:plain

STEP2

フォームを作成。パスはusers#indexの"users_path"を指定。 text_field_tagのシンボルは任意なので":search"とする。

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

submitボタンを押すとエラーが出る。 f:id:mochikichi321:20170326111415p:plain

STEP3

ブラウザからソースコードを確認すると、以下の通りとなっており、method=“post"となっていることがわかる。

<form action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input type="hidden" name="authenticity_token" value="ncc66/h8qa7NC/78Rxyk0UA0UdCNhOReJockKrID+IkNAjgA9gORJ+1KwJiPTT7kRS7zvELfwm1ZeH0gLmSa/g==" />
  <input type="text" name="search" id="search" />
  <input type="submit" name="commit" value="Search" data-disable-with="Search" />
</form>

STEP4

method=“get"へ指定。

[users/index.html.erb]

<%= form_tag(users_path, method: :get) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

STEP5

users_controllerのindexアクションにsearchを追加。

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    #条件分岐
    @users = if params[:search]
      #searchされた場合は、原文+.where('name LIKE ?', "%#{params[:search]}%")を実行
      User.where(activated: true).paginate(page: params[:page]).where('name LIKE ?', "%#{params[:search]}%")
    else
      #searchされていない場合は、原文そのまま
      User.where(activated: true).paginate(page: params[:page])
    end
  end
(中略)
end

これで検索機能は実装された。

STEP6

searchフォームに:searchを加える。

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

STEP7

URLに以下が含まれるが消す。

“commit=Search”

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

STEP8

リファクタリングし、モデルへ移行。

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true).paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[models/user.rb]

class User < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はUser.を意味する
    if search
      where(['name LIKE ?', "%#{search}%"]) #検索とnameの部分一致を表示。#User.は省略
    else
      all #全て表示。#User.は省略
    end
  end
  
  private
(中略)
end

STEP9

何の検索ができるのかをわかりやすくするために"user search"をform上部に追加

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

<!--  検索拡張機能  -->
<p>user search</p>
<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

2.userプロフィールページにマイクロポスト検索機能を実装する

最終的なソースコードと確認画面

[View]

[users/show.html.erb]

<% provide(:title, @user.name) %>
<div class="row">
(中略)
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>

      <!--検索拡張機能  -->
      <p>content serch</p>
      <%= form_tag user_path, :method => 'get' do %>
        <p>
          <%= text_field_tag :search, params[:search] %>
          <%= submit_tag "Search", :name => nil %>
        </p>
      <% end %>
      
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def show
    @user = User.find(params[:id])
    # 検索拡張機能として.search(params[:search])を追加    
    @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[Model]

[models/micropost.rb]

class Micropost < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はMicropost.を意味する
    if search
      where(['content LIKE ?', "%#{search}%"]) #検索とcontentの部分一致を表示。Micropost.は省略。
    else
      all #全て表示。Micropost.は省略。
    end
  end

  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326111930p:plain

[検索後] f:id:mochikichi321:20170326111936p:plain

実装ステップ

基本的には「1.indexページにユーザ検索機能を実装する」と同様のステップ。 [Model]のwhere([‘content LIKE ?’, “%#{search}%”])が"name"から"content"になっている点に注意。

3.Homeページにマイクロポスト検索機能を実装する

最終的なソースコードと確認画面

[View]

[static_pages/home.html.erb]

<% if logged_in? %>
  <%= render 'static_pages/user_logged_in' %>
<% else %>
  <%= render 'static_pages/user_not_logged_in' %>
<% end %>

[static_pages/_user_logged_in.html.erb]

<div class="row">
(中略)
  <div class="col-md-8">
    <h3>Micropost Feed</h3>

    <!--検索拡張機能  -->
    <p>content serch</p>
    <%= form_tag root_path, :method => 'get' do %>
      <p>
        <%= text_field_tag :search, params[:search] %>
        <%= submit_tag "Search", :name => nil %>
      </p>
    <% end %> 
    <%= render 'shared/feed' %>
  </div>
</div>

[Controller]

[static_pages_controller.rb]

class StaticPagesController < ApplicationController
  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      # 検索拡張機能として.search(params[:search])を追加 
      @feed_items = current_user.feed.paginate(page: params[:page]).search(params[:search])
    end
  end
(中略)
end

[Model]

「2.userプロフィールページにマイクロポスト検索機能を実装する」から変更なし。

[models/micropost.rb]

class Micropost < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はMicropost.を意味する
    if search
      where(['content LIKE ?', "%#{search}%"]) #検索とcontentの部分一致を表示。Micropost.は省略。
    else
      all #全て表示。Micropost.は省略。
    end
  end

  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326112226p:plain

[検索後] f:id:mochikichi321:20170326112233p:plain

実装ステップ

基本的には「1.indexページにユーザ検索機能を実装する」と同様のステップ。

4.リファクタリング

上記で検索機能を3つのviewページへ追加しました。 共通する部分が多いのでリファクタリングします。

[users/_search.html.erb]

<p><%= yield(:search_name) %></p>
<%= form_tag(yield(:path), method: :get) do %>
  <%= text_field_tag :search, params[:search] %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

[users/index.html.erb]

<% provide(:title, 'All users') %>
<% provide(:search_name, 'user search') %>
<% provide(:path, users_path) %>
<h1>All users</h1>

<%= will_paginate %>

<ul class="users">
  <%= render 'users/search' %>  <-- searchパーシャル呼出 -->
  <%= render @users %>
</ul>

<%= will_paginate %>

[users/show.html.erb]
<% provide(:title, @user.name) %>
<% provide(:search_name, 'content search') %>
<% provide(:path, user_path) %>
<div class="row">
(中略)
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>
      <%= render 'users/search' %>   <-- searchパーシャル呼出 -->
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>

[static_pages/_user_logged_in.html.erb]

<% provide(:search_name, 'content search') %>
<% provide(:path, root_path) %>
  <div class="row">
(中略)
    <div class="col-md-8">
      <h3>Micropost Feed</h3>
      <%= render 'users/search' %>  <-- searchパーシャル呼出 -->  
      <%= render 'shared/feed' %>
    </div>
  </div>

参考

Simple Search Form in Rails 5

プログラミング未経験から2か月でエンジニアへ転職したノウハウを知りたい方の相談も受け付けております。

関連記事

mochikichi.hatenablog.com

【第14章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ

Ruby on Rails Tutorial最新版の演習と解答です。

f:id:mochikichi321:20170206212242p:plain

目的

Ruby on Rails チュートリアル 5.0(第4版)を学習中です。

学習を進める中で演習問題の解答がなかった(*1,2)ので、自分なりにまとめていくこととしました。

アウトプットし、自分の理解を深めることを目的としています。 もし、記載内容に誤りがあった場合はコメントいただけると幸いです。

(*1)著者による有償版の解答はあるようです。正式な解答をご希望の方はこちらを参照ください。

Learn Web Development with Rails: Michael Hartl's Ruby on Rails Tutorial | Softcover.io

(*2)旧版に関する解答はありましたが、最新版 5.0(第4版)に関しては検索しても出てきませんでした。

プログラミングを学習し始めたきっかけについてはこちら

mochikichi.hatenablog.com

演習問題と解答

演習14.1.1

演習14.1.1.1

<問題> 図 14.7のid=1のユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。ヒント: 4.3.2で紹介したmap(&:method_name)のパターンを思い出してください。例えばuser.following.map(&:id)の場合、idの配列を返します。

<解答> [2,7,8,10]

演習14.1.1.2

<問題> 図 14.7を参考にして、id=2のユーザーに対してuser.followingを実行すると、結果はどのようになるでしょうか? また、同じユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。

<解答> user.following [user_id:1, name:Michael Hartl, email:mhartl@example.com] user.following.map(&:id) [1]

演習14.1.2

演習14.1.2.1

<問題> コンソールを開き、表 14.1のcreateメソッドを使ってActiveRelationshipを作ってみましょう。データベース上に2人以上のユーザーを用意し、最初のユーザーが2人目のユーザーをフォローしている状態を作ってみてください。

<解答>

>> user=User.first
  User Load (1.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: "2017-02-23 23:09:36", updated_at: "2017-02-23 23:09:36", password_digest: "$2a$10$JZHMQYjhJjqvbfW8QGWJ6.UEWjfuhbi1r8GL3/apYKv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$ozX2hLov2jSJ6FpCku7vHO5Ys9EUjY1ZAGzcKQcw938...", activated: true, activated_at: "2017-02-23 23:09:36", reset_digest: nil, reset_sent_at: nil>

>> other_user=User.second
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
=> #<User id: 2, name: "Jamey Windler MD", email: "example-1@railstutorial.org", created_at: "2017-02-23 23:09:37", updated_at: "2017-02-23 23:09:37", password_digest: "$2a$10$HbJ9qu3236xnHHulFCbmweSa3bThCR5XIG2MX2z8yry...", remember_digest: nil, admin: false, activation_digest: "$2a$10$fLt.yeQ3C2hABfXORioyPOHd0X.zNBGyFjIDuRAhqra...", activated: true, activated_at: "2017-02-23 23:09:37", reset_digest: nil, reset_sent_at: nil>

>> user.active_relationships.create(followed_id: other_user.id)                                                                             
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  SQL (0.5ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 1], ["followed_id", 2], ["created_at", 2017-02-24 12:29:46 UTC], ["updated_at", 2017-02-24 12:29:46 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2017-02-24 12:29:46", updated_at: "2017-02-24 12:29:46">

演習14.1.2.2

<問題> 先ほどの演習を終えたら、active_relationship.followedの値とactive_relationship.followerの値を確認し、それぞれの値が正しいことを確認してみましょう。

<解答> 演習14.1.2.1参照。

演習14.1.3

演習14.1.3.1

<問題> リスト 14.5のバリデーションをコメントアウトしても、テストが成功したままになっていることを確認してみましょう。(以前のRailsのバージョンでは、このバリデーションが必須でしたが、Rails 5から必須ではなくなりました。今回はフォロー機能の実装を優先しますが、この手のバリデーションが省略されている可能性があることを頭の片隅で覚えておくと良いでしょう。)

<解答> 動作確認のみなので省略。

演習14.1.4

演習14.1.4.1

<問題> コンソールを開き、リスト 14.9のコードを順々に実行してみましょう。

<解答>

>> michael=User.first
  User Load (1.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: "2017-02-23 23:09:36", updated_at: "2017-02-23 23:09:36", password_digest: "$2a$10$JZHMQYjhJjqvbfW8QGWJ6.UEWjfuhbi1r8GL3/apYKv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$ozX2hLov2jSJ6FpCku7vHO5Ys9EUjY1ZAGzcKQcw938...", activated: true, activated_at: "2017-02-23 23:09:36", reset_digest: nil, reset_sent_at: nil>

>> archer=User.second
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
=> #<User id: 2, name: "Jamey Windler MD", email: "example-1@railstutorial.org", created_at: "2017-02-23 23:09:37", updated_at: "2017-02-23 23:09:37", password_digest: "$2a$10$HbJ9qu3236xnHHulFCbmweSa3bThCR5XIG2MX2z8yry...", remember_digest: nil, admin: false, activation_digest: "$2a$10$fLt.yeQ3C2hABfXORioyPOHd0X.zNBGyFjIDuRAhqra...", activated: true, activated_at: "2017-02-23 23:09:37", reset_digest: nil, reset_sent_at: nil>

>> michael.following?(archer)
  User Exists (0.2ms)  SELECT  1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> false

>> michael.follow(archer)
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  SQL (0.5ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 1], ["followed_id", 2], ["created_at", 2017-02-24 12:58:52 UTC], ["updated_at", 2017-02-24 12:58:52 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2017-02-24 12:58:52", updated_at: "2017-02-24 12:58:52">

>> michael.following?(archer)
  User Exists (0.2ms)  SELECT  1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> true

>> michael.unfollow(archer)
  Relationship Load (0.3ms)  SELECT  "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ?  [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]]
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.4ms)  DELETE FROM "relationships" WHERE "relationships"."id" = ?  [["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2017-02-24 12:58:52", updated_at: "2017-02-24 12:58:52">

>> michael.following?(archer)
  User Exists (0.2ms)  SELECT  1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> false

演習14.1.4.2

<問題> 先ほどの演習の各コマンド実行時の結果を見返してみて、実際にはどんなSQLが出力されたのか確認してみましょう。

<解答>

 SQL (0.4ms)  DELETE FROM "relationships" WHERE "relationships"."id" = ?

演習14.1.5

演習14.1.5.1

<問題> コンソールを開き、何人かのユーザーが最初のユーザーをフォローしている状況を作ってみてください。最初のユーザーをuserとすると、user.followers.map(&:id)の値はどのようになっているでしょうか?

<解答>

>> 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: "2017-02-23 23:09:36", updated_at: "2017-02-23 23:09:36", password_digest: "$2a$10$JZHMQYjhJjqvbfW8QGWJ6.UEWjfuhbi1r8GL3/apYKv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$ozX2hLov2jSJ6FpCku7vHO5Ys9EUjY1ZAGzcKQcw938...", activated: true, activated_at: "2017-02-23 23:09:36", reset_digest: nil, reset_sent_at: nil>

>> user2=User.second
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
=> #<User id: 2, name: "Jamey Windler MD", email: "example-1@railstutorial.org", created_at: "2017-02-23 23:09:37", updated_at: "2017-02-23 23:09:37", password_digest: "$2a$10$HbJ9qu3236xnHHulFCbmweSa3bThCR5XIG2MX2z8yry...", remember_digest: nil, admin: false, activation_digest: "$2a$10$fLt.yeQ3C2hABfXORioyPOHd0X.zNBGyFjIDuRAhqra...", activated: true, activated_at: "2017-02-23 23:09:37", reset_digest: nil, reset_sent_at: nil>

>> user3=User.third
  User Load (0.1ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 2]]
=> #<User id: 3, name: "Micaela Crona", email: "example-2@railstutorial.org", created_at: "2017-02-23 23:09:37", updated_at: "2017-02-23 23:09:37", password_digest: "$2a$10$ng2IzsKTNkOxB5zteVxszuNRdr8YB56vvfLn5aCKNvd...", remember_digest: nil, admin: false, activation_digest: "$2a$10$FYKD7pHxhchKv662YYqIAuvQpuY1HRDHOsoHWtQrt1D...", activated: true, activated_at: "2017-02-23 23:09:37", reset_digest: nil, reset_sent_at: nil>

>> user2.active_relationships.create(followed_id: 1)                                                                                        
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  SQL (0.4ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 2], ["followed_id", 1], ["created_at", 2017-02-24 13:12:55 UTC], ["updated_at", 2017-02-24 13:12:55 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Relationship id: 1, follower_id: 2, followed_id: 1, created_at: "2017-02-24 13:12:55", updated_at: "2017-02-24 13:12:55">

>> user3.active_relationships.create(followed_id: 1)                                                                                        
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 3], ["followed_id", 1], ["created_at", 2017-02-24 13:13:08 UTC], ["updated_at", 2017-02-24 13:13:08 UTC]]
   (0.0ms)  RELEASE SAVEPOINT active_record_1
=> #<Relationship id: 2, follower_id: 3, followed_id: 1, created_at: "2017-02-24 13:13:08", updated_at: "2017-02-24 13:13:08">

>> user.followers.map(&:id)
  User Load (0.3ms)  SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> [2, 3]

演習14.1.5.2

<問題> 上の演習が終わったら、user.followers.countの実行結果が、先ほどフォローさせたユーザー数と一致していることを確認してみましょう。

<解答>

>> user.followers.count
   (0.2ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> 2

演習14.1.5.3

<問題> user.followers.countを実行した結果、出力されるSQL文はどのような内容になっているでしょうか? また、user.followers.to_a.countの実行結果と違っている箇所はありますか? ヒント: もしuserに100万人のフォロワーがいた場合、どのような違いがあるでしょうか? 考えてみてください。

<解答>

>> user.followers.count
   (0.2ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> 2

>> user.followers.to_a.count
=> 2

演習14.2.1

演習14.2.1.1

<問題> コンソールを開き、User.first.followers.countの結果がリスト 14.14で期待している結果と合致していることを確認してみましょう。

<解答> 動作確認のみなので省略。

演習14.2.1.2

<問題> 先ほどの演習と同様に、User.first.following.countの結果も合致していることを確認してみましょう。

<解答> 動作確認のみなので省略。

演習14.2.2

演習14.2.2.1

<問題> ブラウザから /users/2 にアクセスし、フォローボタンが表示されていることを確認してみましょう。同様に、/users/5 では [Unfollow] ボタンが表示されているはずです。さて、/users/1 にアクセスすると、どのような結果が表示されるでしょうか?

<解答> /users/1では、follow/unfollowボタンは表示されない。

演習14.2.2.2

<問題> ブラウザからHomeページとプロフィールページを表示してみて、統計情報が正しく表示されているか確認してみましょう。

<解答> 動作確認のみなので省略。

演習14.2.3

演習14.2.3.1

<問題> ブラウザから /users/1/followers と /users/1/following を開き、それぞれが適切に表示されていることを確認してみましょう。サイドバーにある画像は、リンクとしてうまく機能しているでしょうか?

<解答> 動作確認のみなので省略。

演習14.2.3.2

<問題> リスト 14.29のassert_selectに関連するコードをコメントアウトしてみて、テストが正しく red に変わることを確認してみましょう。

<解答> 動作確認のみなので省略。

演習14.2.4

演習14.2.4.1

<問題> ブラウザ上から /users/2 を開き、[Follow] と [Unfollow] を実行してみましょう。うまく機能しているでしょうか?

<解答> 動作確認のみなので省略。

演習14.2.4.2

<問題> 先ほどの演習を終えたら、Railsサーバーのログを見てみましょう。フォロー/フォロー解除が実行されると、それぞれどのテンプレートが描画されているでしょうか?

<解答> 両方とも、"users/show.html.erb"

演習14.2.5

演習14.2.5.1

<問題> ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。

<解答> 動作確認のみなので省略。

演習14.2.5.2

<問題> 先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。

<解答> フォロー :relationships/create.js.erb フォロー解除 :relationships/destroy.js.erb

演習14.2.6

演習14.2.6.1

<問題> リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?

<解答> createアクションの"format.html { redirect_to @user }“をコメントアウトした場合、 "should_follow_a_user_the_standard_way"がエラー。

createアクションの"format.html { redirect_to @user }“,"format.js"をコメントアウトした場合、 "should_follow_a_user_the_standard_way"と "should_follow_a_user_with_Ajax"がエラー。

destroyアクションの"format.html { redirect_to @user }“をコメントアウトした場合、 "should_unfollow_a_user_the_standard_way"がエラー。

destroyアクションの"format.html { redirect_to @user }“,"format.js"をコメントアウトした場合、 "should_unfollow_a_user_the_standard_way"と "should_unfollow_a_user_with_Ajax"がエラー。

演習14.2.6.2

<問題> リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。

<解答> “format.js"のみをコメントアウトしてもエラーは発生しない。

演習14.3.1

演習14.3.1.1

<問題> マイクロポストのidが正しく並んでいると仮定して (すなわち若いidの投稿ほど古くなる前提で)、図 14.22のデータセットでuser.feed.map(&:id)を実行すると、どのような結果が表示されるでしょうか? 考えてみてください。ヒント: 13.1.4で実装したdefault_scopeを思い出してください。

<解答> [10,9,7,5,4,2,1]

演習14.3.3

演習14.3.3.1

<問題> Homeページで表示される1ページ目のフィードに対して、統合テストを書いてみましょう。リスト 14.49はそのテンプレートです。

<解答>

[test/integration/following_test.rb]

require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    log_in_as(@user)
  end
  .
  .
  .
  test "feed on Home page" do
    get root_path
    @user.feed.paginate(page: 1).each do |micropost|
      assert_match CGI.escapeHTML(micropost.content), response.body
    end
  end
end

演習14.3.3.2

<問題> リスト 14.49のコードでは、期待されるHTMLをCGI.escapeHTMLメソッドでエスケープしています (このメソッドは11.2.3で扱ったCGI.escapeと同じ用途です)。このコードでは、なぜHTMLをエスケープさせる必要があったのでしょうか? 考えてみてください。ヒント: 試しにエスケープ処理を外して、得られるHTMLの内容を注意深く調べてください。マイクロポストの内容が何かおかしいはずです。また、ターミナルの検索機能 (Cmd-FもしくはCtrl-F) を使って「sorry」を探すと原因の究明に役立つはずです。

<解答> エスケープ処理を外すと、HTML中に"I’m sorry.“があるが、"I'm sorry."となる。

関連記事

【第1章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第2章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第3章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第4章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第5章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第6章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第7章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第8章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第9章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第10章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第11章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第12章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第13章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

Rails 検索機能拡張 (rails tutorial) - 新米パパの育児留学

プログラミング未経験から2か月でエンジニアへ転職したノウハウを知りたい方の相談も受け付けております。

転職祝い金50万円のIT・WEB業界特化型転職サービス"Nexstar"を実際に使ってみた

facebook_転職_アプリ_nexstar

Nexstarとは

Nexstarとは、 ITベンチャー企業である株式会社オープンキャリアが提供する、Facebookアプリを用いたIT・WEB業界に特化した転職支援サービスである。

 

株式会社オープンキャリア(open-carrer)(*1)は、2014年に設立されたITベンチャー企業に特化した人材紹介会社であり、東証マザーズ上場の株式会社アイモバイルから誕生した。株式会社アイモバイル(*2)は、インターネット広告システムの構築・配信を主力事業としており、ふるさと納税サイト「ふるなび」等を運営するIT業界において一定の地位と信頼を築いている会社である。

 

(*1)ITベンチャー企業に特化した人材紹介 | open-career

(*2)アイモバイル(i-mobile) 国内最大級のアドネットワーク広告 PC/スマートフォン広告(スマホ広告・アイコン広告・インターステーシャル広告・ウォール広告・レクタングル広告) android iphone広告

 

Nexstarの4つの特徴

Facebookアプリを用いたIT・WEB業界に特化した転職支援サービスであるが、類似サービスとしては「Switch.(*3)」がある。基本的には同様の機能を有していると考えてもらって問題ないが、「Switch.」にはない大きな特徴があるのでそこを含めて以下で説明する。

(*3)Facebookを使った新感覚スカウト型転職サイトSwitch.

1.転職祝い金、50万円!

facebook_転職_アプリ_nexstar_祝い金

ここが最大の特徴である。転職祝い金としてAmazonギフト券や現金数万円というサービスは多くあるが、「50万円(*4)」という金額には驚きだ。

転職が決まったら引越しや生活環境の変化等支出が多くなるのでこれは非常に嬉しい。

自分の行きたい企業がNexstarにあれば間違いなくNexstarから申し込んで臨時ボーナスをもらった方がお得だ。

 (*4)理論年収400万円以上の採用でお祝い金50万円、400万円未満の採用の場合はお祝い金30万円

2.簡単1クリック無料登録

facebook_転職_アプリ_nexstar_簡単登録

そして、登録が簡単だ。登録ボタンを押してFacebookとの連携を認証すればすぐにサービスを開始できる。もちろん、スカウトを受けるためにはプロフィールを充実していった方が好ましいが、まずは登録してみてどのような企業が掲載されていてどのような使い勝手かを「体験」したいのであれば「1クリック」で登録完了する。

 ※Facebookに勝手に投稿されるようなことはないので安心できる。

登録はこちら↓

3.カジュアルメッセージ

facebook_転職_アプリ_nexstar_カジュアルメッセージ

興味を持った企業に自分からチャット形式で気軽に質問できる。チャット機能は、メールのように「〇〇株式会社〇〇様 この度は...」といったかしこまった形式ではなく「いつでも気軽に聞きたいことだけ聞ける」ので導入しているサービスは増えてきており人気の問い合わせツールだ。

※「Switch.」にはチャット機能があるが、「マッチング成立後」となっている。「応募する」となるとかなり重い印象を受けた。

 

4.無料の転職サポート

facebook_転職_アプリ_nexstar_無料の転職サポート

IT・WEB業界経験を持った専門コンサルタントに「転職相談・キャリアの悩み・市場動向」などをチャットで無料相談できる。無料で気軽に何度でも相談できるのでありがたい。

 

実際に使ってみた

上記のような特徴を持っており、すごく興味深いサービスだったので実際に使ってみた。

 

まず1クリック登録をし、どのような企業からの求人があるのかを覗いてみた。

 

以下は一例だが、サイバーエージェントのようなもはやベンチャーではないような大きな企業から、スタートアップのSansanなど個人的に興味を惹かれる企業が並んでいた。(2017年2月現在) 

facebook_転職_アプリ_nexstar_企業一覧

プロフィール情報を充実させてみると約1週間で7社からスカウトが到来。

facebook_転職_アプリ_nexstar_スカウト

その中から、Nexstarの母体でもあるアイモバイルに詳しく聞いてみることに。

チャットでコンタクトを取ると数時間で返答があった。お気軽!

facebook_転職_アプリ_nexstar_チャットでコンタクト

続いてNexstarへ市場動向を問い合わせ。丁寧に返答してくれた。

facebook_転職_アプリ_nexstar_問い合わせ回答

まとめ

実際に使ってみた感想。

・登録は本当に1クリックでできて簡単。

・シンプルなユーザーインターフェースで使いやすい。

・自分の興味ある企業にチャットで気軽に質問できて使いやすい。

・求人企業の数はまだ多くはないが、面白いベンチャー企業が集まりつつある。今後の拡大に更に期待できる。

 

ワンクリックで簡単に登録できるのでまずは"お試し"で登録してみてはどうだろうか。

縁のある企業が見つかれば晴れてお祝い金50万円GET!!

登録はこちら↓

プログラミング未経験から2か月でエンジニアへ転職したノウハウを知りたい方の相談も受け付けております。