Ruby on Rails最佳实践 – 大型控制器与小型控制器

我需要一些关于Ruby on Rails 最佳实践的信息,特别是对于必须做很多事情的Controller ,因此,一个简单的“show”动作现在就行了。 我知道,它不是很好,我有特定的代码。

这是一个示例代码:

def show sound = Sound.find(params[:id]) @xml_path = File.dirname(sound.file.path) s3 = AWS::S3.new( :access_key_id => 'XXX', :secret_access_key => 'XXX') @url = s3.buckets['dev'].objects[sound.file.path[1..-1]].url_for(:read, :expires => 10*60) if sound.id_job != 0 && sound.transcript_progress != 100 @response = Savon.client("http://srap.php?wsdl").request(:avance) do soap.body = { :Jeton => "abcdef", :ID_job => sound.id_job, } end @response = @response.to_hash @progress = @response[:avance][:avancement].to_s.split("#")[1]# ID_job received is formed like "OK#123", we keep "123" if @progress == "Termine" sound.transcript_progress = 100 elsif @progress == "ERROR" flash.now[:alert] = "Oups, il semblerait que le fichier soit illisible, ou qu'il n'y ait rien a ecouter !" elsif @progress != "Queued" sound.transcript_progress = @response[:avance_response][:avancement].to_s.split("#")[2].split("%")[0].to_i end sound.save end if sound.transcript_progress == 100 # If transcription finished # Get XML File URL on the FTP @xml_path = Savon.client("http://srap.php?wsdl").request(:donneResultat) do soap.body = { :Jeton => "XXX", :FichierSon => sound.id_job } end # Parse XML Path URL on Kimsufi @xml_path = @xml_path.to_hash[:donne_resultat_transposition_response][:chemin_fichier].to_s.split("#")[2].to_s.split("/")[5] # Create local directory (/tmp/sounds) for XML Temp Save if ! File.directory?(Rails.root.to_s + '/tmp/sounds') Dir.mkdir(Rails.root.to_s + '/tmp/sounds') end # Get XML from FTP ftp=Net::FTP.new ftp.connect("ftp.com", 21) ftp.login("XXX", "XXX") if ftp.closed? flash.now[:alert] = "Oups, il semblerait qu'il y ait eu un problème ! Merci d'actualiser la page" else ftp.passive = true ftp.chdir('results') ftp.getbinaryfile(@xml_path, Rails.root.to_s + '/tmp/sounds/' + @xml_path) ftp.close end # Send XML on S3 s3 = AWS::S3.new( :access_key_id => 'XXX', :secret_access_key => 'XXX') @xml_new = (File.dirname(@sound.file.path) + '/' + File.basename(@xml_path))[1..-1] s3.buckets['dev'].objects[@xml_new].write(Pathname.new(Rails.root.to_s + '/tmp/sounds/' + @xml_path)) @file = s3.buckets['dev'].objects[@xml_new].read() end # A lot of logic again, i've not did it yet end 

正如你所看到的,我在这里有很多逻辑,我必须检查转录是否结束,如果没有,请更新progress_bar(@ sound.transcript_progress),如果是,我首先必须连接到soap动作才能获得XML路径,然后通过FTP获取XML,然后将其存入Amazon S3(Shitty SOAP,我必须重新解析所有响应……)。

在我的所有动作控制器中,我必须在S3 / SOAP / FTP上连接,而不是以相同的顺序连接。所以我想为每个人做一个类,比如C ++,一个抽象。 我希望这些东西完成,我不关心(很多)它是如何完成的。 但是MVC的最佳实践是什么? 我必须创建一个新文件夹“Class?” 一个新的控制器?

这是一个长篇评论 ,因为它解释了你的困境的根源,但没有提供任何解决方案。

这个问题实际上是由对MVR的误解造成的,而RoR正在普及。

它是两个因素的结合,导致控制器内爆:

  • 一方面,你有贫血模型 ,因为RoR使用ORM实例的集合,而不是真正的模型层。 原因在于Rails最初是为快速原型设计(生成抛弃代码)而创建的。 原型设计正是活跃记录最擅长的。 使用scaffolding,您可以轻松地从现有数据库生成活动记录结构。

    但这会导致某些域业务逻辑泄漏到您的控制器中。

  • 另一方面,你有不存在的观点。 由于目标是原型设计,Rails倾向于通过将视图合并到控制器中来摆脱实际上包含表示逻辑的视图。 现在缺少的视图被简单的模板取代,简称模板称为“视图”。

    这会强制控制器包含表示逻辑。

这两个因素将是我为什么试图断言,RoR甚至不是MVC框架的原因。 生成的模式实际上更接近Model-View-Presenter 。 虽然它已被简化到它开始打破关注点的程度 。

您的大部分逻辑都不属于控制器。 控制器的职责是将输入(HTTP请求及其参数)与输出(您的视图)联系起来。 其他一切都是应该在模型中实现的业务逻辑 – 在你的情况下是Sound ,它看起来像。 例如,每个if块都可以作为Sound类的实例方法实现。 如果您发现自己在各种模型中重用代码(如AWS存储位),请在模块 (在lib下)中实现它们,并在该模型中包含该模块。

看起来所有这些都应该重构为模型(或库模块)并分解为更小的函数。 最好的原因是因为您可以设置unit testing来单独测试较小的部件。 控制器只需要实例化模型并将数据返回给浏览器。