おぴよの気まぐれ日記

おぴよの気まぐれ日記

岡山やプログラミング、ファッションのこと、子育てや人生、生き方についての備忘録。

【Ruby on Rails】`rails db:reset`だと変更/追加したmigrationファイルが反映されない!

rails db:reset

テーブル削除 → schema.rbの情報を元に作り直す

rails db:migrate:reset

テーブル削除 → 作成 → db:migrateが実行される

つまり、migrationファイルを作成した後にそのファイルを修正した場合はdb:resetしても反映されない!

調べてみたら、まさにまさにの情報が。 ありがとうございます! http://easyramble.com/difference-bettween-rake-db-migrate-reset.html

なんだけど、気になって色々やってると新しくrails g migration AddColumnToHogeみたいに新しく作った場合でもdb:resetじゃ反映されないぞ。

って思ったのだけどschema.rbrails db:migrateした時に更新されるっぽいので新しくファイルを作った場合はdb:migrateしないとダメみたい。

すっきりした。

エンジニア=プログラマーは夢のある職業であることを痛感した1年だった

こんちにちは。opiyoです。

今日から新年初出社ですが、世の中的には今週まで休みの人も多いですかね。

僕も30歳目前。外の世界の変化が激しい中、大切な20代も残りわずか。

普通に過ごしているだけでは当たり前のように生きていくことすら厳しい、そんな時代。

もー若くないと感じた一年でもあったので振り返りと今年の目標を。

2017年は色々あったような無かったような、そんな1年でした。

非エンジニアからエンジニアを目指している人

にとっては参考になることも多いと思うので、是非ご覧下さい。

2017年振り返り

人生に詰む

働き方改革、副業容認というニュースが今年は多く見られたと思いますが、これって逆に

「会社に頼った生活は今後は出来ないよ」

って言われているのとイコールだと思います。

年末になると大企業の平均ボーナスは幾らでした。みたいなニュースが流れますが平均ボーナスは確か90万くらいだって報道されていました。 ですが、これで世の中って好景気じゃん!と思うのは間違いだと僕は思います。 そんな額を貰えるのは大企業問わず一部の人間だろうし、この参考値に入っているであろう東芝があんな事件があり職を失う人がいるってのが今の時代だと思います。

このニュース見たとき時代が違えば僕の実家もと思うと怖いです。

www.news-postseven.com

こんな時代だからこそ、企業側も副業を容認して万が一に備えてねってことだと思うのです。

誰もが会社に依存しない自分で稼ぐ力ってのが今後はますます必要な時代になってきていると思います。

だけどどうすりゃいいんだ。自分で稼ぐって言っても右も左も分からない。って状況な時に「りゅうけんさん」を知りました。

りゅうけんさんを知る

人生に絶望していた時に知ったのがりゅうけんさんでした。

りゅうけんさんはフリーランスのエンジニアをやっていて、サロンをやっていたりブログもやられていたり色々な方法で稼いでおられる、とんでもない方です。 エンジニアを目指している方は是非是非ご覧下さい。モヤモヤしていたものが一気に晴れますよ。マジで。

www.ryukke.com

このブログに出会い、サロンに入会したことでエンジニア=プログラマーの道を進むのは間違ってないことだったのだと強く思うようになりました。

先日もこんなつぶやきをしていましたが、エンジニアは間違いの無い選択肢なのです。

なので先ずは自分の現在地を確かめてみようと思いました。

第一転職活動

私の現在のスキルはざっくりこんな感じです。

  • IT業界7年目
  • 保守6年、開発1年
  • Javac#で半年開発経験あり(5年前なので忘れてる)
  • RubyRails独学(Ruby Silver取得)
    • 業務で携わっている製品がRails
  • 現在はプログラム以外の部分を担当(要件定義、テスト、保守)

利用したのはこのサイト

最初は今世の中にどんな会社があるのか、どんなスキルが求められているのかを知るぐらいのつもりで始めたのですが会社側から「話を聞いてみたい」ってのが結構届くのです。

この辺りは別の機会でも話出来ればと思いますが、理由は簡単で「Ruby/Rails」です。

サイトを見てもらえれば分かりますが、多くの会社でRuby/Railsを使われていることが分かります。

なので、私も調子乗っていくつか話を聞かせていただいたのですが結果は惨敗でした。

理由は簡単で「実力不足」です。

パッと見は約4年Railsに携わっているように見えるので、声は凄くかかるのですが僕は本当に基礎的なことしかコードが書けません。

これは僕の肌感覚ですがポテンシャルだけで通用するのは25,6歳までです。 20代後半になったら焦った方が良いと思います。何も実績無くヤル気だけで通用する世界では無くなります。

独学

実力不足を痛感した僕は何か見せれるもの、自分に自信が少しでもつくものと思い「Railsチュートリアル」を勉強材料として学びました。

どんなことを勉強し何を学んだのか見えるように学んだことをアウトプットしようと思いブログにまとめることにしました。

その時のブログが以下です。

opiyotan.hatenablog.com

opiyotan.hatenablog.com

全部で14章あるのですが、1章1章をまとめるような形でまとめていきました。これらはその中の2つです。


正直結構難しいですが、何とかやり切りました。

20代後半はのんびりしてられないことを痛感しましたので、もっかいチャレンジしてみることにしました。

第二転職活動

今度は転職のプロ!

エージェントをこれでもかというくらい使って活動してやろうと思い、色々な所に応募してみました。

  • アイムファクトリー株式会社

顧客常駐はもう嫌だ!社内SEへ転職するなら【社内SE転職ナビ】

  • 株式会社Branding_Engineer

フリーランスエンジニアの豊富な案件なら【midworks】

フリーランスWebクリエイター大募集!【ギークスジョブ】

IT・インターネット・ゲーム業界専門の転職コンシェルジュ【WORKPORT】

  • 株式会社PE─BANK

エンジニアのプロ契約なら【Pe-BANK】

  • レバレジーズ株式会社

IT・Web業界での転職なら【レバテックキャリア】

この中で私が特にオススメなので、「アイムファクトリー株式会社」さんと「 株式会社Branding_Engineer」さんです。

案件の数は勿論ですが、人生の悩み相談までも聞いてもらえるので自分の立ち位置や進む先がどうなのかという将来設計までもしてもらえます。 なので、転職やフリーランスになりたいけど自信が無いって人も是非一度相談してみるのをオススメします。

今後どうやって生きていけば良いのかの道筋をしっかり示してもらえますよ。

で、自分の活動はどうだったのかですが結論としては「実力不足」でした。

何がどうってのは改めてさせてもらえればですが、やはり「エンジニアとしての業務経験」ってのが凄く凄く大切です。

