ar-extensions 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +97 -0
- data/README +123 -0
- data/Rakefile +97 -0
- data/config/database.yml +7 -0
- data/config/database.yml.template +7 -0
- data/config/mysql.schema +72 -0
- data/config/postgresql.schema +39 -0
- data/db/migrate/generic_schema.rb +59 -0
- data/db/migrate/mysql_schema.rb +26 -0
- data/db/migrate/version.rb +4 -0
- data/init.rb +32 -0
- data/lib/ar-extensions.rb +4 -0
- data/lib/ar-extensions/adapters/abstract_adapter.rb +83 -0
- data/lib/ar-extensions/adapters/mysql_adapter.rb +14 -0
- data/lib/ar-extensions/adapters/postgresql.rb +7 -0
- data/lib/ar-extensions/csv.rb +302 -0
- data/lib/ar-extensions/extensions.rb +474 -0
- data/lib/ar-extensions/finders.rb +76 -0
- data/lib/ar-extensions/foreign_keys.rb +70 -0
- data/lib/ar-extensions/fulltext.rb +63 -0
- data/lib/ar-extensions/fulltext/mysql.rb +44 -0
- data/lib/ar-extensions/import.rb +254 -0
- data/lib/ar-extensions/import/mysql.rb +70 -0
- data/lib/ar-extensions/import/postgresql.rb +0 -0
- data/lib/ar-extensions/temporary_table.rb +124 -0
- data/lib/ar-extensions/temporary_table/mysql.rb +3 -0
- data/lib/ar-extensions/version.rb +8 -0
- metadata +83 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
|
3
|
+
create_table :schema_info, :force=>true do |t|
|
4
|
+
t.column :version, :integer, :unique=>true
|
5
|
+
end
|
6
|
+
SchemaInfo.create :version=>SchemaInfo::VERSION
|
7
|
+
|
8
|
+
create_table :topics, :force=>true do |t|
|
9
|
+
t.column :title, :string, :null=>false
|
10
|
+
t.column :author_name, :string
|
11
|
+
t.column :author_email_address, :string
|
12
|
+
t.column :written_on, :datetime
|
13
|
+
t.column :bonus_time, :time
|
14
|
+
t.column :last_read, :datetime
|
15
|
+
t.column :content, :text
|
16
|
+
t.column :approved, :boolean, :default=>'1'
|
17
|
+
t.column :replies_count, :integer
|
18
|
+
t.column :parent_id, :integer
|
19
|
+
t.column :type, :string
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :projects, :force=>true do |t|
|
23
|
+
t.column :name, :string
|
24
|
+
t.column :type, :string
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :developers, :force=>true do |t|
|
28
|
+
t.column :name, :string
|
29
|
+
t.column :salary, :integer, :default=>'70000'
|
30
|
+
t.column :created_at, :datetime
|
31
|
+
t.column :team_id, :integer
|
32
|
+
t.column :updated_at, :datetime
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table :addresses, :force=>true do |t|
|
36
|
+
t.column :address, :string
|
37
|
+
t.column :city, :string
|
38
|
+
t.column :state, :string
|
39
|
+
t.column :zip, :string
|
40
|
+
t.column :developer_id, :integer
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :teams, :force=>true do |t|
|
44
|
+
t.column :name, :string
|
45
|
+
end
|
46
|
+
|
47
|
+
create_table :books, :force=>true do |t|
|
48
|
+
t.column :title, :string, :null=>false
|
49
|
+
t.column :publisher, :string, :null=>false
|
50
|
+
t.column :author_name, :string, :null=>false
|
51
|
+
t.column :created_at, :time
|
52
|
+
end
|
53
|
+
|
54
|
+
create_table :languages, :force=>true do |t|
|
55
|
+
t.column :name, :string
|
56
|
+
t.column :developer_id, :integer
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
|
3
|
+
create_table :test_myisam, :options=>'ENGINE=MyISAM', :force=>true do |t|
|
4
|
+
t.column :my_name, :string, :null=>false
|
5
|
+
t.column :description, :string
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :test_innodb, :options=>'ENGINE=InnoDb', :force=>true do |t|
|
9
|
+
t.column :my_name, :string, :null=>false
|
10
|
+
t.column :description, :string
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :test_memory, :options=>'ENGINE=Memory', :force=>true do |t|
|
14
|
+
t.column :my_name, :string, :null=>false
|
15
|
+
t.column :description, :string
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :books, :options=>'ENGINE=MyISAM', :force=>true do |t|
|
19
|
+
t.column :title, :string, :null=>false
|
20
|
+
t.column :publisher, :string, :null=>false
|
21
|
+
t.column :author_name, :string, :null=>false
|
22
|
+
t.column :created_at, :datetime
|
23
|
+
end
|
24
|
+
execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )"
|
25
|
+
|
26
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
begin ; require 'active_record' ; rescue LoadError; require 'rubygems'; require 'active_record'; end
|
3
|
+
|
4
|
+
dir = File.dirname( __FILE__ )
|
5
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'version' ) )
|
6
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'extensions' ) )
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'faster_csv'
|
10
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'csv' ) )
|
11
|
+
rescue LoadError
|
12
|
+
STDERR.puts "FasterCSV is not installed. CSV functionality will not be included."
|
13
|
+
end
|
14
|
+
|
15
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'foreign_keys' ) )
|
16
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'fulltext' ) )
|
17
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'fulltext', 'mysql' ) )
|
18
|
+
|
19
|
+
db_adapters_path = File.expand_path( File.join( dir, 'lib/ar-extensions', 'adapters' ) )
|
20
|
+
|
21
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'import' ) )
|
22
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'import', 'mysql' ) )
|
23
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'import', 'postgresql' ) )
|
24
|
+
|
25
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'finders' ) )
|
26
|
+
|
27
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'temporary_table' ) )
|
28
|
+
require File.expand_path( File.join( dir, 'lib/ar-extensions', 'temporary_table', 'mysql' ) )
|
29
|
+
|
30
|
+
require File.expand_path( File.join( db_adapters_path, 'abstract_adapter' ) )
|
31
|
+
require File.expand_path( File.join( db_adapters_path,'mysql_adapter' ) )
|
32
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
class AbstractAdapter # :nodoc:
|
4
|
+
NO_MAX_PACKET = 0
|
5
|
+
|
6
|
+
# +sql+ can be a single string or an array. If it is an array all
|
7
|
+
# elements that are in position >= 1 will be appended to the final SQL.
|
8
|
+
def insert_many( sql, values, *args ) # :nodoc:
|
9
|
+
# the number of inserts default
|
10
|
+
number_of_inserts = 0
|
11
|
+
|
12
|
+
base_sql,post_sql = if sql.is_a?( String )
|
13
|
+
[ sql, '' ]
|
14
|
+
elsif sql.is_a?( Array )
|
15
|
+
[ sql.shift, sql.join( ' ' ) ]
|
16
|
+
end
|
17
|
+
|
18
|
+
sql_size = base_sql.size + post_sql.size
|
19
|
+
|
20
|
+
# the number of bytes the requested insert statement values will take up
|
21
|
+
values_in_bytes = self.class.sum_sizes( *values )
|
22
|
+
|
23
|
+
# the number of bytes (commas) it will take to comma separate our values
|
24
|
+
comma_separated_bytes = values.size-1
|
25
|
+
|
26
|
+
# the total number of bytes required if this statement is one statement
|
27
|
+
total_bytes = sql_size + values_in_bytes + comma_separated_bytes
|
28
|
+
|
29
|
+
max = max_allowed_packet
|
30
|
+
|
31
|
+
# if we can insert it all as one statement
|
32
|
+
if NO_MAX_PACKET == max or total_bytes < max
|
33
|
+
number_of_inserts += 1
|
34
|
+
sql2insert = base_sql + values.join( ',' ) + post_sql
|
35
|
+
insert( sql2insert, *args )
|
36
|
+
else
|
37
|
+
value_sets = self.class.get_insert_value_sets( values, sql_size, max )
|
38
|
+
value_sets.each do |values|
|
39
|
+
number_of_inserts += 1
|
40
|
+
sql2insert = base_sql + values.join( ',' ) + post_sql
|
41
|
+
insert( sql2insert, *args )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
number_of_inserts
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the sum of the sizes of the passed in objects. This should
|
49
|
+
# probably be moved outside this class, but to where?
|
50
|
+
def self.sum_sizes( *objects ) # :nodoc:
|
51
|
+
objects.inject( 0 ){|sum,o| sum += o.size }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the maximum number of bytes that the server will allow
|
55
|
+
# in a single packet
|
56
|
+
def max_allowed_packet
|
57
|
+
NO_MAX_PACKET
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.get_insert_value_sets( values, sql_size, max_bytes ) # :nodoc:
|
61
|
+
value_sets = []
|
62
|
+
arr, current_arr_values_size, current_size = [], 0, 0
|
63
|
+
values.each_with_index do |val,i|
|
64
|
+
comma_bytes = arr.size
|
65
|
+
sql_size_thus_far = sql_size + current_size + val.size + comma_bytes
|
66
|
+
if NO_MAX_PACKET == max_bytes or sql_size_thus_far <= max_bytes
|
67
|
+
current_size += val.size
|
68
|
+
arr << val
|
69
|
+
else
|
70
|
+
value_sets << arr
|
71
|
+
arr = [ val ]
|
72
|
+
current_size = val.size
|
73
|
+
end
|
74
|
+
|
75
|
+
# if we're on the last iteration push whatever we have in arr to value_sets
|
76
|
+
value_sets << arr if i == (values.size-1)
|
77
|
+
end
|
78
|
+
[ *value_sets ]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
class MysqlAdapter # :nodoc:
|
4
|
+
|
5
|
+
# Returns the maximum number of bytes that the server will allow
|
6
|
+
# in a single packet
|
7
|
+
def max_allowed_packet # :nodoc:
|
8
|
+
result = execute( "SHOW VARIABLES like 'max_allowed_packet';" )
|
9
|
+
result.fetch_row[1].to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
end #end MysqlAdapter
|
13
|
+
end #end ConnectionAdapters
|
14
|
+
end #end ActiveRecord
|
@@ -0,0 +1,302 @@
|
|
1
|
+
|
2
|
+
require 'faster_csv'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
# Adds CSV export options to ActiveRecord::Base models.
|
6
|
+
#
|
7
|
+
# === Example 1, exporting all fields
|
8
|
+
# class Book < ActiveRecord::Base ; end
|
9
|
+
#
|
10
|
+
# book = Book.find( 1 )
|
11
|
+
# book.to_csv
|
12
|
+
#
|
13
|
+
# === Example 2, only exporting certain fields
|
14
|
+
# class Book < ActiveRecord::Base ; end
|
15
|
+
#
|
16
|
+
# book = Book.find( 1 )
|
17
|
+
# book.to_csv( :only=>%W( title isbn )
|
18
|
+
#
|
19
|
+
# === Example 3, exporting a model including a belongs_to association
|
20
|
+
# class Book < ActiveRecord::Base
|
21
|
+
# belongs_to :author
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# book = Book.find( 1 )
|
25
|
+
# book.to_csv( :include=>:author )
|
26
|
+
#
|
27
|
+
# This also works for a has_one relationship. The :include
|
28
|
+
# option can also be an array of has_one/belongs_to
|
29
|
+
# associations. This by default includes all fields
|
30
|
+
# on the belongs_to association.
|
31
|
+
#
|
32
|
+
# === Example 4, exporting a model including a has_many association
|
33
|
+
# class Book < ActiveRecord::Base
|
34
|
+
# has_many :tags
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# book = Book.find( 1 )
|
38
|
+
# book.to_csv( :include=>:tags )
|
39
|
+
#
|
40
|
+
# This by default includes all fields on the has_many assocaition.
|
41
|
+
# This can also be an array of multiple has_many relationships. The
|
42
|
+
# array can be mixed with has_one/belongs_to associations array
|
43
|
+
# as well. IE: :include=>[ :author, :sales ]
|
44
|
+
#
|
45
|
+
# === Example 5, nesting associations
|
46
|
+
# class Book < ActiveRecord::Base
|
47
|
+
# belongs_to :author
|
48
|
+
# has_many :tags
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# book = Book.find( 1 )
|
52
|
+
# book.to_csv( :includes=>{
|
53
|
+
# :author => { :only=>%W( name ) },
|
54
|
+
# :tags => { :only=>%W( tagname ) } )
|
55
|
+
#
|
56
|
+
# Each included association can receive an options Hash. This
|
57
|
+
# allows you to nest the associations as deep as you want
|
58
|
+
# for your CSV export.
|
59
|
+
#
|
60
|
+
# It is not recommended to nest multiple has_many associations,
|
61
|
+
# although nesting multiple has_one/belongs_to associations.
|
62
|
+
#
|
63
|
+
module ActiveRecord::Extensions::FindToCSV
|
64
|
+
ALIAS_FOR_FIND = :_original_find_before_arext
|
65
|
+
|
66
|
+
def self.included( cl ) # :nodoc:
|
67
|
+
virtual_class = class << cl ; self ; end
|
68
|
+
if not virtual_class.ancestors.include?( self::ClassMethods )
|
69
|
+
cl.instance_eval "alias #{ALIAS_FOR_FIND} :find"
|
70
|
+
cl.extend( ClassMethods )
|
71
|
+
cl.send( :include, InstanceMethods )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class FieldMap# :nodoc:
|
76
|
+
attr_reader :fields, :fields_to_headers
|
77
|
+
|
78
|
+
def initialize( fields, fields_to_headers ) # :nodoc:
|
79
|
+
@fields, @fields_to_headers = fields, fields_to_headers
|
80
|
+
end
|
81
|
+
|
82
|
+
def headers # :nodoc:
|
83
|
+
@headers ||= fields.inject( [] ){ |arr,field| arr << fields_to_headers[ field ] }
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
module ClassMethods # :nodoc:
|
89
|
+
private
|
90
|
+
|
91
|
+
def to_csv_fields_for_nil # :nodoc:
|
92
|
+
self.columns.map{ |column| column.name }.sort
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_csv_headers_for_included_associations( includes ) # :nodoc:
|
96
|
+
get_class = proc { |str| Object.const_get( self.reflections[ str.to_sym ].class_name ) }
|
97
|
+
|
98
|
+
case includes
|
99
|
+
when Symbol
|
100
|
+
[ get_class.call( includes ).to_csv_headers( :headers=>true, :naming=>":model[:header]" ) ]
|
101
|
+
when Array
|
102
|
+
includes.map do |association|
|
103
|
+
clazz = get_class.call( association )
|
104
|
+
clazz.to_csv_headers( :headers=>true, :naming=>":model[:header]" )
|
105
|
+
end
|
106
|
+
when Hash
|
107
|
+
includes.sort_by{ |k| k.to_s }.inject( [] ) do |arr,(association,options)|
|
108
|
+
clazz = get_class.call( association )
|
109
|
+
if options[:headers].is_a?( Hash )
|
110
|
+
options.merge!( :naming=>":header" )
|
111
|
+
else
|
112
|
+
options.merge!( :naming=>":model[:header]" )
|
113
|
+
end
|
114
|
+
arr << clazz.to_csv_headers( options )
|
115
|
+
end
|
116
|
+
else
|
117
|
+
[]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
public
|
122
|
+
|
123
|
+
def find( *args ) # :nodoc:
|
124
|
+
results = self.send( ALIAS_FOR_FIND, *args )
|
125
|
+
results.extend( ArrayInstanceMethods ) if results.is_a?( Array )
|
126
|
+
results
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_csv_fields( options={} ) # :nodoc:
|
130
|
+
fields_to_headers, fields = {}, []
|
131
|
+
|
132
|
+
headers = options[:headers]
|
133
|
+
case headers
|
134
|
+
when Array
|
135
|
+
fields = headers.map{ |e| e.to_s }
|
136
|
+
when Hash
|
137
|
+
headers = headers.inject( {} ){ |hsh,(k,v)| hsh[k.to_s] = v ; hsh }
|
138
|
+
fields = headers.keys.sort
|
139
|
+
fields.each { |field| fields_to_headers[field] = headers[field] }
|
140
|
+
else
|
141
|
+
fields = to_csv_fields_for_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
if options[:only]
|
145
|
+
specified_fields = options[:only].map{ |e| e.to_s }
|
146
|
+
fields.delete_if{ |field| not specified_fields.include?( field ) }
|
147
|
+
elsif options[:except]
|
148
|
+
excluded_fields = options[:except].map{ |e| e.to_s }
|
149
|
+
fields.delete_if{ |field| excluded_fields.include?( field ) }
|
150
|
+
end
|
151
|
+
|
152
|
+
fields.each{ |field| fields_to_headers[field] = field } if fields_to_headers.empty?
|
153
|
+
|
154
|
+
FieldMap.new( fields, fields_to_headers )
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns an array of CSV headers passed in the array of +options+.
|
158
|
+
def to_csv_headers( options={} )
|
159
|
+
options = { :headers=>true, :naming=>":header" }.merge( options )
|
160
|
+
return nil if not options[:headers]
|
161
|
+
|
162
|
+
fieldmap = to_csv_fields( options )
|
163
|
+
headers = fieldmap.headers
|
164
|
+
headers.push( *to_csv_headers_for_included_associations( options[ :include ] ).flatten )
|
165
|
+
headers.map{ |header| options[:naming].gsub( /:header/, header ).gsub( /:model/, self.name.downcase ) }
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
module InstanceMethods
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def to_csv_data_for_included_associations( includes ) # :nodoc:
|
176
|
+
get_class = proc { |str| Object.const_get( self.class.reflections[ str.to_sym ].class_name ) }
|
177
|
+
|
178
|
+
case includes
|
179
|
+
when Symbol
|
180
|
+
association = self.send( includes )
|
181
|
+
association.send( :extend, ArrayInstanceMethods ) if association.is_a?( Array )
|
182
|
+
if association.nil? or (association.respond_to?( :empty? ) and association.empty?)
|
183
|
+
[ get_class.call( includes ).columns.map{ '' } ]
|
184
|
+
else
|
185
|
+
[ *association.to_csv_data ]
|
186
|
+
end
|
187
|
+
when Array
|
188
|
+
siblings = []
|
189
|
+
includes.each do |association_name|
|
190
|
+
association = self.send( association_name )
|
191
|
+
association.send( :extend, ArrayInstanceMethods ) if association.is_a?( Array )
|
192
|
+
if association.nil? or (association.respond_to?( :empty? ) and association.empty?)
|
193
|
+
association_data = [ get_class.call( association_name ).columns.map{ '' } ]
|
194
|
+
else
|
195
|
+
association_data = association.to_csv_data
|
196
|
+
end
|
197
|
+
|
198
|
+
if siblings.empty?
|
199
|
+
siblings.push( *association_data )
|
200
|
+
else
|
201
|
+
temp = []
|
202
|
+
association_data.each do |assoc_csv|
|
203
|
+
siblings.each do |sibling|
|
204
|
+
temp.push( sibling + assoc_csv )
|
205
|
+
end
|
206
|
+
end
|
207
|
+
siblings = temp
|
208
|
+
end
|
209
|
+
end
|
210
|
+
siblings
|
211
|
+
when Hash
|
212
|
+
sorted_includes = includes.sort_by{ |k| k.to_s }
|
213
|
+
siblings = []
|
214
|
+
sorted_includes.each do |(association_name,options)|
|
215
|
+
association = self.send( association_name )
|
216
|
+
association.send( :extend, ArrayInstanceMethods ) if association.is_a?( Array )
|
217
|
+
if association.nil? or (association.respond_to?( :empty ) and association.empty?)
|
218
|
+
association_data = [ get_class.call( association_name ).columns.map{ '' } ]
|
219
|
+
else
|
220
|
+
association_data = association.to_csv_data( options )
|
221
|
+
end
|
222
|
+
|
223
|
+
if siblings.empty?
|
224
|
+
siblings.push( *association_data )
|
225
|
+
else
|
226
|
+
temp = []
|
227
|
+
association_data.each do |assoc_csv|
|
228
|
+
siblings.each do |sibling|
|
229
|
+
temp.push( sibling + assoc_csv )
|
230
|
+
end
|
231
|
+
end
|
232
|
+
siblings = temp
|
233
|
+
end
|
234
|
+
end
|
235
|
+
siblings
|
236
|
+
else
|
237
|
+
[]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
public
|
242
|
+
|
243
|
+
# Returns CSV data without any header rows for the passed in +options+.
|
244
|
+
def to_csv_data( options={} )
|
245
|
+
fields = self.class.to_csv_fields( options ).fields
|
246
|
+
data, model_data = [], fields.inject( [] ) { |arr,field| arr << attributes[field].to_s }
|
247
|
+
if options[:include]
|
248
|
+
to_csv_data_for_included_associations( options[:include ] ).map do |assoc_csv_data|
|
249
|
+
data << model_data + assoc_csv_data
|
250
|
+
end
|
251
|
+
else
|
252
|
+
data << model_data
|
253
|
+
end
|
254
|
+
data
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns CSV data including header rows for the passed in +options+.
|
258
|
+
def to_csv( options={} )
|
259
|
+
FasterCSV.generate do |csv|
|
260
|
+
headers = self.class.to_csv_headers( options )
|
261
|
+
csv << headers if headers
|
262
|
+
to_csv_data( options ).each{ |data| csv << data }
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
module ArrayInstanceMethods # :nodoc:
|
269
|
+
class NoRecordsError < StandardError ; end #:nodoc:
|
270
|
+
|
271
|
+
# Returns CSV headers for an array of ActiveRecord::Base
|
272
|
+
# model objects by calling to_csv_headers on the first
|
273
|
+
# element.
|
274
|
+
def to_csv_headers( options={} )
|
275
|
+
first.class.to_csv_headers( options )
|
276
|
+
end
|
277
|
+
|
278
|
+
# Returns CSV data without headers for an array of
|
279
|
+
# ActiveRecord::Base model objects by iterating over them and
|
280
|
+
# calling to_csv_data with the passed in +options+.
|
281
|
+
def to_csv_data( options={} )
|
282
|
+
inject( [] ) do |arr,model_instance|
|
283
|
+
arr.push( *model_instance.to_csv_data( options ) )
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns CSV data with headers for an array of ActiveRecord::Base
|
288
|
+
# model objects by iterating over them and calling to_csv with
|
289
|
+
# the passed in +options+.
|
290
|
+
def to_csv( options={} )
|
291
|
+
FasterCSV.generate do |csv|
|
292
|
+
headers = to_csv_headers( options )
|
293
|
+
csv << headers if headers
|
294
|
+
each do |model_instance|
|
295
|
+
model_instance.to_csv_data( options ).each{ |data| csv << data }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|