Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

Rails チュートリアルをやってみる(5) 2.3.1〜2.3.3 validationとrelation

2.3.1からやってみよう。

投稿(Micropost)の方も同じようにgenerateする。

> bundle exec rails generate scaffold Micropost content:text user_id:integer
      invoke  active_record
      create    db/migrate/20181121142643_create_microposts.rb
      create    app/models/micropost.rb
      invoke    test_unit
      create      test/models/micropost_test.rb
      create      test/fixtures/microposts.yml
      invoke  resource_route
       route    resources :microposts
      invoke  scaffold_controller
      create    app/controllers/microposts_controller.rb
      invoke    erb
      create      app/views/microposts
      create      app/views/microposts/index.html.erb
      create      app/views/microposts/edit.html.erb
      create      app/views/microposts/show.html.erb
      create      app/views/microposts/new.html.erb
      create      app/views/microposts/_form.html.erb
      invoke    test_unit
      create      test/controllers/microposts_controller_test.rb
      invoke    helper
      create      app/helpers/microposts_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/microposts/index.json.jbuilder
      create      app/views/microposts/show.json.jbuilder
      create      app/views/microposts/_micropost.json.jbuilder
      invoke  test_unit
      create    test/system/microposts_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/microposts.coffee
      invoke    scss
      create      app/assets/stylesheets/microposts.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.scss
> bundle exec rails db:migrate
== 20181121142643 CreateMicroposts: migrating =================================
-- create_table(:microposts)
   -> 0.0039s
== 20181121142643 CreateMicroposts: migrated (0.0040s) ========================

db/migrateのタイムスタンプはUTCなんだね。

スキーマを確認しておこう。

sqlite> .schema microposts
CREATE TABLE IF NOT EXISTS "microposts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "content" text, "user_id" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);

textってデータ型使ったことないなあ・・・。

SQLiteで利用可能なデータ型 - SQLite入門

SQLite3でカラムに指定できるデータ型は「TEXT」「NUMERIC」「INTEGER」「REAL」「NONE」の5つです。

な、なるほど?ここで作られるテーブルスキーマRDBMSによって違うのかな。TEXTがないDBもあるよね?つか、DB2にはないよね?

さて、演習。1は前と同じなのでパス。2はContentもUserも空にしたらどうなるか。空のレコードが作られるね。

sqlite> SELECT id, content, user_id FROM microposts WHERE user_id is NULL;
4||

今度はNULLが入ったっぽい。

演習3。長いのをいれるとどうなるか。SQLite3のTEXT型の制約とかあるのかな?

sqlite> SELECT id, content, user_id FROM microposts WHERE id = 3;
3|みっつめ。ながーいポスト。Ruby(ルビー)は、まつもとゆきひろ(通称 Matz)により開発されたオブジェクト指向スクリプト言語であり、スクリプト言語が用いられてきた領域でのオブジェクト指向プログラミングを実現する。 また日本で開発されたプログラミング言語としては初めて国際電気標準会議で国際規格に認証された事例となった。Ruby は1993年2月24日に生まれ、1995年12月にfj上で発表された。名称の Ruby は、プログラミング言語 Perl が6月の誕生石である Pearl(真珠)と同じ発音をすることから、まつもとの同僚の誕生石(7月)のルビーを取って名付けられた。競合言語として Perl の他に Python があり、「Matz(まつもと) が Python に満足していれば Ruby は生まれなかったであろう」と公式のリファレンスの用語集で言及されている[5]。機能として、クラス定義、ガベージコレクション、強力な正規表現処理、マルチスレッド、例外処理、イテレータ、クロージャ、Mixin、利用者定義演算子などがある。Perl を代替可能であることが初期の段階から重視されている。Perlと同様にグルー言語としての使い方が可能で、C言語プログラムやライブラリを呼び出す拡張モジュールを組み込むことができる。

