Ruby:没有使用initialize方法通过new方法实例化对象

时间:2014-11-30 17:54:59

标签: ruby-on-rails ruby

使用案例:上传并处理csv文件并在数据库中记录上传会话。

方法:使用收集csv文件的新方法和创建,填充和保存上传对象的创建方法,创建一个模型来保存有关上传会话和控制器的数据。

问题:该模型包含一个初始化方法(参见模型代码),该方法似乎具有初始化对象所需的信息(请参阅调试输出),但是,当控制器尝试使用模型的新方法时,该尝试不会得到一个有用的对象(参见控制器代码和调试输出)。

问题:创建call_history_upload对象需要更改什么?

Windows 8.1上的Rails 3.2.13上的Ruby 1.9.3

型号代码:

class CallHistoryUpload < ActiveRecord::Base
  attr_accessible :file_name, :user_id, :record_count, :international_call_count, :unknown_client_count

  has_many :call_histories, dependent: :destroy

  require 'csv'

  def initialize( file_name, user_id )
    logger.debug( "CallHistoryUpload.initialize start")
    logger.debug( "  call_history_file  = " + file_name )
    logger.debug( "  user_id            = " + user_id )
    @file_name = file_name
    @user_id = user_id
    @record_count = 0
    @international_call_count = 0
    @unknown_client_count = 0
    logger.debug( "CallHistoryUpload.initialize end")
  end
end

控制器代码:

class CallHistoryUploadsController < ApplicationController

  def get_client_id
    -1
  end

  # GET /call_history_uploads/new
  # GET /call_history_uploads/new.json
  def new
    @call_history_upload = CallHistoryUpload.new( "Unknown", session[:user_id] ) # Needed to suppoprt json

    respond_to do |format|
     format.html # new.html.erb
      format.json { render json: @call_history_upload }
    end
  end

  # POST /call_history_uploads
  # POST /call_history_uploads.json
  def create
    logger.debug( "CallHistoryUploadsController start")
    @call_history_upload = CallHistoryUpload.new( params[:call_history_file].original_filename, session[:user_id] )
    logger.debug( "CallHistoryUploadsController after instantiation")
<<This is line 24>>logger.debug( "call_history_upload.file_name = " + @call_history_upload.file_name )

    respond_to do |format|
      if @call_history_upload.save
        format.html { redirect_to @call_history_upload, notice: 'Call history upload was successful.' }
        format.json { render json: @call_history_upload, status: :created, location: @call_history_upload }
      else
        format.html { render action: "new" }
        format.json { render json: @call_history_upload.errors, status: :unprocessable_entity }
      end
    end
  end 
end

日志输出:

Started POST "/call_history_uploads" for 127.0.0.1 at 2014-11-30 08:40:27 -0500
Processing by CallHistoryUploadsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"iDugtKa8b/U9q71Gk86sJMK6hnX1gvgQt496PX2q4Oo=", "call_history_file"=>#<ActionDispatch::Http::UploadedFile:0x4717788 @original_filename="Test_kiyvivzokpysxilfoftavyuftot.csv", @content_type="application/vnd.ms-excel", @headers="Content-Disposition: form-data; name=\"call_history_file\"; filename=\"Test_kiyvivzokpysxilfoftavyuftot.csv\"\r\nContent-Type: application/vnd.ms-excel\r\n", @tempfile=#<File:C:/Users/Gene/AppData/Local/Temp/RackMultipart20141130-5144-yn8i9>>, "commit"=>"Submit"}
CallHistoryUploadsController start
CallHistoryUpload.initialize start
  call_history_file  = Test_kiyvivzokpysxilfoftavyuftot.csv
  user_id            = KinteraAdmin
CallHistoryUpload.initialize end
CallHistoryUploadsController after instantiation
Completed 500 Internal Server Error in 0ms

NoMethodError (undefined method `has_key?' for nil:NilClass):
  app/controllers/call_history_uploads_controller.rb:24:in `create'


  Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_trace.erb (0.0ms)
  Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.0ms)
  Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (0.0ms)

2 个答案:

答案 0 :(得分:1)

天啊,多么糟糕......你应该使用构图而不是继承。第一次重构可能是:

class CallHistoryUpload < SimpleDelegator
  class Model < ActiveRecord::Base
    has_many :call_histories, dependent: :destroy
  end

  def initialize(file_name, user_id)
    logger.debug( 'CallHistoryUpload.initialize start')
    logger.debug( "  call_history_file  = #{file_name}" )
    logger.debug( "  user_id            = #{user_id}" )
    super( new_model(file_name, user_id) )
    logger.debug( "CallHistoryUpload.initialize end")
  end

  def model
    __getobj__
  end

  def self.create(file_name, user_id)
    logger.debug('CallHistoryUploadsController start')
    chu = self.new(file_name, user_id)
    logger.debug( 'CallHistoryUploadsController after instantiation')
    logger.debug( "call_history_upload.file_name = #{chu.file_name}")

    return chu
  end

  private
  def new_model(file_name, user_id)
    self.class::Model.new({
      file_name: file_name,
      user_id: user_id,
      record_count: 0,
      international_call_count: 0,
      unknown_client_count: 0,
    })
  end
end

在控制器中,我们只更改创建:

  # POST /call_history_uploads
  # POST /call_history_uploads.json
  def create
    @call_history_upload = CallHistoryUpload.create( params[:call_history_file].original_filename, session[:user_id] )

    # rest of the code

如果您使用form_for之类的内容,则可能需要将@call_history_upload.model传递给它。

总是尽量在控制器中放置尽可能少的逻辑并将其封装在某个对象中:)

答案 1 :(得分:0)

部分问题似乎是你在初始化时凌驾于ActiveRecord::Base的正常行为。活动记录对象允许您填充对象的属性,如MyObject.new(property: 'value')

从对象中删除initialize方法并使用

实例化对象
file_name = params[:call_history_file].original_filename 
user_id = session[:user_id]
CallHistoryUpload.new(file_name: file_name, user_id: user_id)

在初始化方法中调用super以允许ActiveRecord::Base执行此类操作

def initialize(file_name, user_id)
  logger.debug( "CallHistoryUpload.initialize start")
  logger.debug( "  call_history_file  = " + file_name )
  logger.debug( "  user_id            = " + user_id )
  @record_count = 0
  @international_call_count = 0
  @unknown_client_count = 0
  super(file_name: file_name, user_id: user_id)
  logger.debug( "CallHistoryUpload.initialize end")
end

我推荐第一种方法,因为它会清理你的模型。如果需要在模型实例上指定默认值,我建议创建一个带有描述性名称的类方法,该方法在调用new时设置此值,并执行与第一种方法相同的赋值。使用after_initialize回调也可以设置默认值,但要注意它的后果。

如果您是新手并且正在尝试习惯Rails约定,我建议您阅读Rails入门指南。