Метод блока Rails Presenter

РЕДАКТИРОВАТЬ:

Получил много ответов с разными подходами к решению проблемы, большое спасибо!

К сожалению, ни один из них не работал до сих пор.

Чтобы легко понять и воспроизвести сбой, я создал небольшой репозиторий Rails на GitHub. с пакетом Rspec.

Одна из спецификаций проходит (где презентатор инициализируется в представлении). Одна из спецификаций не работает (где презентатор инициализируется в контроллере).

Как заставить их пройти оба?

ОРИГИНАЛЬНЫЙ ВОПРОС НИЖЕ:

Это мой ведущий:

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, чтобы поместить контент в ссылку:

[email protected] do
  p "123"

Моя проблема в том, что я не могу передать блок из представления моему методу linkify.

В коде с отмеченным выше комментарием переданный блок представляет собой все содержимое представления, а не блок p 123.

Когда я инициализирую свой Presenter в представлении через: @presenter = UserPresenter.new(@user, self), он работает, как и ожидалось.

Как заставить метод linkify использовать предоставленный блок без инициализации презентатора в представлении?


person astropanic    schedule 28.01.2014    source источник
comment
вероятно, проблема с уровнем отступа   -  person Richard Jordan    schedule 28.01.2014
comment
не совсем, я получил ‹p›123‹/p›, но не завернутый в ссылку, как ожидалось   -  person astropanic    schedule 28.01.2014
comment
да, я пробовал возиться с этим и не вижу очевидной проблемы :-/   -  person Richard Jordan    schedule 28.01.2014


Ответы (6)


Потому что, если вы собираетесь использовать команду 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
  # ..
}) 
person Marcel Valdez Orozco    schedule 29.01.2014
comment
Спасибо, но это мне не помогло. Пожалуйста, смотрите мой обновленный вопрос. - person astropanic; 29.01.2014
comment
Это не отвечает на вопрос, даже без &block он все равно не работает..., посмотрите на репозиторий выше, вы увидите, в чем проблема. - person astropanic; 30.01.2014

Как указано в ответе на лакросс. Неправильный контекст представления является корнем этой причины. Я попытался обойти вашу ситуацию. И вот как в итоге получилось:

Я создал вспомогательный метод в 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 в конструкторе класса ведущего, или вы можете обновить vc в методе link, определенном в помощнике (по вашему выбору).

представления теперь выглядят примерно так:

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.: Все ваши спецификации сейчас проходят. И, если вам нужен измененный код, я могу отправить его в ваш репозиторий в отдельной ветке. Дай мне знать.

person Surya    schedule 31.01.2014

Из документации Слима о помощниках и захвате:

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
person Subhas    schedule 05.02.2014
comment
Спасибо, хорошая попытка;) github.com/astropanic/presenter_test/commit/ - person astropanic; 06.02.2014

Неправильный контекст представления вызывает эту проблему. Просто измените UserPresenter#initialize, чтобы он не принимал контекст представления, инициализируйте презентер в контроллере и вместо этого передайте правильный контекст представления из представления, например так:

= @presenter.linkify(self) do
  p "123"
person lacrosse    schedule 30.01.2014
comment
Да, это вариант, но вопрос в инициализации презентера видом из контроллера. Неудобно передавать каждому презентатору метод self в качестве одного из аргументов. - person astropanic; 30.01.2014
comment
Как view_context возвращаемое значение не кэшируется, оно каждый раз возвращает разные экземпляры, поэтому я не думаю, что есть другие варианты, если вы хотите работать с правильным контекстом представления. - person lacrosse; 30.01.2014

Какую ошибку вы получаете? Просто глядя на код...

В этом методе

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

где определяется vc?

Я думаю, вы имеете в виду @vc, которая является переменной экземпляра, которую вы инициализируете.

Также в качестве примечания... пустые () в linkify() избыточны в рубиновом методе без переменных. Вы можете их устранить.

Также вы можете взглянуть на жемчужину ячеек. Поскольку вы в основном отражаете это поведение в своих докладчиках и ячейках IMO, это более чистый способ выполнить это в рельсах.

person engineerDave    schedule 31.01.2014

Кажется, я понял, ПОЧЕМУ это не работает. Когда вы передаете 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, либо вы можете использовать вспомогательный метод приложения, который всегда будет иметь контекст представления.

person Sady    schedule 31.01.2014