vibedeck-comma 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ module Comma
2
+
3
+ class Generator
4
+
5
+ def initialize(instance, style)
6
+ @instance = instance
7
+ @style = style
8
+ @options = {}
9
+
10
+ if @style.is_a? Hash
11
+ @options = @style.clone
12
+ @style = @options.delete(:style) || :default
13
+ @filename = @options.delete(:filename)
14
+ end
15
+ end
16
+
17
+ def run(iterator_method)
18
+ if @filename
19
+ FasterCSV.open(@filename, 'w', @options){ |csv| append_csv(csv, iterator_method) } and return true
20
+ else
21
+ FasterCSV.generate(@options){ |csv| append_csv(csv, iterator_method) }
22
+ end
23
+ end
24
+
25
+ private
26
+ def append_csv(csv, iterator_method)
27
+ return '' if @instance.empty?
28
+ csv << @instance.first.to_comma_headers(@style) # REVISIT: request to optionally include headers
29
+ @instance.send(iterator_method) do |object|
30
+ csv << object.to_comma(@style)
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ class ActiveRecord::NamedScope::Scope
2
+ def to_comma(style = :default)
3
+ Comma::Generator.new(self, style).run(:find_each)
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ class Object
2
+ class_attribute :comma_formats
3
+
4
+ def self.comma(style = :default, &block)
5
+ (self.comma_formats ||= {})[style] = block
6
+ end
7
+
8
+ def to_comma(style = :default)
9
+ raise "No comma format for class #{self.class} defined for style #{style}" unless self.comma_formats and self.comma_formats[style]
10
+ Comma::DataExtractor.new(self, &self.comma_formats[style]).results
11
+ end
12
+
13
+ def to_comma_headers(style = :default)
14
+ raise "No comma format for class #{self.class} defined for style #{style}" unless self.comma_formats and self.comma_formats[style]
15
+ Comma::HeaderExtractor.new(self, &self.comma_formats[style]).results
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ if defined?(ActionController::Renderers) && ActionController::Renderers.respond_to?(:add)
2
+ ActionController::Renderers.add :csv do |obj, options|
3
+ filename = options[:filename] || 'data'
4
+ send_data obj.to_comma, :type => Mime::CSV, :disposition => "attachment; filename=#{filename}.csv"
5
+ end
6
+ else
7
+ module RenderAsCSV
8
+ def self.included(base)
9
+ base.alias_method_chain :render, :csv
10
+ end
11
+
12
+ def render_with_csv(options = nil, extra_options = {}, &block)
13
+ return render_without_csv(options, extra_options, &block) unless options.is_a?(Hash) and options[:csv].present?
14
+
15
+ content = options.delete(:csv)
16
+ style = options.delete(:style) || :default
17
+ filename = options.delete(:filename)
18
+
19
+ headers.merge!(
20
+ 'Content-Transfer-Encoding' => 'binary',
21
+ 'Content-Type' => 'text/csv; charset=utf-8'
22
+ )
23
+ filename_header_value = "attachment"
24
+ filename_header_value += "; filename=\"#{filename}\"" if filename.present?
25
+ headers.merge!('Content-Disposition' => filename_header_value)
26
+
27
+ @performed_render = false
28
+
29
+ render_stream :status => 200,
30
+ :content => Array(content),
31
+ :style => style
32
+ end
33
+
34
+ protected
35
+
36
+ def render_stream(options)
37
+ status = options[:status]
38
+ content = options[:content]
39
+ style = options[:style]
40
+
41
+ # If Rails 2.x
42
+ if defined? Rails and (Rails.version.split('.').map(&:to_i) <=> [2,3,5]) < 0
43
+ render :status => status, :text => Proc.new { |response, output|
44
+ output.write FasterCSV.generate_line(content.first.to_comma_headers(style))
45
+ content.each { |line| output.write FasterCSV.generate_line(line.to_comma(style)) }
46
+ }
47
+ else # If Rails 3.x
48
+ self.status = status
49
+ self.response_body = proc { |response, output|
50
+ output.write FasterCSV.generate_line(content.first.to_comma_headers(style))
51
+ content.each { |line| output.write FasterCSV.generate_line(line.to_comma(style)) }
52
+ }
53
+ end
54
+ end
55
+ end
56
+ #credit : http://ramblingsonrails.com/download-a-large-amount-of-data-in-csv-from-rails
57
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Comma, 'generating CSV from an ActiveRecord object' do
4
+ before(:all) do
5
+ class Person < ActiveRecord::Base
6
+ named_scope :teenagers, :conditions => { :age => 13..19 }
7
+ comma do
8
+ name
9
+ age
10
+ end
11
+ end
12
+
13
+ require 'active_record/connection_adapters/abstract_adapter'
14
+ Column = ActiveRecord::ConnectionAdapters::Column
15
+ end
16
+
17
+ before do
18
+ Person.stub!(:columns).and_return [Column.new('age', 0, 'integer', false),
19
+ Column.new('name', nil, 'string', false) ]
20
+ Person.stub!(:table_exists?).and_return(true)
21
+ end
22
+
23
+ describe 'case' do
24
+ before do
25
+ people = [ Person.new(:age => 18, :name => 'Junior') ]
26
+ Person.stub!(:find_every).and_return people
27
+ Person.stub!(:calculate).with(:count, :all, {}).and_return people.size
28
+ end
29
+
30
+ it 'should extend ActiveRecord::NamedScope::Scope to add a #to_comma method which will return CSV content for objects within the scope' do
31
+ Person.teenagers.to_comma.should == "Name,Age\nJunior,18\n"
32
+ end
33
+
34
+ it 'should find in batches' do
35
+ Person.teenagers.to_comma
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,217 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Comma do
4
+
5
+ it 'should extend object to add a comma method' do
6
+ Object.should respond_to(:comma)
7
+ end
8
+
9
+ it 'should extend object to have a to_comma method' do
10
+ Object.should respond_to(:to_comma)
11
+ end
12
+
13
+ it 'should extend object to have a to_comma_headers method' do
14
+ Object.should respond_to(:to_comma_headers)
15
+ end
16
+
17
+ end
18
+
19
+ describe Comma, 'generating CSV' do
20
+
21
+ before do
22
+ @isbn = Isbn.new('123123123', '321321321')
23
+ @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)
24
+
25
+ @books = []
26
+ @books << @book
27
+ end
28
+
29
+ it 'should extend Array to add a #to_comma method which will return CSV content for objects within the array' do
30
+ @books.to_comma.should == "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n"
31
+ end
32
+
33
+ it 'should return an empty string when generating CSV from an empty array' do
34
+ Array.new.to_comma.should == ''
35
+ end
36
+
37
+ it "should change the style when specified" do
38
+ @books.to_comma(:brief).should == "Name,Description\nSmalltalk-80,Language and Implementation\n"
39
+ end
40
+
41
+ describe 'with :filename specified' do
42
+ after{ File.delete('comma.csv') }
43
+
44
+ it "should write to the file" do
45
+ @books.to_comma(:filename => 'comma.csv')
46
+ File.read('comma.csv').should == "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n"
47
+ end
48
+
49
+ it "should accept FasterCSV options" do
50
+ @books.to_comma(:filename => 'comma.csv', :col_sep => ';', :force_quotes => true)
51
+ File.read('comma.csv').should == "\"Title\";\"Description\";\"Issuer\";\"ISBN-10\";\"ISBN-13\"\n\"Smalltalk-80\";\"Language and Implementation\";\"ISBN\";\"123123123\";\"321321321\"\n"
52
+ end
53
+
54
+ end
55
+
56
+ describe "with FasterCSV options" do
57
+ it "should not change when options are empty" do
58
+ @books.to_comma({}).should == "Title,Description,Issuer,ISBN-10,ISBN-13\nSmalltalk-80,Language and Implementation,ISBN,123123123,321321321\n"
59
+ end
60
+
61
+ it 'should accept the options in #to_comma and generate the appropriate CSV' do
62
+ @books.to_comma(:col_sep => ';', :force_quotes => true).should == "\"Title\";\"Description\";\"Issuer\";\"ISBN-10\";\"ISBN-13\"\n\"Smalltalk-80\";\"Language and Implementation\";\"ISBN\";\"123123123\";\"321321321\"\n"
63
+ end
64
+
65
+ it "should change the style when specified" do
66
+ @books.to_comma(:style => :brief, :col_sep => ';', :force_quotes => true).should == "\"Name\";\"Description\"\n\"Smalltalk-80\";\"Language and Implementation\"\n"
67
+ end
68
+ end
69
+ end
70
+
71
+ describe Comma, 'defining CSV descriptions' do
72
+
73
+ describe 'with an unnamed description' do
74
+
75
+ before do
76
+ class Foo
77
+ comma do; end
78
+ end
79
+ end
80
+
81
+ it 'should name the current description :default if no name has been provided' do
82
+ Foo.comma_formats.should_not be_empty
83
+ Foo.comma_formats[:default].should_not be_nil
84
+ end
85
+ end
86
+
87
+ describe 'with a named description' do
88
+
89
+ before do
90
+ class Bar
91
+ comma do; end
92
+ comma :detailed do; end
93
+ end
94
+ end
95
+
96
+ it 'should use the provided name to index the comma format' do
97
+ Bar.comma_formats.should_not be_empty
98
+ Bar.comma_formats[:default].should_not be_nil
99
+ Bar.comma_formats[:detailed].should_not be_nil
100
+ end
101
+ end
102
+ end
103
+
104
+ describe Comma, 'to_comma data/headers object extensions' do
105
+
106
+ describe 'with unnamed descriptions' do
107
+
108
+ before do
109
+ class Foo
110
+ attr_accessor :content
111
+ comma do; content; end
112
+
113
+ def initialize(content)
114
+ @content = content
115
+ end
116
+ end
117
+
118
+ @foo = Foo.new('content')
119
+ end
120
+
121
+ it 'should return and array of data content, using the :default CSV description if none requested' do
122
+ @foo.to_comma.should == %w(content)
123
+ end
124
+
125
+ it 'should return and array of header content, using the :default CSV description if none requested' do
126
+ @foo.to_comma_headers.should == %w(Content)
127
+ end
128
+
129
+ it 'should return the CSV representation including header and content when called on an array' do
130
+ Array(@foo).to_comma.should == "Content\ncontent\n"
131
+ end
132
+
133
+ end
134
+
135
+ describe 'with named descriptions' do
136
+
137
+ before do
138
+ class Foo
139
+ attr_accessor :content
140
+ comma :detailed do; content; end
141
+
142
+ def initialize(content)
143
+ @content = content
144
+ end
145
+ end
146
+
147
+ @foo = Foo.new('content')
148
+ end
149
+
150
+ it 'should return and array of data content, using the :default CSV description if none requested' do
151
+ @foo.to_comma(:detailed).should == %w(content)
152
+ end
153
+
154
+ it 'should return and array of header content, using the :default CSV description if none requested' do
155
+ @foo.to_comma_headers(:detailed).should == %w(Content)
156
+ end
157
+
158
+ it 'should return the CSV representation including header and content when called on an array' do
159
+ Array(@foo).to_comma(:detailed).should == "Content\ncontent\n"
160
+ end
161
+
162
+ it 'should raise an error if the requested description is not avaliable' do
163
+ lambda { @foo.to_comma(:bad) }.should raise_error
164
+ lambda { @foo.to_comma_headers(:bad) }.should raise_error
165
+ lambda { Array(@foo).to_comma(:bad) }.should raise_error
166
+ end
167
+
168
+ end
169
+
170
+ describe 'with block' do
171
+ before do
172
+ class Foo
173
+ attr_accessor :content, :created_at, :updated_at
174
+ comma do
175
+ content
176
+ content('Truncated Content') {|i| i && i.length > 10 ? i[0..10] : '---' }
177
+ created_at { |i| i && i.to_s(:db) }
178
+ updated_at { |i| i && i.to_s(:db) }
179
+ created_at 'Created Custom Label' do |i| i && i.to_s(:short) end
180
+ updated_at 'Updated at Custom Label' do |i| i && i.to_s(:short) end
181
+ end
182
+
183
+ def initialize(content, created_at = Time.now, updated_at = Time.now)
184
+ @content = content
185
+ @created_at = created_at
186
+ @updated_at = updated_at
187
+ end
188
+ end
189
+
190
+ @time = Time.now
191
+ @content = 'content ' * 5
192
+ @foo = Foo.new @content, @time, @time
193
+ end
194
+
195
+ it 'should return yielded values by block' do
196
+ header, foo = Array(@foo).to_comma.split("\n")
197
+ foo.should == [@content, @content[0..10], @time.to_s(:db), @time.to_s(:db), @time.to_s(:short), @time.to_s(:short)].join(',')
198
+ end
199
+
200
+ it 'should return headers with custom labels from block' do
201
+ header, foo = Array(@foo).to_comma.split("\n")
202
+ header.should == ['Content', 'Truncated Content', 'Created at', 'Updated at', 'Created Custom Label', 'Updated at Custom Label'].join(',')
203
+ end
204
+
205
+ end
206
+
207
+
208
+ describe 'on an object with no comma declaration' do
209
+
210
+ it 'should raise an error mentioning there is no comma description defined for that class' do
211
+ lambda { 'a string'.to_comma }.should raise_error('No comma format for class String defined for style default')
212
+ lambda { 'a string'.to_comma_headers }.should raise_error('No comma format for class String defined for style default')
213
+ end
214
+
215
+ end
216
+
217
+ end
@@ -0,0 +1,91 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ # comma do
4
+ # name 'Title'
5
+ # description
6
+ #
7
+ # isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
8
+ # end
9
+
10
+ describe Comma::HeaderExtractor do
11
+
12
+ before do
13
+ @isbn = Isbn.new('123123123', '321321321')
14
+ @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)
15
+
16
+ @headers = @book.to_comma_headers
17
+ end
18
+
19
+ describe 'when no parameters are provided' do
20
+
21
+ it 'should use the method name as the header name, humanized' do
22
+ @headers.should include('Description')
23
+ end
24
+ end
25
+
26
+ describe 'when given a string description as a parameter' do
27
+
28
+ it 'should use the string value, unmodified' do
29
+ @headers.should include('Title')
30
+ end
31
+ end
32
+
33
+ describe 'when an hash is passed as a parameter' do
34
+
35
+ describe 'with a string value' do
36
+
37
+ it 'should use the string value, unmodified' do
38
+ @headers.should include('ISBN-10')
39
+ end
40
+ end
41
+
42
+ describe 'with a non-string value' do
43
+
44
+ it 'should use the non string value converted to a string, humanized' do
45
+ @headers.should include('Issuer')
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ describe Comma::DataExtractor do
54
+
55
+ before do
56
+ @isbn = Isbn.new('123123123', '321321321')
57
+ @book = Book.new('Smalltalk-80', 'Language and Implementation', @isbn)
58
+
59
+ @data = @book.to_comma
60
+ end
61
+
62
+ describe 'when no parameters are provided' do
63
+
64
+ it 'should use the string value returned by sending the method name on the object' do
65
+ @data.should include('Language and Implementation')
66
+ end
67
+ end
68
+
69
+ describe 'when given a string description as a parameter' do
70
+
71
+ it 'should use the string value returned by sending the method name on the object' do
72
+ @data.should include('Smalltalk-80')
73
+ end
74
+ end
75
+
76
+ describe 'when an hash is passed as a parameter' do
77
+
78
+ describe 'with a string value' do
79
+
80
+ it 'should use the string value, returned by sending the hash key to the object' do
81
+ @data.should include('123123123')
82
+ @data.should include('321321321')
83
+ end
84
+
85
+ it 'should not fail when an associated object is nil' do
86
+ lambda { Book.new('Smalltalk-80', 'Language and Implementation', nil).to_comma }.should_not raise_error
87
+ end
88
+ end
89
+ end
90
+
91
+ end