2017年1月13日金曜日

【Ruby】PHPでやっていたように変数や配列の内容があるかどうかをチェックしたい

転職が決まり、今後は Ruby on rails での仕事が中心になりそう。
転職するまでの間にすこし Ruby を勉強しておこうと思い、PHPとの違うところ・気になるところをいくつか調べた。

PHPではよく、「ちゃんと値が入っているか」のチェックをした。
私のお決まりの書き方は以下のようなものだった。

//php

if(
  (isset($value))&&
  (empty($value) === false)
){
  //値がある
}//if
---------------------------

これをRubyでできないかなと思って調べていた。

ちなみに、以下の場合すべて「値がない」としたい。
◆変数が定義されていない
◆配列の中で指定したキーが定義されていない($array['undefined_key'] みたいなもの)
◆null
◆false
◆"" (カラの文字列)
◆[] (カラの配列)
◆0 (数字の0)

わかったのは、RubyとPHPとの大きな違いがあるので上記の発想そのままではできないということだった。

PHP : オブジェクト指向ではあるがゆるい。上記の「isset()」や「empty()」といったグローバルに使える関数がたくさんある。
Ruby : きっちりしたオブジェクト指向なのでそもそもグローバル関数のようなものがほとんどない。関数は必ず主語(オブジェクト)を持つ。

なので、「中身がちゃんとあるかどうかを確認するためのお決まりの処理」を覚えておくのではなく、オブジェクトに合わせて判定方法を変えないといけないということだ。

Webシステムを作るのにおいて、結局、中身があるかどうかのチェックが必要な主要なケースはふたつだと思う。
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか

[1.Modelでのデータベース検索結果があるかどうか]
検索結果がない場合、検索結果オブジェクトはnil(PHPでいうnull)になる。

[2.GETやPOSTでの送信値があるかどうか]
送信値を取り出す時、コントローラーでは「 params[:キー名] 」と書く。
この時、送信されていないキー名でも、変数(というか多分連想配列の該当キーなんだろうけど)の定義自体はされている。ただし中身がnilになっている。

/test/index?id=test&name=ishiitakeru
というとき、GET送信値で以下のキーのものは定義されているのは判る。
params[:id] 中身は「test」
params[:name] 中身は「ishiitakeru」

このとき、例えば以下のものは未定義に思える。
params[:undefined_key]

Rubyで定義済みかどうかを調べるには defined?(変数名) と書く。
そこで、上記の未定義キーのパラメータを調べる

#Ruby

if defined?(params[:undefined_key])
  #定義済み
end

ところがこれは、TRUEになる。定義済み扱いになる。
どうも、Rubyでのdefined?() をハッシュ(連想配列)に使う場合、その連想配列自体があればTRUEになって、キーの指定まで見てないらしい。
ハッシュを主語にしてキーが定義されているかどうかを調べる場合は「ハッシュ.has_key?(キー名)」を使う。

# Ruby

test_hash = {
  'id'   => 666,
  'name' => 'ishiitakeru',
}

if defined?(test_hash["undefined"])
  print "defined(定義されてないハッシュのキー) true \n"   #→こっちが表示される
else
  print "defined(定義されてないハッシュのキー) false \n"
end

if test_hash.has_key?("undefined")
  print "has_key?(定義されてないハッシュのキー) true \n"
else
  print "has_key?(定義されてないハッシュのキー) false \n"   #→こっちが表示される
end

[連想配列の・定義されていないキーにアクセスしようとした時]
PHP : エラーになる。「Notice: Undefined index: キー名」
Ruby : エラーにならない。nil が取得できる。

結局、Rubyでは連想配列から定義されていないキーを取り出そうとしてもその動作自体はエラーにならないので、値がカラでないかだけをチェックすればいい。


------------------------------------------------------
結論
------------------------------------------------------
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか

→どちらも、Ruby on rails では、「.present?」「.blank?」でチェックできる。ただし数字の0もTRUE扱いになる。

もし、レイルズではないRuby自体を使う場合、レイルズで拡張された関数である「.present?」「.blank?」が使えないので、自作でチェッカークラスを作ったほうがいいように感じる。以下が自作したもの。これだと数値の0もfalse扱いになる。

# Ruby
# custom_checker.rb
# 自作チェッカークラス

class Checker
  #############################################
  # 値チェック
  # 
  # empty? が使えないものが注意が必要
  #   数字型 : FixNum
  #   nil    : NilCass
  #   false  : FalseClass
  #   true   : TrueClass
  # 
  # empty? が使えるもの
  #   文字列クラス : String
  #   配列         : Array
  #   連想配列     : Hash
  # 
  # @param  値をチェックする対象
  # @return bool 有効な値があればtrue
  # 
  def self.content_exist?(value)
    # データ型確認
    # print value.class
    # print "\n"

    # empty が使えるならそれで判定
    # empty?判定 文字列・配列・連想配列がチェックできる
    # obj.respond_to?(:メソッド名) で、
    #   そのメソッドがそのインスタンスで使用可能かが取得できる
    if value.respond_to?(:empty?)
      #返したいbool値はempty?と逆の値になる
      return !(value.empty?)

    # true
    elsif value == true
      return true

    # false
    elsif value == false
      return false

    # nil
    elsif value == nil
      return false

    #数値型
    #数値型であればすべてここの処理を通したいので
    #  数値型判定だけでifを独立させる
    elsif value.kind_of?(Numeric)
      #数値0
      if value == 0
        return false
      end

    end

    #ここまでくれば true 判定
    return true
  end

end

こんな感じで使う。

# Ruby

require 'custom_checker.rb'

if Checker.content_exist?(value)
  print " : true"
else
  print " : false"
end

0 件のコメント:

コメントを投稿