1つのカラムにvalidatesとvalidateをつけたときに気をつけたいこと

仕事中に引っかかりそうになったので、備忘録がてら書きたいと思います。

 

RailsにおけるValidatesとValidateの違い

Validates

validatesは特定のカラムに対して、予めRailsが用意している条件で

バリデーションをかけたい時に使うメソッドです。

例えば下記では、nameカラムに必須条件を加えています。

他にも一意条件である、uniquenessや値の長さをチェックするlengthなどがあります。

```

class Person < ApplicationRecord

  validates :name, presence: true

end

```

 

Validate(カスタムメソッド)

カスタムメソッドとは、ユーザーが独自に作成出来る、モデルの状態を確認し不正な状態の時にerrorsコレクションにメッセージを追加することが出来る機能です。

例えば下記の様なMemberモデルがあるとします。

Memberモデルは入会日(start_date)と退会日(end_date)を持つとした時、

入会日は常に退会日より以前であるということをバリデーションの条件として

追加したい時は下記の様に設定できます。

```

class Member < ApplicationRecord

  validate :check_start_date_before_end_date

 

  def check_start_date_before_end_date

    return if start_date < end_date

 

    errors.add(:start_date, ": 入会日は退会日より後にはできません")

  end

```

 

validatesメソッドもvalidateメソッドもRailsの開発では必須なので、使う機会は頻繁にあるので、ちゃんと理解しておかないといけなかったのですが、今回は下記の様なパターンでちょっと引っかかってしまいました。

 

同一カラムに対してvalidatesとvalidteを使う

ずばり下記の様な条件で引っかかりました。

```

class Member < ApplicationRecord

  validates :start_date, presence: true

  validates :end_date, presence: true

  validate :check_start_date_before_end_date

 

  def check_start_date_before_end_date

    return if start_date < end_date

 

    errors.add(:start_date, ": 入会日は退会日より後にはできません")

  end

```

 

start_dateにもend_dateにも必須制約をつけていたので、すっかり油断していました。

これ、check_start_date_before_end_dateを通る時に、

start_dateもしくはend_dateがnilだった時に、

`NoMethodError: undefined method start_date for nil:NilClass`

で落ちてしまうんですね。

 

バリデーションはそれぞれ独立している

バリデーションでは、どこかで制約に引っかかった、引っかかっていない関係なしに、

設定しているバリデーションを全て実行する訳ですね。

そして、制約に引っかかったものは全てerrorsに追加され、

最終的にerrors.empty?がtrueならvalidはtrue

1つ以上でもエラーメッセージが追加されており、errors.empty?がfalseなら

validはfalseになるわけです。

 

ついつい、validatesで必須制約は作ったから

validateではチェックしなくていいと安心しがちですが、気をつけるべきでした。

コードレビューで心がけたいこと

仕事では、チームで開発をしているので、毎日お互いにコードレビューをする機会が多くあります。

しかし、コードレビューはどうしてもレビュワーの技術力や好みに左右される部分があり、私はまだまだ技術的に未熟な部分もあり、質の高いレビューは難しいな〜

と思うことが度々あります。

 

現在読んでいる最中の本の中に、コード妥当性レビュー観点という内容がありました。

そこでは、7つの設計原理といわれるコード構造上の核心観点のことで、

それをレビュー上での観点として持つことで、レビューの質の安定や向上が図ることが出来るという内容でした。

非常に参考になったので、7つの設計原理について、1から纏めてみたいと思います。

 

ちなみに、読んでいる本はこれです。

 

 


 

1. 単純原理

単純原理とはシンプルに実装するという原理です。

これだとそのままなので、シンプルとはでイメージしてみたのですが

  • ロジックが単純である
  • 見通しが良い
  • 初心者でも読みやすい(必要以上に複雑でない、基礎的に技術で構成されている)
  • 1つのメソッドが長すぎない

とかになると思います。(他にも多々あると思いますが)

 

ここは実装する上で真っ先に意識する点だと思いますが、

「初心者でも読みやすい」は特に大事にしたいなと思いました。

 

「メソッドを長くしない」に囚われすぎて、なかなか使うことの無いような

ニッチなメソッドを使ったり、ちょっと凝ったロジックを実装したりとかは無意識のうちにあることなので、意識していきたいなと思いました。

 

2. 同型原理 3. 対称原理

ここは一緒に説明した方が分かりやすいかなと思ったので、一緒に説明します。

ここは、アプリケーション全体として、

  • 同じようなものは、同じように実装する
  • 反対のものは、明らかに反対なように実装する

というものでした。

 

ここは非常に重要だなと思って、

コードを読むときって、ミスが無いようにコードを遡って丁寧に読むことは

大事ですが、同じような実装は同じように実装してあった方が

スピーディーにミスなく読めるからです。

 

例えば、あるエクスポート処理の実装があったとして、

エクスポート処理の実装は、色々なデータの種類に応じて複数個作る必要がある

としたら、一連の処理が同じ様に実装されていた方が読みやすいですよね。

 

また、反対なものについてもそうで、

メソッド名でいうとgetの反対はsetで。pushの反対派popで命名するべき

などがあります。

 

