I am using active admin and uploading the records into the database from CSV file. However, there are two issues occurs here; Code breaks if any filed is missing and secondly performance is very week, can't we do it in the batch process. It is an important and I believe many developers thinking a good solution for such problem.
require 'CSV'
class CsvDb
class << self
def convert_save(model_name, csv_data, field_name=nil)
target_model = model_name.classify.constantize
csv_file = csv_data.read
row_headers={}
counter=0;
#Thread.new do
CSV.parse(csv_file) do |row|
if counter==0
temp=row
row_headers = Hash[temp.map.with_index.to_a]
counter +=1
next
end
unless row[row_headers["name"]].nil?
temp={}
business_type = row[row_headers["business_type_id"]]
business_type_id = business_type=='Online' ? 1: business_type=='Local' ? 2: 3
temp[:business_type_id] = business_type_id
temp[:user_id] = row[row_headers["user_id"]]
temp[:name] = row[row_headers["name"]]
temp[:country_id] = row[row_headers["country_id"]]
temp[:homepage] = row[row_headers["homepage"]] ||=""
temp[:telephone] = row[row_headers["telephone"]] ||=""
temp[:email] = row[row_headers["email"]] ||=""
temp[:address] = row[row_headers["address"]] ||=""
temp[:latitude] = row[row_headers["latitude"]]
temp[:longitude] = row[row_headers["longitude"]]
temp[:facebook] = row[row_headers["facebook"]] ||=""
temp[:twitter] = row[row_headers["twitter"]] ||=""
temp[:google] = row[row_headers["google"]] ||=""
temp[:instagram] = row[row_headers["instagram"]] ||=""
temp[:pinterest] = row[row_headers["pinterest"]] ||=""
temp[:free_shipping] = row[row_headers["free_shipping"]]
temp[:ship_details] = row[row_headers["ship_details"]] ||=""
temp[:category_ids] = [row[row_headers["category_ids"]]]
temp[:style_ids] = [row[row_headers["style_ids"]]]
temp[:shipping_country_ids] = [row[row_headers["shipping_country_ids"]]]
temp[:filter_ids] = [row[row_headers["filter_ids"]]]
business = target_model.new(temp)
business.save
end
end
ActiveRecord::Base.connection.close
end
#end
end
end
I'll just focus on the translation of the CSV rows into an object. There's just to much repetition in the unless body. All those rows should be mentioned once, then handled in a loop instead of hard coding them like this. E.g.
unless row[row_headers["name"]].nil?
temp={}
business_type = row[row_headers["business_type_id"]]
business_type_id = business_type == "Online" ? 1: business_type == "Local" ? 2 : 3
temp[:business_type_id] = business_type_id
for name in [:user_id, :name, :country_id, :latitude, :longitude, :free_shipping, :category_ids, :style_ids, :shipping_country_ids]
temp[name] = row[row_headers[name.to_s]]
end
for name in [:homepage, :telephone, :email, :address, :facebook, :twitter, :google, :instagram, :pinterest, :ship_details]
temp[name] = row[row_headers[name.to_s]] ||= ""
end
business = target_model.new(temp)
business.save
end
Alternate solution received from code review site:
CSV.parse(csv_file, {headers: true, header_converters: :symbol}) do |row|
business_type_id = case row[:business_type_id]
when 'Online' then 1
when 'Local' then 2
else 3
end
target_model.new( {business_type_id: business_type_id} + extract_required_fields(row) + extract_optionals_fiels(row) )
.save()
end
def extract_required_fields(row)
[:user_id, :name, :country_id, :free_shipping, :category_ids, :style_ids, :shipping_country_ids]
.reduce({}) do |carry, item|
carry[item] = row[item]
end
end
def extract_optionals_fiels(row)
[:homepage, :telephone, :email, :address, :facebook, :twitter, :google, :instagram, :pinterest, :ship_details]
.reduce({}) do |carry, item|
carry[item] = row[item] ||= ''
end
end
Related
I have the following code which is legacy code and I need to refactor it to .where clauses but I'm having issues in refactoring it and the best way to do it.
Here is the code
# legacy
#debit_transactions = FinancialTransaction.legacy_find(
:all,
:include => [:project, :department, :stock, :debit_account, :credit_account, :transaction_type],
:conditions => ["dr_account_id = ?", #customer.id],
:order => 'financial_transactions.id desc',
:limit => 20)
# refactor attempt
#debit_transactions = FinancialTransaction.where(dr_account_id: #dr_account_id, customer: #customer.id).order('financial_transactions.id desc').limit(20)
# legacy
contact_or = ''
contact_or = ' OR contact_id IN(?) ' if #customer.contacts.present?
#customer_complaints = Event.legacy_find(:all, { :order => 'id desc', :limit => 10, :conditions => ["complaint is true and (customer_account_id = ? #{contact_or} )", #customer.id].push_if(#customer.contacts.to_a.map(&:id), #customer.contacts.present?) })
# refactor attempt
#customer_complaints = Event.where(complaints: true, customer_account_id: [#customer_account_id], customer: #customer.id).order('id desc').limit(10)
Any help would be appreciated
edit some methods that might be help in understanding the legacy_find
def self.legacy_find(type, args=nil)
# need to add capability to handle array of id's
if type.kind_in?([Numeric, String, Array]) && !args
result = self.find(type)
elsif !args
result = self.send(type.to_s)
else
result = self
if type.kind_of?(Array)
if !args[:conditions]
args[:conditions] = ["1 = 1"]
elsif args[:conditions].kind_of?(String)
args[:conditions] = [args[:conditions]]
end
args[:conditions][0] = '(' + args[:conditions][0] + ')'
args[:conditions][0] += " AND `" + self.table_name + "`.`id` in(" + type.join(',') + ")"
elsif type && !type.kind_of?(Symbol)
if !args[:conditions]
args[:conditions] = ["1 = 1"]
elsif args[:conditions].kind_of?(String)
args[:conditions] = [args[:conditions]]
end
args[:conditions][0] = '(' + args[:conditions][0] + ')'
args[:conditions][0] += " AND `" + self.table_name + "`.`id` = ?"
args[:conditions].push(type)
end
result = self.legacy_conditions(args)
if type && ((type.kind_of?(String) && type.to_i.to_s == type) || type.kind_of?(Numeric))
result = result.first
elsif type && !type.kind_of?(Symbol)
result = result.to_a
else
result = type ? result.send((type == :all ? 'to_a' : type).to_s) : result
end
end
new_result = result
return new_result
end
def self.legacy_count(args=nil)
new_result = self.legacy_conditions(args).count
end
def self.legacy_sum(col, args=nil)
new_result = self.legacy_conditions(args).sum(col.to_s)
end
def self.legacy_conditions(args)
return self if !args
args[:conditions] = [] if args[:conditions] && args[:conditions][0].kind_of?(String) && args[:conditions][0].size == 0
result = self
result = result.where(args[:conditions]) if (args.has_key?(:conditions) && args[:conditions] && args[:conditions].size > 0)
result = result.select(args[:select]) if args.has_key?(:select) && args[:select]
result = result.includes(args[:include]) if args.has_key?(:include) && args[:include]
result = result.includes(args[:include_without_references]) if args.has_key?(:include_without_references) && args[:include_without_references]
result = result.references(args[:include]) if args.has_key?(:include) && args[:include]
result = result.joins(args[:joins]) if args.has_key?(:joins) && args[:joins]
result = result.order(args[:order]) if args.has_key?(:order) && args[:order]
result = result.group(args[:group]) if args.has_key?(:group) && args[:group]
result = result.limit(args[:limit]) if args.has_key?(:limit) && args[:limit]
result = result.offset(args[:offset]) if args.has_key?(:offset) && args[:offset]
result = result.from(args[:from]) if args.has_key?(:from) && args[:from]
result = result.lock(args[:lock]) if args.has_key?(:lock) && args[:lock]
result = result.readonly(args[:readonly]) if args.has_key?(:readonly) && args[:readonly]
result
end
Honestly why this code exists is beyond me so I'm trying to phase it out.
edit 2
Based on the answers below I've come up with the following
#debit_transactions = FinancialTransaction
.includes(:project, :department, :stock, :debit_account, :credit_account, :transaction_type)
.where(dr_account_id: #dr_account_id)
.order(id: :desc)
.limit(20)
#credit_transactions = FinancialTransaction
.includes(:project, :department, :stock, :debit_account, :credit_account, :transaction_type)
.where(cr_account_id: #cr_account_id)
.order(id: :desc)
.limit(20)
contact_or = ''
contact_or = ' OR contact_id IN(?) ' if #customer.contacts.size > 0
#customer_complaints = Event.where(customer_account_id: #customer_id, complaint: true).order(id: :desc).limit(10).or(Event.where(contact_id: #customer.contacts)) if #customer.contacts.present?
#customer_leads = Event.where(customer_account_id: #customer_id, lead: true).order(id: :desc).limit(10)
#customer_quotes = SalesQuote.where(customer_account_id: #customer_id).or(SalesQuote.where(contact_id: #contact_id)).order(id: :desc).limit(10)
#customer_orders = SalesOrder.where(customer_account_id: #customer_id).order(id: :desc).limit(10)
#customer_invoices = Invoice.where(customer_account_id: #customer_id).order(id: :desc).limit(10)
#customer_credits = CreditNote.where(customer_account_id: #customer_id).order(id: :desc).limit(10)
#customer_opportunities = Opportunity.where(customer_account_id: #customer_id).or(Opportunity.where(contact_id: #contact_id)).order(id: :desc).limit(10)
#customer_estimates = Estimate.where(customer_account_id: #customer_id).or(Estimate.where(contact_id: #contact_id)).order(id: :desc).limit(10)
#customer_support_tickets = SupportTicket.where(customer_account_id: #customer_id).order(id: :desc).limit(10)
#financial_matching_sets = FinancialMatchingSet.where(customer_account_id: #customer_id).order(id: :desc).limit(10)
However, I'm getting the following
Mysql2::Error: Unknown column 'sales_orders.customer_account_id' in 'where clause': SELECT `sales_orders`.* FROM `sales_orders` WHERE `sales_orders`.`customer_account_id` IS NULL ORDER BY `sales_orders`.`id` DESC LIMIT 10
the best way to do it.
I don't have a concrete answer to this, but there's a few things you could try, such as:
Write test cases for the behaviour of the old implementation, thus ensuring that they still behave the same with the new implementation. (Maybe you already have some such tests in place??!!)
Write test cases for the implementation of the old vs new code, by checking query.to_sql remains unchanged?!
Try running both versions on production, in parallel, assuming you have good error logging. For example, could you gradually switch over 10% of users to use the "new" implementations, thus catching any errors without causing mass failures for everyone?
But anyway... Aside from the pain of actually rewriting all of these in a safe/robust/well-tested way:
First query:
#debit_transactions = FinancialTransaction.legacy_find(
:all,
:include => [:project, :department, :stock, :debit_account, :credit_account, :transaction_type],
:conditions => ["dr_account_id = ?", #customer.id],
:order => 'financial_transactions.id desc',
:limit => 20)
# refactor attempt
#debit_transactions = FinancialTransaction
.where(dr_account_id: #dr_account_id, customer: #customer.id)
.order('financial_transactions.id desc')
.limit(20)
This refactor ignores the include parameters. The legacy method says:
# ...
result = result.includes(args[:include]) if args.has_key?(:include) && args[:include]
result = result.references(args[:include]) if args.has_key?(:include) && args[:include]
# ...
So, your version should have been:
#debit_transactions = FinancialTransaction
.includes([:project, :department, :stock, :debit_account, :credit_account, :transaction_type])
.references([:project, :department, :stock, :debit_account, :credit_account, :transaction_type])
.where(dr_account_id: #dr_account_id, customer: #customer.id)
.order('financial_transactions.id desc')
.limit(20)
Second query:
# legacy
contact_or = ''
contact_or = ' OR contact_id IN(?) ' if #customer.contacts.present?
#customer_complaints = Event.legacy_find(
:all,
:order => 'id desc',
:limit => 10,
:conditions => ["complaint is true and (customer_account_id = ? #{contact_or} )", #customer.id].push_if(#customer.contacts.to_a.map(&:id), #customer.contacts.present?)
)
# refactor attempt
#customer_complaints = Event
.where(complaints: true, customer_account_id: [#customer_account_id], customer: #customer.id)
.order('id desc')
.limit(10)
Your refactor ignores the OR clause in the condition; you've written this as 3 AND clauses instead.
I think this can be written as something like:
Event.where(customer_account_id: [#customer_account_id])
.or(Event.where(customer: #customer.id))
.merge(Event.where(complaints: true))
.order('id desc')
.limit(10)
...Or something like that. Check the generated SQL in both cases.
The first query is relatively straight forward:
#debit_transactions = FinancialTransaction
# you missed the includes
.includes(
:project, :department, :stock, :debit_account,
:credit_account, :transaction_type
)
.where(
dr_account_id: #dr_account_id.id,
)
.order(id: :desc)
.limit(20)
Then second query is a bit tougher.
You can create a WHERE x IN (...) clause simply by passing an array:
#debit_transactions = FinancialTransaction.where(
id: [1,2,3]
)
You can also create a WHERE x IN (subquery) by passing a ActiveRecord::Relation:
Event.where(contact_id: #customer.contacts)
This is far more effective them using .map(:id) or .ids as you remove a full round trip to the DB.
Support for OR was added in Rails 5:
scope = Event.where(customer_account_id: #customer_id)
scope = scope.or(Event.where(contact_id: #customer.contacts)) if #customer.contacts.present?
So altogether it would look something like:
scope = Event.where(
customer_account_id: #customer_id
complaint: true
).order('id desc')
.limit(10)
scope = scope.or(Event.where(contact_id: #customer.contacts)) if #customer.contacts.present?
I have this two methods I want to refactor
def construct_discount_hash(product_adjustments)
discounts = {
events: {},
subjects: {},
products: {}
}
# product_adjustments is a model
# This is a problem from legacy database structure where event_id, subject_id, product_id is in their own column
product_adjustments.each do |adjustment|
if (adjustment.event_id.present?)
discounts[:events][adjustment.event_id] = {'$' => adjustment.amount, '%' => adjustment.percentage}
end
if (adjustment.subject_id.present?)
discounts[:subjects][adjustment.subject_id] = {'$' => adjustment.amount, '%' => adjustment.percentage}
end
if (adjustment.product_id.present?)
discounts[:products][adjustment.product_id] = {'$' => adjustment.amount, '%' => adjustment.percentage}
end
end
discounts
end
and I will use above method results within below method
# discounts is a hash generated from above code, item is a rails model
def calculate_order_content_price(discounts, item)
product = item.product
if (item.product_variant.present?)
price = item.product_variant.price
else
price = product.price
end
price_adjustments = {}
popped_from = []
if (discounts[:products][item.product_id])
price_adjustments = discounts[:products][item.product_id]
discounts[:products].delete(item.product_id)
popped_from = [:products, item.product_id]
elsif (discounts[:subjects][product.subject_id])
price_adjustments = discounts[:subjects][product.subject_id]
discounts[:subjects].delete(product.subject_id)
popped_from = [:subjects, product.subject_id]
elsif (discounts[:events][product.event_id])
price_adjustments = discounts[:events][product.event_id]
discounts[:events].delete(product.event_id)
popped_from = [:events, product.event_id]
end
if (adjustment = price_adjustments['$'])
adjusted_price = price + adjustment
elsif (adjustment = price_adjustments['%'])
adjusted_price = price + price * (adjustment / 100.0)
discounts[popped_from[0]][popped_from[1]] = price_adjustments
else
adjusted_price = price
end
{ price: adjusted_price, discount: (price - adjusted_price) }
end
I know from above code there's a lot of code-smell there. Firstly I'm thinking that the if-else logic can be refactored somehow. Can someone give an advice a technique that I can use to refactor that if-else condition? I'm confused because of the if-condition is checking for the existences of the value.
Any suggestions will be helpful.
I've tried to simplify a little bit your construct_discount_hash with two helpers:
def amount_and_percentage(adjustment, model)
{ adjustment.attributes["#{model}_id"] => { '$': adjustment.amount, '%': adjustment.percentage } }
end
def construct_discount_hash(product_adjustments)
product_adjustments.each_with_object({}) do |adjustment, hash|
case
when adjustment.event_id.present?
hash[:event] = amount_and_percentage(adjustment, 'event')
when adjustment.subject_id.present?
hash[:subject] = amount_and_percentage(adjustment, 'subject')
when adjustment.product_id.present?
hash[:product] = amount_and_percentage(adjustment, 'product')
end
end
end
A refacto for you.
def construct_discount_hash(product_adjustments)
# product_adjustments is a model
# This is a problem from legacy database structure where event_id, subject_id, product_id is in their own column
product_adjustments.each do |adjustment|
adjustement_hash = {'$' => adjustment.amount, '%' => adjustment.percentage} if adjustement
(discounts ||= {})[:events][adjustment.event_id] = adjustement_hash if adjustment.event_id.present?
(discounts ||= {})[:subjects][adjustment.subject_id] = adjustement_hash if adjustment.subject_id.present?
(discounts ||= {})[:products][adjustment.product_id] = adjustement_hash if adjustment.product_id.present?
end
discounts
end
I hope it will help you. Have fun.
Here is my Lesson model:
#encoding: utf-8
class Lesson < ActiveRecord::Base
attr_accessible :content, :title, :parsed_content, :html_content, :user_id
serialize :parsed_content, Array
serialize :html_content, Array
serialize :pinyin_content, Array
serialize :defined_content, Array
serialize :literal_content, Array
validates :title, :presence => true
validates :content, :presence => true
belongs_to :user
before_update do |lesson|
lesson.makesandwich
end
before_save do |lesson|
lesson.delay.makesandwich
end
def makesandwich
require 'rmmseg'
#require 'to_lang'
require 'bing_translator'
require 'ruby-pinyin'
self.parsed_content = []
RMMSeg::Dictionary.load_dictionaries
content = self.content
paragraphs = content.split(/\r\n\r\n/) #convert to array of paragraphs
self.parsed_content = paragraphs
paragraphs.each_with_index do |text, ti|
text = text.gsub("。", "^^.")
text = text.gsub("?", "~~?")
text = text.gsub("!", "||!")
text = text.gsub(":", ":") #fix missing colons
text = text.split(/[.?!]/u) #convert to an array
text.each do |s|
s.gsub!("^^", "。")
s.gsub!("~~", "?")
s.gsub!("||", "!")
#s.gsub!("———————————",":")
end
text.each_with_index do |val, index|
algor = RMMSeg::Algorithm.new(text[index])
splittext = []
loop do
tok = algor.next_token
break if tok.nil?
tex = tok.text.force_encoding('UTF-8')
splittext << tex
text[index] = splittext
end
paragraphs[ti] = text
end
end
bing = BingTranslator.new(BING_API)
self.parsed_content = paragraphs
textarray = Marshal.load(Marshal.dump(paragraphs))
self.defined_content = Marshal.load(Marshal.dump(paragraphs))
self.literal_content = Marshal.load(Marshal.dump(paragraphs))
self.pinyin_content = Marshal.load(Marshal.dump(paragraphs))
textarray.each_with_index do |paragraph, pi|
paragraph.each_with_index do |sentence, si|
sentence.each_with_index do |word, wi|
if DictionaryEntry.find_by_simplified(word) != nil
self.defined_content[pi][si][wi] = DictionaryEntry.find_by_simplified(word).definition
#self.literal_content is down below
self.pinyin_content[pi][si][wi] = DictionaryEntry.find_by_simplified(word).pinyin
else
self.defined_content[pi][si][wi] = bing.translate(word, :from => 'zh-CHS', :to => 'en')
#self.defined_content[pi][si][wi] = word
#self.literal_content is down below
if PinYin.of_string(word, true).length > 1 #for punctuation
self.pinyin_content[pi][si][wi] = PinYin.of_string(word, true).join(" ").downcase
else
self.pinyin_content[pi][si][wi] = word
end
end
end
end
end
#Literal
literalarray = Marshal.load(Marshal.dump(paragraphs))
literalarray.each_with_index do |paragraph, pi|
paragraph.each_with_index do |sentence, si| #iterate array of sentence
literalarray[pi][si] = []
sentence.each_with_index do |word, wi| #iterate sentence's array of words
entrytobesliced = DictionaryEntry.find_by_simplified(word)
slicedentry = []
if entrytobesliced == nil
if word.length > 1 && word !~ /\w/ #/^\s*\w\d+\s*$/ #number regex #for cases where there is no DictionaryEntry
split = []
wordarray = word.split("").each_with_index() do |ws, wsi|
split << [DictionaryEntry.find_by_simplified(ws).definition]
end
literalarray[pi][si] << split
else
literalarray[pi][si] << [word] #in case none of the above work
end
else
entrytobesliced.simplified.each_char do |w|
singlechar = DictionaryEntry.find_by_simplified(w)
slicedentry << singlechar.definition.split("\", \"")
end
literalarray[pi][si] << slicedentry
end
self.literal_content = literalarray #slicedentry #literalarray
end
end
end
end
end
When I try to create a new lesson it errors like this: Jobs cannot be created for records before they've been persisted
But if I change it to after_save instead of before_save then I can see the work run, but it doesn't update the serialized arrays in the database.
Can someone please help me implement delayed_jobs for this? It was working when I had:
before_save do |lesson|
lesson.makesandwich #no delay
end
I think you're getting these errors:
Jobs cannot be created for records before they've been persisted
because your Lesson instances won't have an id until they've been saved and without an id, DJ has no way to know which instance it should be working with. So you have to use an after_save so that your Lesson has an id and can be uniquely identified. But then your updates from the delayed job won't be saved because, well, nothing asks for them to be saved. You should be able to get around that simply by adding a self.save or self.save! call at the end of makesandwich.
I have such code:
def accum_search
if params[:akbcap].present?
akbcap_array = [12,18,19,20,25,30,35,36,38,40,41,42,44,45,46,47,50,52,53,54,55,56,58,60,61,62,63,64,65,66,68,69,70,71,72,74,75,77,80,85,88,90,91,92,95,98,100,102,110,115,120,125,130,135,140,170,180,185,190,192,200,210,220,225]
min, max = params[:akbcap].split('-').map {|s| s.to_i }
logger.warn("!#!!!!!!!!!!!! AAA !!!!!!!!!!")
logger.warn(min)
logger.warn(max)
caprange = min...max
sa = akbcap_array.select {|n| caprange.include? n }
##cross = OtherProductsCrossList.find(:all, :conditions => {:cross_value => 1})
cap = "*"+params[:akbcap]+"*"
sa.each do |s|
logger.warn(s)
#accums = Accumulator.by_capacity(s).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
else
#accums = Accumulator.by_capacity(50).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
end
As you see i have such part:
sa.each do |s|
logger.warn(s)
#accums = Accumulator.by_capacity(s).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
but could i add on every iteration in #accums data from search? now it has last value( I could done it via arrays... but how to do via class-variable?
Yes, initiate it before the loop and use the << operator to append. End with flatten to make it a single dimension array.
#accums = []
# ...
sa.each do |s|
#accums << Accumulator.several_method_calls......
end
#accums.flatten!
or for compactness:
result = sa.map{|s| Accumulator.several_method_calls...... }.flatten
I have written method to load data to DB from CSV. For me it's a little bit unsexy and it's far away from DRY:
def self.from_csv(data)
c = Company.new
FasterCSV.parse(data) do |row|
c.name = row[0]
c.street = row[1]
c.street_number = row[2]
c.apartament_number = row[3]
c.city = row[4]
c.post_code = row[5]
c.post_office_city = row[6]
c.nip = row[7]
c.regon = row[8]
c.vatin = row[9]
end
end
How to make it more sexy or may be there is already any lib to load data?
You can put a sexy lady on top of it:
# ___
# _ _.-"_< }
# ""--"" 7(
# /())
# / )/
# ^ ( \
# / /
# /.'
# //
# ______/L___ sexii
def self.from_csv(data)
#...
end
But, seriously, the only problem I see with your code is that you cannot rearrange attributes easily because you will have to manually update all indexes. I'd prefer to have an ordered list of attributes in an array and use some Ruby's dynamic method calling:
def self.from_csv(data)
company = new
row = CSV.parse_line(data)
[:name, :street, :street_number, :apartament_number,
:city, :post_code, :post_office_city,
:nip, :regon, :vatin].each_with_index do |name, i|
company.send(:"#{name}=", row[i])
end
company
end
Also note that you need to return a constucted company instance at the end otherwise you will get some random value when calling Company.from_csv.
c = Company.new
cols = ["name", "street", "street_number", "apartament_number", "city", "post_code", "post_office_city", "nip", "regon", "vatic"]
FasterCSV.parse(data) do |row|
cols.each_index { |i| c.send("#{cols[i]}=", row[i]) }
end
I wrote this lightweight wrapper to load CSV files:
https://github.com/stackpilot/loady
It works with ruby 1.9 and uses the standard ruby CSV library (formerly known as FasterCSV).
You do this with it:
Loady.csv "/your/file.csv" do |row|
Company.create row.to_attributes [:name, :street, :street_number, ...]
end