我有一个有两种方法的Rails控制器。两种方法都使用了一些相同的变量,我想知道如何将它重构为控制器中某个模型中的一个方法,使它们比现在更容易重用。
class ChartsController < ApplicationController
before_filter :authenticate_user!, :company_id
def service_level
latest_date = Invoice.where(:account_id => @company.accounts).maximum(:invc_date)
invoices_filter = { :invoices => { :invc_date => (latest_date - 3.months)..latest_date } }
invoices = Invoice.where({:account_id => @company.accounts}.merge(invoices_filter))
details = InvoiceDetail.joins(:type).where(:invoice_id => invoices)
freight_details = details.where(:invoice_detail_types => { :category => 'freight' })
freight_groups = freight_details.group(:family).select("family, count(distinct package_id), sum(base_charge + discount)")
vol_data = {}
spend_data = {}
@charts = {}
@charts[:service_analysis] = {
:vol_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.count.to_i] }],
:spend_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.sum.to_f] }]
}
render partial: 'service_level'
end
def weight_summary
latest_date = Invoice.where(:account_id => @company.accounts).maximum(:invc_date)
invoices_filter = { :invoices => { :invc_date => (latest_date - 3.months)..latest_date } }
invoices = Invoice.where({:account_id => @company.accounts}.merge(invoices_filter))
details = InvoiceDetail.joins(:type).where(:invoice_id => invoices)
freight_details = details.where(:invoice_detail_types => { :category => 'freight' })
packages = freight_details.joins(:package, :invoice)
vol_data = {}
spend_data = {}
packages.group(:zone).select("zone, count(distinct package_id), sum(base_charge + discount)").each do |row|
case row.zone
when '02'..'08', '002'..'008', '102'..'108', '132'..'138', '202'..'208', '242'..'248', '302'..'308'
zg = row.zone[-1]
when '09'..'17', '124'..'126', '224'..'226'
zg = 'AK/HI/PR'
else
zg = 'Import/Export'
end
vol_data[zg] = (vol_data[zg] || 0) + row.count.to_i
spend_data[zg] = (spend_data[zg] || 0) + row.sum.to_f
end
@charts = {}
@charts[:weight_analysis] = {
:vol_data => Hash[(vol_data.sort_by {|key, value| key.scan(/\d+/)[0].to_i})],
:spend_data => Hash[(spend_data.sort_by {|key, value| key.scan(/\d+/)[0].to_i})]
}
render partial: 'weight_summary'
end
end
答案 0 :(得分:3)
我建议使用模型类方法来处理数据。例如
freight_details = details.where(:invoice_detail_types => { :category => 'freight' })
freight_groups = freight_details.group(:family).select("family, count(distinct package_id), sum(base_charge + discount)")
vol_data = {}
spend_data = {}
@charts = {}
@charts[:service_analysis] = {
:vol_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.count.to_i] }],
:spend_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.sum.to_f] }]
}
可以移动到返回charts
的模型类方法。以同样的方式,你可以重构你的第二种方法。任何类型的业务逻辑和数据处理都应该在模型中处理
此外,我可以看到控制器中有太多未使用的局部变量。控制器应尽可能薄。
答案 1 :(得分:1)
使用像装饰者这样的概念
module Chart
extend self
def service_analysis(freight_groups, freight_groups)
end
end
class ChartsController < ApplicationController
@chart = Chart.service_analysis(freight_groups, freight_groups)
end
注意:不要将计算代码放在视图中,它很慢
答案 2 :(得分:1)
如果您决定将其保留在控制器中,请尝试以下操作:
class ChartsController < ApplicationController before_filter :authenticate_user!, :company_id before_filter :load_data, :only => [:service_level, weight_summary] def service_level freight_groups = @freight_details.group(:family).select("family, count(distinct package_id), sum(base_charge + discount)") @charts = {} @charts[:service_analysis] = { :vol_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.count.to_i] }], :spend_data => Hash[freight_groups.map { |row| [InvoiceDetailFamily[row.family].name, row.sum.to_f] }] } render partial: 'service_level' end def weight_summary packages = @freight_details.joins(:package, :invoice) vol_data = {} spend_data = {} packages.group(:zone).select("zone, count(distinct package_id), sum(base_charge + discount)").each do |row| case row.zone when '02'..'08', '002'..'008', '102'..'108', '132'..'138', '202'..'208', '242'..'248', '302'..'308' zg = row.zone[-1] when '09'..'17', '124'..'126', '224'..'226' zg = 'AK/HI/PR' else zg = 'Import/Export' end vol_data[zg] = (vol_data[zg] || 0) + row.count.to_i spend_data[zg] = (spend_data[zg] || 0) + row.sum.to_f end @charts = {} @charts[:weight_analysis] = { :vol_data => Hash[(vol_data.sort_by {|key, value| key.scan(/\d+/)[0].to_i})], :spend_data => Hash[(spend_data.sort_by {|key, value| key.scan(/\d+/)[0].to_i})] } render partial: 'weight_summary' end private def load_data latest_date = Invoice.where(:account_id => @company.accounts).maximum(:invc_date) invoices_filter = { :invoices => { :invc_date => (latest_date - 3.months)..latest_date } } invoices = Invoice.where({:account_id => @company.accounts}.merge(invoices_filter)) details = InvoiceDetail.joins(:type).where(:invoice_id => invoices) @freight_details = details.where(:invoice_detail_types => { :category => 'freight' }) end end
两种方法都可以使用实例变量@freight_details。 before_filter只对这两种方法执行load_data方法。
祝你好运!