4. 階層原理

これは、処理の階層関係を理解し、整理することが大事だという内容でした。

個人的には一番出来ていなくてハットした原理でした。

階層関係でも十分伝わると思うのですが、

1つのメソッドの中には、同じ抽象度のものを扱う様にする

という様に理解しました。

 

例えば、基本的にあるデータをDBに保存するまでの処理があったとして、

簡単に考えると、流れとしては

  1. データをどこからか受け取る
  2. 保存できる形に整形する
  3. 保存する

といったような順番になると思います。

この時、1.2.3はそれぞれさらに細かく分けることが出来て

1.データをどこからか受け取る(APIで受け取るとします)

     1. リクエスト先のURLを取得する

     2. リクエストするときのパラメータを設定

     3. 例外処理の対応

     4. etc...

 

などなど、1を達成するために必要なことがさらに複数あります。

で、このときに、これらの詳細な項目と、最初の1.2.3の大項目を

同じ階層で扱わないことが大事になります。

これらは抽象度が違うことなので、完全に分けて実装すべきで、

詳細項目は、大項目の下の階層で扱うべき問題になります。

 

5. 線形原理

これは処理の流れは直線的であるべきという内容です。

受け取るデータによって、通るべき処理が全く異なってしまう様な実装は非常に複雑なので控えようと思いました。

 

6. 明証原理 7. 安全原理

ここは1の単純原理とも近しい部分があるのですが、

常に実装は明確になっていて、後にメンテナンスをする技術者が読んでも

確実に理解できる実装を心がけなさいということでした。

また、安全原理に関しては、実装は基本的に常に安全サイドに倒して実装しなさい

という内容でした。

「ここのメソッドはこんなデータを受け取ることは無いだろう」

という安易な予測がバグをうむ原因になることは多々あると思うので、

必要以上な対応は返って複雑性をうむので、加減が大事ですが、

例外処理や、データチェックは念入りに行うに越したことは無いということでした。

 

基本的にこれらは、自らが実装するときに心がけるべき内容という感じなのですが、

実装する時より、ついつい観点がブレがちなレビューでも、これらを意識してレビュー

することで、今までより安定的、かつ質の高いレビューを心がけたいなと思いました。

 

RSpecのexpect() と expect{ } がややこしい

expect() と expect{ }の使い分けを理解する

最近RSpecのテストを書くようになったのですが、 expect()expect{ } の違いがよく分かっておらず、「どっちかでテストを書いてみて、 テストが落ちたら、もう片方に修正する。」 なんて、非常に無駄な作業が発生していたので、調べてみました!

もうお分かりかと思いますが、RSpec初心者向けの記事です笑

結論

結論から言うと、expect()expect{ }2つを使い分けるコツは expectはmatcherに中身をそのまま渡すので、() で書けるか、{}でかけるかはmatcherがProcを評価できるか出来ないか次第 と言うことでした!

なので、この2つの理解が大事と言うことですね。 - matcher - Proc

どちらも、ご存知とは思いますが、一応簡単に説明入れると

matcher

matcherとは下でいう to の部分のことです。 expect() で呼んでいる変数とeqの後に続く期待する答えの橋渡し的な存在です。 to以外では、not_to be raise_errorなどがあります。

hoge = 3
expect(hoge).to eq 3

Proc

Procとはブロック(何らかの)をオブジェクト化するためのクラスです。 Procオブジェクトはcallメソッドによって実行されます。

ex

add_number = Proc.new { |num_1, num_2|
  puts num_1 + num_2
}
add_number.call(1, 2) #=> 3

Procについては下記の記事がわかりやすいと思います Ruby Procについて学ぶ - Qiita

下記の記事を参考にしたので、理由は下記の記事を参照してください。 should を捨て expect を使うようになった、たった一つの理由 - MUGENUP技術ブログ

Rubyのattr_accessorについて

こちらの記事を参考にしました!

以下は自分なりにこの記事を咀嚼したものですので、詳しくは元記事をお読みください。

bryankawa.hatenablog.com

 

Rubyのattr_accessorについてのお勉強

なんとなくModelクラスにいるあいつってぐらいしかきちんと分かっていないので、まとめてみました。

 

結論からいうと

👇を省略して記述するためのものがattr_accessor

class User
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

user = User.new
user.name = 'Yusuke'
user.name # => "Yusuke"

 

`User`インスタンスにある`name`は

読み取り書き込みが必要である。

そのため本来は👆の様に、

読み取りようのnameメソッドと

書き込みようのnameメソッド

が必要だが、それを簡単に記述できるようにしてくれるものがattr_accessorになる。

 

で、実際に使うとこうなる。

 

class User
  attr_accessor :name
end

user = User.new
user.name = "Yusuke"
user.name # => "Yusuke"

 

ちなみにこの2つの実装の中間がこんな感じ

 

class User
  attr_reader :name
  attr_writer :name
end

 

readerをゲッター

writerをセッターと呼ぶ

 

そして最後に。

attr_accessorで設定したnamはインスタンス変数でUserオブジェクトに設定することが出来る

 

class User
  attr_accessor :name

  def greeting
    "Hello #{@name}"
  end
