class_evalでDRYにしてみたかったけど
Railsアプリで書いたビューのためのヘルパークラスをしつこくリファクタリング。以下の2つのクラスがDRYじゃない。今後、LinkedInも足すかもしれないし。
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
まず、TwitterPhotoUrlとかFacebookPhotoUrlという名前がいけない。外側を「module PhotoUrl」とくくって、トップレベルの名前空間の汚染は1つだけとし、その中にBase、Twitter、Facebook、Localの4つクラスを用意した。Base以外はBaseを継承している。
で、次にPhotoUrl::Twitterクラスと、PhotoUrl::Facebookクラスを動的に定義するようにしてみた。
Mapping = { twitter: { small:"_normal", normal:"_bigger", big:"" }, facebook:{ small:"_q", normal:"_q", big:"_b" }, } %w(twitter facebook).each do |provider| c = Class.new(Base) c.class_eval <<-DEF def path size = @options[:size] if [:small, :normal, :big].include?(size) add_suffix_to_path(@user.remote_photo_url, PhotoUrl::Mapping[:#{provider}][size]) else raise "Invalid size option for photo_tag" end end DEF PhotoUrl.const_set(provider.capitalize, c) end
ちゃんと意図通りに動いてるけど、これはやりすぎ感がある。単にclass_evalを使ってみたかっただけで、「なるほどなー」という感じ。そもそも、PhotoUrl::Mappingというハッシュを定義するんなら、TwitterとFacebookでクラスを分ける必要もない。