これは独学で幾ら勉強してもよっぽどの人じゃ無い限り難しいと思います。

会社の中に入ってコードを書くということは見た目通り作れるだけでは当然ダメでチームとしてコードを書くというスキルも求められます。 こういった背景もあるからこそ、最近話題のエンジニアスクールが重宝されているのかもしれません。

  • インターノウス株式会社

【未経験からプログラマ】完全無料であなたの就職をサポート

  • コードキャンプ株式会社

オンラインプログラミング研修のCodeCamp

  • 株式会社Dive into Code

未経験から稼げるエンジニアになれる(無料説明会有り)

こういった場所で学ぶことで一緒に学ぶ仲間も出来ますし、いつだって相談できる講師もいる。そして何より就職支援までしてくれるのですから人生やり直すには本当に最高な環境ですよね。

特にインターノウスさんは「完全無料」ですよ? 関東圏に住んでいるならば、学ばない理由が無いです。

私も聞いてみたいことがあり問い合わせをしてみたことがありますが、すごく親切に対応してもらえますし返信もめちゃくちゃ早いので一度相談してみると良いと思います。

開発部への移動

長くなりましたが、現在の僕は結果的に会社にだだをこねて「開発部」へ移動させてもらいました。

この1年で学んだことはただ一つ

  • 業務を通して開発の経験を積むこと

これに尽きます。

なので結果的に開発部に戻ることが出来たので、良かった。というのが僕の2017年でした。

2018年の目標

エンジニア=プログラマーになる

言葉の通りですが、先ずは「仕様書通りにコードを書く」ということを目標にやっていきたいと思います。 汚くても良いから、先ずは要求された通りに実装することがファーストステップだと思います。

前部署から持ち帰っている仕事もあり1日全てをコード書く時間に使えないので、いかに効率良く仕事を回しコードを書く時間を増やすかがポイントになりそうです。

副業で稼ぐ

やはり今の時代一つの柱で生きていくことは不可能だと思いますので、自分でサービスを作りたいなと思っています。 アイデアは幾つかあるので、途中で諦めずに公開するところまでやり切る。ことを目標に2018年はやっていけたらなと思っています。

これが出来れば、これに関することを何処かの勉強会で話出来ればと思いますので何かあれば宜しくお願い致します。

ということで来年はより一層技術的な部分で多くのアウトプットを残していき一回り成長できればと思います。

また、この発信が誰かの少しでも力になれればと思いますので今度とも宜しくお願い致します。

CentOS6で新しいRubyのバージョンが無い時どうすればいいの?

gitを使ってrbenvをインストールしている。

Rubyのバージョンを見てみると2.3.3までしかインストールされてない状況。

$ cd ~/.rbenv
$ git pull origin master
$ rbenv install --list
2.3.3

git pullすればいけるんじゃ無いかと思ったけど、ダメぽ。

で色々調べてみたらrbenv updateするプラグインがあるみたい。

$ mkdir -p "$(rbenv root)/plugins" # rbenv rootは僕の場合は「~/.rbenv」です。
$ git clone https://github.com/rkh/rbenv-update.git "$(rbenv root)/plugins/rbenv-update"

これでrbenv updateすればok

$ rbenv update
$ rbenv install --list
$ rbenv install 2.3.6

とここまで来て疑問。 ruby-buildplugins以下にあるのだけど、普通にgit pullすりゃー同じことだったような気がしてる。

ソース読んでみたけど、plugins/以下をグルグル回してgit pullしてるっぽいからやってることは同じっぽい。 https://github.com/rkh/rbenv-update/

最後に

$ rbenv global 2.3.6
$ gem install bundler

を忘れずにと。

Ruby on Rails チュートリアルで30歳までに人生を変える(番外編:シェア=リツイート機能の拡張)

こんにちは。opiyoです。

今回は、番外編:シェア=リツイート機能の拡張をやっていきます。

マイクロポストにリツイートアイコンを表示して、「シェア=リツイート」できるようにします。

ではでは、早速行ってみましょう。

railsチュートリアルシェア=リツイート機能の拡張でやること

仕様

【できたこと】

【できてないこと】

  • フォローしている人がリツイートしたポストがトップページにも表示される
  • 誰がリツイートしたのかポストに表示する

対象画面とイメージ

f:id:opiyotan:20170922135854p:plain

railsチュートリアルシェア=リツイート機能の拡張の完成版

【view】

# app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
  <% if micropost.retweet_user(current_user) %>
    <p><%= fa_icon("retweet green", text: "自分がリツイート") %></p>
  <% end %>
  <%= 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.
  </span>
  <span class="action">
    <span id="like">
      <%= render "microposts/hart", micropost: micropost %>
    </span>
    <% if micropost.retweet_user(current_user) %>
      <%= link_to fa_icon("retweet green", text: micropost.retweets.count), retwwets_destroy_path(micropost_id: micropost.id) %>
    <% else %>
      <%= link_to fa_icon("retweet", text: micropost.retweets.count), retwwets_create_path(micropost_id: micropost.id) %>
    <% end %>
    <% if current_user?(micropost.user) %>
      <%= link_to fa_icon("trash"), micropost, method: :delete, data: {confirm: "You sure?"}, micropost_id: micropost.id  %>
    <% end %>
  </span>
</li>

【controller】

# app/controllers/retwwets_controller.rb
class RetwwetsController < ApplicationController
  before_action :correct_user, only: [:edit, :update]

  def create
    micropost = Micropost.find(params[:micropost_id])
    Retweet.create(user_id: current_user.id, micropost_id: micropost.id)

    # 対象のマイクロポストのupdate_atを現在時刻にする
    Micropost.find(micropost.id).update_attribute(:updated_at, Time.now)

    redirect_to root_path
  end

  def destroy
    micropost = Micropost.find(params[:micropost_id])
    Retweet.find_by(user_id: current_user.id, micropost_id: micropost.id).destroy

    # 対象のマイクロポストのupdate_atを現在時刻にする
    Micropost.find(micropost.id).update_attribute(:updated_at, Time.now)

    redirect_to root_path
  end
end
# app/controllers/users_controller.rb
  def show
    @user = User.find(params[:id])
    @microposts = @user.myfeed.paginate(page: params[:page])
      .where('content LIKE ?', "%#{params[:search]}%")
      .reorder(updated_at: :DESC)
  end

【model】

# app/models/micropost.rb
  has_many :retweets, dependent: :destroy

  # シェア=リツイートしたかしてないか確認する
  def retweet_user(user_id)
    self.retweets.find_by(user_id: user_id)
  end
# app/models/retweet.rb
class Retweet < ApplicationRecord
  belongs_to :micropost, counter_cache: :retweets_count
  belongs_to :user
