构造复杂的ruby辅助例程

时间:2010-10-02 23:23:59

标签: ruby-on-rails ruby

我正在研究一个相当复杂的应用程序,并试图找出如何在将数据呈现为各种格式方面实现某些功能。

简单来说,应用程序正在获取键值数据并将其呈现为html,或者可能呈现为其他格式。我知道关键价值数据很糟糕,但对于有问题的应用程序,它是必需的。这将是一个rails应用程序,基本上我正在处理类似于:

的模型结构

Foo有很多酒吧   酒吧有很多领域     字段由名称,值,类型

组成

我想在控制器中执行的操作是获取Foo并将其提供给视图。然后,视图将使用Foo对象作为参数调用某种构建器类,以及渲染格式(例如html,pdf,xml)。

从这里开始如何构建它我有点失落,我希望有人可以指出正确的方向,因为我对ruby很新,并弄清楚类,模块和混合是如何适应的进入画面令我困惑。

在这里,我正在思考的问题......构建器类会说啊,我们想要html输出,所以将它传递给html构建器。 html构建器会说,好吧,我们有Foo,我们需要渲染这个输出。它的逻辑将迭代字段并试图找出如何渲染每个字段,如:

1)我们是否有特别知道如何在Foo.id = 3中的Bar.id = 8中渲染字段4的内容?

2)我们是否知道如何在Foo.id = 3上为Bar.id = 8渲染field.type ='phone'?

3)我们是否知道如何为Foo.id = 3渲染field.type ='phone'?

4)我们是否知道如何渲染field.type ='phone'?

5)调用默认渲染器

最终我的目标是尽可能少地编写代码。例如,对于超过一半的字段,html的呈现只是纯文本。但是,应用程序的性质要求我们能够容纳任何必要的边缘情况。例如,完全有可能在某些情况下我们需要将电话号码呈现为xxx-xxx-xxxx,而有时则需要将其呈现为(xxx)xxx-xxxx。我的总体想法是创建一些可以将模型作为参数并从特定渲染器转换为通用渲染器的东西,这样在任何特殊情况下我都可以专门针对Bar 3和Foo 7中的字段'phone'它应该以特殊方式输出。但显然,我不想为永远的Foo中的每个字段创建一个类heirarchy,而是我想要一个方法,如果需要特殊情况并且让Builder例程找到,我可以实现一些代码它

希望这是相当清楚的,如果有人有任何关于如何在rails应用程序中构建它的提示或正确方向的一些指示我会很感激。

2 个答案:

答案 0 :(得分:0)

您可以设置一个线程变量来了解您正在渲染的数据类型:

Thread.current [:render_type] ='abc'

然后你不必将渲染样式一直传递到每​​个部分或不是。

你可以使用rails的普通.rhtml渲染系统,并在适当或不适当的地方使用不同的部分。并且可能使方法名称具体,以便您知道发生了什么:)

答案 1 :(得分:0)

因此,您的目标是使用一些单个构建器对象或构建器对象集合,这些对象可以以不同的格式发出类似结构的数据,这样您就不必维护太多单独的代码路径。

除非我在这里误解你的情况,否则我建议坚持使用vanilla Rails惯例。让partials和helpers成为您的视图构建器。我将概述这里的方法......

路由&建模

配置/ routes.rb中

map.resources :foos

此路线使您能够区分请求中的不同格式。这样,控制器操作中的respond_to方法将检查params[:format]以确定要呈现的视图。对/foos/1.xml的请求会将params[:format]设置为xml,依此类推。

app / models / foo.rb,app / models / bar.rb,app / models / field.rb

class Foo < ActiveRecord::Base
  has_many :bars
  has_many :fields, :through => :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo
  has_many :fields

  def quux?
    self[:quux] || foo.quux? # sample overridable property
  end
end

class Field < ActiveRecord::Base
  # t.string :name, :value, :type
  belongs_to :bar
  delegate :foo, :to => :bar

  def quux?
    self[:quux] || bar.quux? # sample overridable property
  end
end

我对您描述的建模的基本了解。特别值得注意的是:如果范围内有Field对象,则可以访问层次结构中的所有父对象。您可以在模型上为您希望在链中的更高级别覆盖的任何属性创建访问器方法。上面给出了这种特性的一个例子。

控制器

foos_controller.rb

class FoosController < ApplicationController
  def show
    @foo = Foo.find(params[:id])
    respond_to do |format|
      format.html
      format.xml
      format.pdf
    end
  end
end

你的控制器真的不应该比这更复杂。 “瘦的控制器,脂肪模型”,以及所有这些。它是respond_to,它为所请求的格式选择不同的视图(您可能已经熟悉),这很重要。

观点:

这是事情变得有趣并且代码路径开始分歧的地方。

我要说明的是,不要直接跳到某种类型的构建器对象,而应该坚持使用partials和helpers。使用restful约定(map.resourcesrespond_to)将选择正确格式的模板。所以,是的,对于每种字段类型和每种格式,最终可能会有很多小文件。但是从那里,你可以将共享逻辑抽象为帮助者。

如果你需要主要在Field级别的构建器,那么这种方法会特别有效,在那里你的文档都具有类似的结构。

接下来是一个随意的例子,只是为了概述我所倡导的方法的简单性:

应用程序/视图/ FOOS / show.html.erb

<%= render @foo.bars %>

应用程序/视图/棒/ _bar.html.erb

<%= render bar.fields %>

应用程序/视图/场/ _field.html.erb

<%= render field.type, :field => field %>

这是一个重要的技巧:使用field.type的值来选择适当的部分。请记住也要传递字段对象,以便在该部分范围内可用。

(我认为Rails会为你正确地构建部分路径的其余部分 - 值得进行一些实验。这种方法可能变得足够复杂,足以证明抽象是一个帮助。render_field(field)或其他一些。)

应用程序/视图/场/ _telephone.html.erb

<!--
  This partial has access to field, which has access to both foo and bar.
  We know the format based on the context of the partial file name.
  Need further abstraction? Call a helper.
-->
<some_wrapper_code_for_this_format>
  <%= format_telephone_field(field) %>
</some_wrapper_code_for_this_format>

最后:助手

应用程序/助手/ fields_helper.rb

class FieldsHelper
  def format_telephone_field(field)
    # don't forget, we have access to params here, too
    # including params[:format]
    if field.quux?
      number_to_phone(field.value)
    else
      number_to_phone(field.value, :area_code => true)
    end
  end
end

XML,PDF等

支持不同的格式将是使用适当的扩展创建新模板的问题。您可以在这些模板和部分位置放置特定于格式的标记。应尽可能将共享文档语义推入帮助程序。

总之

我认为我没有回答你的确切问题。或者我也许。重点是:我不是从一开始就构建复杂的东西,而是坚持简单的约定和分歧,只是为了处理过多的重复。 YAGNI,TDD,YMMV等。

玩得开心!