csv_shaper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +145 -0
- data/Rakefile +5 -0
- data/csv_shaper.gemspec +29 -0
- data/lib/csv_shaper/encoder.rb +42 -0
- data/lib/csv_shaper/header.rb +76 -0
- data/lib/csv_shaper/row.rb +77 -0
- data/lib/csv_shaper/shaper.rb +72 -0
- data/lib/csv_shaper/version.rb +3 -0
- data/lib/csv_shaper.rb +15 -0
- data/lib/csv_shaper_template.rb +25 -0
- data/spec/csv_shaper_spec.rb +19 -0
- data/spec/encoder_spec.rb +36 -0
- data/spec/fixtures/user.rb +13 -0
- data/spec/header_spec.rb +43 -0
- data/spec/row_spec.rb +25 -0
- data/spec/spec_helper.rb +7 -0
- metadata +106 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Paul Springett
|
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,145 @@
|
|
1
|
+
# CSV Shaper
|
2
|
+
|
3
|
+
Beautiful DSL for creating CSV output in Ruby & Rails.
|
4
|
+
|
5
|
+
Creating CSV files in Ruby is painful! CSV Shaper makes life easier! It's ideal for converting database backed models with attrbiutes into CSV output. It can be used without Rails, but works great with ActiveRecord models and even comes with support for it's own template handling.
|
6
|
+
|
7
|
+
### Example Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
csv_string = CsvShaper::Shaper.encode do |csv|
|
11
|
+
csv.header :name, :age, :gender, :pet_names
|
12
|
+
|
13
|
+
csv.rows @users do |csv, user|
|
14
|
+
csv.cells :name, :age, :gender
|
15
|
+
|
16
|
+
if user.pets.any?
|
17
|
+
csv.cell :pet_names
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
### Install
|
24
|
+
|
25
|
+
Install using Rubygems
|
26
|
+
|
27
|
+
```bash
|
28
|
+
$ gem install csv_shaper
|
29
|
+
```
|
30
|
+
|
31
|
+
Or if you want to use it in your Rails app, add the following line to your Gemfile
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'csv_shaper'
|
35
|
+
```
|
36
|
+
|
37
|
+
and then run
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ bundle install
|
41
|
+
```
|
42
|
+
|
43
|
+
### Usage
|
44
|
+
|
45
|
+
Everything goes inside the `encode` block, like so
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
csv_string = CsvShaper::Shaper.encode do |csv|
|
49
|
+
...
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
If you're using it in Rails 3.0+ you are already inside the block so you can just call the `csv` object.
|
54
|
+
|
55
|
+
Create a Rails view, set the content-type to `csv` and the handler to `shaper`, like so
|
56
|
+
|
57
|
+
index.csv.shaper
|
58
|
+
|
59
|
+
### Headers
|
60
|
+
|
61
|
+
You must define the headers for your CSV output. This can be done in one of 3 ways.
|
62
|
+
|
63
|
+
#### Standard attribute list
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
csv.headers :name, :age, :location
|
67
|
+
```
|
68
|
+
|
69
|
+
This would create headers like so:
|
70
|
+
|
71
|
+
```csv
|
72
|
+
Name,Age,Location
|
73
|
+
```
|
74
|
+
|
75
|
+
#### Using the attribute names of a Class
|
76
|
+
|
77
|
+
Say you have a User ActiveRecord class with attributes of `:name, :age, :location`. Simply pass the class to the headers method
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
csv.headers User
|
81
|
+
```
|
82
|
+
|
83
|
+
#### Using a block to define headers and custom mappings
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
csv.headers do |csv|
|
87
|
+
csv.columns :name, :age, :location
|
88
|
+
csv.mappings name: 'Full name', location: 'Region'
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
This would create headers like so:
|
93
|
+
|
94
|
+
```csv
|
95
|
+
Full name,Age,Region
|
96
|
+
```
|
97
|
+
|
98
|
+
### Rows & Cells
|
99
|
+
|
100
|
+
CSV Shaper allows you to define rows and cells in a variety of ways.
|
101
|
+
|
102
|
+
#### Basic row without a model
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
csv.row do |csv|
|
106
|
+
csv.cell :name, "Joe"
|
107
|
+
csv.cell :age, 24
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Passing a model and attributes
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
csv.row @user, :name, :age, :location
|
115
|
+
```
|
116
|
+
|
117
|
+
This will call the column names (name, age...) on @user and assign them to the correct cells.
|
118
|
+
|
119
|
+
#### Passing a model to a block
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
csv.row @user, do |csv, user|
|
123
|
+
csv.cells :name, :age
|
124
|
+
if user.show_gender?
|
125
|
+
csv.cell :gender
|
126
|
+
end
|
127
|
+
|
128
|
+
csv.cell :exported_at, Time.now
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
Any calls here to `cell` or `cells` without a value are called on the model (`user`), otherwise the second parameter is assigned.
|
133
|
+
|
134
|
+
### Multiple Rows
|
135
|
+
|
136
|
+
You can pass an Enumerable and a block to `csv.rows` like so
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
csv.rows @users do |csv, user|
|
140
|
+
csv.cells :name, :age, :location, :gender
|
141
|
+
csv.cell :exported_at, Time.now
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Copyright (c) Paul Springett 2012
|
data/Rakefile
ADDED
data/csv_shaper.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/csv_shaper/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Paul Springett"]
|
6
|
+
gem.email = ["paul@springett.me"]
|
7
|
+
|
8
|
+
gem.summary = %q{Beautiful DSL for creating CSV output in Ruby & Rails}
|
9
|
+
gem.description = %q{
|
10
|
+
Creating CSV files in Ruby is painful! CSV Shaper makes life easier! It's
|
11
|
+
ideal for converting database backed models with attrbiutes into CSV output.
|
12
|
+
It can be used without Rails, but works great with ActiveRecord models and even
|
13
|
+
comes with support for it's own template handling.
|
14
|
+
}
|
15
|
+
|
16
|
+
gem.homepage = "http://github.com/paulspringett/csv_shaper"
|
17
|
+
|
18
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
gem.files = `git ls-files`.split("\n")
|
20
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
gem.name = "csv_shaper"
|
22
|
+
gem.require_paths = ["lib"]
|
23
|
+
gem.version = CsvShaper::VERSION
|
24
|
+
|
25
|
+
gem.add_dependency 'activesupport', '>= 3.0.0'
|
26
|
+
gem.add_dependency 'blankslate', '>= 2.1.2.4'
|
27
|
+
|
28
|
+
gem.add_development_dependency 'rspec'
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module CsvShaper
|
4
|
+
class Encoder < BlankSlate
|
5
|
+
def initialize(header, rows = [])
|
6
|
+
if header.nil?
|
7
|
+
raise MissingHeadersError, 'you must define some headers using csv.headers ...'
|
8
|
+
end
|
9
|
+
|
10
|
+
@header = header
|
11
|
+
@rows = rows
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: converts the Shaper mapped headers and rows into
|
15
|
+
# a CSV String
|
16
|
+
#
|
17
|
+
# Returns a String
|
18
|
+
def to_csv
|
19
|
+
rows = padded_rows.map do |data|
|
20
|
+
CSV::Row.new(@header.mapped_columns, data, false)
|
21
|
+
end
|
22
|
+
|
23
|
+
table = CSV::Table.new(rows)
|
24
|
+
table.to_csv
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Internal: make use of CSV#values_at to pad out the
|
30
|
+
# cells into the correct columns for the headers
|
31
|
+
#
|
32
|
+
# Returns an Array of Arrays
|
33
|
+
def padded_rows
|
34
|
+
rows = @rows.map do |row|
|
35
|
+
CSV::Row.new(row.cells.keys, row.cells.values)
|
36
|
+
end
|
37
|
+
|
38
|
+
table = CSV::Table.new(rows)
|
39
|
+
table.values_at(*@header.columns)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module CsvShaper
|
2
|
+
|
3
|
+
# Header
|
4
|
+
# Handles creating and mapping of the headers
|
5
|
+
# Examples:
|
6
|
+
# # assign the headers from the attributes of a class
|
7
|
+
# csv.headers User
|
8
|
+
#
|
9
|
+
# # assigns headers normally
|
10
|
+
# csv.headers :name, :age, :location
|
11
|
+
#
|
12
|
+
# # pass a block
|
13
|
+
# csv.headers do |csv|
|
14
|
+
# csv.columns :name, :age, :location
|
15
|
+
# csv.mappings name: 'Full name, location: 'Region'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
class Header < BlankSlate
|
19
|
+
attr_reader :klass, :mappings, :mapped_columns
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
@mappings = {}
|
23
|
+
@columns = []
|
24
|
+
|
25
|
+
# csv.headers do |head|
|
26
|
+
if block_given?
|
27
|
+
yield self
|
28
|
+
elsif args.any?
|
29
|
+
# csv.headers User
|
30
|
+
if (@klass = args.first.respond_to?(:attribute_names) && args.first)
|
31
|
+
columns(*@klass.attribute_names)
|
32
|
+
# csv.headers :name, :age, :location
|
33
|
+
else
|
34
|
+
columns(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: serves as the getter and setter for the Array
|
40
|
+
# of Symbol column names. Union join the existing column
|
41
|
+
# names with those passed
|
42
|
+
# Example:
|
43
|
+
# header.columns :name, :age, :location
|
44
|
+
#
|
45
|
+
# args - Array of Symbol arguments passed
|
46
|
+
#
|
47
|
+
# Returns as Array of Symbols
|
48
|
+
def columns(*args)
|
49
|
+
@columns = @columns | args.map(&:to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Define mappings of the Symbol column names
|
53
|
+
# to nicer, human names
|
54
|
+
# Example:
|
55
|
+
# header.mappings name: 'Full name', age: 'Age of person'
|
56
|
+
#
|
57
|
+
# hash - Hash of mappings where the key is the column name to map
|
58
|
+
# and the value is the human readable value
|
59
|
+
#
|
60
|
+
# Returns a Hash of mappings
|
61
|
+
def mappings(hash = {})
|
62
|
+
@mappings.merge!(hash)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Public: converts columns and mappings into mapped columns
|
66
|
+
# ready for encoding. If a mapped value is found that is used,
|
67
|
+
# else the Symbol column name is humanized
|
68
|
+
#
|
69
|
+
# Returns an Array of Strings
|
70
|
+
def mapped_columns
|
71
|
+
@columns.map do |column|
|
72
|
+
@mappings[column] || column.to_s.humanize
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module CsvShaper
|
2
|
+
|
3
|
+
# Row
|
4
|
+
# Handles creating of cells within a row and
|
5
|
+
# assigning of the model's values to cells
|
6
|
+
# Examples:
|
7
|
+
# # pass a model to the row
|
8
|
+
# csv.row @model do |csv, model|
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# # create an empty row instance
|
13
|
+
# csv.row do |csv|
|
14
|
+
# ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # create a row with prefilled cells from a model
|
18
|
+
# # note no block is passed
|
19
|
+
# csv.row @model, :name, :age, :location
|
20
|
+
#
|
21
|
+
class Row < BlankSlate
|
22
|
+
attr_reader :model, :cells
|
23
|
+
|
24
|
+
def initialize(*args)
|
25
|
+
@cells = ActiveSupport::OrderedHash.new
|
26
|
+
|
27
|
+
# csv.row @user do |csv, user|
|
28
|
+
if args.one? && block_given?
|
29
|
+
@model = args.first
|
30
|
+
yield self, @model
|
31
|
+
|
32
|
+
# csv.row do |csv|
|
33
|
+
elsif args.empty? && block_given?
|
34
|
+
yield self
|
35
|
+
|
36
|
+
# csv.row @user, :name, :age, :location
|
37
|
+
elsif args.length > 1
|
38
|
+
@model = args.shift
|
39
|
+
args.each { |col| cell(col) }
|
40
|
+
else
|
41
|
+
raise ArgumentError, 'invalid args passed to csv.row'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: assign the given Array of args to cells in this Row
|
46
|
+
#
|
47
|
+
# args - Array of the arguments passed (expected to be Symbols)
|
48
|
+
#
|
49
|
+
# Returns an Array of the Cells in this row
|
50
|
+
def cells(*args)
|
51
|
+
args.each { |col| cell(col) }
|
52
|
+
@cells
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public: add a cell to this Row
|
56
|
+
# If the Row has a @model defined passing just a Symbol will
|
57
|
+
# call that method on the @model and assign it to a column of
|
58
|
+
# the same name. Otherwise a value will need to be passed also
|
59
|
+
#
|
60
|
+
# column - Symbol of the column to add to value to
|
61
|
+
# value - data to assign to the cell (default: nil)
|
62
|
+
#
|
63
|
+
# Returns an Array of the Row's cells
|
64
|
+
def cell(column, value = nil)
|
65
|
+
column = column.to_sym
|
66
|
+
|
67
|
+
if @model && value.nil?
|
68
|
+
@cells[column] = @model.send(column)
|
69
|
+
else
|
70
|
+
@cells[column] = value
|
71
|
+
end
|
72
|
+
|
73
|
+
@cells
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CsvShaper
|
2
|
+
class Shaper < BlankSlate
|
3
|
+
attr_reader :header, :rows
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@rows = []
|
7
|
+
yield self if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: creates a new instance of Shaper taps it with
|
11
|
+
# with the given block and encodes it to a String of CSV data
|
12
|
+
# Example:
|
13
|
+
# data = CsvShaper::Shaper.encode do |csv|
|
14
|
+
# csv.rows @users do |csv, user|
|
15
|
+
# csv.cells :name, :age, :gender
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# puts data
|
20
|
+
# => "Name,Age,Gender\n'Joe Bloggs',25,'M'\n'John Smith',34,'M'"
|
21
|
+
#
|
22
|
+
# Returns a String
|
23
|
+
def self.encode
|
24
|
+
new.tap { |shaper| yield shaper }.to_csv
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: creates a header row for the CSV
|
28
|
+
# This is delegated to the Header class
|
29
|
+
# see header.rb for usage examples
|
30
|
+
#
|
31
|
+
# Returns a Header instance
|
32
|
+
def headers(*args, &block)
|
33
|
+
@header = Header.new(*args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: adds a row to the CSV
|
37
|
+
# This is delegated to the Row class
|
38
|
+
# See row.rb for usage examples
|
39
|
+
#
|
40
|
+
# Returns an updated Array of Row objects
|
41
|
+
def row(*args, &block)
|
42
|
+
@rows.push Row.new(*args, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: adds several rows to the CSV
|
46
|
+
#
|
47
|
+
# collection - an Enumerable of objects to be passed to #row
|
48
|
+
#
|
49
|
+
# Returns an updated Array of Row objects
|
50
|
+
def rows(collection = nil, &block)
|
51
|
+
return @rows if collection.nil?
|
52
|
+
|
53
|
+
unless collection.respond_to?(:each)
|
54
|
+
raise ArgumentError, 'csv.rows only accepts Enumerable object (that respond to #each). Use csv.row for a single object.'
|
55
|
+
end
|
56
|
+
|
57
|
+
collection.each do |element|
|
58
|
+
row(element, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
@rows
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: converts the Header and Row objects into a string
|
65
|
+
# of valid CSV data. Delegated to the Encoder class
|
66
|
+
#
|
67
|
+
# Returns a String
|
68
|
+
def to_csv
|
69
|
+
Encoder.new(@header, @rows).to_csv
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/csv_shaper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
require 'active_support/ordered_hash'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
|
5
|
+
require 'csv_shaper/version'
|
6
|
+
require 'csv_shaper/header'
|
7
|
+
require 'csv_shaper/row'
|
8
|
+
require 'csv_shaper/encoder'
|
9
|
+
require 'csv_shaper/shaper'
|
10
|
+
|
11
|
+
module CsvShaper
|
12
|
+
class MissingHeadersError < StandardError; end
|
13
|
+
end
|
14
|
+
|
15
|
+
require "csv_shaper_template" if defined?(ActionView::Template)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CsvShaperTemplate < CsvShaper::Shaper
|
2
|
+
def self.encode(context)
|
3
|
+
new(context).tap { |shaper| yield shaper }.to_csv
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
super()
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class CsvShaperHandler
|
13
|
+
cattr_accessor :default_format
|
14
|
+
self.default_format = Mime::CSV
|
15
|
+
|
16
|
+
def self.call(template)
|
17
|
+
%{
|
18
|
+
CsvShaperTemplate.encode(self) do |csv|
|
19
|
+
#{template.source}
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActionView::Template.register_template_handler :shaper, CsvShaperHandler
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CsvShaper::Shaper do
|
4
|
+
it "should return a version" do
|
5
|
+
CsvShaper::VERSION.should be_kind_of(String)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should raise an exception when passing a non-enumarble to rows" do
|
9
|
+
csv = CsvShaper::Shaper.new
|
10
|
+
expect {
|
11
|
+
csv.rows :name
|
12
|
+
}.to raise_exception(ArgumentError, 'csv.rows only accepts Enumerable object (that respond to #each). Use csv.row for a single object.')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should respond to to_csv" do
|
16
|
+
csv = CsvShaper::Shaper.new
|
17
|
+
csv.should respond_to(:to_csv)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
describe CsvShaper::Encoder do
|
5
|
+
let(:user) { User.new(name: 'Paul', age: 27, gender: 'Male') }
|
6
|
+
|
7
|
+
it "should raise an exception if the headers are missing" do
|
8
|
+
expect {
|
9
|
+
CsvShaper::Encoder.new(nil)
|
10
|
+
}.to raise_exception(CsvShaper::MissingHeadersError, 'you must define some headers using csv.headers ...')
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:users) { [User.new(name: 'Paul', age: 27, gender: 'Male'), User.new(name: 'Bob', age: 31, gender: 'Male'), User.new(name: 'Jane', age: 23, gender: 'Female')] }
|
14
|
+
let(:csv) {
|
15
|
+
CsvShaper::Shaper.new do |csv|
|
16
|
+
csv.headers do |csv|
|
17
|
+
csv.columns :name, :gender, :age
|
18
|
+
csv.mappings name: "Full name", gender: "Sex"
|
19
|
+
end
|
20
|
+
|
21
|
+
csv.rows users do |csv, user|
|
22
|
+
csv.cells :name, :age
|
23
|
+
csv.cell :gender
|
24
|
+
end
|
25
|
+
|
26
|
+
csv.row do |csv|
|
27
|
+
csv.cell :age, users.map(&:age).reduce(:+)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
it "should encode a Shaper instance to a CSV string" do
|
33
|
+
encoder = CsvShaper::Encoder.new(csv.header, csv.rows)
|
34
|
+
encoder.to_csv.should eq("Full name,Sex,Age\nPaul,Male,27\nBob,Male,31\nJane,Female,23\n,,81\n")
|
35
|
+
end
|
36
|
+
end
|
data/spec/header_spec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
describe CsvShaper::Header do
|
5
|
+
it "should accept and store a list of symbols" do
|
6
|
+
header = CsvShaper::Header.new(:name, :age, :location)
|
7
|
+
|
8
|
+
header.columns.should eq [:name, :age, :location]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a Model and store it's attributes" do
|
12
|
+
header = CsvShaper::Header.new(User)
|
13
|
+
|
14
|
+
header.columns.should eq [:name, :age, :gender]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept a block with columns and mappings" do
|
18
|
+
header = CsvShaper::Header.new do |csv|
|
19
|
+
csv.columns :name, :age, :foo
|
20
|
+
csv.mappings name: 'User name'
|
21
|
+
end
|
22
|
+
|
23
|
+
header.columns.should eq [:name, :age, :foo]
|
24
|
+
header.mapped_columns.should eq ['User name', 'Age', 'Foo']
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should merge columns with a union join" do
|
28
|
+
header = CsvShaper::Header.new(:name, :age, :location)
|
29
|
+
header.columns :age, :gender
|
30
|
+
|
31
|
+
header.columns.should eq [:name, :age, :location, :gender]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should merge mappings" do
|
35
|
+
header = CsvShaper::Header.new do |csv|
|
36
|
+
csv.columns :name, :age, :foo
|
37
|
+
csv.mappings name: 'User name'
|
38
|
+
csv.mappings foo: 'Bar'
|
39
|
+
end
|
40
|
+
|
41
|
+
header.mapped_columns.should eq ['User name', 'Age', 'Bar']
|
42
|
+
end
|
43
|
+
end
|
data/spec/row_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
describe CsvShaper::Row do
|
5
|
+
let(:user) { User.new(name: 'Paul', age: 27, gender: 'Male') }
|
6
|
+
|
7
|
+
it "should assign a model" do
|
8
|
+
row = CsvShaper::Row.new user do |csv, user|
|
9
|
+
# ...
|
10
|
+
end
|
11
|
+
|
12
|
+
row.model.should eq user
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should assign a model's attributes" do
|
16
|
+
row = CsvShaper::Row.new(user, :name, :age)
|
17
|
+
row.cells.should eq({ name: 'Paul', age: 27 })
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should yield to a block" do
|
21
|
+
CsvShaper::Row.new { |csv|
|
22
|
+
csv.should be_kind_of(CsvShaper::Row)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv_shaper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Springett
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70336817645940 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70336817645940
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: blankslate
|
27
|
+
requirement: &70336817644820 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.1.2.4
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70336817644820
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70336817643920 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70336817643920
|
47
|
+
description: ! "\n Creating CSV files in Ruby is painful! CSV Shaper makes life
|
48
|
+
easier! It's\n ideal for converting database backed models with attrbiutes into
|
49
|
+
CSV output.\n It can be used without Rails, but works great with ActiveRecord
|
50
|
+
models and even\n comes with support for it's own template handling.\n "
|
51
|
+
email:
|
52
|
+
- paul@springett.me
|
53
|
+
executables: []
|
54
|
+
extensions: []
|
55
|
+
extra_rdoc_files: []
|
56
|
+
files:
|
57
|
+
- .gitignore
|
58
|
+
- Gemfile
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- csv_shaper.gemspec
|
63
|
+
- lib/csv_shaper.rb
|
64
|
+
- lib/csv_shaper/encoder.rb
|
65
|
+
- lib/csv_shaper/header.rb
|
66
|
+
- lib/csv_shaper/row.rb
|
67
|
+
- lib/csv_shaper/shaper.rb
|
68
|
+
- lib/csv_shaper/version.rb
|
69
|
+
- lib/csv_shaper_template.rb
|
70
|
+
- spec/csv_shaper_spec.rb
|
71
|
+
- spec/encoder_spec.rb
|
72
|
+
- spec/fixtures/user.rb
|
73
|
+
- spec/header_spec.rb
|
74
|
+
- spec/row_spec.rb
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
homepage: http://github.com/paulspringett/csv_shaper
|
77
|
+
licenses: []
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.16
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Beautiful DSL for creating CSV output in Ruby & Rails
|
100
|
+
test_files:
|
101
|
+
- spec/csv_shaper_spec.rb
|
102
|
+
- spec/encoder_spec.rb
|
103
|
+
- spec/fixtures/user.rb
|
104
|
+
- spec/header_spec.rb
|
105
|
+
- spec/row_spec.rb
|
106
|
+
- spec/spec_helper.rb
|