cloudxls-rails 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = CloudXLSRails::VERSION
17
17
 
18
- gem.add_dependency('cloudxls', '~> 0.4.0')
18
+ gem.add_dependency('cloudxls', '~> 0.5.0')
19
19
 
20
20
  gem.add_development_dependency "rake"
21
21
  gem.add_development_dependency "webmock"
@@ -0,0 +1,88 @@
1
+ require 'active_model/serialization'
2
+
3
+ module ActiveModel
4
+ module Serializers
5
+ module CSV
6
+ include ActiveModel::Serialization
7
+
8
+ # Returns an array representing the model. Some configuration can be
9
+ # passed through +options+.
10
+ #
11
+ # Without any +options+, the returned Array will include all the model's
12
+ # attributes.
13
+ #
14
+ # user = User.find(1)
15
+ # user.as_csv
16
+ # # => [1, "Konata Izumi", 16, "2006-08-01", true]
17
+ #
18
+ # The :only and :except options can be used to limit
19
+ # the attributes included, and work similar to the +attributes+ method.
20
+ #
21
+ # user.as_csv(only: [:id, :name])
22
+ # # => [1, "Konata Izumi"]
23
+ #
24
+ # user.as_csv(except: [:id, :created_at, :age])
25
+ # # => ["Konata Izumi", true]
26
+ #
27
+ # To include the result of some method calls on the model use :methods:
28
+ #
29
+ # user.as_csv(methods: :permalink)
30
+ # # => [1, "Konata Izumi", 16, "2006-08-01", true, "1-konata-izumi"]
31
+ #
32
+ def as_csv(options = nil)
33
+ options ||= {}
34
+
35
+ attribute_names = attributes.keys
36
+ if only = options[:only]
37
+ attribute_names = Array(only).map(&:to_s).select do |key|
38
+ attribute_names.include?(key)
39
+ end
40
+ elsif except = options[:except]
41
+ attribute_names -= Array(except).map(&:to_s)
42
+ end
43
+
44
+ arr = []
45
+ attribute_names.each do |n|
46
+ arr.push(read_attribute_for_serialization(n).as_csv)
47
+ end
48
+
49
+ Array(options[:methods]).each do |m|
50
+ if respond_to?(m)
51
+ val = send(m)
52
+ if idx = attribute_names.index(m.to_s)
53
+ arr[idx] = val
54
+ else
55
+ arr.push(val)
56
+ end
57
+ end
58
+ end
59
+
60
+ arr
61
+ end
62
+
63
+ private
64
+
65
+ # Hook method defining how an attribute value should be retrieved for
66
+ # serialization. By default this is assumed to be an instance named after
67
+ # the attribute. Override this method in subclasses should you need to
68
+ # retrieve the value for a given attribute differently:
69
+ #
70
+ # class MyClass
71
+ # include ActiveModel::Validations
72
+ #
73
+ # def initialize(data = {})
74
+ # @data = data
75
+ # end
76
+ #
77
+ # def read_attribute_for_serialization(key)
78
+ # @data[key]
79
+ # end
80
+ # end
81
+ alias :read_attribute_for_serialization :send
82
+
83
+ end
84
+ end
85
+ end
86
+
87
+ ActiveRecord::Base.send(:include, ActiveModel::Serializers::CSV)
88
+
@@ -0,0 +1,9 @@
1
+ require 'active_support/time_with_zone'
2
+
3
+ module ActiveSupport
4
+ class TimeWithZone
5
+ def as_csv(options = nil)
6
+ to_datetime.as_csv
7
+ end
8
+ end
9
+ end
@@ -33,12 +33,11 @@ end
33
33
 
34
34
  ActionController::Renderers.add :csv do |scope, options|
35
35
  filename = options.fetch(:filename, "data-#{DateTime.now.to_s}.csv")
36
- columns = options[:columns]
37
36
 
38
37
  if options[:stream]
39
38
  CloudXLSRails::CSVResponder.stream!(self, scope, options)
40
39
  else # no stream:
41
- data = CloudXLS::CSVWriter.text(scope, {:columns => columns})
40
+ data = CloudXLS::CSVWriter.text(scope, options)
42
41
 
43
42
  send_data data,
44
43
  type: Mime::CSV,
@@ -6,13 +6,12 @@ end
6
6
  module CloudXLSRails
7
7
  class XLSResponder
8
8
  def self.redirect!(controller, scope, options)
9
- columns = options.fetch(:columns, nil)
10
9
  xdata = options[:data] || {}
11
10
  unless (xdata.has_key?(:text) ||
12
11
  xdata.has_key?(:url ) ||
13
12
  xdata.has_key?(:file) )
14
13
 
15
- xdata[:text] = CloudXLS::CSVWriter.text(scope, {:columns => columns})
14
+ xdata[:text] = CloudXLS::CSVWriter.text(scope, options)
16
15
  end
