デカい条件分岐のリファクタリング
FacebookやTwitterのCDN上にあるアイコン、Paperclipでローカルに置いたユーザーアイコンのimgタグをビューから呼び出して生成するために、Railsのヘルパーメソッドで以下のように書いた。
require 'uri' module ApplicationHelper ICON_SIZE = {small:"24x24", normal:"73x73", big:"240x240"} : : def photo_tag(user, options = {:size => :normal}) icon_path = lambda do |path, suffix| u = URI.parse(path) ext = File.extname(u.path) path.chomp(ext) << suffix + ext end icon_size = ICON_SIZE[options[:size]] raise "Invalid size option for photo_tag" if icon_size.nil? photo_url = if user.remote_photo_url && user.provider == "twitter" case options[:size] when :small icon_path.call(user.remote_photo_url, "_normal") when :normal icon_path.call(user.remote_photo_url, "_bigger") when :big user.remote_photo_url else raise "Invalid size option for photo_tag" end elsif user.remote_photo_url && user.provider == "facebook" case options[:size] when :small icon_path.call(user.remote_photo_url, "_q") when :normal icon_path.call(user.remote_photo_url, "_q") when :big icon_path.call(user.remote_photo_url, "_b") else raise "Invalid size option for photo_tag" end else case options[:size] when :small user.photo.url(:thumb) when :normal user.photo.url(:thumb) when :big user.photo.url(:medium) else raise "Invalid size option for photo_url" end end image_tag photo_url, :size => icon_size end : :
順番に並べてるだけだし、これでもいっかという気もするけど、なんだかごちゃっとしている。結局、FacebookかTwitterかPaperclipかという分岐とアイコンサイズによる分岐とで、2次元のマトリックスになっているのが嫌な感じ。
そういえばマーチン・ファウラーの本に、条件分岐はオブジェクト指向ではポリモーフィズムで書き換えられるんだと書いてあったなと思って、よく意味が分からないけど、以下のように書き換えてみた。
require 'photo_url.rb' module ApplicationHelper : : def photo_tag(user, options = {:size => :normal}) case user.provider when "twitter" photo_url = TwitterPhotoUrl.new(user, options) when "facebook" photo_url = FacebookPhotoUrl.new(user, options) else photo_url = LocalPhotoUrl.new(user, options) end icon_size = photo_url.icon_size raise "Invalid size option for photo_tag" if icon_size.nil? image_tag photo_url.path, :size => photo_url.icon_size end (application_helper.rb) ------------------------------ require 'uri' class PhotoUrl ICON_SIZE = {small:"24x24", normal:"73x73", big:"240x240"} def initialize(user, options) @user, @options = user, options end def icon_size ICON_SIZE[@options[:size]] end def add_suffix_to_path(path, suffix) u = URI.parse(path) ext = File.extname(u.path) path.chomp(ext) << suffix + ext end end class TwitterPhotoUrl < PhotoUrl def path case @options[:size] when :small add_suffix_to_path(@user.remote_photo_url, "_normal") when :normal add_suffix_to_path(@user.remote_photo_url, "_bigger") when :big @user.remote_photo_url else raise "Invalid size option for photo_tag" end end end class FacebookPhotoUrl < PhotoUrl def path case @options[:size] when :small add_suffix_to_path(@user.remote_photo_url, "_q") when :normal add_suffix_to_path(@user.remote_photo_url, "_q") when :big add_suffix_to_path(@user.remote_photo_url, "_b") else raise "Invalid size option for photo_tag" end end end class LocalPhotoUrl < PhotoUrl def path case @options[:size] when :small @user.photo.url(:thumb) when :normal @user.photo.url(:thumb) when :big @user.photo.url(:medium) else raise "Invalid size option for photo_url" end end end (photo_url.rb)
あんまりスッキリした感じがない。