可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Currently, if I want to apply a method to a group of ActiveRecord objects, I have to structure the call like so:
messages = Message.find(:all)
csv = Message.to_csv(messages)
How can I define the method so it's structured like so?
messages = Message.find(:all)
csv = messages.to_csv
This is the current model code:
require 'fastercsv'
class Message < ActiveRecord::Base
def Message.to_csv(messages)
FasterCSV.generate do |csv|
csv << ["from","to", "received"]
for m in messages
csv << [m.from,m.to,m.created_at]
end
end
end
end
回答1:
The following will call to_csv on all instances included in the messages array.
messages = Message.find(:all)
csv = messages.map { |message| message.to_csv }
In Rails, in Ruby 1.9 or with Symbol#to_proc available through other means, you can also shorten it to:
csv = messages.map(&:to_csv)
The longer form is useful when you want to make a more complex operation:
csv = messages.map { |message|
if message.length < 1000
message.to_csv
else
"Too long"
end
}
回答2:
Put this in a file in lib/. I would recommend calling it something like base_ext.rb
require 'fastercsv'
class ActiveRecord::Base
def self.to_csv(objects, skip_attributes=[])
FasterCSV.generate do |csv|
csv << attribute_names - skip_attributes
objects.each do |object|
csv << (attribute_names - skip_attributes).map { |a| "'#{object.attributes[a]}'" }.join(", ")
end
end
end
end
After that, go to config/environment.rb and put require 'base_ext'
at the bottom of the file to include the new extension. Upon restarting your server you should have access to a to_csv
method on all model classes and when you pass it an array of objects should generate a nice CSV format for you.
回答3:
FasterCSV patches the Array class and adds a 'to_csv' method to it already, but it doesn't do what you want. You could overwrite it yourself by doing something like:
class Array
def to_csv(options = Hash.new)
collect { |item| item.to_csv }.join "\n"
end
end
Or something along those lines, but that's kind of crappy.
Honestly, it makes more sense the way you've done it as a class method on your model.
回答4:
You could create a method on your Message class to do something along the lines of...
In your controller....
@csv_file = Message.send_all_to_csv
In your model...
require 'fastercsv'
class Message < ActiveRecord::Base
def send_all_to_csv
@messages = Find.all
FasterCSV.generate do |csv|
csv << ["from","to", "received"]
for message in @messages
csv << [message.from,message.to,message.created_at]
end
end
# do something with your csv object (return it to the controller
# or pass it on to another class method
end
end
回答5:
You could define the method directly on the messages
object itself, but if you do that, the method would only be available to that specific instance:
def messages.to_csv()
FasterCSV.generate do |csv|
csv << ["from", "to", "received"]
self.each { |m| csv << [m.from, m.to, m.created_at] }
end
end
Then you could call it like so:
messages.to_csv
I'm a Ruby newbie, so I'm not sure if this is idiomatic Ruby or not: that is, I'm not sure how common or accepted it is to define new methods directly on object instances, I only know it's possible ;-)
回答6:
If it is isolated to one AR model I would add a to_custom_csv_array instance method
def to_custom_csv_array
[self.from,self.to,self.created_at]
end
then override find on the class
def self.find(*args)
collection = super
collection.extend(CustomToCSV) if collection.is_a?(Array)
end
and in CustomToCSV define the to_custom_csv to generate the csv
module CustomToCSV
def to_custom_csv
FasterCSV.generate do |csv|
csv << ["from","to", "received"]
csv << self.map {|obj| obj.to_custom_csv_array}
end
end
end
This is from memory but should work.
回答7:
I know that it is a very old question but just thought to provide a feedback anyway.
check blog http://blog.zahiduzzaman.com/2013/07/adding-tocsv-method-to-active-record.html
just another way of achieving this