Rails 4如何从Model类重定向到Controller action

时间:2015-10-03 09:46:34

标签: ruby-on-rails ruby-on-rails-4

我想实现以下逻辑:
我们通过普通视图与数据库进行交互。通过观点 可以对数据库对象执行基本的CRUD操作。每当现在一个特定的布尔值(对于任何记录)设置为true时,我想将此记录传递给REST API控制器,然后构建一个http字符串以发送给已知的客户端。

在模型类中,我使用after_update回调

观察所需的值
 private
    def check_boolean
        @listofusers ||= Array.new
         if self.boolean_changed?
             @listofusers.push(self)
         end    
             #redirect_to send_updates_path
             #redirect_to :controller =>'api/users',:action=>"send_updates"
      end

这是我在routes.rb中包含的获取指定助手

的路线
get 'send_updates', to: 'api/user#send_updates', as: :send_updates

但是我收到以下错误消息:

NoMethodError (undefined method `redirect_to' for #<User:0x00000005abc778>):

重定向到控制器操作的正确方法是什么?非常感谢提前。

4 个答案:

答案 0 :(得分:5)

你问的是antipattern

保持模型中的数据完整性&amp; db,Rails在modelcontroller代码之间保持着很大的差距:

enter image description here

这是一种分歧,一些看似简单的交叉,例如Devise current_user方法实际上被阻止发生。

因此,您必须重新思考您的结构是如何工作的。

-

简而言之,您应将逻辑保留在控制器中

#app/controllers/your_controller.rb
class YourController < ApplicationController
   def update
      @model = Model.find params[:id]
      @model = Model.update model_params
      if @model.boolean_changed?
          ## do something here
      else
          redirect_to ...
      end
   end
end

任何数据操作都可以保存在您的模型中;您的控制器是您的所有业务逻辑所在的位置。

除此之外,您还必须记住,根据上图,Rails不会直接向您的模型发送数据。它首先通过 controller ,因此是保持逻辑的正确位置。

你正在做的就是试着用引擎引导你的汽车。

答案 1 :(得分:3)

您尝试做的事情是不可能的,而且违背了MVC模式。

你的after_update#check_boolean方法何时被调用?在控制器操作期间对吗?没有黑巫师突然执行一段代码。

基本上从我的理解,每当用户使用视图更新模型实例时,您希望“更新”某些HTTP客户端的更新。您应该将“广播代码”放在一个单独的库(我称之为Broadcaster)中,并从您的控制器中调用该库。

此外,您无法执行控制器操作来发送更新。控制器动作旨在回复来自特定客户端的请求,而不是向任何客户端发送消息。您的客户如何与Rails服务器保持连接? Rails是CRUD / RESTful,并没有真正保持活动连接。我相信你的client_list存储在你的模型中,你将不得不使用像Net :: HTTP这样的宝石来广播这些更新

应用程序/控制器/ foos_controller.rb

def FoosController

  def your_action_that_triggers_broadcast_to_clients
    if @your_model.update_attributes(model_params)
      if Broadcaster.broadcast_updates(@your_model)
        flash[:notice} = "Model updated and changes broadcasted"
      else
        flash[:warning] = "Model updates but changes were not broadcasted successfully"
      end 
      redirect_to @your_model, 
    else
      render 'show', alert: "Problems with validation"
  end

end

广播更新的图书馆代码

module Broadcaster

  module_functions

  def broadcast_updates(model)
    @user_list = model.get_broadcast_list
    @user_list.each do |client|
      Net::HTTP.post_form(....)
    end

    # Return true/false on successful/unsuccessful broadcast
  end
end

答案 2 :(得分:1)

有趣的问题。

我同意上面的回答者提到这是一个Rails反模式,并建议使用Rails-way解决方案。是。这些都很好,但我想提出一些思路。这种类型的触发操作通常通过异步任务循环和信号量或通过硬件中断存在于操作系统中。

异步任务循环

由于您在数据库中的布尔值上没有可用的硬件中断,因此您需要一个异步任务循环和一个信号量,甚至可能是一个互斥锁来锁定信号量用户正在使用特定的布尔值。但要保持简单,请考虑一个基于计时器的任务循环,它检查每个数据库中的信号量布尔值。

这在Rails Web应用程序中是否可行?这也是一种反模式吗?

这可能只是一个理论上的争论,但我认为每个适当的MVC架构的定时器任务设置不是反模式,它是一个解决方案。似乎有人为定期运行异步(到Rails应用程序本身)计时器任务编写了一个gem whenever。我无法保证,但它表明这种解决方案是可行的。

计时器循环缺陷

  • 一个可预见的缺点是,如果循环被频繁安排并且命中数据库中的每个表,则可能严重影响性能。
  • 如果您的布尔值正在快速不断地翻转,那么 计时器任务可能不是一个合适的解决方案
  • 关于您的应用的所有未知数(对我而言)可能会使其成为不合适的解决方案。

服务器任务

另一个想法是创建一个服务器端任务,它基本上会做同样的事情,但速度更快,可能开销更​​少。

答案 3 :(得分:0)

我建议查看pubsub框架来执行此操作。它不仅像其他人所说的那样反模式,它实际上不是向客户发送通知的有效方式。

我个人使用PubNub,但Google has oneAWS一样。 Rails 5将包含ActionCable,这将允许本地完成。