2011-06-07 51 views
5

我有多个ActiveRecord子类Item的实例数组,它们需要根据最早的事件循环打印。在这种情况下,我需要打印的打印出来的付款和维护日期如下:5天in 3天需要
B项支付7天
需要
项目的支付需要循环遍历Ruby中的多个阵列

项目A保养。中8天

需要B项维修

我现在有寻找maintenance两个查询和payment项目(非排他性查询)和类似以下输出它们:

<%- item_p = nil -%> 
<%- item_m = nil -%> 

<%- loop do -%> 
    <% item_p ||= @items_p.shift %> 
    <% item_m ||= @items_m.shift %> 

    <%- if item_p.nil? and item_m.nil? then break -%> 
    <%- elsif item_p and (item_m.nil? or item_p.paymt < item_m.maint) then -%> 
    <%= item_p.name %> payment required in ... 
    <%- elsif item_m and (item_p.nil? or item_m.maint < item_p.paymt) then -%> 
    <%= item_m.name %> maintenance required in ... 
    <%- end -%> 
<%- end -%> 

任何方式来清理上面(丑)代码的可读性?

回答

2

这是快速和肮脏的(即不优化):

# In your controller: 
@items = @items_p.map{ |item| {:item => item, :days => item.paymt, :description => "payment"} } 
@items += @items_m.map{ |item| {:item => item, :days => item.maint, :description => "maintenance"} } 
@items = @items.sort_by{ |item| item[:day] } 

# In your view: 
<% @items.each do |item| %> 
    <%= item[:item].name %> <%= item[:description] %> required in <%= item[:days] %> days 
<% end %> 
+0

+1这也是我的想法,很自然地我喜欢这个想法:-) – DigitalRoss 2011-06-07 20:51:48

5

拥抱鸭打字,并确保你的对象是多态性。您希望您的付款项目为可比较维护项目,以便对它们进行分类。

因此,假设你有一个PaymentMaintenance类:

module Due 
    include Comparable 

    # Compare this object with another. Used for sorting. 
    def <=>(other) 
    self.due <=> other.due 
    end 
end 

class Payment < ActiveRecord::Base 
    include Due 

    alias_method :due, :payment 

    def action 
    "#{name} requires payment" 
    end 
end 

class Maintenance < ActiveRecord::Base 
    include Due 

    alias_method :due, :maintenance 

    def action 
    "#{name} requires maintenance" 
    end 
end 

看看我们如何创造类的actiondue<=>方法?我们也注意包含Ruby内置模块Comparable。这使我们可以做到以下几点:

# Assuming 'payment' and 'maintenance' are date fields... 
a = Payment.new :payment => 3.days.from_now 
b = Maintenance.new :maintenance => 2.days.from_now 
[a, b].sort 
#=> [b, a] 

的看法则变得简单:

<% (@payment_items + @maintenance_items).sort.each do |item| %> 
    <%= item.action %> in <%= distance_of_time_in_words_to_now(item.due) %><br/> 
<% end %> 

我敢肯定,我没有得到你的执行权的细节,但我希望这让你了解如何处理你的问题。

+0

感谢您的回应,但是我现在的模型是一个'Item',它具有'required'和'payment'两个属性(作为日期) 。这会继续吗?如我的示例所示,项目A显示为维护和付款。在这种情况下,所述物品具有需要付款的“周年纪念”和需要对所述物品进行维护的“周年纪念”。 – Stussa 2011-06-07 21:20:18

1

在你看来你太过分了。真的,你应该在控制器中找出所有这些,并通过一个可以迭代显示的清理结构。

举个例子:

length = [ @items_p.length, @items_m.length ].sort.last 

@messages = [ ] 

length.times do |i| 
    item_p = @items_p[i] 
    item_m = @items_m[i] 

    if (item_p and (item_m and item_p.paymt < item_m.maint) or !item_m) 
    @messages << "#{item_p.name} payment required in ..." 
    elsif (item_m and (item_p and item_m.maint < item_p.paymt) or !item_p) 
    @messages << "#{item_m.name} maintenance required in ..." 
    end 
end 

可以根据实际需要将随后遍历@messages

这里真正的问题是,你没有从战略上讲这些东西的结构这些对象。如果您在截止日期有单一方法,而不必根据类型区分paymtmaint,那将会很好。同样,如果两者都配对成一个数组而不是单独提供,会更好。

如果您在[ p, m ]对有他们,你可以遍历更加简单:

items.each do |pair| 
    first_due = pair.compact.sort_by(&:due).first 
    @messages << "#{first_due.name} #{first_due.action} required in ..." 
end 

action方法会根据需要返回paymentmaintenance

+0

在我看来,你不想在你的控制器中做到这一点。 – molf 2011-06-07 21:13:44

+0

@molf这看起来像'胖控制器'(我听说要避免)。在这种情况下,我应该在模型中做到这一点,谢谢! – Stussa 2011-06-07 21:22:11

+0

从视图到控制器,以及后来,如果从控制器到模型都可以找到一个好的模式,那么就会有重构的程度。不过,我不知道这些模型是什么。关于在哪里放置重型设备,无论是型号还是控制器,都有不同的思路,这可能取决于您的面向对象背景。铁轨倾向于较重的车型。 – tadman 2011-06-08 00:57:06