17
16
 
18
17
  xopts = {:data => xdata}
@@ -6,13 +6,12 @@ end
6
6
  module CloudXLSRails
7
7
  class XLSXResponder
8
8
  def self.redirect!(controller, scope, options)
9
- columns = options.fetch(:columns, nil)
10
9
  xdata = options[:data] || {}
11
10
  unless (xdata.has_key?(:text) ||
12
11
  xdata.has_key?(:url ) ||
13
12
  xdata.has_key?(:file) )
14
13
 
15
- xdata[:text] = CloudXLS::CSVWriter.text(scope, {:columns => columns})
14
+ xdata[:text] = CloudXLS::CSVWriter.text(scope, options)
16
15
  end
17
16
 
18
17
  xopts = {:data => xdata}
@@ -1,3 +1,3 @@
1
1
  module CloudXLSRails
2
- VERSION = '0.4.3'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -2,6 +2,8 @@ require 'cloudxls' # cloudxls gem
2
2
 
3
3
  require 'action_controller'
4
4
  require 'cloudxls-rails/version'
5
+ require 'cloudxls-rails/activesupport'
6
+ require 'cloudxls-rails/activemodel'
5
7
  require 'cloudxls-rails/handlers/csv'
6
8
  require 'cloudxls-rails/handlers/xls'
7
9
  require 'cloudxls-rails/handlers/xlsx'
@@ -1,12 +1,58 @@
1
1
  require 'spec_helper'
2
2
 
3
+ describe CloudXLS::Util do
4
+ describe "titles_for_serialize_options" do
5
+ it "should work with a Post.all" do
6
+ expect( CloudXLS::Util.titles_for_serialize_options(Post.new, :only => "title") ).to eq(["title"])
7
+ end
8
+ end
9
+ end
10
+
3
11
  describe "CloudXLS::CSVWriter" do
4
12
  before do
5
13
  @writer = CloudXLS::CSVWriter
6
14
  end
7
15
 
8
16
  describe "with array" do
9
- # spec'ed in cloudlxs-ruby gem
17
+ it "should not titleize" do
18
+ expect( @writer.text([['foo','bar'],[1,2]]) ).to eq("foo,bar\n1,2")
19
+ end
20
+
21
+ it "should escape titles" do
22
+ expect( @writer.text([['bar"baz']]) ).to eq("\"bar\"\"baz\"")
23
+ end
24
+
25
+ it "should escape rows" do
26
+ expect( @writer.text([['title'],['bar"baz']]) ).to eq("title\n\"bar\"\"baz\"")
27
+ end
28
+
29
+ it "should write YYYY-MM-DD for Date" do
30
+ expect( @writer.text([[Date.new(2012,12,24)]]) ).to eq("2012-12-24")
31
+ end
32
+
33
+ it "should write xmlschema for DateTime" do
34
+ # TODO: make UTC consistent
35
+ expect( DateTime.new(2012,12,24,18,30,5,'+0000').as_csv ).to eq("2012-12-24T18:30:05.000+0000")
36
+ expect( [DateTime.new(2012,12,24,18,30,5,'+0000')].as_csv ).to eq(["2012-12-24T18:30:05.000+0000"])
37
+ expect( @writer.text([[DateTime.new(2012,12,24,18,30,5,'+0000')]]) ).to eq("2012-12-24T18:30:05.000+0000")
38
+ expect( @writer.text([[DateTime.new(2012,12,24,18,30,5,'+0000').to_time.utc]]) ).to eq("2012-12-24T18:30:05.000+0000")
39
+ end
40
+
41
+ it "should write nothing for nil" do
42
+ expect( @writer.text([[nil,nil]]) ).to eq(",")
43
+ end
44
+
45
+ it "should write \"\" for empty string" do
46
+ expect( @writer.text([["",""]]) ).to eq('"",""')
47
+ end
48
+
49
+ it "should write integers" do
50
+ expect( @writer.text([[-1,0,1,1_000_000]]) ).to eq('-1,0,1,1000000')
51
+ end
52
+
53
+ it "should write floats" do
54
+ 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')
55
+ end
10
56
  end
11
57
 
12
58
  describe "#text with AR" do
@@ -22,29 +68,29 @@ describe "CloudXLS::CSVWriter" do
22
68
  :published => false)
23
69
  end
24
70
 
25
- it "given no records should just return titles" do
71
+ it "given no records returns empty string" do
26
72
  Post.delete_all
27
- expect( @writer.text(Post.all, :columns => [:title, :visits]) ).to eq("Title,Visits")
73
+ expect( @writer.text(Post.all, :only => [:title, :visits]) ).to eq("")
28
74
  end
29
75
 
30
76
  it "should work with a Post.all" do
