as_csv 1.0.0

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.
Files changed (61) hide show
  1. data/.gitignore +20 -0
  2. data/.travis.yml +3 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +107 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +166 -0
  7. data/Rakefile +1 -0
  8. data/as_csv.gemspec +23 -0
  9. data/lib/as_csv/action_renderer.rb +5 -0
  10. data/lib/as_csv/active_model.rb +11 -0
  11. data/lib/as_csv/core_ext/array.rb +7 -0
  12. data/lib/as_csv/csv_builder.rb +46 -0
  13. data/lib/as_csv/version.rb +3 -0
  14. data/lib/as_csv.rb +6 -0
  15. data/spec/controllers/render_widgets_controller_spec.rb +5 -0
  16. data/spec/controllers/respond_with_widgets_controller_spec.rb +5 -0
  17. data/spec/core_ext/array_spec.rb +28 -0
  18. data/spec/dummy/README.rdoc +261 -0
  19. data/spec/dummy/Rakefile +7 -0
  20. data/spec/dummy/app/assets/images/rails.png +0 -0
  21. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  22. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  24. data/spec/dummy/app/controllers/render_widgets_controller.rb +15 -0
  25. data/spec/dummy/app/controllers/respond_with_widgets_controller.rb +12 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  27. data/spec/dummy/app/models/widget.rb +3 -0
  28. data/spec/dummy/app/models/widget_with_options.rb +12 -0
  29. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/spec/dummy/config/application.rb +53 -0
  31. data/spec/dummy/config/boot.rb +10 -0
  32. data/spec/dummy/config/database.yml +7 -0
  33. data/spec/dummy/config/environment.rb +8 -0
  34. data/spec/dummy/config/environments/development.rb +32 -0
  35. data/spec/dummy/config/environments/production.rb +54 -0
  36. data/spec/dummy/config/environments/test.rb +37 -0
  37. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/spec/dummy/config/initializers/inflections.rb +15 -0
  39. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  40. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  41. data/spec/dummy/config/initializers/session_store.rb +8 -0
  42. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/spec/dummy/config/locales/en.yml +5 -0
  44. data/spec/dummy/config/routes.rb +4 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/db/schema.rb +22 -0
  47. data/spec/dummy/db/seeds.rb +7 -0
  48. data/spec/dummy/public/404.html +26 -0
  49. data/spec/dummy/public/422.html +26 -0
  50. data/spec/dummy/public/500.html +25 -0
  51. data/spec/dummy/public/favicon.ico +0 -0
  52. data/spec/dummy/public/index.html +241 -0
  53. data/spec/dummy/public/robots.txt +5 -0
  54. data/spec/dummy/script/rails +6 -0
  55. data/spec/lib/csv_builder_spec.rb +114 -0
  56. data/spec/models/active_model_spec.rb +16 -0
  57. data/spec/models/widget_spec.rb +47 -0
  58. data/spec/models/widget_with_options_spec.rb +29 -0
  59. data/spec/spec_helper.rb +27 -0
  60. data/spec/support/widgets_controller_mixin.rb +45 -0
  61. metadata +199 -0
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ spec/dummy/db/*.sqlite3
18
+ spec/dummy/log/*.log
19
+ spec/dummy/tmp/
20
+ spec/dummy/.sass-cache
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm: 1.9.3
3
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in as_csv.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,107 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ as_csv (0.0.1.pre)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionmailer (3.2.11)
10
+ actionpack (= 3.2.11)
11
+ mail (~> 2.4.4)
12
+ actionpack (3.2.11)
13
+ activemodel (= 3.2.11)
14
+ activesupport (= 3.2.11)
15
+ builder (~> 3.0.0)
16
+ erubis (~> 2.7.0)
17
+ journey (~> 1.0.4)
18
+ rack (~> 1.4.0)
19
+ rack-cache (~> 1.2)
20
+ rack-test (~> 0.6.1)
21
+ sprockets (~> 2.2.1)
22
+ activemodel (3.2.11)
23
+ activesupport (= 3.2.11)
24
+ builder (~> 3.0.0)
25
+ activerecord (3.2.11)
26
+ activemodel (= 3.2.11)
27
+ activesupport (= 3.2.11)
28
+ arel (~> 3.0.2)
29
+ tzinfo (~> 0.3.29)
30
+ activeresource (3.2.11)
31
+ activemodel (= 3.2.11)
32
+ activesupport (= 3.2.11)
33
+ activesupport (3.2.11)
34
+ i18n (~> 0.6)
35
+ multi_json (~> 1.0)
36
+ arel (3.0.2)
37
+ builder (3.0.4)
38
+ diff-lcs (1.1.3)
39
+ erubis (2.7.0)
40
+ hike (1.2.1)
41
+ i18n (0.6.1)
42
+ journey (1.0.4)
43
+ json (1.7.6)
44
+ mail (2.4.4)
45
+ i18n (>= 0.4.0)
46
+ mime-types (~> 1.16)
47
+ treetop (~> 1.4.8)
48
+ mime-types (1.19)
49
+ multi_json (1.5.0)
50
+ polyglot (0.3.3)
51
+ rack (1.4.3)
52
+ rack-cache (1.2)
53
+ rack (>= 0.4)
54
+ rack-ssl (1.3.2)
55
+ rack
56
+ rack-test (0.6.2)
57
+ rack (>= 1.0)
58
+ rails (3.2.11)
59
+ actionmailer (= 3.2.11)
60
+ actionpack (= 3.2.11)
61
+ activerecord (= 3.2.11)
62
+ activeresource (= 3.2.11)
63
+ activesupport (= 3.2.11)
64
+ bundler (~> 1.0)
65
+ railties (= 3.2.11)
66
+ railties (3.2.11)
67
+ actionpack (= 3.2.11)
68
+ activesupport (= 3.2.11)
69
+ rack-ssl (~> 1.3.2)
70
+ rake (>= 0.8.7)
71
+ rdoc (~> 3.4)
72
+ thor (>= 0.14.6, < 2.0)
73
+ rake (10.0.3)
74
+ rdoc (3.12)
75
+ json (~> 1.4)
76
+ rspec-core (2.12.2)
77
+ rspec-expectations (2.12.1)
78
+ diff-lcs (~> 1.1.3)
79
+ rspec-mocks (2.12.1)
80
+ rspec-rails (2.12.2)
81
+ actionpack (>= 3.0)
82
+ activesupport (>= 3.0)
83
+ railties (>= 3.0)
84
+ rspec-core (~> 2.12.0)
85
+ rspec-expectations (~> 2.12.0)
86
+ rspec-mocks (~> 2.12.0)
87
+ sprockets (2.2.2)
88
+ hike (~> 1.2)
89
+ multi_json (~> 1.0)
90
+ rack (~> 1.0)
91
+ tilt (~> 1.1, != 1.3.0)
92
+ sqlite3 (1.3.7)
93
+ thor (0.16.0)
94
+ tilt (1.3.3)
95
+ treetop (1.4.12)
96
+ polyglot
97
+ polyglot (>= 0.3.1)
98
+ tzinfo (0.3.35)
99
+
100
+ PLATFORMS
101
+ ruby
102
+
103
+ DEPENDENCIES
104
+ as_csv!
105
+ rails (~> 3.2)
106
+ rspec-rails (~> 2.12)
107
+ sqlite3
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Daniel Fone
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # AsCSV
2
+
3
+ This gem allows you to expose CSV in your apps as you'd expose JSON or XML.
4
+
5
+ Rails is not strictly required, but currently the magic only works with rails 3.x.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'as_csv'
12
+
13
+ ## Basic Usage
14
+
15
+ Simply add `respond_to :csv` in a controller
16
+
17
+ ```ruby
18
+ class WidgetsController < ApplicationController
19
+ respond_to :xml, :json, :csv
20
+
21
+ def index
22
+ respond_with Widget.all
23
+ end
24
+
25
+ def show
26
+ respond_with Widget.find(params[:id])
27
+ end
28
+
29
+ end
30
+ ```
31
+
32
+ Alternatively:
33
+
34
+ ```ruby
35
+ class WidgetsController < ApplicationController
36
+ def index
37
+ respond_to do |format|
38
+ format.csv { render csv: Widget.all }
39
+ end
40
+ end
41
+
42
+ def show
43
+ respond_to do |format|
44
+ format.csv { render csv: Widget.find(params[:id]) }
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ By default, you don't need to change your models at all.
51
+
52
+ ```ruby
53
+ class Widget < ActiveRecord::Base
54
+ # attributes :code, :description, :name
55
+ end
56
+ ```
57
+ ```
58
+ > puts Widget.all.to_csv
59
+ id,name,description,code
60
+ 1,widget-1,widget-description-1,1001
61
+ 2,widget-2,widget-description-2,1002
62
+ 3,widget-3,widget-description-3,1003
63
+ 4,widget-4,widget-description-4,1004
64
+ => nil
65
+
66
+ > puts Widget.where(code: [1001, 1002]).to_csv
67
+ id,name,description,code
68
+ 1,widget-1,widget-description-1,1001
69
+ 2,widget-2,widget-description-2,1002
70
+
71
+ > puts Widget.first.to_csv
72
+ id,name,description,code
73
+ 1,widget-1,widget-description-1,1001
74
+ => nil
75
+ ```
76
+
77
+ Behind the scenes, any classes that `include ActiveModel::Serialization` will expose their `attributes` with `to_csv`.
78
+
79
+ ## Advanced Usage
80
+
81
+ ### Customizing the CSV
82
+
83
+ To change what's included in the CSV, define an `as_csv` method. This method must return a Hash.
84
+
85
+ ```ruby
86
+ class Widget < ActiveRecord::Base
87
+ # attributes :code, :description, :name
88
+
89
+ def as_csv(options={})
90
+ attributes.slice('name', 'code')
91
+ end
92
+
93
+ end
94
+ ```
95
+ ```
96
+ > puts Widget.all.to_csv
97
+ name,code
98
+ widget-1,1001
99
+ widget-2,1002
100
+ widget-3,1003
101
+ widget-4,1004
102
+ => nil
103
+ >
104
+ ```
105
+
106
+ You can also accept options:
107
+
108
+ ```ruby
109
+ class WidgetsController < ApplicationController
110
+ respond_to :csv
111
+
112
+ def index
113
+ respond_with Widget.all, style: :short
114
+ end
115
+ end
116
+ ```
117
+ ```ruby
118
+ class Widget < ActiveRecord::Base
119
+ attr_accessible :code, :description, :name
120
+
121
+ def as_csv(options={})
122
+ if options[:style] == :short
123
+ attributes.slice('name', 'code')
124
+ else
125
+ attributes
126
+ end
127
+ end
128
+
129
+ end
130
+ ```
131
+
132
+ ### Heterogenous Arrays
133
+ You can render any Array of objects that respond to `as_csv`.
134
+
135
+ ```ruby
136
+ class Foo < ActiveRecord::Base
137
+ # attributes: name, description, code
138
+ end
139
+ ```
140
+ ```ruby
141
+ class Bar < ActiveRecord::Base
142
+ # attributes: name, description, barcode
143
+ end
144
+ ```
145
+ ```
146
+ > puts (Foo.all + Bar.all).to_csv
147
+ name,description,code,barcode
148
+ foo1,foo1-description,111,
149
+ foo2,foo2-description,222,
150
+ bar1,bar1-description,,acb12345
151
+ bar2,bar2-description,,xyz98765
152
+ ```
153
+
154
+ ## Contributing
155
+
156
+ 1. Fork it
157
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
158
+ 3. Ensure you've got development dependencies: (`bundle`)
159
+ 4. Ensure existing tests run: (`bundle exec rspec`)
160
+ 5. Make your changes, including new specs
161
+ 6. Commit your changes (`git commit -am 'Add some feature'`)
162
+ 7. Push to the branch (`git push origin my-new-feature`)
163
+ 8. Create new Pull Request
164
+
165
+ [![Build Status](https://travis-ci.org/danielfone/as_csv.png)](https://travis-ci.org/danielfone/as_csv)
166
+ [![Dependency Status](https://gemnasium.com/danielfone/as_csv.png)](https://gemnasium.com/danielfone/as_csv)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/as_csv.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'as_csv/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "as_csv"
8
+ gem.version = AsCSV::VERSION
9
+ gem.authors = ["Daniel Fone"]
10
+ gem.email = ["daniel@fone.net.nz"]
11
+ gem.description = %q{Instant CSV support for Rails}
12
+ gem.summary = %q{Instant CSV support for Rails}
13
+ gem.homepage = "https://github.com/danielfone/as_csv"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rails", "~> 3.2"
21
+ gem.add_development_dependency "rspec-rails", "~> 2.12"
22
+ gem.add_development_dependency "sqlite3"
23
+ end
@@ -0,0 +1,5 @@
1
+ if defined? ActionController::Renderers
2
+ ActionController::Renderers.add :csv do |obj, options|
3
+ obj.respond_to?(:to_csv) ? obj.to_csv(options) : obj
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ if defined? ActiveModel::Serialization
2
+ module ActiveModel::Serialization
3
+ def as_csv(options={})
4
+ attributes
5
+ end
6
+
7
+ def to_csv(*args)
8
+ [self].to_csv(*args)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+ def to_csv_with_builder(opts={})
3
+ AsCSV::CSVBuilder.new(self, opts).to_csv || to_csv_without_builder
4
+ end
5
+ alias_method :to_csv_without_builder, :to_csv
6
+ alias_method :to_csv, :to_csv_with_builder
7
+ end
@@ -0,0 +1,46 @@
1
+ module AsCSV
2
+ class CSVBuilder
3
+ attr_reader :records, :options
4
+
5
+ def initialize(records, options={})
6
+ @records = [*records]
7
+ @options = options
8
+ end
9
+
10
+ def to_csv
11
+ rows.collect { |row| CSV.generate_line row }.join if valid?
12
+ end
13
+
14
+ def rows
15
+ @rows ||= [headers] + data_rows if valid?
16
+ end
17
+
18
+ def headers
19
+ @headers ||= csv_hashes.collect(&:keys).flatten.uniq if valid?
20
+ end
21
+
22
+ def data_rows
23
+ @data_rows ||= csv_hashes.collect { |csv_hash| data_row csv_hash } if valid?
24
+ end
25
+
26
+ def valid?
27
+ csv_hashes.any? && validate_hashes!
28
+ end
29
+
30
+ private
31
+
32
+ def validate_hashes!
33
+ csv_hashes.each do |hash|
34
+ raise TypeError, "expected as_csv to return Hash" unless hash.is_a? Hash
35
+ end
36
+ end
37
+
38
+ def csv_hashes
39
+ @csv_hashes ||= records.collect { |r| r.as_csv(options) if r.respond_to? :as_csv }.compact
40
+ end
41
+
42
+ def data_row(csv_hash)
43
+ headers.collect { |header| csv_hash[header] }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module AsCSV
2
+ VERSION = "1.0.0"
3
+ end
data/lib/as_csv.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "csv"
2
+ require "as_csv/version"
3
+ require "as_csv/csv_builder"
4
+ require "as_csv/core_ext/array"
5
+ require "as_csv/active_model"
6
+ require "as_csv/action_renderer"
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe RenderWidgetsController, :widgets_controllers do
4
+ exercise_controller
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe RespondWithWidgetsController, :widgets_controllers do
4
+ exercise_controller
5
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Array do
4
+ it { should respond_to(:to_csv) }
5
+ it { should respond_to(:to_csv_with_builder) }
6
+ it { should respond_to(:to_csv_without_builder) }
7
+
8
+ context 'with objects responding to `as_csv`' do
9
+ before { subject << stub(:foo, as_csv: {header: 'value'})}
10
+
11
+ specify do
12
+ AsCSV::CSVBuilder.any_instance.should_receive(:to_csv).and_call_original
13
+ subject.should_not_receive(:to_csv_without_builder)
14
+ subject.to_csv
15
+ end
16
+ end
17
+
18
+ context 'without objects responding to `as_csv`' do
19
+ before { subject << 1 }
20
+
21
+ specify do
22
+ AsCSV::CSVBuilder.any_instance.should_receive(:to_csv).and_call_original
23
+ subject.should_receive(:to_csv_without_builder)
24
+ subject.to_csv
25
+ end
26
+ end
27
+
28
+ end