cloudxls 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cloudxls/core_ext.rb +106 -0
- data/lib/cloudxls/csv_writer.rb +95 -64
- data/lib/cloudxls/version.rb +1 -1
- data/lib/cloudxls.rb +2 -0
- data/spec/csv_writer_spec.rb +0 -39
- metadata +3 -2
@@ -0,0 +1,106 @@
|
|
1
|
+
# Object#as_csv ensures that object formats are written the same across ruby
|
2
|
+
# versions.
|
3
|
+
#
|
4
|
+
# If an object is known to have issues cross-platform #as_json returns the
|
5
|
+
# desired string.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# DateTime.now.as_csv # => "2012-12-24T12:30:01.000+0000"
|
10
|
+
# 5.as_csv # => 5
|
11
|
+
# (0.0/0.0).as_csv # => "#DIV/0!"
|
12
|
+
#
|
13
|
+
|
14
|
+
class Time
|
15
|
+
def as_csv(options = nil)
|
16
|
+
strftime(CloudXLS::DATETIME_FORMAT)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class DateTime
|
21
|
+
def as_csv(options = nil)
|
22
|
+
strftime(CloudXLS::DATETIME_FORMAT)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class String
|
27
|
+
def as_csv(options = nil)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Date
|
33
|
+
def as_csv(options = nil)
|
34
|
+
strftime(CloudXLS::DATE_FORMAT)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NilClass
|
39
|
+
def as_csv(options = nil)
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class FalseClass
|
45
|
+
def as_csv(options = nil)
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class TrueClass
|
51
|
+
def as_csv(options = nil)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Symbol
|
57
|
+
def as_csv(options = nil)
|
58
|
+
to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Numeric
|
63
|
+
def as_csv(options = nil)
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Float
|
69
|
+
# Encoding Infinity or NaN to JSON should return "null". The default returns
|
70
|
+
# "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
|
71
|
+
def as_csv(options = nil) #:nodoc:
|
72
|
+
finite? ? self : "#DIV/0!"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Regexp
|
77
|
+
def as_csv(options = nil)
|
78
|
+
to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module Enumerable
|
83
|
+
def as_csv(options = nil)
|
84
|
+
to_a.as_json(options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Range
|
89
|
+
def as_csv(options = nil)
|
90
|
+
to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Array
|
95
|
+
def as_csv(options = nil)
|
96
|
+
map do |val|
|
97
|
+
val.as_csv(options)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Object
|
103
|
+
def as_csv(options = nil)
|
104
|
+
to_s
|
105
|
+
end
|
106
|
+
end
|
data/lib/cloudxls/csv_writer.rb
CHANGED
@@ -1,108 +1,139 @@
|
|
1
1
|
require 'csv'
|
2
2
|
|
3
3
|
module CloudXLS
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
DATETIME_FORMAT = "%FT%T.%L%z".freeze
|
5
|
+
DATE_FORMAT = "%F".freeze
|
6
|
+
|
7
|
+
# Wrapper around stdlib CSV methods.
|
8
|
+
#
|
9
|
+
class CSV
|
10
|
+
def self.generate_line(arr, options = nil)
|
11
|
+
::CSV.generate_line(arr.as_csv, options)
|
12
|
+
end
|
13
|
+
end
|
7
14
|
|
15
|
+
|
16
|
+
class CSVWriter
|
8
17
|
# Generates CSV string.
|
9
18
|
#
|
10
|
-
# @param [
|
19
|
+
# @param [Enumerable] scope
|
11
20
|
# Method/attribute keys to for the export.
|
12
21
|
#
|
22
|
+
# @option opts [String] :encoding ("UTF-8")
|
23
|
+
# Charset encoding of output.
|
24
|
+
#
|
25
|
+
# @option opts [Boolean] :skip_headers (false)
|
26
|
+
# Do not output headers if first element is an ActiveRecord object.
|
27
|
+
#
|
28
|
+
# @option opts [Array] :only same as #as_json
|
29
|
+
# @option opts [Array] :except same as #as_json
|
30
|
+
# @option opts [Array] :methods same as #as_json
|
31
|
+
#
|
13
32
|
# @return [String]
|
14
33
|
# The full CSV as a string. Titleizes *columns* for the header.
|
15
34
|
#
|
16
|
-
def self.text(scope,
|
17
|
-
|
35
|
+
def self.text(scope, opts = {})
|
36
|
+
encoding = opts.delete(:encoding) || "UTF-8"
|
37
|
+
skip_headers = opts.delete(:skip_headers) || false
|
18
38
|
|
19
|
-
str = ::CSV.generate do |csv|
|
39
|
+
str = ::CSV.generate(:encoding => encoding) do |csv|
|
20
40
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
csv <<
|
41
|
+
enum_method = scope_enumerator(scope)
|
42
|
+
scope.send(enum_method) do |record|
|
43
|
+
|
44
|
+
if skip_headers == false && !record.is_a?(Array)
|
45
|
+
titles = CloudXLS::Util.titles_for_serialize_options(record, opts)
|
46
|
+
csv << titles.map(&:titleize)
|
47
|
+
skip_headers = true
|
27
48
|
end
|
28
|
-
end
|
29
49
|
|
30
|
-
|
31
|
-
scope.send(enum) do |record|
|
32
|
-
csv << csv_row(record, columns)
|
50
|
+
csv << record.as_csv(opts)
|
33
51
|
end
|
34
52
|
end
|
35
53
|
str.strip!
|
54
|
+
str
|
36
55
|
end
|
37
56
|
|
57
|
+
|
58
|
+
# Generates Enumerator for streaming response.
|
59
|
+
#
|
38
60
|
# Example
|
39
61
|
#
|
40
|
-
#
|
62
|
+
# def index
|
63
|
+
# # setup headers...
|
64
|
+
# stream = CloudXLS::CSVWriter.csv_enumerator(Post.all, only: [:title, :author])
|
65
|
+
# self.response_body = stream
|
66
|
+
# end
|
41
67
|
#
|
42
|
-
#
|
43
|
-
# An activerecord scope object for the records to be exported.
|
44
|
-
# Example: Post.all.limit(500).where(author: "foo")
|
68
|
+
# Same options and parameters as #text.
|
45
69
|
#
|
46
70
|
# @return [Enumerator] enumerator to use for streaming response.
|
47
71
|
#
|
48
72
|
def self.enumerator(scope, options = {})
|
49
|
-
|
50
|
-
|
51
|
-
Enumerator.new do |row|
|
52
|
-
if options[:skip_headers] != true
|
53
|
-
if scope.respond_to?(:column_names)
|
54
|
-
columns ||= scope.column_names
|
55
|
-
end
|
56
|
-
if columns
|
57
|
-
row << csv_titles(columns, :titleize).to_csv
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
enum = scope_enumerator(scope)
|
62
|
-
scope.send(enum) do |record|
|
63
|
-
row << csv_row(record, columns).to_csv
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
73
|
+
encoding = options.delete(:encoding) || "UTF-8"
|
74
|
+
skip_headers = options.delete(:skip_headers) || false
|
67
75
|
|
68
|
-
|
76
|
+
Enumerator.new do |stream|
|
77
|
+
enum_method = scope_enumerator(scope)
|
69
78
|
|
79
|
+
scope.send(enum_method) do |record|
|
80
|
+
if !skip_headers && !record.is_a?(Array)
|
81
|
+
titles = CloudXLS::Util.titles_for_serialize_options(record, options)
|
82
|
+
stream << titles.map(&:titleize).to_csv
|
83
|
+
skip_headers = true
|
84
|
+
end
|
70
85
|
|
71
|
-
|
72
|
-
if obj.is_a?(Array)
|
73
|
-
obj.map{ |el| encode_for_csv(el) }
|
74
|
-
else
|
75
|
-
columns.map do |key|
|
76
|
-
encode_for_csv(obj.send(key))
|
86
|
+
stream << record.as_csv(options).to_csv
|
77
87
|
end
|
78
88
|
end
|
79
89
|
end
|
80
90
|
|
81
91
|
|
82
|
-
def self.
|
83
|
-
|
84
|
-
|
85
|
-
|
92
|
+
def self.scope_enumerator(scope)
|
93
|
+
if (scope.respond_to?(:arel) &&
|
94
|
+
scope.arel.orders.blank? &&
|
95
|
+
scope.arel.taken.blank?)
|
96
|
+
:find_each
|
86
97
|
else
|
87
|
-
|
98
|
+
:each
|
88
99
|
end
|
89
100
|
end
|
101
|
+
end
|
102
|
+
end
|
90
103
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
module CloudXLS
|
105
|
+
class Util
|
106
|
+
# Column and method-names of a model that correspond to the values from
|
107
|
+
# a #as_json/#as_csv call. In the same order.
|
108
|
+
#
|
109
|
+
# Example
|
110
|
+
#
|
111
|
+
# CloudXLS::Util.titles_for_serialize_options(Post.new, only: [:author, :title], method: [:slug])
|
112
|
+
# # => ['title', 'author', 'slug']
|
113
|
+
#
|
114
|
+
def self.titles_for_serialize_options(record, options = nil)
|
115
|
+
options ||= {}
|
116
|
+
|
117
|
+
attribute_names = record.attributes.keys
|
118
|
+
if only = options[:only]
|
119
|
+
arr = []
|
120
|
+
Array(only).map(&:to_s).each do |key|
|
121
|
+
arr.push(key) if attribute_names.include?(key)
|
122
|
+
end
|
123
|
+
attribute_names = arr
|
124
|
+
elsif except = options[:except]
|
125
|
+
attribute_names -= Array(except).map(&:to_s)
|
96
126
|
end
|
97
|
-
end
|
98
127
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
128
|
+
Array(options[:methods]).each do |m|
|
129
|
+
m = m.to_s
|
130
|
+
if record.respond_to?(m)
|
131
|
+
unless attribute_names.include?(m)
|
132
|
+
attribute_names.push(val)
|
133
|
+
end
|
134
|
+
end
|
105
135
|
end
|
136
|
+
attribute_names
|
106
137
|
end
|
107
138
|
end
|
108
|
-
end
|
139
|
+
end
|
data/lib/cloudxls/version.rb
CHANGED
data/lib/cloudxls.rb
CHANGED
data/spec/csv_writer_spec.rb
CHANGED
@@ -44,43 +44,4 @@ describe "CloudXLS::CSVWriter" do
|
|
44
44
|
expect( @writer.text([[-1.0,0.0,1.0,1_000_000.0,1.234567]]) ).to eq('-1.0,0.0,1.0,1000000.0,1.234567')
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
48
|
-
# describe "#text with AR" do
|
49
|
-
# before do
|
50
|
-
# Post.delete_all
|
51
|
-
# @post = Post.create(
|
52
|
-
# :title => "hello world",
|
53
|
-
# :visits => 12_032,
|
54
|
-
# :conversion_rate => 0.24,
|
55
|
-
# :published_on => Date.new(2013,12,24),
|
56
|
-
# :expired_at => DateTime.new(2013,12,25,12,30,30),
|
57
|
-
# :unix_timestamp => DateTime.new(2013,12,25,12,30,30),
|
58
|
-
# :published => false)
|
59
|
-
# end
|
60
|
-
|
61
|
-
# it "given no records should just return titles" do
|
62
|
-
# Post.delete_all
|
63
|
-
# expect( @writer.text(Post.all, :columns => [:title, :visits]) ).to eq("Title,Visits")
|
64
|
-
# end
|
65
|
-
|
66
|
-
# it "should work with a Post.all" do
|
67
|
-
# expect( @writer.text(Post.all, :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
|
68
|
-
# end
|
69
|
-
|
70
|
-
# it "should work with a Post.limit" do
|
71
|
-
# expect( @writer.text(Post.limit(10), :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
|
72
|
-
# end
|
73
|
-
|
74
|
-
# it "should work with a Post.all.to_a" do
|
75
|
-
# expect( @writer.text(Post.all.to_a, :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
|
76
|
-
# end
|
77
|
-
|
78
|
-
# it "should write xmlschema for DateTime" do
|
79
|
-
# expect( @writer.text(Post.all, :columns => [:expired_at]) ).to eq("Expired At\n2013-12-25T12:30:30.000+0000")
|
80
|
-
# end
|
81
|
-
|
82
|
-
# it "should write YYYY-MM-DD for Date" do
|
83
|
-
# expect( @writer.text(Post.all, :columns => [:published_on]) ).to eq("Published On\n2013-12-24")
|
84
|
-
# end
|
85
|
-
# end
|
86
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudxls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- Rakefile
|
111
111
|
- cloudxls.gemspec
|
112
112
|
- lib/cloudxls.rb
|
113
|
+
- lib/cloudxls/core_ext.rb
|
113
114
|
- lib/cloudxls/csv_writer.rb
|
114
115
|
- lib/cloudxls/version.rb
|
115
116
|
- lib/data/ca-certificates.txt
|