Rails:重构、视图、助手:这一切是如何结合在一起的?

发布于 2024-08-20 03:47:26 字数 1379 浏览 4 评论 0原文

警告:这里是菜鸟。

我知道这是一个微不足道的主题,但我很难弄清楚如何通过将其中的一部分转移到助手中来简化我的视图。例如,我一直读到您观点中的条件是提取为助手的主要候选者,但我无法真正找到这方面的示例,并且我实现这一目标的尝试失败了。

例如,假设我有:

#index.html.erb

<% for beast in @beasts do -%>
  <% if beast.dead? -%>
    <%= beast.body %>
    <%= link_to "bury", bury_beast_path( :id => beast.id ) %>
  <% else -%>
    <%= beast.body %>
    <%= link_to "kill!", kill_beast_path( :id => beast.id ) %>
  <% end -%>
<% end -%>

在我看来,这让我有点恼火,但我到底如何才能将其转移给助手呢?如果可能的话,进一步简化它。 (我在某处读到过,条件语句很糟糕,但我无法理解如何在没有它们的情况下编写任何东西。)

另一个例子:我需要使用 id 我的 body 标签格式controller_action。到目前为止我得到的最好的结果是:

#index.html.erb

<body id="<%= controller_action %>">

……而且……

#application_helper.rb

def controller_action
  @id = @controller.controller_name + "_" + @controller.action_name
end

我不是专家,但即使对我来说这仍然很丑陋。

为了让事情变得更复杂,Ryan Singer 说了一些我喜欢的话:将 ERB 视为图像标签,使用助手来“揭示意图”。然后在下一次呼吸中说你不应该在帮助程序中使用 HTML,因为那是通往地狱的道路。搞什么?两者如何兼容?如果已经到了可以在视图中声明行为的地步,那么肯定应该有很多 HTML 需要在幕后渲染?我无法把握它。

所以,基本上就是这样。如果有人能就此分享一些想法,或者向我指出一些关于该主题的深入读物,我将不胜感激——我发现网络上的报道非常薄弱。我已经用谷歌搜索得筋疲力尽了,但谁知道呢。

Warning: Noob here.

I know this is a trivial subject but I'm having a lot of difficulty in figuring out how exactly I can simplify my views by moving parts of them into helpers. For example, I've always read that conditionals in your views are prime candidates for extraction into helpers, but I couldn't really find examples of this, and my attempts to achieve this failed.

For example, suppose I have:

#index.html.erb

<% for beast in @beasts do -%>
  <% if beast.dead? -%>
    <%= beast.body %>
    <%= link_to "bury", bury_beast_path( :id => beast.id ) %>
  <% else -%>
    <%= beast.body %>
    <%= link_to "kill!", kill_beast_path( :id => beast.id ) %>
  <% end -%>
<% end -%>

It annoys me a little to have this in my view, but how exactly could I move this to a helper instead? And further simplify it, if possible. (I've read somewhere that conditionals are bad but it's just beyond me how you could program anything without them.)

Another example: I need to id my body tags with the format controller_action. The best I've got so far is this:

#index.html.erb

<body id="<%= controller_action %>">

…and…

#application_helper.rb

def controller_action
  @id = @controller.controller_name + "_" + @controller.action_name
end

I'm no expert, but that's still ugly even to me.

To make things more complicated, Ryan Singer said something I liked: to treat ERB like an image tag, using helpers to "reveal intention". Then in the next breath saying that you should have no HTML in helpers for that is the way to hell. WTF? How are both things compatible? If it's come to the point where you can just declare behaviors in the view, surely there should be a lot of HTML to be rendered behind the scenes? I can't grasp it.

So, that's basically it. I'd appreciate if anyone could share some thoughts on this, or point me to some good in depth reading on the subject – which I've found to have a really weak coverage on the web. I've already googled it to exhaustion but who knows.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一杯敬自由 2024-08-27 03:47:26

重构使您的视图更易于维护。问题在于选择重构代码的去向。

您的两个选择是partialshelpers。没有一成不变的规则规定哪些应该在哪里使用。有一些准则正在流传,例如声明帮助程序不应包含 HTML 的准则。

一般来说,部分代码比 ruby​​ 更适合 HTML/ERB/H​​AML 较多的重构部分。另一方面,助手用于具有最少 HTML 的 ruby​​ 代码块或从参数生成简单的 HTML。

然而,我不同意助手不应该包含任何 HTML 的观点。一点点就可以了,只是不要过度。帮助程序的处理方式阻碍了它们用于生成大量 HTML。这就是为什么建议您的帮助程序包含最少量的 HTML。如果您查看 Rails 附带的助手的源代码,您会发现它们中的大多数都会生成 html。少数没有的主要用于生成参数和评估常见条件。

例如,任何形式帮助器或 link_to 变体都适合第一种形式的帮助器。而像 url_for 和logged_in 这样的东西?各种认证模型提供的都是第二类。

