2012-04-11 27 views
5

Tôi đang tạo tính năng nhập để nhập tệp CSV vào một vài bảng. Tôi đã tạo một mô-đun có tên là CsvParser phân tích tệp CSV và tạo bản ghi. Các mô hình của tôi nhận hành động tạo mở rộng CsvParser. Họ thực hiện cuộc gọi đến CsvParser.create và chuyển đúng thứ tự thuộc tính và một lambda tùy chọn được gọi là value_parser. Lambda này biến đổi các giá trị trong một băm thành một định dạng được dựng sẵn.Kiểm tra lambda

class Mutation < ActiveRecord::Base 
    extend CsvParser 

    def self.import_csv(csv_file) 
    attribute_order = %w[reg_nr receipt_date reference_number book_date is_credit sum balance description] 

    value_parser = lambda do |h| 
     h["is_credit"] = ((h["is_credit"] == 'B') if h["is_credit"].present?) 
     h["sum"] = -1 * h["sum"].to_f unless h["is_credit"] 
     return [h] 
    end 

    CsvParser.create(csv_file, self, attribute_order, value_parser)  
    end 
end 

Lý do tôi sử dụng lambda thay vì kiểm tra bên trong phương pháp CsvParser.create là vì lambda giống như quy tắc kinh doanh thuộc về mô hình này.

Câu hỏi của tôi là cách tôi nên kiểm tra lambda này. Tôi có nên thử nghiệm nó trong mô hình hoặc CsvParser? Tôi có nên kiểm tra bản thân lambda hoặc kết quả của một mảng của phương pháp self.import không? Có lẽ tôi nên tạo cấu trúc mã khác?

My CsvParser trông như sau:

require "csv" 

module CsvParser 

    def self.create(csv_file, klass, attribute_order, value_parser = nil) 
    parsed_csv = CSV.parse(csv_file, col_sep: "|") 

    records = []  

    ActiveRecord::Base.transaction do 
     parsed_csv.each do |row| 
     record = Hash.new {|h, k| h[k] = []}  
     row.each_with_index do |value, index| 
      record[attribute_order[index]] = value 
     end 
     if value_parser.blank? 
      records << klass.create(record) 
     else 
      value_parser.call(record).each do |parsed_record| 
      records << klass.create(parsed_record) 
      end 
     end 
     end 
    end 
    return records 
    end 

end 

Tôi đang thử nghiệm các mô-đun riêng của mình: require 'spec_helper'

describe CsvParser do 

    it "should create relations" do 
    file = File.new(Rails.root.join('spec/fixtures/files/importrelaties.txt')) 
    Relation.should_receive(:create).at_least(:once) 
    Relation.import_csv(file).should be_kind_of Array 
    end 

    it "should create mutations" do 
    file = File.new(Rails.root.join('spec/fixtures/files/importmutaties.txt')) 
    Mutation.should_receive(:create).at_least(:once)  
    Mutation.import_csv(file).should be_kind_of Array 
    end 

    it "should create strategies" do 
    file = File.new(Rails.root.join('spec/fixtures/files/importplan.txt')) 
    Strategy.should_receive(:create).at_least(:once) 
    Strategy.import_csv(file).should be_kind_of Array 
    end 

    it "should create reservations" do 
    file = File.new(Rails.root.join('spec/fixtures/files/importreservering.txt')) 
    Reservation.should_receive(:create).at_least(:once) 
    Reservation.import_csv(file).should be_kind_of Array 
    end 

end 

Trả lời

4

Một số câu hỏi thú vị. Một vài lưu ý:

  1. Có thể bạn không nên trả lại trong lambda. Chỉ cần thực hiện câu lệnh cuối cùng [h].
  2. Nếu tôi hiểu mã chính xác, dòng đầu tiên và dòng thứ hai của lambda của bạn bị quá tải. Giảm họ để làm cho họ dễ đọc hơn và dễ dàng hơn để cấu trúc lại:

    h["is_credit"] = (h['is_credit'] == 'B') # I *think* that will do the same 
    h['sum'] = h['sum'].to_f # Your original code would have left this a string 
    h['sum'] *= -1 unless h['is_credit'] 
    
  3. Dường như lambda của bạn không phụ thuộc vào bất cứ điều gì bên ngoài (ngoài h), vì vậy tôi sẽ kiểm tra nó riêng rẽ. Bạn thậm chí có thể biến nó thành hằng số:

    class Mutation < ActiveRecord::Base 
        extend CsvParser # <== See point 5 below 
    
        PARSE_CREDIT_AND_SUM = lambda do |h| 
        h["is_credit"] = (h['is_credit'] == 'B') 
        h['sum'] = h['sum'].to_f 
        h['sum'] *= -1 unless h['is_credit'] 
        [h] 
        end 
    
  4. Nếu không biết lý do, thật khó để nói bạn nên đặt mã này ở đâu. Bản năng ruột của tôi là nó không phải là công việc của trình phân tích cú pháp CSV (mặc dù một trình phân tích cú pháp tốt có thể phát hiện các số dấu phẩy động và chuyển đổi chúng từ các chuỗi?) Giữ cho trình phân tích cú pháp CSV của bạn có thể tái sử dụng được. (Lưu ý: Đọc lại, tôi nghĩ bạn đã tự trả lời câu hỏi này - đó là logic nghiệp vụ, gắn với mô hình. Hãy đi với ruột của bạn!)

  5. Cuối cùng, bạn xác định và phương pháp CsvParser.create. Bạn không cần phải mở rộng CsvParser để có được quyền truy cập vào nó, mặc dù nếu bạn có cơ sở vật chất khác trong CsvParser, hãy xem xét làm CsvParser.create một phương pháp mô-đun bình thường gọi là cái gì đó như create_from_csv_file

+0

Cám ơn kỹ lưỡng, câu trả lời rõ ràng của bạn. Tôi đã làm theo lời khuyên của bạn và đã có thể kiểm tra hằng số vì tôi có thể gọi Class :: . Tôi chỉ tự hỏi làm thế nào tôi nên kiểm tra một cái gì đó như thế này khi nó sẽ là một biến trong một phương pháp. –

+1

Nếu lambda không thể được refactored ra, tôi sẽ kiểm tra nó như bất kỳ phương pháp khác. Nếu nó là một lambda rất phức tạp, dựa trên rất nhiều biến cục bộ, thì điều này có thể chỉ ra rằng nó muốn là một cá thể riêng của nó. Như một quy luật chung, các phương pháp khó kiểm tra cần một trình tái cấu trúc! – user208769

+0

Hôm nay tôi nhận ra có một cái gì đó gọi là một đối tượng phương pháp. Nó có thể được sử dụng giống như một lambda với sự khác biệt duy nhất mà tôi có thể sử dụng mã của một phương thức thay vì một lambda.Tôi nghĩ nó sạch hơn việc định nghĩa một lambda như một hằng số. Vì vậy, thay vì truyền lambda như một tham số, tôi sử dụng phương thức (: ) –

Các vấn đề liên quan