2011-12-13 48 views
0

我今天开始阅读Rails Antipatterns,并希望将其中一些实践付诸实践。我正在重构最初内置在控制器中的CSV导出。由于这是一个不好的做法,我把它分解成模型......然后是它自己的模型。这样我可以将该方法用于其他目的。在Rails 3模型中调用实例方法

我有下面的方法模型:

#app/models/imagery_request.rb 
class ImageryRequest < ActiveRecord::Base 

def convert 
    ImageryRequestConverter.new(self) 
    end 

end 

我有另一种模式是这样的:

#app/models/imagery_request_converter.rb 
class ImageryRequestConverter 
    attr_reader :imagery_requests 

    def initialize(imagery_requests) 
    @imagery_requests = imagery_requests 
    end 

    def to_csv 
    csv_string = FasterCSV.generate do |csv| 
     # header row 
     csv << ["id", "service_name", "description", "first_name", "last_name", "email", "phone_contact", "region", 
     "imagery_type", "file_type", "pixel_type", "total_images", 
     "tile_size", "progress", "expected_date", "high_priority", "priority_justification", 
     "raw_data_location", "service_overviews", "is_def", 
     "isc_def", "special_instructions", "navigational_path", "FY Queue", 
     "created_at", "updated_at"] 
     # data rows 
     @imagery_requests.each do |ir| 
     csv << [ir.id, ir.service_name, ir.description, ir.first_name, ir.last_name, ir.email, 
      ir.phone_contact, ir.region, ir.imagery_type, ir.file_type, ir.pixel_type, 
      ir.total_images, ir.tile_size, ir.progress, ir.expected_date, ir.high_priority, 
      ir.priority_justification, ir.raw_data_location, ir.service_overviews, 
      ir.is_def, ir.isc_def, ir.special_instructions, ir.navigational_path, 
      ir.fyqueue, ir.created_at, ir.updated_at 
     ] 
     end 
     # send it to the browser with proper headers 
     send_data csv_string, 
     :type => 'text/csv; charset=iso-8859-1; header=present', 
     :disposition => "attachment; filename=Imagery_Requests-#{Time.now.strftime("%Y%m%d")}.csv" 
    end 
    end 
end 

当我尝试在我看来与参考这个:

<%= link_to @imagery_requests.convert.to_csv %> 

我收到一个错误:

undefined method `convert' for #<ActiveRecord::Relation:0x21f966d0> 

如何调用此方法?

回答

0

@imagery_requests变量实际上是一个Relation对象,它一旦被调用就会始终是记录的集合。您正在对此对象调用实例方法,因为您在整个集合上调用该方法,而不是此集合中的对象,所以无法工作。

除此之外,调用link_to中的方法,就像你在那里做的一样,不会全部工作。该链接应转到控制器操作,该操作解析这些请求并正确返回CSV。

+0

所以我基本上必须把所有CSV创建代码放在控制器动作中?必须有更优雅/可重用的方式。 –

0

后续瑞恩比格的回答

你有两个问题:

  1. 要调用转换方法,你需要指定集合中的对象。例如。 @imagery_requests.first.convert.to_csv@imagery_requests[i].convert.to_csv

  2. 您不能链接到文件的内容,这是您的代码正在尝试执行的操作。相反,您需要链接到一个新的操作,例如download_csv,它将返回csv。

由于新的动作(下载文件的CSV版本)是不标准的REST风格的套件的一部分,你需要添加额外行动的资源。 Eg article

用户体验(UX)的选择: 您可以创建一个下载作为ImageryRequests集合的每个成员 CSV作用,这将意味着谁想要CSV版本5条的要求将需要一个人下载5个不同的csv文件。

或者您可以为集合创建下载。但是每个http请求都有一个响应。通常的解决方案:使用多个文件将一个zip文件返回给客户端。

在任何情况下,您都应该将代码移入模型并移出控制器。

EMail UX解决方案如果创建csv文件需要一秒多的时间,这个特别好,因为Rails是单线程的,所有响应都应该非常快。

csv下载的表单应该将电子邮件地址作为参数,而不是使用文件进行响应。然后使用DelayJob或其他调度程序在后台通过电子邮件发送csv文件。

由于电子邮件可能有多个附件,您可以将该zip文件作为电子邮件附件发送或发送多个csv文件。

提示:您的表单应处理多个电子邮件地址,并允许用户在电子邮件中包含封面注释。这将使请求者能够将报告发送给多个人。

+0

对,我了解有关link_to的查看问题。就我的用户体验而言,我希望所有行都能以一个CSV文件返回。将来,我将会有一个高级搜索,它会根据日期范围返回一个CSV。 –

相关问题