Techioz Blog

コントローラーから静的メソッドを呼び出し、そのクラスのインスタンスを更新する - AR::Association::HasOneAssociation の未定義メソッドを取得する

概要

私の質問:

正しい構造はどのようなものであるべきでしょうか?これを何度か再設計しようとしましたが、密結合の問題が発生し続けます。

関連情報:

いくつかのサードパーティ ソフトウェア用の新しいエンドポイントを作成しています。 ペイロードを受け取り、それを Subscription クラスの静的メソッドに渡します。 そこから、ペイロード関連のサブスクリプションを検索し、そのインスタンスを確立して、クラスの残りの情報に基づいて更新を実行したいと考えています。次のようなエラーが発生します: ActiveRecording::Association::HasOneAssociation のメソッド更新が未定義です

エンドポイントコントローラー:

class Api::V2::EndpointController < Api::V5::BaseController
    def connect
        data = decode(request.body.read)
        Subscription.static_method(data)
    end
end

サブスクリプションモデル:

class Subscription < ActiveRecord::Base

    def self.static_method(data)
        @subscription = Subscription.find_by_id(data.subscription_id)
        @subscription.update(data)
    end
end

サブスクリプションコントローラー:

class SubscriptionsController < ApplicationController
    def update
        #execute update
    end
end

解決策

コードをモデルに移動することは、最初からあまり良いアイデアではありませんでした。更新が成功したかどうかを確認し、それに応じて応答する必要があるのはコントローラーです。モデルには、渡されたもの以外のコンテキストの概念がなく、すでに多大な責任を負っています。

可能な限り最も単純な方法で、このコードをコントローラーに書き込むだけです。

# @todo setup the correct inflections in config/initializers/inflections.rb
# @see https://github.com/rubocop/ruby-style-guide#camelcase-classes
# @see https://github.com/rubocop/ruby-style-guide#namespace-definition
module API
  module V2
    # @todo Write a high level description of the what the responsibility of this class is.
    # the name itself is pretty smelly. 
    # "Endpoint" is very vague - every controller method is an endpoint.
    # Why is this inheriting from a different version of the API? Very smelly.    
    class EndpointController < API::V5::BaseController
      # @todo write a description of the purpose of this method
      # @todo document the route that this method corresponds to
      def connect
        data = decode(request.body.read) # WTF is this? 
        # .find will raise an exception if the id is not valid
        # Rails will catch this and return a 404.
        subscription = Subscription.find(data.subscription_id)
        # When you assume, you make an ass out of you and me.
        # Always check if the update is successful
        if subscription.update(data)
          head :ok # or provide some other response
        else
          head :unprocessable_entity
        end
      end
    end
  end
end

後でこのコードを 2 回以上繰り返していることがわかった場合は、サービス オブジェクトなどの抽象化を追加してください。