【问题标题】:Rails Presenter block methodRails Presenter 块方法
【发布时间】:2014-01-28 09:43:52
【问题描述】:

编辑:

我收到了很多关于解决问题的不同方法的回复,非常感谢!

遗憾的是,直到现在它们都没有工作。

为了便于理解和重现故障,我在 GitHub 上使用 Rspec 套件创建了一个小型 Rails 存储库。

其中一个规范正在通过(演示者在视图中被初始化)。 其中一个规范失败(演示者在控制器中初始化)。

如何让他们都通过?

下面的原始问题:

这是我的演示者:

class UserPresenter
  def initialize(user, vc)
    @user = user
    @vc   = vc
  end

  def linkify()
    #
    # HERE IS THE PROBLEM
    #
    vc.link_to("foo") do
      yield
    end
  end
end

这是我的控制器:

我在控制器中初始化我的 Presenter,将控制器的视图上下文与呈现的模型一起传递。

class UserController
  def show
    @user = User.find(#.....
    @presenter = UserPresenter.new(@user, view_context)
  end
end

在我的 Slim 模板中,我调用我的 Presenter 将内容放入链接中:

=@presenter.linkify do
  p "123"

我的问题是,我无法将视图中的块传递给我的 linkify 方法。

在上面标有注释的代码中,传递的块是整个视图内容,而不是p 123

当我通过 @presenter = UserPresenter.new(@user, self) 在视图中初始化我的 Presenter 时,它按预期工作。

如何使 linkify 方法使用提供的块,而不在视图中初始化演示者?

【问题讨论】:

  • 可能是缩进级别问题
  • 不是真的,我得到了

    123

    但没有按预期包含在链接中
  • 是的,我试着摆弄这个,但看不到明显的问题:-/

标签: ruby-on-rails ruby slim-lang


【解决方案1】:

因为如果你要使用yield命令,你不能指定&block,因为现在你正在使用普通的参数语法有效地接收一个块作为参数。

class UserPresenter
  def initialize(user, vc)
    @user = user
    @vc   = vc
  end

  def linkify() # <-- Remove &block
    vc.link_to("foo") do
      yield
    end
  end
end

# ...
# Somewhere else, assuming you have access to @presenter which is an instance of
# UserPresenter
# ...

def show
  @presenter.linkify do
    # ...
    # do my view stuff here
    # ...
  end
end

show()

# Now, if your "View" is nothing but a block that needs to get passed in
# then you'd do this...

def show(&block)
  @presenter.linkify do
    block.call()
  end
end

# This would be used this way:

show(lambda {  
  # ...
  # View stuff here
  # ..
}) 

【讨论】:

  • 谢谢,但这对我没有帮助。请查看我更新的问题。
  • 这并没有回答问题,即使没有 &block 它仍然不起作用...,看看上面的 repo,你会发现问题所在。
【解决方案2】:

如曲棍球的答案中所述。错误的视图上下文是这个原因的根源。我试图解决你的情况。而且,这就是最终的做法:

我在ApplicationHelper中创建了一个辅助方法:

module ApplicationHelper
  def link(presenter)
    presenter.linkify(self) do
      yield
    end
  end
end

将 linkify() 更改为:

def linkify(vc)
  vc.link_to("foo") do
    yield
  end
end

这意味着,不需要在演示者的类构造器中包含 vc,或者您可以更新帮助程序中定义的link 方法中的 vc(您的选择)。

视图现在看起来像这样:

presenter_from_view.html.slim

-@presenter = UserPresenter.new(@user, self)
=link @presenter do
  p 123

presenter_from_controller.html.slim

=link @presenter do
  p 123

我同意,也许这不是您想要的解决方案。但是,我无法为此找到任何更清洁的工作。但是,在这里,您不必担心在使用 link @presenter do.. 时在视图中传递 self(当您使用 linkify 时,编写代码可能会变得太多我猜在多个视图中)。

P.S.:您的所有规格现在都通过了。而且,如果您需要修改后的代码,那么我可以将其推送到您在单独分支中的存储库。告诉我。

【讨论】:

    【解决方案3】:

    来自 Slim's documentation 的 Helpers 和 Capturing:

    module Helpers
      def headline(&block)
        if defined?(::Rails)
          # In Rails we have to use capture!
          "<h1>#{capture(&block)}</h1>"
        else
          # If we are using Slim without a framework (Plain Tilt),
          # this works directly.
          "<h1>#{yield}</h1>"
        end
      end
    end
    

    您可以尝试如下使用捕获吗?

    def linkify(&block)
      result = capture(&block)
      vc.link_to("foo") do
        result
      end
    end
    

    【讨论】:

    【解决方案4】:

    错误的视图上下文导致了这个问题。只需将UserPresenter#initialize 更改为不接受视图上下文,在控制器中初始化演示者并从视图传递正确的视图上下文,如下所示:

    = @presenter.linkify(self) do
      p "123"
    

    【讨论】:

    • 是的,这是一个选项,但问题是关于使用来自控制器的视图初始化演示者。将 self 作为参数之一传递给每个演示者方法是不方便的。
    【解决方案5】:

    您遇到了什么错误?只是看代码...

    在这个方法中

    def linkify()
      #
      # HERE IS THE PROBLEM
      #
      vc.link_to("foo") do
        yield
      end
    end
    

    vc在哪里定义?

    我认为您的意思是 @vc 这是您正在初始化的实例变量。

    另外附带说明... linkify() 中的空 () 在没有变量的 ruby​​ 方法中是多余的。你可以消除它们。

    您可能还想看看cells gem。因为您基本上在演示者中反映了这种行为,而 IMO 单元格是在 Rails 中完成此操作的一种更清洁的方式。

    【讨论】:

      【解决方案6】:

      我想我知道为什么它不起作用了。当您在控制器中传递 view_context 时,它会在您将 view_context 传递给演示者时渲染一次,然后在您实际渲染视图时再次渲染。

      def initialize(user, vc)
        @user = user
        @vc = vc
      end
      
      # When called from the view:
      @presenter = UserPresenter.new(@user, self)
      # You're passing the view directly in "as is" and renders as expected.
      
      # However, when you pass it in from the controller:
      @presenter = UserPresent.new(@user, view_context)
      # you're essentially rendering the view_context in the controller, and then again
      # once it renders at the end of your action. That's why you're getting this:
      #   "<p>123</p><a href="foo"><p>123</p></a>"
      

      您需要在每次调用 linkify 时从视图中发送 self,或者您可以使用始终具有视图上下文的应用程序帮助方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 2018-12-01
        • 2011-11-19
        • 2014-07-15
        • 2015-01-22
        • 1970-01-01
        相关资源
        最近更新 更多