这是我用来确定是否将视图中的代码分解为部分代码或帮助程序的决策链。

  1. 重复或几乎相同的语句生成单个浅 html 标签? =>帮手。
  2. 用作另一个助手的参数的通用表达式? =>帮手。
  3. 长表达式(超过 4 个术语)用作另一个助手的参数? =>帮手。
  4. 4 行或更多 Ruby 行(未评估为 HTML)? =>帮手。
  5. 几乎其他一切=>部分的。

我将使用您要重构的代码作为示例:

我将以这种方式重构问题中的视图:

app/helpers/beast_helper.rb:

def beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path(beast)
  else
    link_to "kill!", kill_beast_path(beast)
  end
end

app/views/beasts/_beast.html.erb:

<%= beast.body %>
<%= beast_action(beast) %>

app /views/beasts/index.html.erb:

<%= render :partial => "beast", :collection => @beasts %>

它在技术上更复杂,因为它总共有 3 个文件和 10 行,而不是 1 个文件和 10 行。现在,视图只有 3 行,分布在 2 个文件中。最终结果是你的代码更加干燥。允许您在其他控制器/操作/视图中重用部分或全部内容,以最小的增加复杂性。

至于你的身体标签ID。您确实应该使用 content_for/yield。对于那种事情。

app/views/layouts/application.html.erb

...
<body id="<%= yield(:body_id) %>">
...

app/views/beasts/index.html.erb

<% content_for :body_id, controller_action %>
...

这将允许您在任何需要它的视图中覆盖正文的 id。例如:

app/views/users/preferences.html.erb

<% content_for :body_id, "my_preferences" %>

Refactoring makes your views easier to maintain. The problem is choosing where the refactored code goes.

Your two choices are partials and helpers. There's no stone-set rules dictating which should be used where. There are a couple of guidelines floating around like the one stating that helpers should not contain HTML.

Generally partials are better suited for refactoring sections that are more HTML/ERB/HAML than ruby. Helpers on the other hand are used for chunks of ruby code with minimal HTML or generating simple HTML from parameters.

However, I don't agree with the sentiment that helpers should contain no HTML at all. A little is ok, just don't over do it. The way helpers are processed hinder their use for producing large amounts of HTML. Which is why it's suggested that your helpers contain minimal amounts of HTML. If you look at the source the helpers that ship with rails you will notice that most of them generate html. The few that don't, are mainly used to generate parameters and evaluate common conditions.

For example, any of the form helpers or link_to variants fit the first form of helpers. While things like url_for and logged_in? as supplied by various authentication models are of the second kind.

This is the decision chain I use to determine whether to factor code from a view into a partial or helper.

  1. Repeating or nearly identical statements producing a single shallow html tag? => helper.
  2. Common expression used as an argument for another helper? => helper.
  3. Long expression (more than 4 terms) used as an argument for another helper? => helper.
  4. 4 or more lines of ruby (that is not evaluated into HTML)? => helper.
  5. Pretty much everything else => partial.

I'm going to use the code you're looking to refactor as an example:

I would refactor the view in the question this way:

app/helpers/beast_helper.rb:

def beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path(beast)
  else
    link_to "kill!", kill_beast_path(beast)
  end
end

app/views/beasts/_beast.html.erb:

<%= beast.body %>
<%= beast_action(beast) %>

app/views/beasts/index.html.erb:

<%= render :partial => "beast", :collection => @beasts %>

It's technically more complicated, because it's 3 files, and 10 lines total as opposed to 1 file and 10 lines. The views are now only 3 lines combined spread over 2 files. The end result is your code is much more DRY. Allowing you to reuse parts or all of it in other controllers/actions/views with minimal added complexity.

As for your body tag id. You should really be using content_for/yield. For that kind of thing.

app/views/layouts/application.html.erb

...
<body id="<%= yield(:body_id) %>">
...

app/views/beasts/index.html.erb

<% content_for :body_id, controller_action %>
...

This will allow you to override the id of the body in any view that requires it. Eg:

app/views/users/preferences.html.erb

<% content_for :body_id, "my_preferences" %>
森末i 2024-08-27 03:47:26

我要做的第一件事是:

#index.html.erb
<%= render @beasts %>

#_beast.html.erb
<%= beast.body %>
<%= link_to_next_beast_action(beast) %>    

#beast_helper.rb
def link_to_next_beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path( :id => beast.id ) 
  else
    link_to "kill!", kill_beast_path( :id => beast.id )
  end
end

我所做的是将野兽的渲染分离为使用集合语义的部分。

然后我将显示杀死/埋葬链接的逻辑移至野兽助手中。这样,如果您决定添加另一个操作(例如,“起死回生”),您只需更改助手即可。

这有帮助吗?

The first thing I'd do would be this:

#index.html.erb
<%= render @beasts %>

