Rails - ActiveDecoratorのコード読んでみたヘ(^o^)ノ
いつもお世話になります。ActiveDecoratorさま。
@izumin5210さんの「ActiveDecorator読んでみたら超勉強になった」を読んで、そういやコードあんま見てなかったので読んでみた
なるほど編
alias_method_chain 既存のメソッドを拡張するときにちょー便利♪
コードよむ編
# lib/active_decorator/monkey/abstract_controller/rendering.rb module AbstractController module Rendering # to_aメソッドのエイリアス↓ def view_assigns_with_decorator # 既存のto_aメソッドをコール hash = view_assigns_without_decorator # メソッドを拡張 hash.values.each do |v| ActiveDecorator::Decorator.instance.decorate v end hash end # :view_assigns => :view_assigns_with_decorator # :view_assigns_without_decorator => :view_assigns alias_method_chain :view_assigns, :decorator end end
view_assignsは、Controllerで取得したインスタンスなんかが格納されたHashを返しますよー
こんな感じ↓
postsはActiveRecord::Relation
です(Post.allしたやつ)
testはインスタンス変数に文字列をいれてみた
このHashをActiveDecorator::Decorator
へ
# lib/active_decorator/decorator.rb module ActiveDecorator class Decorator include Singleton def initialize @@decorators = {} end def decorate(obj) [...] end [...] end end
singletonをMix-inしてるので、ActiveDecorator::Decorator.instance.decorate
で呼び出してますよ
decorateメソッド
if obj.is_a?(Array) # *2でArrayが引数となるのでここ # Arrayの要素(ModelClass)を引数として自身を呼び出してる *3 obj.each do |r| decorate r end elsif defined?(ActiveRecord) && obj.is_a?(ActiveRecord::Relation) && !obj.respond_to?(:to_a_with_decorator) # ActiveRecord::Relationならクラスを再オープンしてメソッド定義してる *1 class << obj def to_a_with_decorator to_a_without_decorator.tap do |arr| # Arrayを引数として自身を呼び出してる *2 ActiveDecorator::Decorator.instance.decorate arr end end alias_method_chain :to_a, :decorator end else # *3で引数がmodelクラスとなるのでここ(ActiveRecord::Relation、Array以外もここ) # decoratorのクラス取得してる d = decorator_for obj.class return obj unless d # PostDecoratorモジュールに属してなければextendしてる obj.extend d unless obj.is_a? d end
decorator_forメソッド
private def decorator_for(model_class) return @@decorators[model_class] if @@decorators.has_key? model_class decorator_name = "#{model_class.name}Decorator" d = decorator_name.constantize unless Class === d d.send :include, ActiveDecorator::Helpers @@decorators[model_class] = d else @@decorators[model_class] = nil end rescue NameError @@decorators[model_class] = nil end
ViewContextをごっそり
# lib/active_decorator/view_context.rb module Filter extend ActiveSupport::Concern included do before_filter do |controller| ActiveDecorator::ViewContext.current = controller.view_context end end end
helperの拡張
# lib/active_decorator/helpers.rb module Helpers def method_missing(method, *args, &block) super #TODO need to make sure who raised the error? rescue NoMethodError, NameError => original_error begin # helperでmethod_missingならActiveDecorator::ViewContextから(なければoriginal_errorの例外発生 ActiveDecorator::ViewContext.current.send method, *args, &block rescue NoMethodError, NameError raise original_error end end end
ViewContextをごっそりとってるから、flashとかもdecoratorで使えるよー
ActiveDecorator::ViewContext.current.flash => #<ActionDispatch::Flash::FlashHash:0x007fda40e27d78 @discard=#<Set: {}>, @flashes={"error"=>"test error"}, @now=nil> ActiveDecorator::ViewContext.current.flash[:error] => "test error" # app/decorators/post_decorator.rb module PostDecorator def error_flash flash[:error] end end
ActiveDecoratorだと読みやすくて勉強にも良いかと思います♪
Happy Hacking٩( ‘ω’ )و