Rails控制器变量混乱

时间:2017-10-31 19:24:50

标签: ruby-on-rails ruby-on-rails-4 model-view-controller controller instance-variables

我有一个控制器,我觉得有太多的实例变量。

控制器从各个地方提取数据,感觉真的很草率。 我看过一些Sandi Metz谈话,读书和其他研究,我希望有良好的练习,但我不知道该怎么做。

这个方法是拉动所有数据并将其发送到我的视图,我能够让它工作,我只是知道这不是一个很好的方法来实现它,我希望有人可以点我可以看一些代码示例,文档,视频,或者帮助我了解如何实现更好的风格。

我搜索了SO和Google ,但我发现大多数人都说要向视图发送哈希或JSON,我想在开始之前知道这是否理想。

客户端,项目,人员,角色控制器和模型具有非常相似的代码,我正在努力将其重构为更干燥。

例如,Client,Project,Person和Role财务控制器几乎具有与此完全相同的控制器索引代码。 :(

如果有帮助,我会很乐意添加更多代码!

这是project_financials_controller#index

它几乎从视图中获取数据并从数据库中提取大量数据并将其发送到视图。我目前只使用索引方法,因为它只应该是一个'视图',但现在我们可以添加时间,不同客户端等过滤器,所以我想我需要以某种方式解决它。

我确实有一个financial_reports_nav模型,这个模型可以调用我可以使用更多,甚至可以创建一个financial_reports_controller来从适当的模型中提取数据,我甚至不需要4个不同的控制器... < / p>

我完全接受任何意见/批评!

def index
  # CPPR = Client, Project, Person, Role
  @financial_type = 'project'
  @financial_params = params

  # This pulls the timeframe from the view and figures out the dates requested. (eg. "Last Week")
  @timeframe = Financial.time_frame(@financial_params[:timeframe], current_company.timezone, params[:start_date], params[:end_date])

  # This grabs all the data required to recall this financial report view at a later time
  @financial_nav = FinancialReportNav.set_financial_type(@current_user.id,@financial_type, @start_date, @end_date)

  # Grab all active and inactive people for client
  @people = Person.active.all
  @deleted_people = Person.inactive.all

  # This sends over all the info needed to generate the financial reports
  @project_financial_populate = Financial.new(@financial_params, @financial_type).populate_project_financials(current_company.default_hourly_cost, current_company.billing_rate, @timeframe[:start_date],@timeframe[:end_date])

  # This just pulls all the data from the database that the @project_financial_populate just populated (Can't we just use that??)
  @financial_rows = ProjectFinancial.all.map { |p| [ p.project_id, p.billable_hours, p.revenue,p.real_rate, p.hourly_expense, p.labor_expense_total, p.salary_expense,  p.gross_profit, p.profit_margin, p.missing_hourly_expense, p.missing_billable_rate ] }

  # Using the same view for CPPR's
  # Clients has an items count, so we just stuff everything into the first array slot
  @items = [1]

  # If these are not null then they show an option to change the financial filter type.
  @filter_by_client = Client.find_by('id = ?', @financial_params[:filter_by_client])
  @filter_by_project = Project.find_by('id = ?', @financial_params[:filter_by_project])
  @filter_by_person = Person.find_by('id = ?', @financial_params[:filter_by_person])
  @filter_by_role = PersonRole.find_by('id = ?', @financial_params[:filter_by_role])

  # This pulls a list of CPPR's that have tracked time in the requested timeframe
  @project_list = Financial.project_list(@timeframe[:start_date], @timeframe[:end_date])
  @client_list = Financial.client_list(@timeframe[:start_date], @timeframe[:end_date])
  @people_list = Financial.people_list(@timeframe[:start_date], @timeframe[:end_date])
end

1 个答案:

答案 0 :(得分:0)

每当我注意到我至少有3个重复代码实例时,我总是倾向于将代码重构为DRY,但是我需要在将来证明新代码足够灵活,以备将来可能的更改;然而,所有这些都考虑了时间。

鉴于你当前的代码已经告诉了我的偏好,这就是我要做的事情:

  1. 模型继承
  2. 控制器继承
  3. 共享模板
  4. 路线

    <强>配置/ routes.rb中

    resources :client_financial
    resources :project_financial
    resources :person_financial
    resources :role_financial
    

    模型

    应用/模型/ financial_record.rb

    class FinancialRecord < ActiveRecord::Base # or ApplicationRecord if > Rails 5
      self.abstract_class = true
    
      # your shared "financials" model logic here
    end
    

    应用/模型/ client_financial.rb

    class ClientFinancial < FinancialRecord
      # override "financials" methods here if necessary
      # or, add new model specific methods / implementation
    end
    

    应用/模型/ project_financial.rb

    class ProjectFinancial < FinancialRecord
      # override "financials" methods here if necessary
      # or, add new model specific methods / implementation
    end
    

    应用/模型/ person_financial.rb

    class PersonFinancial < FinancialRecord
      # override "financials" methods here if necessary
      # or, add new model specific methods / implementation
    end
    

    应用/模型/ role_financial.rb

    class RoleFinancial < FinancialRecord
      # override "financials" methods here if necessary
      # or, add new model specific methods / implementation
    end
    

    控制器

    应用/控制器/ financial_controller.rb

    class FinancialController < ApplicationController
      before_action :set_instance_variables, only: :index
    
      protected
    
      def set_instance_variables
        # strips the last "Controller" substring and change to underscore: i.e. ProjectFinancialsController becomes project_financials
        @financial_type = controller_name[0..(-'Controller'.length - 1)].underscore
    
        # get the corresponding Model class
        model = @financial_type.camelcase.constantize
        # get the correspond Financial Model class
        financial_model = "#{@financial_type.camelcase}Financial".constantize
    
        @financial_params = params
    
        @timeframe = Financial.time_frame(@financial_params[:timeframe], current_company.timezone, params[:start_date], params[:end_date])
    
        # I dont know where you set @start_date and @end_date
        @financial_nav = FinancialReportNav.set_financial_type(@current_user.id,@financial_type, @start_date, @end_date)
    
        # renamed (or you can set this instance variable name dynamically)
        @records = model.active.all
        # renamed (or you can set this instance variable name dynamically)
        @deleted_records = model.inactive.all
    
        @financial_populate = Financial.new(@financial_params, @financial_type).populate_project_financials(current_company.default_hourly_cost, current_company.billing_rate, @timeframe[:start_date],@timeframe[:end_date])
    
        @financial_rows = financial_model.all.map { |p| [ p.project_id, p.billable_hours, p.revenue,p.real_rate, p.hourly_expense, p.labor_expense_total, p.salary_expense,  p.gross_profit, p.profit_margin, p.missing_hourly_expense, p.missing_billable_rate ] }
    
        @items = [1]
    
        @filter_by_client = Client.find_by('id = ?', @financial_params[:filter_by_client])
        @filter_by_project = Project.find_by('id = ?', @financial_params[:filter_by_project])
        @filter_by_person = Person.find_by('id = ?', @financial_params[:filter_by_person])
        @filter_by_role = PersonRole.find_by('id = ?', @financial_params[:filter_by_role])
    
        @project_list = Financial.project_list(@timeframe[:start_date], @timeframe[:end_date])
        @client_list = Financial.client_list(@timeframe[:start_date], @timeframe[:end_date])
        @people_list = Financial.people_list(@timeframe[:start_date], @timeframe[:end_date])
      end
    end
    

    应用/控制器/ client_financials_controller.rb

    class ClientFinancialsController < FinancialController
      def index
        render template: 'financials/index'
      end
    end
    

    应用/控制器/ project_financials_controller.rb

    class ProjectFinancialsController < FinancialController
      def index
        render template: 'financials/index'
      end
    end
    

    应用/控制器/ person_financials_controller.rb

    class ProjectFinancialsController < FinancialController
      def index
        render template: 'financials/index'
      end
    end
    

    应用/控制器/ role_financials_controller.rb

    class ProjectFinancialsController < FinancialController
      def index
        render template: 'financials/index'
      end
    end
    

    浏览

    应用/视图/金融/ index.html.erb

    <!-- YOUR SHARED "FINANCIALS" INDEX HTML HERE -->
    

    P.S。这只是一个简单的重构。在不知道项目的更全面范围和未来计划的情况下,我会做到这一点。话虽如此,我会考虑使用“多态”关联,然后只有一个路由端点(即resources :financials)然后只传入一个params过滤器,如:params[:financial_type],它直接映射financial_type 1}}多态列名。