#_beast.html.erb
<%= beast.body %>
<%= link_to_next_beast_action(beast) %>    

#beast_helper.rb
def link_to_next_beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path( :id => beast.id ) 
  else
    link_to "kill!", kill_beast_path( :id => beast.id )
  end
end

What I've done is separate out the rendering of the beast into a partial which uses collection semantics.

Then I've moved the logic for showing the kill/bury links into a beast helper. This way if you decide to add another action (for example, 'bring back from dead'), you'll only have to change your helper.

Does this help?

一花一树开 2024-08-27 03:47:26

第三种选择是使用 Cells gem 中的视图模型。这是一个非常流行的框架,它将面向对象引入了 Rails 的视图层。

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept
  def show
    return dead if model.dead?
    kill
  end

private
  def dead
    link_to "bury", bury_beast_path( :id => model.id ) 
    # you could render a view here, too!
  end

  def kill
    link_to "kill!", kill_beast_path( :id => model.id )
  end
end

然后,您使用助手(在视图或控制器中)渲染视图模型。

# app/views/beasts/index.erb

<%= concept(:beast, @beast).call %>
<%-# this returns the link content %>

就这样!您可以在单独的测试中隔离该单元格进行测试。单元格还为您提供视图渲染、视图继承等等。

例如,您可以使用 kill 链接的视图。

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept

  # ..

  def kill
    render :kill
  end
end

这呈现出细胞的杀手级视野。

# app/cells/beast/views/index.erb

<%= link_to "kill!", kill_beast_path( :id => model.id ) %>

请注意视图的位置,它很好地打包到单元目录中。

是的,单元格可以执行 HAML 以及 AbstractController 支持的任何其他模板引擎。

A third choice is to use a view model from the Cells gem. This is a very popular framework that brings object-orientation to the view layer in Rails.

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept
  def show
    return dead if model.dead?
    kill
  end

private
  def dead
    link_to "bury", bury_beast_path( :id => model.id ) 
    # you could render a view here, too!
  end

  def kill
    link_to "kill!", kill_beast_path( :id => model.id )
  end
end

You then render a view model using a helper (in the view or controller).

# app/views/beasts/index.erb

<%= concept(:beast, @beast).call %>
<%-# this returns the link content %>

That's all! You can test this cell isolated in a separate test. Cells also give you view rendering, view inheritance and many more things.

As an example, you could use a view for the kill link.

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept

  # ..

  def kill
    render :kill
  end
end

This renders the cell's killer view.

# app/cells/beast/views/index.erb

<%= link_to "kill!", kill_beast_path( :id => model.id ) %>

Note the location of the view, it's nicely packaged into the cell directory.

And, yes, cells can do HAML and any other template engine supported by AbstractController.

平定天下 2024-08-27 03:47:26

另一个策略是根本不使用模板和助手
对于渲染,您可以:

  1. 使用 render(:inline => ) 直接从控制器渲染视图。如果您仍然想保持视图和控制器正式分离,您可以创建包含在控制器中的模块/混合。
  2. 或者创建您自己的视图类并使用它们来呈现您的响应。

这背后的想法是助手和rails erb模板系统不利用OOP,因此最终您无法定义根据每个控制器/请求的需求专门化的一般行为;通常情况下,人们最终会重写看起来非常相似的代码块,从维护的角度来看,这并不是很好。

然后,如果您仍然需要一些辅助方法(例如 form_tag、h、raw 等),您只需将它们包含在您的控制器/专用视图类中。

看看这个:rails-misappregnons-helpers-are-shit 一篇有趣但有用的文章。

编辑:为了听起来不像是一个彻底的混蛋,我想说实现这一点取决于您的应用程序应该有多大,以及您需要更新代码的频率。另外,如果您将设计委托给非程序员,他/她很可能会在深入研究您的代码之前参加一些编程课程,这无疑比模板语法更难以直接理解。

Another startegy would be to not use templates and helpers at all.
For rendering you could :

  1. render your views directly from your controllers using render(:inline => ). If you still want to keep Views and Controllers formally separated you can create modules / mixins that you include into the controllers.
  2. or create your own view classes and use them to render your response.

The idea behind this is that helpers and rails erb templating system don't take advantage of OOP, so that at the end of the day you can't define general behaviours that you'll specialize according to each controller's/request's needs; more often than not one ends up rewriting very similar looking chunks of code, which is not very nice from a maintenance standpoint.

Then if you still need some helper methods (eg. form_tag, h, raw, ...) you only have to include them in your controller / dedicated view class.

See this : rails-misapprehensions-helpers-are-shit for a fun but useful article.

EDIT: to not sound like a complete douche, I'd say implementing this depends on how big your application is supposed to be, and how often you're going to have to update your code. Plus, if you're delegating the design to a non-programmer, he/she may well be in for some programming courses before digging into your code, which admittedly would be less directly understandable than with templates syntax.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文