ar-extensions 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|