Ruby 処理系は、主にインタプリタとして実装されている(詳しくは#実装を参照)。

可読性を重視した構文となっている。Ruby においては整数や文字列なども含めデータ型はすべてがオブジェクトであり、純粋なオブジェクト指向言語といえる。

長らく言語仕様が明文化されず、まつもとによる実装が言語仕様に準ずるものとして扱われて来たが、2010年6月現在、JRuby や Rubinius といった互換実装の作者を中心に機械実行可能な形で明文化する RubySpec という試みが行われている。公的規格としては2011年3月22日にJIS規格(JIS X 3017)が制定され、その後2012年4月1日に日本発のプログラム言語では初めてISO/IEC規格(ISO/IEC 30170)として承認された [4]。

フリーソフトウェアとしてバージョン1.9.2までは Rubyライセンス(Ruby License や Ruby'sと表記されることもある。GPLかArtisticに似た独自ライセンスを選択するデュアルライセンス)で配布されていたが、バージョン1.9.3以降は2-clause BSDLで配布されている。 |3

普通に入った。validationがないよねって確認か。4は削除。別に普通に削除できる。

さて、2.3.2だ。validationを入れてみようのコーナー。

class Micropost < ApplicationRecord
  validates :content, length: { maximum: 15}
end

例は140字までになっているけど、それじゃ確かめるのが面倒なので、15文字にしてみる。

f:id:Tambourine:20181122001110p:plain

おお、ちゃんと機能してるね。画面はちょっとアレだけど。演習2でエラー画面を見てみようとなっている。 こんな感じ。

<div id="error_explanation">
      <h2>1 error prohibited this micropost from being saved:</h2>

      <ul>
        <li>Content is too long (maximum is 15 characters)</li>
      </ul>
</div>
<div class="field">
    <div class="field_with_errors"><label for="micropost_content">Content</label></div>
    <div class="field_with_errors"><textarea id="micropost_content" name="micropost[content]">15文字制限をつけてみたんだけど、どうかな?</textarea></div>
</div>

ふむふむ。

では、2.3.3。テーブルの関連すな。

app/models/user.rb

class User < ApplicationRecord
  has_many :microposts
end

app/models/micropost.rb

class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 15}
end

これで、ActiveRecordがちゃんと関連を認識してくれるかどうか。 rails consoleで確認してみる。rails consoleってirbなんだね。

> bundle exec rails console
Loading development environment (Rails 5.1.6)
irb(main):001:0> first_user = User.first
  User Load (0.7ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "太郎冠者", email: "taro@ex.com", created_at: "2018-11-21 11:49:49", updated_at: "2018-11-21 11:49:49">
irb(main):002:0> first_user.microposts
  Micropost Load (0.3ms)  SELECT  "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content: "いっこめ。ほげほげ", user_id: 1, created_at: "2018-11-21 14:41:07", updated_at: "2018-11-21 14:41:07">]>
irb(main):003:0> mp = first_user.microposts.first
  Micropost Load (0.4ms)  SELECT  "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ?  [["user_id", 1], ["LIMIT", 1]]
=> #<Micropost id: 1, content: "いっこめ。ほげほげ", user_id: 1, created_at: "2018-11-21 14:41:07", updated_at: "2018-11-21 14:41:07">
irb(main):004:0> mp.user
Traceback (most recent call last):
        1: from (irb):4
NoMethodError (undefined method `user' for #<Micropost:0x00007f959e832080>)
Did you mean?  user_id

あれ?最後動いてないぞ?あ、micropost.rbが保存されてなかった。

> bundle exec rails console
Loading development environment (Rails 5.1.6)
irb(main):001:0> mp = Micropost.first
  Micropost Load (0.1ms)  SELECT  "microposts".* FROM "microposts" ORDER BY "microposts"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Micropost id: 1, content: "いっこめ。ほげほげ", user_id: 1, created_at: "2018-11-21 14:41:07", updated_at: "2018-11-21 14:41:07">
irb(main):002:0> mp.user
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "太郎冠者", email: "taro@ex.com", created_at: "2018-11-21 11:49:49", updated_at: "2018-11-21 11:49:49">

つまり、この関連の設定は別に繋がっている訳じゃなくて、それぞれmicropostsとuserというメソッドを 作る設定をしているってことね。

さて、演習1。/users/1にポストを表示してみる。viewを触るだけで十分。

app/views/show.html.erb

<p>
  <strong>Content:</strong>
  <%= @user.microposts.first.content %>
</p>

これを追加するだけやね。表示してみる。

f:id:Tambourine:20181122004402p:plain

おっけーっぽい。

演習2。validationの追加。

app/models/micropost.rb

class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 15}, presence: true
end

f:id:Tambourine:20181122004616p:plain

おっけー。

演習3。Userにも存在チェックを追加

app/models/user.rb

class User < ApplicationRecord
  has_many :microposts
  validates :name, presence: true
  validates :email, presence: true
end

f:id:Tambourine:20181122004835p:plain