gpt4 book ai didi

ruby-on-rails - 在 Rails 中是否有比 Observers 更直接的方式来执行发布/订阅模式?

转载 作者:数据小太阳 更新时间:2023-10-29 07:03:29 24 4
gpt4 key购买 nike

我有一个模型依赖于一个单独的、联合的模型。

class Magazine < ActiveRecord::Base
has_one :cover_image, dependent: :destroy, as: :imageable
end

class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end

图像是多态的,可以附加到许多对象(页面和文章),而不仅仅是杂志。

杂志需要在相关图像发生任何变化时自行更新

该杂志还保存了一张自己的截图,可用于宣传:

class Magazine < ActiveRecord::Base
has_one :cover_image, dependent: :destroy, as: :imageable
has_one :screenshot

def generate_screenshot
# go and create a screenshot of the magazine
end
end

现在如果图像发生变化,杂志也需要更新其截图。所以杂志真的需要知道图片什么时候出了问题。

所以我们可以天真地直接从封面图片触发屏幕截图更新

class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
after_save { update_any_associated_magazine }

def update_any_associated_magazine
# figure out if this belongs to a magazine and trigger
# screenshot to regenerate
end
end

...但是图片不应该代表杂志做事

然而,图片可以用于许多不同的对象,实际上不应该对杂志进行特定的操作,因为这不是图片的责任。该图像也可能附加到页面或文章,并且不需要为它们做各种事情。

“正常”的 rails 方法是使用观察者

如果我们采用 Rails(y) 方法,那么我们可以创建一个第三方观察器,然后触发相关杂志上的事件:

class ImageObserver < ActiveRecord::Observer
observe :image

def after_save image
Magazine.update_magazine_if_includes_image image
end
end

然而,这对我来说是一个糟糕的解决方案。

我们通过更新杂志避免了 Image 的负担,这很棒,但我们实际上只是将问题推到了下游。这个观察者的存在并不明显,在 Magazine 对象内部不清楚图像的更新实际上会触发对杂志的更新,我们有一个奇怪的 float 对象,它具有真正只属于 Magazine 的逻辑。

我不想要一个观察者——我只想要一个对象能够订阅另一个对象上的事件。

有什么方法可以直接从另一个模型订阅一个模型的更改吗?

我更愿意做的是让杂志直接订阅图片上的事件。所以代码看起来像:

class Magazine < ActiveRecord::Base
...

Image.add_after_save_listener Magazine, :handle_image_after_save

def self.handle_image_after_save image
# determine if image belongs to magazine and if so update it
end
end

class Image < ActiveRecord::Base
...

def self.add_after_save_listener class_name, method
@@after_save_listeners << [class_name, method]
end

after_save :notify_after_save_listeners

def notify_after_save_listeners
@@after_save_listeners.map{ |listener|
class_name = listener[0]
listener_method = listener[1]
class_name.send listener_method
}
end

这是一种有效的方法吗?如果不是,为什么不呢?

这种模式对我来说似乎很合理。它使用类变量和方法,因此不会对特定实例可用做出任何假设。

然而,我现在已经足够成熟和聪明了,我知道如果一些看似显而易见的事情还没有在 Rails 中完成,那可能是有充分理由的。

我觉得这很酷。它有什么问题呢?为什么我在第三方对象中看到的所有其他解决方案都是草案来处理事情?这行得通吗?

最佳答案

我使用 Redis:

在初始化程序中我设置了 Redis:

# config/initializers/redis.rb
uri = URI.parse ENV.fetch("REDISTOGO_URL", 'http://127.0.0.1:6379')
REDIS_CONFIG = { host: uri.host, port: uri.port, password: uri.password }
REDIS = Redis.new REDIS_CONFIG

在开发中它将默认为我本地的 redis 安装,但在 Heroku 上它将使用 Redis To Go。

然后我使用模型回调发布:

class MyModel < ActiveRecord::Base
after_save { REDIS.publish 'my_channel', to_json }
end

然后我可以从任何地方订阅,例如我用来使用 Event Source 推送事件的 Controller

class Admin::EventsController < Admin::BaseController
include ActionController::Live

def show
response.headers["Content-Type"] = "text/event-stream"

REDIS.psubscribe params[:event] do |on|
on.pmessage do |pattern, event, data|
response.stream.write "event: #{event}\n"
response.stream.write "data: #{data}\n\n"
end
end
rescue IOError => e
logger.info "Stream closed: #{e.message}"
ensure
redis.quit
response.stream.close
end
end

Redis 非常适合灵活的发布/订阅。我在 Controller 中的代码可以放在任何地方,比如在初始化程序中:

# config/initializers/subscribers.rb

REDIS.psubscribe "image_update_channel" do |on|
on.pmessage do |pattern, event, data|
image = Image.find data['id']
image.imageable # update that shiz
end
end

现在它将在您更新图像时处理消息:

class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
after_save { REDIS.publish 'image_update_channel', to_json }
end

关于ruby-on-rails - 在 Rails 中是否有比 Observers 更直接的方式来执行发布/订阅模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24962319/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com