end
# app/models/user.rb
  # マイクロポストを取得する
   # 自分と自分がフォローしているユーザー
   # 自分がリツイート
   # フォローしているユーザーがリツイート(未実装)
  def feed
    following_ids = "SELECT followed_id FROM relationships
      WHERE follower_id = :user_id"
    retweet_ids = "SELECT micropost_id FROM retweets
      WHERE user_id = :user_id"

    Micropost.where("id IN (#{retweet_ids}) OR user_id IN (#{following_ids})
      OR user_id = :user_id", user_id: id)
  end

  # 自分が投稿、リツイートしたマイクロポストを取得する
  def myfeed
    retweet_ids = "SELECT micropost_id FROM retweets
      WHERE user_id = :user_id"
    Micropost.where("id IN (#{retweet_ids}) OR user_id = :user_id", user_id: self.id).order(:updated_at)
  end

【その他】

# config/routes.rb
get 'retwwets/create'
get 'retwwets/destroy'
# db/migrate/20170912225854_create_retweets.rb
class CreateRetweets < ActiveRecord::Migration[5.0]
  def change
    create_table :retweets do |t|
      t.integer :user_id
      t.integer :micropost_id

      t.timestamps
    end
  end
end
# db/migrate/20170919223052_add_retweets_count_to_microposts.rb
class AddRetweetsCountToMicroposts < ActiveRecord::Migration[5.0]
  def change
    add_column :microposts, :retweets_count, :integer
  end
end

railsチュートリアルシェア=リツイート機能の拡張で学んだこと

orderを指定してもソート順が変化しない?

自分のタイムラインに表示するポストを更新日順の降順=新しい投稿を上にするをしたくて、最初はこう書いていた。

  def show
    @user = User.find(params[:id])
    @microposts = @user.myfeed.paginate(page: params[:page])
      .where('content LIKE ?', "%#{params[:search]}%")
      .order(updated_at: :DESC)
  end

だけど、なんだか上手くいかないなーと思っていたら犯人を見つけた!

# app/models/micropost.rb
default_scope -> { order(created_at: :desc) }

これが定義されていることで、micropost へのデータ取得はこれが絶対に設定されてしまう。

だから違う設定を反映したい場合は再定義してやる必要があるのだが、解決方法はすごーくシンプル。orderreorderにすればOK

  def show
    @user = User.find(params[:id])
    @microposts = @user.myfeed.paginate(page: params[:page])
      .where('content LIKE ?', "%#{params[:search]}%")
      .reorder(updated_at: :DESC)
  end

リツイートしたポストを表示する

やり方あってるか分からないけど、リツイートしたポストはリツイートテーブルに登録されるのでログインユーザーで引っ掛けて取得。

それをin句を使って取得って感じでやった。

  def myfeed
    retweet_ids = "SELECT micropost_id FROM retweets
      WHERE user_id = :user_id"
    Micropost.where("id IN (#{retweet_ids}) OR user_id = :user_id", user_id: self.id).order(:updated_at)
  end

最初where句の書き方を反対に書いていたのだけど、それだと上手くいかなかったんだが良く分かってない。

これは引き続き調べよう。

トップページのタイムラインにフォローしているユーザーがリツイートしたポストを表示する

やり方としてはこんな感じなのかなぁ

  • ログインユーザーがフォローしているユーザーを取得する
  • ↑を使ってリツイートテーブルからマイクロポストのIDを取得する
  • ↑を使ってマイクロポストテーブルからデータを取得する

自分とフォローしているユーザー、自分とフォローしているユーザーがリツイートしているデータをin句で書けば良いのかなと思ったのだが上手くいかないぁ

- 一応解決したので追記 -

それぞれSQL走ってしまうから、良くないことは分かっているのだが一様解決したのでメモです。

  def feed
    Micropost.where(id:
      (self.microposts.pluck(:id) +
      Retweet.where(user_id: self.id).pluck(:micropost_id) +
      Micropost.where(user_id: self.followers.pluck(:id)).pluck(:id) +
      Retweet.where(user_id: self.followers.pluck(:id)).pluck(:id)
      )
    )
  end

やりたいことを一個づつSQLで取得して、最後にmidropostidに配列で渡して取得する。

  • 自分が投稿したポスト
  • 自分がリツイートしたポスト
  • 自分がフォローしているユーザーが投稿したポスト
  • 自分がフォローしているユーザーがリツイートしたポスト

railsチュートリアルシェア=リツイート機能の拡張のまとめ

リツイートアイコンの表示やリツイートした数の表示は、いいね機能とほとんど同じなので比較的素直に実装できた。

が、やはりSQL力が全然無い。

複数テーブルを参照しながらデータを取得しないといけないとなると途端に分からなくなる。

以前お世話になった現場の先輩が「プログラムの仕事は7割くらいSQL力」のような事を言っていた事を思い出した。

少し大げさな気がするが、SQL力が大事なことは改めて実感した。

プログラマーは本当に次から次へと覚えることが多いから大変だけど、一つ一つしっかり学んで「こういう時はこーする」みたいな感覚がしっかり掴むことが大事なのかなとも思う。

だからとにかく手を止めずに頑張ろう。

Ruby on Rails チュートリアルで30歳までに人生を変える(番外編:いいね機能の拡張)

こんにちは。opiyoです。

今回は、番外編:いいね機能の拡張をやっていきます。

マイクロポストにハートアイコンを表示して、「いいね」できるようにします。

ではでは、早速行ってみましょう。

railsチュートリアルのいいね機能の拡張でやること

仕様

「いいね」機能を実装するに当たって、ざっくりだけど仕様を明確にしてみようと思う。

  • 一つ一つのマイクロポストに「いいね」することができる
  • 「いいね」は一つのマイクロポストに対して一人一回まで
  • 「いいね」表示場所は、投稿日下にアイコンを使って表示する(いいね:ハート、削除:ゴミ箱)
  • 「いいね」されたら赤いハート、取り消されたら白いハートにする
  • 「いいね」された数を表示する(実装中)
  • 「いいね」ボタンはajaxで処理する(困ってる)

こんな感じだろうか。一人一回までって制御が出来れば色々サンプルはありそうだし出来そうかな?

対象画面とイメージ

対象になる画面は2つ

【トップページ:app/views/static_pages/home.html.erb】 f:id:opiyotan:20170908180203p:plain

【ユーザー詳細ページ:app/views/users/show.html.erb】 f:id:opiyotan:20170908180300p:plain

railsチュートリアルのいいね機能の拡張の完成版

全部乗っけると数が多くなるので、主要な部分を載せます。

【view】

# app/views/likes/create.js.erb
$(".micropost<%= @micropost.id %> i").addClass("fa-heart");
$(".micropost<%= @micropost.id %> i").removeClass("fa-heart-o");
// $(".micropost<%= @micropost.id %>").text("<%= @micropost.likes_count %>"); # これやるとアイコンが消えちゃう
# app/views/likes/destroy.js.erb
$(".micropost<%= @micropost.id %> i").addClass("fa-heart-o");
$(".micropost<%= @micropost.id %> i").removeClass("fa-heart");
// $(".micropost<%= @micropost.id %>").text("<%= @micropost.likes_count %>"); # これやるとアイコンが消えちゃう
# app/views/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.
  </span>
  <span class="action">
    <% if micropost.like_user(current_user.id) %> # ----- ここから -----
      <%= link_to fa_icon("heart", text: micropost.likes_count), likes_destroy_path(micropost_id: micropost.id), remote: true, class: "micropost#{micropost.id}" %>
    <% else %>
      <%= link_to fa_icon("heart-o", text: micropost.likes_count), likes_create_path(micropost_id: micropost.id), remote: true, class: "micropost#{micropost.id}"  %>
    <% end %>
    <% if current_user?(micropost.user) %>
      <%= link_to fa_icon("trash"), micropost, method: :delete, data: {confirm: "You sure?"}, micropost_id: micropost.id  %>
    <% end %>  # ----- ここまで -----
  </span>
</li>

【controller】

# app/controllers/likes_controller.rb
class LikesController < ApplicationController
  def create
    micropost = Micropost.find(params[:micropost_id])
    Like.create(user_id: current_user.id, micropost_id: micropost.id)
    @micropost = Micropost.find(params[:micropost_id])

    respond_to do |format|
      format.html { redirect_to root_path}
      format.js
    end
  end

  def destroy
    micropost = Micropost.find(params[:micropost_id])
    Like.find_by(user_id: current_user.id, micropost_id: micropost.id).destroy
    @micropost = Micropost.find(params[:micropost_id])

    respond_to do |format|
      format.html { redirect_to root_path}
      format.js
    end
  end
end

【model】

# app/models/like.rb
class Like < ApplicationRecord
  belongs_to :micropost, counter_cache: :likes_count
  belongs_to :user
end
# app/models/micropost.rb
class Micropost < ApplicationRecord
  has_many :likes, dependent: :destroy
end

【routes】

# config/routes.rb
  get 'likes/create'
  get 'likes/destroy'

【migration】

class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.integer :user_id
      t.integer :micropost_id

      t.timestamps
    end
  end
end
class AddIndexToLikesUserIdAndMicropostId < ActiveRecord::Migration[5.0]
  def change
    add_index :likes, [:user_id, :micropost_id], unique: true
  end
end
class AddlikesCountToMicroposts < ActiveRecord::Migration[5.0]
  def change
    add_column :microposts, :likes_count, :integer
  end
end

githubにpushしています。

色々と試行錯誤している跡がコミット追うと分かりますね。

railsチュートリアルのいいね機能の拡張で学んだこと

いいねの数が変わらない

ハートのアイコンを押したらいいねの数がカウントアップするって処理をしたいのだけど、数が変わらない。

DB見ると間違いなく登録されている。何が問題なのかなーと思っていたら...馬鹿やろうだった。

# app/controllers/likes_controller.rb
  def create
    micropost = Micropost.find(params[:micropost_id])
    Like.create(user_id: micropost.user_id, micropost_id: micropost.id)
    @micropost = Micropost.find(params[:micropost_id]) # これしてなかった

    respond_to do |format|
      format.html { redirect_to root_path}
      format.js
    end
  end

createした後にデータを再取得する処理をしてなかったので、viewに返しているデータはcreateする前のオブジェクト。

そりゃー変わらないわ

railsチュートリアルのいいね機能の拡張で分からないこと

いいねボタンの連続で押せない

今の段階だとajaxでアイコンといいねの数だけ切り替えてしまっている。 これだと切り替えたあと、link_toのパスとかが変わらないからおかしな状況になる。

だからきっと、その部分だけを別ファイルにしてrender hogeみたいにしてやればいいのかなって思ってる。

いいねの数を書き換えるとアイコンが消える!

やりたいことは、いいねの数を更新させたいだけなのだけど、単純に.text("hoge")ってやると子階層にあるタグが消えちゃう。

<div id="parent">
    親要素
  <p>子要素</p>
</div>
<button id="button">変更する</button>

$("#button").on("click", function(){
  $("#parent").text("変更後の文章");
});

<div id="parent">
    変更後の文章
</div>

こんな状況の時、pタグは消えます!

これはどうすればいいじゃろうか。多分根本的には上で書いたようにviewrenderするってのが正しいアプローチなんだとは思う。

railsチュートリアルのいいね機能の拡張のまとめ

まだ完成してないのだが、どうにかこうにか形になってきたので一先ずまとめてみました。

実装時間が長くなってしまったので当時ハマったことが少し思い出せない所もあり、もったい無いなと感じております。

解決できた時は嬉しくて先へ先へ進んじゃいがちだけど、一度立ち止まって何がダメだったのかをしっかり考える→まとめてみるってのは今後やっていこうと思いました。きっとそこが一番成長できる所だと思うので次回出会った時に「あーこの前のあれねって」思えるように。

まだまだ解決できてない部分もあるし、Likeモデルのcounter_cacheの機能もいまいち良く分かって無いので引き続き頑張ろうと思います。

railsチュートリアルのいいね機能の拡張の追記

未解決問題のajaxでの処理を追記

上で書いた2つの問題点を解決することに成功しましたので、追記でまとめます!

問題点

上に書いてある通りですが、大きく二つ

  • クラスの付けかけだけしてるので、link_toのパスが変わらない
  • いいねの数が消える

これに対するアプローチは、やはり

更新したい場所のみを再度renderする

うん。アプローチはやはり間違ってなかった!

解決したソース

とりあえずソースを貼ります!

【「いいね」前】 f:id:opiyotan:20170912074005p:plain

【「いいね」後】 f:id:opiyotan:20170912074034p:plain

# app/views/microposts/_micropost.html.erb
  <span class="action">
    <span id="like"> # ajaxで処理する時に「どこを差し替える」が必要なので、それを追加
      <%= render "microposts/hart", micropost: micropost %> # この部分を`_hart`としてテンプレート化する!
    </span>
    <% if current_user?(micropost.user) %>
      <%= link_to fa_icon("trash"), micropost, method: :delete, data: {confirm: "You sure?"}, micropost_id: micropost.id  %>
    <% end %>
  </span>
# app/views/microposts/_hart.html.erb
<% if micropost.like_user(current_user.id) %>
  <%= link_to fa_icon("heart", text: micropost.likes_count), likes_destroy_path(micropost_id: micropost.id), remote: true, class: "micropost#{micropost.id}" %>
<% else %>
  <%= link_to fa_icon("heart-o", text: micropost.likes_count), likes_create_path(micropost_id: micropost.id), remote: true, class: "micropost#{micropost.id}"  %>
<% end %>
# app/views/likes/create.js.erb
$("#like").html("<%= escape_javascript(render 'microposts/hart', micropost: @micropost) %>");
console.log("ajaxで処理")
# app/views/likes/destroy.js.erb
$("#like").html("<%= escape_javascript(render 'microposts/hart', micropost: @micropost) %>");
console.log("ajaxで処理")

解決のポイント

アプローチの仕方は間違ってなかった。ようはhartの部分だけ再度描画させたかったので、その部分を外出ししてそこだけ描画させる。

描画のやり方がjsのファイルで書いてあるやつだ。

likeクラスを持ったspanタグの中身をhtmlメソッドで全て置き換えているイメージ。

<%= escape_javascript(render 'microposts/hart', micropost: @micropost) %>の部分は極端に言うと展開されたhtmlになるようなイメージ。

だからこんな感じなのかな。

$("#like").html(<a class="micropost301" data-remote="true" href="/likes/create?micropost_id=301">
  <i class="fa fa-heart-o"></i> 0
</a>)


やっていく中でハマったポイントはmicropostインスタンスの渡し方。

最初jsのファイルにはmicropost: @micropostを書いて無くてずっとエラーになっていた。

Completed 500 Internal Server Error in 181ms (ActiveRecord: 2.7ms)

ActionView::Template::Error (undefined local variable or method `micropost' for #<#<Class:0x007fe0a1a1a368>:0x007fe0a6dc1110>
Did you mean?  @micropost):

micropostがねーよってことですね。だからコントローラーで取得したインスタンスを渡してあげて解決!

railsチュートリアルのいいね機能の拡張の再まとめ

ajaxの処理にめちゃくちゃハマって、すげー時間を使ってしまった。

最終的には教えてもらう形で解決出来たのだが、本当に感謝感謝感謝ですね。

よく未経験エンジニアからエンジニアへっていう記事がありますが、そこに絶対書かれているのが

メンターを見るけること

これは絶対そー思います。僕が3日くらい悩んでいたものが30分で解決出来てしまうのですから間違い無いです。

いつでも相談出来るちゃうものや無料でプログラミグを教えてくれて転職支援までしてくれるサービスなど本当にいっぱいあるので一度覗いてみてください。

オンラインプログラミング研修のCodeCamp

プログラミングのオンラインスクールCodeCamp

【未経験からプログラマ】完全無料であなたの就職をサポート


ちなみに、今回ajax処理を頑張って実装しましたが、別に無くても良いんじゃ無い?という話もありました。

普通にhtmlで返しても全く動きは同じだし、このぐらいのレベルならば正直あまりメリットは無い。だからスピードが遅くてダメだーってなってから対策するってアプローチの方で問題無いって。

一つのことにこだわることも大事だけど、別の事もやりたいとか全体が終わってないとかなるぐらいならば思い切って後回しってのは良いアプローチなのかもしれないですね。

ってな感じでした。

Ruby on Rails チュートリアルで30歳までに人生を変える(番外編:検索機能の拡張)

こんにちは。opiyoです。

今回は、番外編:検索機能の拡張をやっていきます。

ユーザーとマイクロポストをあいまい検索できるような機能を各画面に追加します。

ではでは、早速行ってみましょう。

railsチュートリアル検索機能の拡張で学んだこと

ユーザー一覧に名前をあいまい検索できる

f:id:opiyotan:20170905090515p:plain

# app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>

<%= will_paginate %>

<%= form_tag users_path, method: :get do %>
  <p>
    <%= text_field_tag :search %>
    <%= submit_tag "検索" %>
  </p>
<% end %>

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

<%= will_paginate %>
# app/controllers/users_controller.rb
  def index
    @user_form = User.new

    @users = User.paginate(page: params[:page]).search(params[:search])
  end
# app/models/user.rb
  def self.search(search)
    if search
      where('name LIKE ?', "%#{sanitize_sql_like(search)}%") # Likeインジェクション対策をしてみた
    else
      all
    end
  end

ユーザー詳細のマイクロポストのコンテンツをあいまい検索できる

【検索前】

f:id:opiyotan:20170905090343p:plain

【検索後】

f:id:opiyotan:20170905090442p:plain

# app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
    </section>
  </aside>
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>
    <% if @microposts.any? %>
      <h3>MicroPosts (<%= @microposts.count %>)</h3> # 検索結果のマイクロポストの数を表示させる
      <%= form_tag user_path, method: :get do %> # ----- ここから追加した検索フォーム -----
        <p>
          <%= text_field_tag :search %>
          <%= submit_tag "検索" %>
        </p>
      <% end %> # ----- ここまで -----
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>
# app/controllers/users_controller.rb
  def show
    @user = User.find(params[:id])

    @microposts = @user.microposts.paginate(page: params[:page]).where('content LIKE ?', "%#{params[:search]}%") # 何も入力せず検索しても「""」で渡ってくるので、エラーにはならない
    # @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search]) # こう書いてモデル側でSQL組み立てたいのだが...
  end

トップページのマイクロポストのコンテンツをあいまい検索できる

f:id:opiyotan:20170905085817p:plain

# app/views/static_pages/home.html.erb
<% if logged_in? %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <%= render 'shared/user_info' %>
      </section>
      <section class="stats">
        <%= render 'shared/stats' %>
      </section>
      <section class="micropost_form">
        <%= render 'shared/micropost_form' %>
      </section>
    </aside>
    <div class="col-md-8">
      <h3>Micropost Feed</h3>
      <%= form_tag root_path, method: :get do %> # ----- ここからformの追加 -----
        <p>
          <%= text_field_tag :search %>
          <%= submit_tag "検索" %>
        </p>
      <% end %> # ----- ここまで -----
      <%= render 'shared/feed' %>
    </div>
  </div>
<% else %>
  <% provide(:title, "Home") %>
  <div class="center jumbotron">
    <h1>Welcome to the Sample App</h1>

    <h2>
      This is the home page for the
      <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
      sample application.
    </h2>

    <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
  </div>

  <%= link_to image_tag("rails.png", alt: "Rails logo"), "https://rubyonrails.org/" %>
<% end %>
# app/controllers/static_pages_controller.rb
  def home
    if logged_in?
      @micropost = current_user.microposts.build if logged_in?
      @feed_items = current_user.feed.paginate(page: params[:page]).where('content LIKE ?', "%#{params[:search]}%") # 何も入力せず検索しても「""」で渡ってくるので、エラーにはならない
    end
  end

railsチュートリアル検索機能の拡張で学んだこと

form_forは難しい

# index.html.erb
<%= form_for @user_form, url: users_path, method: :get do |f| %>
  <%= f.text_field :name %>
  <%= f.submit "Serach", class: "btn btn-primary" %>
<% end %>

オブジェクトを指定するときはコントローラー側で必ずnewすべし。検索結果があるから大丈夫って思ってたんだけど何故かエラーになる。 だからとりあえず、form_forで使うオブジェクトは検索結果を格納したオブジェクトとは別にnewしたオブジェクトを作って指定した。

あとは、f.text_field :nameで渡すシンボルの部分がform_forで使ったモデルにあるカラム名と同じじゃないとエラーになる。

paginateの後にwhereしてもいける!

@user.microposts.where("content LIKE ?", "%#{params[:search]}%").paginate(page: params[:page])

@user.microposts.paginate(page: params[:page]).where("content LIKE ?", "%#{params[:search]}%") # whereを後ろに持ってこれる。

これ見てもらった方が分かりやすいと思うのだけど、検索結果にはpaginateメソッドを使ってページネーションができるようにしてます。 だから、paginateメソッドの前にしかwhereできないと思ってたのだけどそんなことなかった。

あいまい検索

where("検索したい項目 LIKE ?", "%#{sanitize_sql_like(hogehoge)}%")

基本的にはwhereの第一引数に文字列を渡して、第二引数に?に入る値を渡してやればOK

SQL文の組み立てはモデルで

コントローラーでやる場合

# users_controller.rb
  def index
    @user_form = User.new

    @users = User.where("name like ?", "%#{params[:user][:name]}%").paginate(page: params[:page]) # かっこ悪い。
  end

モデルでやる場合

# users_controller.rb
  def index
    @user_form = User.new
    @users = User.paginate(page: params[:page]).search(params[:search]) # Userモデルの`search`メソッドを読んでUserモデル側で処理する
  end
# user.rb
  def self.search(search)
    if search
      where('name LIKE ?', "%#{search}%")
    else
      all
    end
  end

最初コントローラーでやってたんだけど、Railsチュートリアルにある例を見るとSQL文はモデルで組み立てる。

ユーザーモデルの方は何も問題なかったのだが、マイクロポストモデルの方では戻り値がマイクロポストじゃないから上手くモデル側で処理できなかった。 こういう場合は

# ユーザーの検索処理の場合
(byebug) User.paginate(page: params[:page]).class
User::ActiveRecord_Relation

(byebug) User.paginate(page: params[:page]).search("User") # Userモデルに定義した`search`メソッドが呼べる。
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE (name LIKE '%User%') LIMIT ? OFFSET ?  [["LIMIT", 30], ["OFFSET", 0]]
#<ActiveRecord::Relation [#<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2017-08-31 03:24:43", updated_at: "2017-08-31 04:05:36", password_digest: "$2a$10$c6SeUXq5hRG9FLJqNIM3EOUBiRuv/CXa3C3u6vEgRky...", remember_digest: "$2a$10$FPWHJckDrGrMyR7wTT.RjeybzbZjmR2uHBVtPPoYwGm...", admin: true, activation_digest: "$2a$10$Ww6ud8vjul5IqocU7vvQTecZYH4dqaBS0MuPAvQNDcQ...", activated: true, activated_at: "2017-08-31 03:24:43", reset_digest: nil, reset_sent_at: nil>]>

# マイクロポストの検索処理の場合
(byebug) @user.microposts.paginate(page: params[:page]).search(params[:search]) # そんなメソッドねーよってなっちゃう
*** NoMethodError Exception: undefined method `search' for #<Micropost::ActiveRecord_AssociationRelation:0x007fb506959698>
Did you mean?  each

(byebug) @user.microposts.paginate(page: params[:page]).class
Micropost::ActiveRecord_AssociationRelation

うーん。普通にMicropostモデルのsearchメソッドが呼べそうだけど、何がダメなんだろうか。

railsチュートリアル検索機能の拡張のまとめ

当たり前だけど新しい機能を実装していくと全て考えて試行錯誤しながらやらないといけない。

Railsチュートリアルをやっていると訳分からずでも同じようにコピーすれば、とりあえず動く。だけどそうはいかない。エラーしか出てこない。

だけどこうやって頭使ってやっていくことが一番成長するのだろうと思う。

まだまだ全然思い通りにならない。今回の場合はお手本もあり何とか動くところまで持っていけた。

だけどきっと少しずつ出来るようにはなっているはずだから、手を止めずに前向いて頑張っていこう!

railsチュートリアル検索機能の拡張の追記

Nfm4yxnW8さんご指摘いただきました。

入門者に無粋な指摘で申し訳ないけどLIKE injectionを実行できちゃうよ。ここでは大きな問題にならないけど、他の場所では致命傷になるかも。 http://euglena1215.hatenablog.jp/entry/2016/09/22/171850 https://githubengineering.com/like-injection/

僕が書いたコードだと、「Likeインジェクション」っていう脆弱性がある。ユーザーが入力された文字をそのままSQLの条件にしてしまっているからだ。

教えていただいたサイトと同じようにやってみる

# params[:search]に「');--」が入ってくると...
(byebug) params[:search]
"');--"
(byebug) @user.microposts.paginate(page: params[:page]).where('content LIKE ?', "%#{params[:search]}%").count
   (0.2ms)  SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? AND (content LIKE '%'');--%')  [["user_id", 1]]
0
# params[:search]に「’ AND password LIKE ‘password’);—」が入ってくると...
(byebug) params[:search]
"’ AND password LIKE ‘password’);—"
(byebug) @user.microposts.paginate(page: params[:page]).where('content LIKE ?', "%#{params[:search]}%").count
   (0.3ms)  SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? AND (content LIKE '%’ AND password LIKE ‘password’);—%')  [["user_id", 1]]
0

ごめんなさい。SQL力が低すぎてどうやったらダメなのか分からなかったから、例題通りやってみたのだけど上手くいかなかった。改めて勉強します。

とはいえ、何か上手くやれば攻撃されちゃうってことなので、こういった記号はエスケープされるように処理する。

# あー多分コントローラーじゃ使えないんだなー後で直す
@user.microposts.paginate(page: params[:page]).where('content LIKE ?', "%#{sanitize_sql_like(params[:search])}%")
NoMethodError (undefined method `sanitize_sql_like' for #<UsersController:0x007ffcddb73ce8>):
# userモデル
  def self.search(search)
    if search
      where('name LIKE ?', "%#{sanitize_sql_like(search)}%")
    else
      all
    end
  end

# コンソール
(byebug) params[:search]
"');--"
(byebug) User.paginate(page: params[:page]).search(params[:search])
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE (name LIKE '%'');--%') LIMIT ? OFFSET ?  [["LIMIT", 30], ["OFFSET", 0]]
#<ActiveRecord::Relation []>

うーん。エラーにはならなかったけどエスケープされているようには見えない。 うーん。よくわからないな。

とりあえずタイムアップ。また調べてみよう。

プログラミング初心者でも出来た!ビットコイン自動売買システムをRubyで作る

こんにちは。opiyoです。

今日は私が入会している人生逃げ切りサロンのメンバーである迫 佑樹さんが

Rubyで作る! ビットコイン自動売買システム

の作り方を解説した動画について紹介させてもらいます。


この動画の最大の魅力はプログラミング経験が全くない人でもPCさえあれば誰でも作れてしまうところです!

私は、この動画で学んだことをフル活用して自分でも簡単なRailsアプリを作って見ました。

これについても最後に簡単に紹介できればと思います。


皆さん。プログラミングを学べば誰でも金持ちになれるそうですよ?

そのきっかけを迫 佑樹さんから学ばせてもらいましょう。

迫 佑樹(さこ ゆうき)さんって何者?

f:id:opiyotan:20170903020123p:plain

先ずは簡単ではありますが迫さんの紹介です。

  • 現役の大学生
  • Web、iPhoneアプリのフリーランスエンジニア
  • プログラミングスクールの現役講師
  • 月間12万PVのブログ「ロボット・IT雑食日記」を運営(はてなブログで良くホットエントリーしてますよね)
  • ブログの中の大人気記事「暗記しない数学」が書籍化

とんでもなくスゲー人ってことが伝わると思います。

今回紹介する動画は、現役のプログラミングスクール講師である迫さんが作っているってのが僕はポイントだと思っていて本当に分かりやすいです。

ブログの中にもプログラミング勉強法についての記事があったり、色々な言語の入門記事があったりと見ているだけでプログラミング力が上がります。

動画の内容

では、本題。

このRubyで作る! ビットコイン自動売買システムの動画で何が学べるのか。大きくは2つです。

1. Rubyの基礎

正直これだけでもめちゃくちゃ勉強になりますし、くどいですがとんでもなく分かりやすい。さすが先生です。


例えば、「配列」

話の流れとしては、各教科のテストの点数の平均を出したい。 その場合は以下のように解くことが出来ます。

japanese = 80
math = 60
science = 30
history = 60
english = 70

(japanese + math + science + history + english) / 5

だけどこれが100人分ってなると、この組み合わせが100個準備しないといけない → これは大変。じゃーどうするのか。

見ないな形で、何故この仕組み/工夫が必要なのかが順を追って説明してくれるのでイメージしやすいのです。(あー俺の説明が逆に分かりづらくしてしまっている気がしますが、是非動画を。動画を見てください。)


その他の内容としては、こんな感じです。

  • 条件分岐(if …)
  • 繰り返し文(while …)
  • 変数(hoge = 80)
  • 配列(score = [80,30,60,50,20])
  • ハッシュ(score = “japanese” => 80, “math” => 60 …)
  • メソッド(def hoge …. end)

この基礎の部分が理解できれば色々出来ることが一気に広がると思います。その基礎固めに、この動画完璧な内容だと僕は感じました。

2. bitFlyerAPIの基礎

bitFlyerってのは今流行りの仮想通貨ビットコインの取引所になります。

このビットコイン取引所とのやり取りをAPIと呼ばれる仕組みを使ってプログラミングを学んでいきます。

最後までやり切ると、自動でビットコインの購入/売却が可能になっちゃいます。


僕は全然こういう知識が無いのですが、きっとここまでのことが出来てしまうと更に応用を効かせて本当に自動で何もせずとも稼ぐ人が現るのでは無いでしょうか。

動画を見て作ったサービスの紹介

お見せする程のものではありませんが、今回動画で学んだ経験を生かして現在の各仮想通貨の値段が幾らなのかを表示するWebアプリをRuby on Railsを使って作成してみました。

実際のソースコードとサイトURLを以下になります。

このソースコードの紹介は改めてできればと思います。

最後に

数多くあるRubyの基礎勉強ページですが、これを見てしまうと全部無駄に感じてしまいます。 それくらい価値ある動画だと感じました。

何かサービス/アプリを作るのに最低限の知識を、これでもかってくらい分かりやすく解説されています。


プログラミングの本って入門本を一冊やり切るだけで大変で、当初思い描いていた「こんなの作りたい!」って気持ちを忘れてしまうこと多くあると思います。

ですが、このRubyで作る! ビットコイン自動売買システムであれば1時間くらいで基礎についてはバッチリ学ぶことが出来ますし動画なのでつまづくことが無いのも素敵なポイントです。


プログラミングが出来ると人生は変わると多くの人が言っていますし、これを実現している人もいっぱいいます。

そのきっかけをこの動画からってのは大いにある話だと思いますので、皆さんも是非楽しんでプログラミングを学び明るい未来を切り開きましょう!

Ruby on Railsチュートリアルの環境はCloud9で決まり!

こんにちは。opiyoです。

Webアプリケーションの勉強をする際に先ず引っかかるので環境構築ではないでしょうか。

  1. 参考書や記事の通りやってるのにエラーになる。
  2. ググって色々やってみる
  3. 解決できない
  4. 辞める

これ凄いもったいないですよね。せっかく何かを学びたいと思ってもその手前で諦めてしまう。

近くに分かる人が入れば良いですが、中々そんな状況もない。私も何度も経験があります。

ですがそんな悩みは昔のこと。既に出来上がった環境を使えるサービスがあるそうです。それが

Cloud9


無料だよ!

Cloud9とは

クラウド上に構築された開発環境を私たちが使えるようにしたサービスです。

Cloud9へユーザー登録する

先ずはユーザー登録していきます。この際に一点だけ注意点はクレジットカードが必要になります。 普通に使う分には料金は発生しないので、ご安心ください。

  1. 「メールアドレス」を登録してNEXTボタンをクリック
  2. 「名前」を入力してNEXTボタンをクリック
  3. 「develloer(開発なのか趣味なのか」と「なぜ使うのか(仕事なのか趣味なのか」を洗濯してNEXTボタンをクリック
  4. 1.2.3の内容を確認してNEXTボタンをクリック
  5. クレジットカードの情報を入力してNEXTボタンをクリック(無料なので安心を)
  6. ロボットじゃなければCreate accountボタンをクリック

ユーザー登録が完了するとこんな感じの画面が表示されるよ。 f:id:opiyotan:20170901175711p:plain

Cloud9でプロジェクトを作成する

では実際にプロジェクトを作っていこうと思います。

今回は、Railsチュートリアルをやるための設定を行っていきます。

  1. 「Create a new workspace」をクリック
  2. 「Workspace name:rails-tutorial」「Description:Railsチュートリアル」「Choose a template:Railsチュートリアルのアイコン」を入力、選択します。 f:id:opiyotan:20170901180246p:plain

そうするとCloud9の環境が立ち上がります! f:id:opiyotan:20170901180503p:plain

すげー多分これで、もー出来た。ここまできっと10分ですよ!

奥さんどうですか?

Cloud9の使い方

Railsプロジェクトを作成するところまでを進めて行こうと思います。

先ず最初に使う所が一番見慣れない下側にあるコマンドラインです。

Windowsだとコマンドプロンプトに当たるのかな。Macだとターミナルですね。

これらは普段全く使わない部分だと思うのですが、Railsプロジェクトを作っていく際は多くのコマンドを使ってプロジェクトを作っていきます。

ではRailsプロジェクトを作ってみましょう。

以下の通りにコマンドを入力し実際にRailsアプリケーションを作ってみましょう!

$ gem install rails -v 5.0.3
$ rails _5.0.3_ new hello_app
$ cd hello_app/
$ bundle install
$ rails s -b $IP -p $PORT 

f:id:opiyotan:20170901183736p:plain

f:id:opiyotan:20170901183802p:plain


ここまで表示されれば一先ずOKですかね。

今後、実際にRailsチュートリアルを進めていく場合は、こちらの記事を参考にしてください。 opiyotan.hatenablog.com


では、本日はここまで。 Cloud9を使うとあっという間に開発環境が整います。Macがあればローカルに環境作るのも良いですが、上手くいかずやらなくなってしまうぐらいならCloud9を使ってみるのも良いと思います。

【完全版】RubyonRailsのActiveRecord基礎!

こんにちは。opiyoです。

今日はRailsの勉強をしていると出てくる「Active Record 」について、勉強したいと思います。

  • User.first
  • User.find(1)
  • User.update
  • User.create!(hoge: hoge)

こんな奴らですね。

ではでは早速、行ってみましょう。

railsでデータを取得するActiveRecordとは

Ruby on Railsで使われているO/Rマッパー。データベースからデータを取り出すときのアプローチの一つ。

O/Rマッパーとは

  • 「オブジェクトリレーショナルマッピング」の略。 (ORMと略されることもある)
  • オブジェクトをリレーショナルデータベース(RDBMS)のテーブルに接続するもの
  • ORMを使用することで、SQL文を直接書かなくて良い
  • わずかなコードで、オブジェクトの属性やリレーションシップをデータベースに保存/読み出しができる

ActiveRecord命名ルール

  • モデル/クラス名:単数形、テーブル名:複数形
  • モデル/クラス:2語以上の場合はキャメルケース(語頭を大文字にしてスペースなしでつなぐ)
  • テーブル:小文字かつアンダースコアで区切る
モデル/クラス名 テーブル/スキーマ
Post posts
LineTime line_time
Person people

スキーマのルール

  • 外部キー:テーブル名の単数形_idにする
  • 主キー:デフォルトはidのカラム

モデルの作成

Userモデルを作成するにはApplicationRecordクラスのサブクラスを作成します。

SQLでテーブルを作成するとこうなります。

CREATE TABLE products (
   id int(11) NOT NULL auto_increment,
   name varchar(255),
   PRIMARY KEY  (id)
);

後から出てくるマイグレーションを使うとコマンドを使ってモデルの作成が可能です。

$ rails g model Procust name:string

CRUD データの読み書き

登録(Create)

  • newメソッドは新しい「オブジェクト」を作成する
  • createメソッドはデータベースに保存される
user = Usre.create(name: "David", email: "kosmo.waizu0804@gmail.com")

newメソッドを使う場合は、オブジェクトは保存されない。saveして保存する。

user = User.new
user.name = "David"
user.email = "kosmo.waizu0804@gmail.com"

user.save

一覧表示(Read)

# すべてのユーザーを返す
User.all
# 最初のユーザーを返す
User.first
# Davidという名前を持つ最初のユーザーを返す
david = User.find_by(name: 'David')
# 名前がDavidで、職業がコードアーティストのユーザーをすべて返し、created_atカラムで逆順ソートする
users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')

更新(update)

# saveメソッドを使う場合
user = User.find_by(name: "David")
user.name = 'Dave'
user.save
# updateメソッドを使う場合
user = User.find_by(name: "David")
user.update(name: 'Dave')
# 複数属性、複数レコード更新する場合
user.update_all "max_login_attempts = 3, must_change_password = 'true'"

削除(delete)

user = User.find_by(name: "David")
user.destroy

検証(validation)

  • ActiveRecordを使用すると、データベースに書き込まれる前に状態を検証することができる
  • 例えば
    • 空でないこと
    • 一意であること
    • すでにデータベースにないこと
  • save、updateメソッドは検証に失敗した場合は「false」を返す
class User < ApplicationRecord
  validates :name, presence: true # presenceは空を許さない
end
 
user = User.new 
user.save  # => false # 空のままsaveしてるので失敗 → false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank # 空のままsave!してるので失敗 → 例外

コールバック

データを作成、更新、登録、削除する前後に何かしら処理をしたい場合などに利用します。 例えばRailsチュートリアルでは、Userモデルを登録する前(before_save)でメールアドレスを小文字にするメソッドを呼び出す。などをしてます。

マイグレーション

Railsではデータベースの情報を履歴として管理する仕組みがあり、これをマイグレーション(migration)よ呼びます。 どのマイグレーションファイルが、データベースに反映されているかRailsは知っているので一つ前の状態に戻すなどが簡単にできます。

# db/migrate/20170629005430_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
# db/migrate/20170806092710_add_admin_to_users.rb
class AddAdminToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :admin, :boolean, default: false
  end
end

実行方法はrails db:migrate。一つ前に戻る時はrails db:rollback。一からやり直したい時はrails db:migration:reset

これらをまとめた元ネタはRailsガイドになります。 今後も学んだことは追記し、充実させていければと思っております。

【完全版】RubyonRailsチュートリアルで人生を変える28歳の夏(演習問題の回答あり)

こんにちわ。opiyoです。

改めてちゃんと書こうと思いますが、一先ず今日。ってかさっき今月中を目標にしていたRailsチュートリアル14章までのまとめ記事をアップしました。

せっかくなので、まとめポストを書こうと思います。(今月の目標記事数に届かないから急いて書いてるってのは内緒な)

ではでは、どうぞー

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com

opiyotan.hatenablog.com