0

コントローラが3つあります。 2つの帰国特産品(記事、アナウンス)、どちらか一方を返す。レールに冗長アクションロジックを持つ複数コントローラ

GET api/announcements/1 -- produces Announcement json 
GET api/articles/2  -- produces Article json 
GET api/posts/1   -- produces Announcement json 
GET api/posts/2   -- produces Article json 

記事・コントローラー用show方法は、投稿コントローラーに複製されなければならないロジックが含まれています。具体的には:

def show 
    deal_with_params(...) 
    authorize!(...) 
    render json: @resource 
end 

私はunique_idによってリソースを取得した後、私はそれがタイプだし、そこから出て分岐することができます知っているが、私は唯一のauthorizeしたいと種類Articleのリソースにいくつかの他の操作を行います。

ポストコントローラでは、ArticlesController#showへの変更を繰り返す必要がないことを確認するのに役立つ推奨、パターン、またはアイデアがありますか?

答えて

1

controller concernsを使用して共通の機能を抽出できます。あなたの特定のユースケースでは、2つの懸念を持っている可能性があり:

  • Announcementsに対処するために必要なすべてのコードを持っていAnnouncementsConcern
  • ArticlesConcernArticlesを扱うためのすべてのコードがあります。

次に、これらの問題を必要に応じてコントローラに含めます。私。 AnnouncementsConcernAnnouncementsControllerに、ArticlesConcernArticlesControllerに含めてから、両方の問題をPostsControllerに含めます。

+0

申し訳ありません。これはすばらしいアイデアのように見えますが、実装には苦労しています。私はPostsControllerがどのように問題のメソッドを呼び出すことになっているのか分かりません。 私は2つの部分を持っています: (1) 'show'メソッドは 'authorize!(...)if @ resource.type == 'Article''です。 (2) 'deal_with_params(...)'は同じ条件付きです。 あなたは 'ArticlesConcern'、' PostsController'、そしておそらくArticlesControllerの例を教えてくれますか? – Yason

0

継承を使用して、それを乾かすことができます。これは、Punditを使用してめちゃめちゃドライベースコントローラの一例であるRespondersjsonapi

class ResourcesController < ApplicationController 

    respond_to :json 

    before_action :authenticate_user # use a secure by default model 
    before_action :set_resource, only: [:show, :update, :destroy] 
    before_action :authorize_resource, only: [:show, :update, :destroy] 
    before_action :set_resources, only: [:index] 

    def create 
    @resource = resource_class.create(permitted_attributes) do |r| 
     yield(r) if block_given? 
    end 
    respond_with(@resource) 
    end 

    def show 
    respond_with(@resource) 
    end 

    def index 
    respond_with(@resources) 
    end 

    def update 
    @resource.update(permitted_attributes) do |r| 
     yield(r) if block_given? 
    end 
    respond_with(@resource) 
    end 

    def destroy 
    @resource.destroy 
    respond_with(@resource) 
    end 


    private 

    def set_resource 
    @resource = resource_class.find(params[:id]) 
    end 

    def set_resources 
    @resources = policy_scope(resource_class) 
    end 

    def authorize_resource 
    authorize(@resource) 
    end 

    def resource_class 
    self.class.controller_name.classify.constantize 
    end 

    def permitted_attributes 
    whitelist = policy(@resource || self.class.resource_class) 
         .permitted_attributes 
    params.require(:data).require(:attributes).permit(whitelist) 
    end 
end 

いくつか本当に気の利いたトリックがここにあります

  • self.class.controller_name.classify.constantizeはに基づいてモデルを推測する設定より規約を使用していますコントローラ名。
  • before_actionコールバックを使用すると、skip_before_actionを使用してサブクラスをオプトアウトするか、コールバックメソッドを宣言して動作を変更できます。
  • yieldを使用すると、サブクラスはコントローラのフローに入ることができます。
  • JSON API形式では、レールのデフォルトではなく、model_name.param_keyをルートパラメータキーとして使用する代わりに、エンティティに関係なく同じデータ形式が使用されます。少ないバイケッス・ペインティングは良いことです。サブクラスの

例:こんなに遅く戻ってこれに来て

# Nothing special going on here 
class ThingsController < ResourcesController; end 

class ArticlesController < ResourcesController 
    def create 
    # taps into the yield 
    super do |article| 
     article.author = current_user 
    end 
    end 
end 

class StranglyNamedController < ResourcesController 
    def resource_class 
    Thing 
    end 
end 
+0

もちろんこれをモジュールミックスインと組み合わせることもできます(懸念事項)。 – max

関連する問題