Rails Presenter块方法

时间:2014-01-28 09:43:52

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

编辑:

我有很多回答用不同的方法解决问题,非常感谢!

可悲的是,直到现在他们都没有工作过。

为了便于理解和重现失败,我使用Rspec套件在 GitHub 上创建了一个小型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方法使用提供的块,而不在视图中初始化演示者?

6 个答案:

答案 0 :(得分:2)

因为如果要使用yield命令,则不能指定&块,因为现在您使用常规参数语法有效地接收块作为参数。

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
  # ..
}) 

答案 1 :(得分: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

我同意,也许这不是您希望解决方案的完成方式。但是,我无法为此做任何更清洁的工作。但是,在这里必须担心关于在self的任何地方传递link @presenter do..(这对于编写代码可能会变得过多)当你在多个视图中使用linkify时,我想)。

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

答案 2 :(得分:2)

来自助手和捕获的Slim's documentation

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

你得到什么错误?只看代码......

在这个方法中

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

vc 定义在哪里?

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

另外作为旁注...在没有变量的ruby方法中,linkify()中的empty()是多余的。你可以消除它们。

另外,您可能需要查看cells gem。因为您基本上在演示者和IMO单元格中镜像这种行为是一种更简洁的方式来实现这一功能。

答案 5 :(得分:1)

我想我知道为什么它不起作用。当您传入控制器中的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时,您都需要从视图中发送自身,或者您可以使用始终具有视图上下文的应用程序帮助方法。