end

user = User.new
user.name = "Yusuke"
user.greeting # => "Hello Yusuke"

Rails5へのバージョンアップ ApplicationRecordへの変更

Rails4からRails5にバージョンアップする時の変更点として、ApplicationRecordを継承するようになるのが大きな変更点の一つとしてある。

 

それぞれRails4、5のモデルを見てみると、

 

これがRails4

class Article < ActiveRecord::Base

end

 

これがRails5

class Article < ApplicationRecord

 end

 

4ではActiveRecord::Baseを継承していたのが、

5からApplicationRecordを継承するようになった。

 

で、ApplicationRecordはどうやってできているかと言うと、

# app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base

self.abstract_class = true

end

 

ActiveRecord::Baseを継承している。

つまり、各モデルとActiveRecord::Baseの間に一つクラスが挟まるようになった。

 

最初は余計なものが増えて面倒臭いだけでは?と思ったが、

モデルに何か機能を追加したいときに、今まではActiveRecord::Baseに追加するので、ActiveRecord::Baseを継承する全てのクラスが強制的に機能を継承してしまい、無駄な影響を与えてしまう危険性があったが、それを今までよりは回避しやすくなったのがメリットで、目的だと思われる。

BigDecimalとfloat

 

少数

→ 0以下の数字(0.1や0.25など)を含む数字

ex 1,44  2,25など

1.44 x 10^0、0.144 x 10^1などでも表せる。

仮数部 x 基数^指数の構造)

このような仮数、基数、指数で表現される小数を浮動小数点の表記方法という。

 

注意点

浮動小数点数はコンピュータに置いて丸め誤差が発生するので、正確な計算が求められるものに関しては適切ではない。

 

BigDecimal

浮動小数点数演算ライブラリであり、任意の精度で10進数で表現された浮動小数点を扱える。

0.xxxxxxxx10nという形で(10進数)数値を保持する。

浮動小数点数で発生していた丸め誤差を生じずに演算ができるようになっている。

 

計算は若干浮動小数点数を使った方が早い

 

 

 

エンジニアがデザインシンキングを学んでみる

週末の時間を使って、デザインシンキングのワークショップに参加してみました。

UIやUXに関しては興味はあったものの、全くの無知だったため、

初心者も歓迎という言葉につられて、参加。

結果としては、非常に参加しがいのある、有意義なワークショップでした!!

 

今日学んだことをサクッと振り返りたいと思います。

 

そもそもデザインとは何をさすのか?

日頃バックエンド中心で開発をしているプログラマとしては、「デザイン」といえば、

  • ユーザーに使いやすいフロントを考える
  • 美しいフロントを構築する
  • 必要な機能をどのページに配置するかを考える

などなどが思い浮かぶのですが、

 

今回のワークショップにおいて、「webデザイン」とは

ユーザーの課題を発見、解決するためのプロセスを作成する

と捉えていました。

 

デザイナーといえば、フロントの画面を作成することを専門としているのかと勘違いしがちなのですが、あくまでもデザインとは、ユーザの課題を解決するプロセスを構築することらしいです!

 

「むむ、そうなるとちょっとプログラマとデザイナーの境界線はどこなんだろう、、、」

と混乱しましたが、

ここで大事なことはデザインとは画面に絞ったことではないということで、

 

ユーザーが抱える問題の発見から、その問題を解決する手段を解決する方法を明確化する

ということにあって、非常に広い範囲を網羅しているんですね。

デザイナーの皆さん、大変失礼いたしました。。。

 

デザインとは足し算と引き算が重要!! 

今日のワークショプとは5時間超となかなかコンテンツも豊富で、基本的なデザインのプロセスなど多くのことを学ぶことが出来ました。(会社の同僚や友人のプログラマも参加したのですが、非常に満足どは高いようでした!!)

 

その中でも、今日一番刺さったことは

 

「デザインではTrade-offを大事にしています。」

 

trade-off?? 交換?なんの話?

と最初は思いましたが、説明を聞くとなるほど、

 

  • 機能を盛り込みすぎることには気をつけろ
  • 何かを足すメリットは同時に何かをなくすデメリットを孕んでいる
  • これ以上引くことが出来ない状態がゴール(これはminimalというらしいです。)

という事を意味しているらしく、

 

よくありますよね。使わない機能が盛り込まれすぎたアプリケーション。

「これじゃかえって使いたい機能も使いにくいっつーの!!」

という悲劇が発生している状態。

 

プログラマ的にはその気持ちは痛いほどわかりますし、ついつい気づけばその方向性に行きがちなのですが、、、

 

デザインでは、こういったことも非常に意識して作成することが大事だということ。

確かに、そもそもデザインとはユーザーの課題を解決することが大事なのに、

いらないお節介で対して重要じゃない課題の様なものまで解決しようとして、結局本来の課題解決自体が薄くなったら意味ないですもんね。

 

これは、今後開発をしていく中でも意識していくとかなり自分にとってプラスだなと思いました。

 

と簡単でしたが、今日のワークショップの感想とまとめでした。

今後はデザイナーの視点や、考え方も尊重しながら開発を出来たらと思います!!

 

ではでは!