rubyの条件分岐
if File.exist?(path)
File.delete(path)
end
#...
class Node
def parent
return nil if self.root?
# ルートノードのめんどくさいことは気にせず純粋に親ノードをとる処理に集中できる
end
end
def index
if smart_phone?
render 'sp_index.html'
else
render 'pc_index.html'
end
end
# こういうのはふさわしくない
def index
if smart_phone?
return render 'sp_index.html'
end
render 'pc_index.html'
end
# 一方で,こういう場合は後置ifを使うほうがのぞましい
# (リダイレクトが例外的な処理である場合)
def index
return redirect_to 'top_page' if smart_phone?
render 'index.html'
end
def cheese_io(arg)
if arg.is_a?(IO)
arg
elsif arg.is_a?(String)
File.open(arg)
else
StringIO.new(arg)
end
end
# elseに例外
if xxx
#xxx
elsif yyy
#yyy
else
raise ArgumentError
end
# 例外の条件を先に排出可能であれば
raise ArgumentError if zzz
if xxx
#xxx
else
#yyy
end
# return [] if !users.empty?
return [] unless users.empty?
# つらい
if user.logged_in? && !user.admin?
# ...
end
# 非特権ユーザの判定であることがすぐにわかる
class User
def non_privilege_user?
logged_in? && !admin?
end
end
if user.non_privilege_user?
# ...
end
class Node
def parent
# 親ノードをとってくる処理
end
def root?
parent == nil
end
def has_parent?
!root?
end
# A
def siblings
return [] if root?
parent.children.delete_if {|sibling| sibling == self }
end
# B
def siblings
return [] unless has_parent?
parent.children.delete_if {|sibling| sibling == self }
end
end
# それぞれのクラスに同一メソッドを定義しておいてポリモーフィックにできるとイイ感じになる
travel_plan = has_money? ? ExpensiveTravelPlan.new : CheapTravelPlan.new
travel_plan.price
travel_plan.term
# ?系メソッドを新たに生やす
def find?(element)
find(element) ? true : false
end
# Regex
# http://docs.ruby-lang.org/ja/2.2.0/method/Regexp/i/=3d=3d.html
# http://docs.ruby-lang.org/ja/2.2.0/method/Regexp/i/=3d=3d=3d.html
/^http/ == "http" #=> false
/^http/ === "http" #=> true
def protocol(url)
case str
when /^https/ then "HTTPS"
when /^http/ then "HTTP"
when /^ftp/ then "FTP"
end
end
protocol("https://google.com") #=> "HTTPS"
protocol("http://example.com") #=> "HTTP"
protocol("unknown") #=> nil
# Range
# http://docs.ruby-lang.org/ja/2.2.0/class/Range.html
(1..10) == 0 #=> false
(1..10) == 1 #=> false
(1..10) == 2 #=> false
(1..10) === 0 #=> false
(1..10) === 1 #=> true
(1..10) === 5 #=> true
def rank(score)
case score
when (80...100) then "A"
when (70...80) then "B"
when (60...70) then "C"
when (0...60) then "D"
end
end
rank(90) #=> "A"
rank(70) #=> "B"
rank(55) #=> "D"
直接的には条件分岐ではないが || などもあったりする
あるゲームを考える.ゲームには100点満点のscoreがある.80点以上ならAランク, 70点以上80点未満ならBランク, 60点以上70点未満ならCランク,60未満ならDランクであり,A,Bランクの場合次のステージはボーナスステージになる.それ以外のC, D の場合通常のステージになる.
この時,スコアを受け取り,次のステージ情報を返すメソッドnext_stageを作成する (ボーナスステージ,通常のステージを表すクラスはBonusStage, NormalStageで予め定義済みとする)
愚直に書くとこんな感じ.
def next_stage(score)
if score >= 80
BonusStage.new
elsif score >= 70
BonusStage.new
elsif score >= 60
NormalStage.new
else
NormalStage.new
end
end
あるいは,考慮外の数字などを考えたり,どうせ70点以上は全部BonusStageだからなどと考えたらこうなる
def next_stage(score)
if score >= 70
BonusStage.new
elsif score >= 0
NormalStage.new
else
raise "out of range"
end
end
さらにはcase文をつかったりetc...
上記の愚直に書いた例の場合ランクを決定することと,ランクによって次のステージが変わることが混ざってしまっている.さらに入力が想定外の範囲だった場合のバリデーションエラーも同一の分岐に混ざってしまっている. どこが主題なのかがわかりづらくなってしまい,コードからランクの情報が欠落してしまっている
# ゲームロジックのための分岐
def grade(score)
# バリデーションのif(ここはガード節早期リターン or 例外)
raise 'out of range' unless (0..100).include?
case score
when 80..100; 'A'
when 70...80; 'B'
when 60...70; 'C'
when 0...60; 'D'
end
end
# フローのための分岐
def dispatch_stage(grade)
case grade
when 'A', 'B'
BonusStage.new
when 'C', 'D'
NormalStage.new
end
end
def next_stage(score)
dispatch_stage(grade(score))
end