从控制器调用静态方法并更新该类的实例 - 获取 AR::Association::HasOneAssociation 的未定义方法

Calling a Static Method from a Controller and Updating instance of that class - Getting undefined method for AR::Association::HasOneAssociation

提问人:dehuff 提问时间:10/24/2023 更新时间:10/24/2023 访问量:26

问:

我的问题:

正确的结构应该是什么?我已经尝试过多次重新设计,但一直遇到紧密耦合问题。

相关信息:

我正在为某些第三方软件编写一个新的端点。 它将接收一个有效负载,然后我将该有效负载传递到我的 Subscription 类的静态方法中。 从那里,我想查找与有效负载相关的订阅,建立它的实例,然后根据我的类的其余信息执行更新。我遇到一个错误,内容如下:undefined method update for 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
Ruby-on-Rails 红宝石 Ruby-on-Rails-3

评论

0赞 max 10/24/2023
static_method的实现非常奇怪,将实例变量分配给 Subscription 类。此实例变量对控制器不可用,您返回的只是一个布尔值,而不是模型。“静态方法”在 Ruby 中也不是一回事。我们称它们为类方法,因为它们实际上并不比实例方法更静态,而且 Ruby 中不存在这样的关键字。@subscription = Subscription.find_by_id(data.subscription_id)
0赞 max 10/24/2023
这里的正确解决方案实际上是一个意见和要求的问题。最直接的解决方案是将代码移动到控制器中,并遵循WET方法,并在重复一次后对其进行抽象。该放弃可以是服务对象或类似对象。虽然模型中的工厂方法绝对是一回事,但您应该警惕在模型和终结点的输入之间创建紧密耦合。模型通常不受版本控制,当您的端点略有变化时,这会产生问题。
0赞 max 10/24/2023
Rails 中的模型也非常多,即使里面没有一行代码。它们执行存储库对象的工作,形成对象,通过回调强制执行业务逻辑,与路由交互,I18n等。

答:

1赞 max 10/24/2023 #1

一开始将代码移动到模型中并不是一个好主意。控制器需要检查更新是否成功并做出相应的响应。除了你传入的内容之外,模型也没有上下文的概念,并且已经被责任感所淹没。

我会以最直接的方式在控制器中编写此代码。

# @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

如果后来发现重复此代码两次以上,请添加抽象,例如服务对象。