31
- expect( @writer.text(Post.all, :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
77
+ expect( @writer.text(Post.all, :only => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
32
78
  end
33
79
 
34
80
  it "should work with a Post.limit" do
35
- expect( @writer.text(Post.limit(10), :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
81
+ expect( @writer.text(Post.limit(10), :only => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
36
82
  end
37
83
 
38
84
  it "should work with a Post.all.to_a" do
39
- expect( @writer.text(Post.all.to_a, :columns => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
85
+ expect( @writer.text(Post.all.to_a, :only => [:title, :visits]) ).to eq("Title,Visits\nhello world,12032")
40
86
  end
41
87
 
42
88
  it "should write xmlschema for DateTime" do
43
- expect( @writer.text(Post.all, :columns => [:expired_at]) ).to eq("Expired At\n2013-12-25T12:30:30.000+0000")
89
+ expect( @writer.text(Post.all, :only => [:expired_at]) ).to eq("Expired At\n2013-12-25T12:30:30.000+0000")
44
90
  end
45
91
 
46
92
  it "should write YYYY-MM-DD for Date" do
47
- expect( @writer.text(Post.all, :columns => [:published_on]) ).to eq("Published On\n2013-12-24")
93
+ expect( @writer.text(Post.all, :only => [:published_on]) ).to eq("Published On\n2013-12-24")
48
94
  end
49
95
  end
50
96
  end
@@ -14,6 +14,13 @@ describe 'Request', :type => :request do
14
14
  :published => false)
15
15
  end
16
16
 
17
+ it "#as_csv" do
18
+ expect( @post.as_csv(except: [:unix_timestamp, :created_at, :updated_at]) ).to eq([@post.id, "hello world", 12_032, 0.24, "2013-12-24", "2013-12-25T12:30:30.000+0000", false])
19
+ expect( @post.as_csv(only: [:title]) ).to eq(["hello world"])
20
+ expect( @post.as_csv(only: [:title, :visits]) ).to eq(["hello world", 12_032])
21
+ expect( @post.as_csv(only: [:expired_at]) ).to eq(["2013-12-25T12:30:30.000+0000"])
22
+ end
23
+
17
24
  it "has a working test_app" do
18
25
  visit '/'
19
26
  page.should have_content "Users"
@@ -27,6 +34,14 @@ describe 'Request', :type => :request do
27
34
  ].join("\n")
28
35
  end
29
36
 
37
+ it "/posts/all_columns.csv streams csv" do
38
+ visit '/posts/all_columns.csv'
39
+ page.should have_content [
40
+ "Id,Title,Visits,Conversion Rate,Published On,Expired At,Published,Unix Timestamp,Created At,Updated At",
41
+ "1,hello world,12032,0.24,2013-12-24,2013-12-25T12:30:30.000+0000,false,2013-12-25T12:30:30.000+0000,#{@post.created_at.as_csv},#{@post.updated_at.as_csv}"
42
+ ].join("\n")
43
+ end
44
+
30
45
  it "/posts/stream.csv streams csv" do
31
46
  visit '/posts/stream.csv'
32
47
  page.should have_content [
@@ -3,19 +3,23 @@ class PostsController < ApplicationController
3
3
 
4
4
  def index
5
5
  respond_with(Post.all,
6
- :columns => export_attributes)
6
+ :only => export_attributes)
7
+ end
8
+
9
+ def all_columns
10
+ respond_with(Post.all.to_a)
7
11
  end
8
12
 
9
13
  def stream
10
14
  respond_with(Post.all,
11
15
  :stream => true,
12
- :columns => export_attributes)
16
+ :only => export_attributes)
13
17
  end
14
18
 
15
19
  def stream_with_custom_url
16
20
  respond_with(Post.all,
17
21
  :stream => "/successful_redirect",
18
- :columns => export_attributes)
22
+ :only => export_attributes)
19
23
  end
20
24
 
21
25
  # Used for stub/mocking a redirect request
@@ -3,6 +3,7 @@ TestApp::Application.routes.draw do
3
3
  collection do
4
4
  get "stream"
5
5
  get "stream_with_custom_url"
6
+ get "all_columns"
6
7
  end
7
8
  end
8
9
  get "successful_redirect" => "posts#successful_redirect"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudxls-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.4.0
21
+ version: 0.5.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 0.4.0
29
+ version: 0.5.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rake
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,8 @@ files:
120
120
  - Rakefile
121
121
  - cloudxls-rails.gemspec
122
122
  - lib/cloudxls-rails.rb
123
+ - lib/cloudxls-rails/activemodel.rb
124
+ - lib/cloudxls-rails/activesupport.rb
123
125
  - lib/cloudxls-rails/handlers/csv.rb
124
126
  - lib/cloudxls-rails/handlers/xls.rb
125
127
  - lib/cloudxls-rails/handlers/xlsx.rb