csv_pirate 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +189 -0
- data/Rakefile +59 -0
- data/VERSION.yml +4 -0
- data/about.yml +7 -0
- data/csv_pirate.gemspec +53 -0
- data/init.rb +9 -0
- data/install.rb +1 -0
- data/lib/csv_pirate.rb +392 -0
- data/lib/ninth_bit/pirate_ship.rb +69 -0
- data/rails/init.rb +11 -0
- data/spec/csv_pirate_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -0
- metadata +70 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Peter H. Boling
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
== CsvPirate v2.0.1 (20091001)
|
2
|
+
|
3
|
+
Easily create CSVs of any data that can be derived from your models.
|
4
|
+
|
5
|
+
CsvPirate is the easy way to create a CSV of essentially anything in Rails, in full pirate regalia.
|
6
|
+
It works better if you are wearing a tricorne!
|
7
|
+
|
8
|
+
CsvPirate only works for commissions of swag OR grub!
|
9
|
+
|
10
|
+
Initialize method (a.k.a new()) takes a hash of params:
|
11
|
+
|
12
|
+
swag: the ARrr collection of swag to work on (optional)
|
13
|
+
grub: the ARrr class that the spyglasses will be used on (optional)
|
14
|
+
spyglasses: named scopes in your model that will refine the rows in the CSV according to conditions of the spyglass,
|
15
|
+
and order them according to the order of the spyglass (only used with grub, not swag)
|
16
|
+
booty: booty on your model that you want printed in the CSV
|
17
|
+
chart: name of directory where you want to hide your loot
|
18
|
+
waggoner: name of document where you will give detailed descriptions of the loot
|
19
|
+
chronometer: keeps track of when you hunt for treasure
|
20
|
+
gibbet: filename spacer after the date, and before the iterative counter/timestamp. MuST contain a '.'
|
21
|
+
swab: can be :counter, :timestamp, or :none
|
22
|
+
:counter - default, each successive run will create a new file using a counter
|
23
|
+
:timestamp - each successive run will create a new file using a HHMMSS time stamp
|
24
|
+
:none - no iterative file naming convention, just use waggoner and aft
|
25
|
+
mop: can be :clean or :dirty (:overwrite or :append) (only has an effect if :swab is :none) since overwriting is irrelevant for a new file
|
26
|
+
:clear - do not use :counter or :timestamp, and instead overwrite the file
|
27
|
+
:dirty - do not use :counter, or :timestamp, or :overwrite. Just keep adding on.
|
28
|
+
|
29
|
+
The create method has the same parameters, and actually creates the CSV.
|
30
|
+
|
31
|
+
Avast! Here be pirates! To brush up on pirate coding naming conventions:
|
32
|
+
|
33
|
+
http://www.privateerdragons.com/pirate_dictionary.html
|
34
|
+
|
35
|
+
Perhaps the next version will have CSV importing abilities...
|
36
|
+
|
37
|
+
== On The Web
|
38
|
+
|
39
|
+
Source:
|
40
|
+
http://github.com/pboling/csv_pirate
|
41
|
+
|
42
|
+
Release Announcement:
|
43
|
+
http://galtzo.blogspot.com/2009/03/csv-pirate.html
|
44
|
+
|
45
|
+
|
46
|
+
== Installation
|
47
|
+
|
48
|
+
Gem Using Git building from source:
|
49
|
+
|
50
|
+
mkdir -p ~/src
|
51
|
+
cd ~/src
|
52
|
+
git clone git://github.com/pboling/csv_pirate.git
|
53
|
+
cd csv_pirate
|
54
|
+
gem build csv_pirate.gemspec
|
55
|
+
sudo gem install csv_pirate-2.0.1.gem # (Or whatever version gets built)
|
56
|
+
|
57
|
+
Then cd to your rails app to optionally freeze the gem into your app:
|
58
|
+
|
59
|
+
rake gems:freeze GEM=csv_pirate
|
60
|
+
|
61
|
+
Installing Gem from Github's Gem Server:
|
62
|
+
|
63
|
+
sudo gem install pboling-csv_pirate -s http://gems.github.com
|
64
|
+
|
65
|
+
Plugin using Git:
|
66
|
+
|
67
|
+
ruby script/plugin install git://github.com/pboling/csv_pirate.git
|
68
|
+
|
69
|
+
|
70
|
+
== Example & Usage
|
71
|
+
|
72
|
+
Assuming a Make (as in manufacturers of automobiles) model like this:
|
73
|
+
|
74
|
+
# == Schema Information
|
75
|
+
#
|
76
|
+
# Table name: makes
|
77
|
+
#
|
78
|
+
# id :integer(4) not null, primary key
|
79
|
+
# name :string(255)
|
80
|
+
# factory :string(255)
|
81
|
+
# sales :integer(4)
|
82
|
+
#
|
83
|
+
|
84
|
+
class Make < ActiveRecord::Base
|
85
|
+
has_many :vehicle_models
|
86
|
+
named_scope :factory_in_germany, :conditions => ["factory = ?", "Germany"]
|
87
|
+
|
88
|
+
has_csv_pirate_ship :spyglasses => [:factory_in_germany],
|
89
|
+
:booty => [:id, :name]
|
90
|
+
end
|
91
|
+
|
92
|
+
To create a csv of the names and ids of makes with factories in germany:
|
93
|
+
|
94
|
+
Make.walk_the_plank # Get it? HA! If you can't believe I wrote this whole thing JUST to be able to make jokes like that... check ma sources :)
|
95
|
+
|
96
|
+
The name of the csv that comes out will be (by defualt located in log directory):
|
97
|
+
|
98
|
+
Make.20090930.export.13.csv
|
99
|
+
|
100
|
+
Where Make is the class, 20090930 is today's date, .export is the gibbet, and 13 is the iterative file counter, meaning I've run this export 13 times today.
|
101
|
+
|
102
|
+
All of those filename parts are customizable to a degree.
|
103
|
+
|
104
|
+
You can also customize the CSV, for example if you want to add a column to the csv:
|
105
|
+
|
106
|
+
Make.walk_the_plank({:booty => [:id, :name, :sales]})
|
107
|
+
Make.walk_the_plank({:booty => [:id, :name, :sales], :spyglasses => [:all], :swab => :timestamp})
|
108
|
+
Make.walk_the_plank({:booty => [:id, :name, :sales], :spyglasses => [:all], :swab => :none, :mop => :dirty})
|
109
|
+
|
110
|
+
If you have a method in the Make class like this:
|
111
|
+
|
112
|
+
def to_slug
|
113
|
+
"#{self.name}_#{self.id}"
|
114
|
+
end
|
115
|
+
|
116
|
+
getting it in the CSV is easy peasy:
|
117
|
+
|
118
|
+
Make.walk_the_plank({:booty => [:id, :name, :to_slug]})
|
119
|
+
|
120
|
+
Add whatever methods you want to the :booty array. Write new methods, and add them! Make lots of glorious CSVs full of data to impress the pointy ones in the office.
|
121
|
+
|
122
|
+
You can also use the raw CsvPirate class itself directly wherever you want. These are the available options to methods:
|
123
|
+
new, create, or walk_the_plank
|
124
|
+
|
125
|
+
# CsvPirate only works for commissions of swag OR grub!
|
126
|
+
# :swag the ARrr collection of swag to work on (optional)
|
127
|
+
# :grub the ARrr class that the spyglasses will be used on (optional)
|
128
|
+
# :spyglasses named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses,
|
129
|
+
# and order them according to the order of the spyglasses (optional)
|
130
|
+
# :booty booty (columns/methods) on your model that you want printed in the CSV, also used to create the figurehead (CSV header)
|
131
|
+
# :chart name of directory where you want to hide your loot
|
132
|
+
# :wagonner name of document where you will give detailed descriptions of the loot
|
133
|
+
# :aft filename extention ('.csv')
|
134
|
+
# :chronometer keeps track of when you hunt for treasure
|
135
|
+
# :gibbet filename spacer after the date, and before the iterative counter/timestamp. MuST contain a '.'
|
136
|
+
# :swab can be :counter, :timestamp, or :none
|
137
|
+
# :counter - default, each successive run will create a new file using a counter
|
138
|
+
# :timestamp - each successive run will create a new file using a HHMMSS time stamp
|
139
|
+
# :none - no iterative file naming convention, just use waggoner and aft
|
140
|
+
# :mop can be :clean or :dirty (:overwrite or :append) (only has an effect if :swab is :none) since overwriting is irrelevant for a new file
|
141
|
+
# :clear - do not use :counter or :timestamp, and instead overwrite the file
|
142
|
+
# :dirty - do not use :counter, or :timestamp, or :overwrite. Just keep adding on.
|
143
|
+
|
144
|
+
The following two sets of code are identical:
|
145
|
+
|
146
|
+
csv_pirate = CsvPirate.new({
|
147
|
+
:grub => User,
|
148
|
+
:spyglasses => [:active,:logged_in],
|
149
|
+
:waggoner => 'active_users_logged_in',
|
150
|
+
:booty => ["id","number","login","created_at"],
|
151
|
+
:chart => 'log/csv/'
|
152
|
+
})
|
153
|
+
csv_pirate.hoist_mainstay() # creates CSV file and writes out the rows
|
154
|
+
|
155
|
+
CsvPirate.create({
|
156
|
+
:grub => User,
|
157
|
+
:spyglasses => [:active,:logged_in],
|
158
|
+
:waggoner => 'active_users_logged_in',
|
159
|
+
:booty => ["id","number","login","created_at"],
|
160
|
+
:chart => 'log/csv/'
|
161
|
+
})# creates CSV file and writes out the rows
|
162
|
+
|
163
|
+
Another example using swag instead of grub:
|
164
|
+
|
165
|
+
users = User.logged_out.inactive
|
166
|
+
csv_pirate = CsvPirate.new({
|
167
|
+
:swag => users,
|
168
|
+
:waggoner => 'inactive_users_not_logged_in',
|
169
|
+
:booty => ["id","number","login","created_at"],
|
170
|
+
:chart => 'log/csv/'
|
171
|
+
})
|
172
|
+
csv_pirate.hoist_mainstay()
|
173
|
+
|
174
|
+
Then if you want to get your hands on the loot immediately:
|
175
|
+
|
176
|
+
csv_pirate.weigh_anchor()
|
177
|
+
|
178
|
+
For those who can't help but copy/paste into console and then edit:
|
179
|
+
|
180
|
+
csv_pirate = CsvPirate.new({:grub => User,:spyglasses => [:active,:logged_in],:waggoner => 'active_users_logged_in',:booty => ["id","number","login","created_at"],:chart => 'log/csv/'})
|
181
|
+
|
182
|
+
OR
|
183
|
+
|
184
|
+
csv_pirate = CsvPirate.new({:swag => users,:waggoner => 'inactive_users_not_logged_in',:booty => ["id","number","login","created_at"],:chart => 'log/csv/'})
|
185
|
+
|
186
|
+
|
187
|
+
----------------------------------------------------------------------------------
|
188
|
+
Author: Peter Boling, peter.boling at gmail dot com
|
189
|
+
Copyright (c) 2009 Peter H. Boling of 9thBit LLC, released under the MIT license. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "csv_pirate"
|
7
|
+
gemspec.summary = "Easily create CSVs of any data that can be derived from your models (using pirates!)."
|
8
|
+
gemspec.description = %q{CsvPirate is the easy way to create a CSV of essentially anything in Rails, in full pirate regalia.
|
9
|
+
It works better if you are wearing a tricorne!}
|
10
|
+
gemspec.email = "peter.boling@gmail.com"
|
11
|
+
gemspec.homepage = "http://github.com/pboling/csv_pirate"
|
12
|
+
gemspec.authors = ["Peter Boling"]
|
13
|
+
gemspec.files = ["README.rdoc",
|
14
|
+
"csv_pirate.gemspec",
|
15
|
+
"init.rb",
|
16
|
+
"rails/init.rb",
|
17
|
+
"install.rb",
|
18
|
+
"about.yml",
|
19
|
+
"lib/csv_pirate.rb",
|
20
|
+
"lib/ninth_bit/pirate_ship.rb",
|
21
|
+
"Rakefile",
|
22
|
+
"LICENSE",
|
23
|
+
"CHANGELOG",
|
24
|
+
"VERSION.yml"]
|
25
|
+
end
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
rescue LoadError
|
28
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
Rake::RDocTask.new do |rdoc|
|
33
|
+
rdoc.rdoc_dir = 'rdoc'
|
34
|
+
rdoc.title = 'csv_pirate'
|
35
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
36
|
+
rdoc.rdoc_files.include('README*')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'spec/rake/spectask'
|
41
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
42
|
+
t.libs << 'lib' << 'spec'
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
+
end
|
45
|
+
|
46
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
47
|
+
t.libs << 'lib' << 'spec'
|
48
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
49
|
+
t.rcov = true
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
require 'cucumber/rake/task'
|
54
|
+
Cucumber::Rake::Task.new(:features)
|
55
|
+
rescue LoadError
|
56
|
+
puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
57
|
+
end
|
58
|
+
|
59
|
+
task :default => :spec
|
data/VERSION.yml
ADDED
data/about.yml
ADDED
data/csv_pirate.gemspec
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{csv_pirate}
|
8
|
+
s.version = "2.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Peter Boling"]
|
12
|
+
s.date = %q{2009-10-01}
|
13
|
+
s.description = %q{CsvPirate is the easy way to create a CSV of essentially anything in Rails, in full pirate regalia.
|
14
|
+
It works better if you are wearing a tricorne!}
|
15
|
+
s.email = %q{peter.boling@gmail.com}
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE",
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"CHANGELOG",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION.yml",
|
26
|
+
"about.yml",
|
27
|
+
"csv_pirate.gemspec",
|
28
|
+
"init.rb",
|
29
|
+
"install.rb",
|
30
|
+
"lib/csv_pirate.rb",
|
31
|
+
"lib/ninth_bit/pirate_ship.rb",
|
32
|
+
"rails/init.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/pboling/csv_pirate}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.5}
|
38
|
+
s.summary = %q{Easily create CSVs of any data that can be derived from your models (using pirates!).}
|
39
|
+
s.test_files = [
|
40
|
+
"spec/csv_pirate_spec.rb",
|
41
|
+
"spec/spec_helper.rb"
|
42
|
+
]
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
49
|
+
else
|
50
|
+
end
|
51
|
+
else
|
52
|
+
end
|
53
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'csv_pirate'
|
2
|
+
require 'ninth_bit/pirate_ship'
|
3
|
+
|
4
|
+
# If you are using this on a vanilla Ruby class (no rails or active record) then extend your class like this:
|
5
|
+
# MyClass.send(:extend, NinthBit::PirateShip::ActMethods) if defined?(MyClass)
|
6
|
+
# Alternatively you can do this inside your class definition:
|
7
|
+
# class MyClass
|
8
|
+
# extend NinthBit::PirateShip::ActMethods
|
9
|
+
# end
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
|
data/lib/csv_pirate.rb
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
# CsvPirate
|
2
|
+
#Copyright ©2009 Peter H. Boling of 9thBit LLC, released under the MIT license
|
3
|
+
#Gem / Plugin for Rails / Active Record: Easily make CSVs of anything that can be derived from your models
|
4
|
+
#Language: Ruby (written by a pirate)
|
5
|
+
#License: MIT License
|
6
|
+
#Labels: Ruby, Rails, Gem, Plugin
|
7
|
+
#Version: 1.0
|
8
|
+
#Project owners:
|
9
|
+
# peter.boling (The Cap'n)
|
10
|
+
require 'fastercsv'
|
11
|
+
|
12
|
+
class CsvPirate
|
13
|
+
|
14
|
+
BOOKIE = [:counter, :timestamp, :none]
|
15
|
+
MOP_HEADS = [:clean, :dirty]
|
16
|
+
|
17
|
+
attr_accessor :waggoner #filename
|
18
|
+
attr_accessor :chart #directory, default is ('log/')
|
19
|
+
attr_accessor :aft #extension, default is ('.csv')
|
20
|
+
attr_accessor :gibbet #part of the filename after waggoner and date, before swabbie and aft
|
21
|
+
attr_accessor :chronometer
|
22
|
+
|
23
|
+
# Must provide swag or grub (not both)
|
24
|
+
attr_accessor :swag # ARrr array of objects
|
25
|
+
attr_accessor :grub # ARrr class
|
26
|
+
# spyglasses is only used with grub, not swag
|
27
|
+
attr_accessor :spyglasses # named_scopes
|
28
|
+
|
29
|
+
# These are the booty of the CSV
|
30
|
+
# Should be methods/columns on the swag
|
31
|
+
# also used to create the figurehead (CSV header)
|
32
|
+
attr_accessor :booty # methods / columns
|
33
|
+
|
34
|
+
attr_accessor :bury_treasure # should we store the csv data in an array for later inspection, or just write the CSV?
|
35
|
+
# default is true
|
36
|
+
# The array that gets built as we write the CSV... could be useful?
|
37
|
+
attr_accessor :buried_treasure # info array
|
38
|
+
|
39
|
+
attr_accessor :rhumb_lines # the file object to write the CSV lines to
|
40
|
+
|
41
|
+
attr_accessor :astrolabe # when true then read only CsvPirate instance for loading of CSVs
|
42
|
+
|
43
|
+
attr_accessor :swab # default is :counter
|
44
|
+
attr_accessor :mop # default is :clean (only has an effect if :swab is :none) since overwriting is irrelevant for a new file
|
45
|
+
attr_accessor :swabbie # value of the counter / timestamp
|
46
|
+
|
47
|
+
cattr_accessor :parlay # verbosity on a scale of 0 - 3 (0=:none, 1=:error, 2=:info, 3=:debug, 0 being no screen output, 1 is default
|
48
|
+
|
49
|
+
# CsvPirate only works for commissions of swag OR grub!
|
50
|
+
# :swag the ARrr collection of swag to work on (optional)
|
51
|
+
# :grub the ARrr class that the spyglasses will be used on (optional)
|
52
|
+
# :spyglasses named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses,
|
53
|
+
# and order them according to the order of the spyglasses (optional)
|
54
|
+
# :booty booty (columns/methods) on your model that you want printed in the CSV, also used to create the figurehead (CSV header)
|
55
|
+
# :chart name of directory where you want to hide your loot
|
56
|
+
# :wagonner name of document where you will give detailed descriptions of the loot
|
57
|
+
# :aft filename extention ('.csv')
|
58
|
+
# :chronometer keeps track of when you hunt for treasure
|
59
|
+
# :gibbet filename spacer after the date, and before the iterative counter/timestamp. MuST contain a '.'
|
60
|
+
# :swab can be :counter, :timestamp, or :none
|
61
|
+
# :counter - default, each successive run will create a new file using a counter
|
62
|
+
# :timestamp - each successive run will create a new file using a HHMMSS time stamp
|
63
|
+
# :none - no iterative file naming convention, just use waggoner and aft
|
64
|
+
# :mop can be :clean or :dirty (:overwrite or :append) (only has an effect if :swab is :none) since overwriting is irrelevant for a new file
|
65
|
+
# :clear - do not use :counter or :timestamp, and instead overwrite the file
|
66
|
+
# :dirty - do not use :counter, or :timestamp, or :overwrite. Just keep adding on.
|
67
|
+
# See README for examples
|
68
|
+
|
69
|
+
def initialize(*args)
|
70
|
+
|
71
|
+
@swag = args.first[:swag]
|
72
|
+
@grub = args.first[:grub]
|
73
|
+
|
74
|
+
# if they provide both
|
75
|
+
raise ArgumentError, "must provide either specify :swag or :grub" if !args.first[:swag].blank? && !args.first[:grub].blank?
|
76
|
+
# if they provide neither
|
77
|
+
raise ArgumentError, "must provide either specify :swag or :grub, not both" if args.first[:swag].blank? && args.first[:grub].blank?
|
78
|
+
|
79
|
+
@swab = args.first[:swab] || :counter
|
80
|
+
raise ArgumentError, ":swab is #{args.first[:swab].inspect}, but must be one of #{CsvPirate::BOOKIE.inspect}" unless CsvPirate::BOOKIE.include?(args.first[:swab])
|
81
|
+
|
82
|
+
@mop = args.first[:mop] || :clean
|
83
|
+
raise ArgumentError, ":mop is #{args.first[:mop].inspect}, but must be one of #{CsvPirate::MOP_HEADS.inspect}" unless CsvPirate::MOP_HEADS.include?(args.first[:mop])
|
84
|
+
|
85
|
+
@gibbet = args.first[:gibbet] || '.export'
|
86
|
+
raise ArgumentError, ":gibbet is #{args.first[:gibbet].inspect}, and does not contain a '.' character, which is required for iterative filenames" if args.first[:gibbet].nil? || !args.first[:gibbet].include?('.')
|
87
|
+
|
88
|
+
@waggoner = args.first[:waggoner] || "#{self.grub}"
|
89
|
+
raise ArgumentError, ":waggoner is #{args.first[:waggoner].inspect}, and must be a string at least one character long" if args.first[:waggoner].nil? || args.first[:waggoner].length < 1
|
90
|
+
|
91
|
+
@booty = args.first[:booty] || []
|
92
|
+
raise ArgumentError, ":booty is #{args.first[:booty].inspect}, and must be an array of methods to call on a class for CSV data" if args.first[:booty].nil? || args.first[:booty].empty?
|
93
|
+
|
94
|
+
@chart = args.first[:chart] || 'log/'
|
95
|
+
@aft = args.first[:aft] || '.csv'
|
96
|
+
@chronometer = args.first[:chronometer] || Date.today
|
97
|
+
|
98
|
+
@spyglasses = (args.first[:spyglasses] || [:all]) if self.grub
|
99
|
+
|
100
|
+
|
101
|
+
@astrolabe = args.first[:astrolabe] || false
|
102
|
+
|
103
|
+
@bury_treasure = args.first[:astrolabe] || true
|
104
|
+
@buried_treasure = []
|
105
|
+
|
106
|
+
# Initialize doesn't write anything to a CSV,
|
107
|
+
# but does create the traverse_board and opens the waggoner for reading / writing
|
108
|
+
Dir.mkdir(self.traverse_board) if !self.astrolabe && Dir.glob(self.traverse_board).empty?
|
109
|
+
|
110
|
+
# Once the traverse_board (dir) exists, then check if the rhumb_lines (file) already exists, and set our rhumb_lines counter
|
111
|
+
@swabbie = self.insult_swabbie
|
112
|
+
# Then open the rhumb_lines
|
113
|
+
self.rhumb_lines = File.open(File.expand_path(self.poop_deck),self.astrolabe ? "r" : "a")
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.create(*args)
|
117
|
+
csv_pirate = CsvPirate.new({
|
118
|
+
:chart => args.first[:chart],
|
119
|
+
:aft => args.first[:aft],
|
120
|
+
:gibbet => args.first[:gibbet],
|
121
|
+
:chronometer => args.first[:chronometer],
|
122
|
+
:waggoner => args.first[:waggoner],
|
123
|
+
:swag => args.first[:swag],
|
124
|
+
:swab => args.first[:swab],
|
125
|
+
:mop => args.first[:mop],
|
126
|
+
:grub => args.first[:grub],
|
127
|
+
:spyglasses => args.first[:spyglasses],
|
128
|
+
:booty => args.first[:booty],
|
129
|
+
:astrolabe => args.first[:astrolabe],
|
130
|
+
:bury_treasure => args.first[:bury_treasure]
|
131
|
+
})
|
132
|
+
csv_pirate.hoist_mainstay()
|
133
|
+
csv_pirate
|
134
|
+
end
|
135
|
+
|
136
|
+
########################################
|
137
|
+
########### INSTANCE METHODS ###########
|
138
|
+
########################################
|
139
|
+
|
140
|
+
# This is the hardest working method. Get your shovels!
|
141
|
+
def dig_for_treasure(&block)
|
142
|
+
return false unless block_given?
|
143
|
+
|
144
|
+
if !grub.nil?
|
145
|
+
self.swag = grub
|
146
|
+
spyglasses.each {|x| self.swag = self.swag.send(x) }
|
147
|
+
end
|
148
|
+
|
149
|
+
treasure_chest = self.swag.map do |spoils|
|
150
|
+
self.prize(spoils)
|
151
|
+
end
|
152
|
+
|
153
|
+
treasure_chest.each do |loot|
|
154
|
+
yield loot
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def prize(spoils)
|
159
|
+
gold_doubloons = []
|
160
|
+
self.booty.each do |plunder|
|
161
|
+
gold_doubloons << spoils.send(plunder.to_sym)
|
162
|
+
end
|
163
|
+
gold_doubloons
|
164
|
+
end
|
165
|
+
|
166
|
+
def scrivener(msg)
|
167
|
+
self.rhumb_lines.puts msg
|
168
|
+
end
|
169
|
+
|
170
|
+
# Sail through your db looking for buried treasure!
|
171
|
+
# - restricted to loot that can be seen through spyglasses (if provided)!
|
172
|
+
def hoist_mainstay
|
173
|
+
|
174
|
+
self.swab_poop_deck
|
175
|
+
|
176
|
+
self.figurehead
|
177
|
+
|
178
|
+
self.bury_treasure ? self.buried_treasure = self.dead_mans_chest : self.dead_mans_chest
|
179
|
+
|
180
|
+
self.rhumb_lines.close
|
181
|
+
|
182
|
+
self.jolly_roger if CsvPirate.parlay > 1
|
183
|
+
|
184
|
+
# returns the array that was created before exporting it to CSV
|
185
|
+
return self.bury_treasure ? self.buried_treasure : true
|
186
|
+
end
|
187
|
+
|
188
|
+
def dead_mans_chest
|
189
|
+
self.dig_for_treasure do |treasure|
|
190
|
+
self.scrivener(treasure.map {|x| "#{x}"}.to_csv) # |x| marks the spot!
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def jolly_roger
|
195
|
+
if self.buried_treasure.is_a?(Array)
|
196
|
+
puts "Found #{self.buried_treasure.length} deniers buried here: '#{self.poop_deck}'" if CsvPirate.parlay > 1
|
197
|
+
puts "You must weigh_anchor to review your plunder!" if CsvPirate.parlay > 1
|
198
|
+
else
|
199
|
+
puts "Failed to locate treasure" if CsvPirate.parlay > 1
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# write the header of the CSV (column/method names)
|
204
|
+
def figurehead
|
205
|
+
self.scrivener(self.booty.to_csv)
|
206
|
+
end
|
207
|
+
|
208
|
+
def traverse_board
|
209
|
+
"#{RAILS_ROOT}/#{self.chart}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def sand_glass
|
213
|
+
"#{self.chronometer.respond_to?(:strftime) ? '.' + self.chronometer.strftime("%Y%m%d") : ''}"
|
214
|
+
end
|
215
|
+
|
216
|
+
#complete file path
|
217
|
+
def poop_deck
|
218
|
+
"#{self.analemma}#{self.swabbie}#{self.aft}"
|
219
|
+
end
|
220
|
+
|
221
|
+
def analemma
|
222
|
+
"#{self.traverse_board}#{self.waggoner}#{self.sand_glass}#{self.gibbet}"
|
223
|
+
end
|
224
|
+
|
225
|
+
def lantern
|
226
|
+
"#{self.analemma}.*"
|
227
|
+
end
|
228
|
+
|
229
|
+
def filibuster(flotsam)
|
230
|
+
base = File.basename(flotsam, self.aft)
|
231
|
+
index = base.rindex('.')
|
232
|
+
tail = index.nil? ? nil : base[index+1,base.length]
|
233
|
+
# Ensure numbers
|
234
|
+
counter = tail.nil? ? 0 : tail[/\d*/].to_i
|
235
|
+
end
|
236
|
+
|
237
|
+
def boatswain
|
238
|
+
return self.swabbie unless self.swabbie.nil?
|
239
|
+
counter = 0
|
240
|
+
bowspirit = Dir.glob(self.lantern)
|
241
|
+
highval = 0
|
242
|
+
bowspirit.each do |flotsam|
|
243
|
+
counter = self.filibuster(flotsam)
|
244
|
+
highval = ((highval <=> counter) == 1) ? highval : counter
|
245
|
+
counter = 0
|
246
|
+
end
|
247
|
+
self.swabbie = ".#{highval + 1}"
|
248
|
+
end
|
249
|
+
|
250
|
+
def coxswain
|
251
|
+
return self.swabbie unless self.swabbie.nil?
|
252
|
+
self.swabbie = ".#{Time.now.strftime("%I%M%S")}"
|
253
|
+
end
|
254
|
+
|
255
|
+
# Swabs the poop_deck if the mop is clean. (!)
|
256
|
+
def swab_poop_deck
|
257
|
+
self.rhumb_lines.truncate(0) if self.swab == :none && self.mop == :clean && File.size(self.poop_deck) > 0
|
258
|
+
end
|
259
|
+
|
260
|
+
# Sets the swabby file counter
|
261
|
+
def insult_swabbie
|
262
|
+
return case self.swab
|
263
|
+
when :counter
|
264
|
+
self.boatswain
|
265
|
+
when :timestamp
|
266
|
+
self.coxswain
|
267
|
+
else
|
268
|
+
self.swabbie = ""
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Must be done on order to rummage through the loot found by the pirate ship
|
273
|
+
def weigh_anchor
|
274
|
+
CsvPirate.rinse(self.poop_deck)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Sink your own ship! Or run a block of code on each row of the current CSV
|
278
|
+
def scuttle(&block)
|
279
|
+
return false unless block_given?
|
280
|
+
CsvPirate.broadside(self.poop_deck) do |careen|
|
281
|
+
yield careen
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
########################################
|
286
|
+
############ CLASS METHODS #############
|
287
|
+
########################################
|
288
|
+
|
289
|
+
# Used to read any loot found by any pirate
|
290
|
+
def self.rinse(quarterdeck)
|
291
|
+
File.open(File.expand_path(quarterdeck), "r") do |bucket_line|
|
292
|
+
bucket_line.each_line do |bucket|
|
293
|
+
puts bucket
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Sink other ships! Or run a block of code on each row of a CSV
|
299
|
+
def self.broadside(galley, &block)
|
300
|
+
return false unless block_given?
|
301
|
+
count = 1 if report_kills
|
302
|
+
FasterCSV.foreach(galley, {:headers => :first_row, :return_headers => false}) do |gun|
|
303
|
+
puts "Galleys sunk: #{count+=1}" if CsvPirate.parlay > 1
|
304
|
+
yield gun
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# During a mutiny things are a little different!
|
309
|
+
# Essentially you are using an existing CSV to drive queries to create a second CSV cased on the first
|
310
|
+
# The capn hash is:
|
311
|
+
# :grub => is the class on which to make booty [method] calls
|
312
|
+
# :swag => column index in CSV (0th, 1st, 2nd, etc. column?)
|
313
|
+
# (swag OR spyglasses can be specified, but code defers to swag if provided)
|
314
|
+
# :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the first CSV
|
315
|
+
# (swag OR spyglasses can be specified, but code defers to swag if provided)
|
316
|
+
# :waggoner => where the capn's loot was stashed (filename)
|
317
|
+
# :chart => directory where capn's waggoner is located
|
318
|
+
# :astrolabe => true (file is opened at top of file in read only mode when true)
|
319
|
+
# The first_mate hash is:
|
320
|
+
# :grub => is the class on which to make booty [method] calls, or
|
321
|
+
# is a method (as a string) we call to get from the object loaded by capn,
|
322
|
+
# to the object on which we'll make the first_mate booty [method] calls, or nil, if same object
|
323
|
+
# :swag => is the method to call on first CSV row's object to find second CSV row's object (if grub is a class)
|
324
|
+
# :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the second CSV (if grub is a class)
|
325
|
+
# :booty => is the methods to call on the ARrr object for each row on the second CSV
|
326
|
+
# :waggoner => where to stash the first mate's loot (filename)
|
327
|
+
# :chart => directory where first mate's waggoner is located
|
328
|
+
# :astrolabe => false (false is the default for astrolabe, so we could leave it off the first_mate)
|
329
|
+
#
|
330
|
+
# Example:
|
331
|
+
# capn = {:grub => User,:spyglasses => [:inactive],:booty => ['id','login','status'],:waggoner => 'orig',:chart => 'log/csv/',:astrolabe => false}
|
332
|
+
# make_orig = CsvPirate.new(capn)
|
333
|
+
# make_orig.hoist_mainstay
|
334
|
+
# make_orig.weigh_anchor
|
335
|
+
#
|
336
|
+
# first_mate = {:grub => 'account',:booty => ["id","number","name","created_at"],:waggoner => 'fake',:chart => 'log/csv/'}
|
337
|
+
# OR
|
338
|
+
# # for same class, we re-use the object loaded from first CSV and make the booty [method] calls on it
|
339
|
+
# first_mate = {:grub => User,:booty => ["id","login","visits_count"],:waggoner => 'fake',:chart => 'log/csv/'}
|
340
|
+
# OR
|
341
|
+
# first_mate = {:grub => Account,:spyglasses => 'id',:swag=>'user_id',:booty => ["id","name","number"],:waggoner => 'fake',:chart => 'log/csv/'}
|
342
|
+
# AND
|
343
|
+
# capn = {:grub => User,:spyglasses => 'login',:swag => 1,:waggoner => 'orig',:chart => 'log/csv/',:astrolabe => true}
|
344
|
+
# after_mutiny = CsvPirate.mutiny(capn, first_mate)
|
345
|
+
#
|
346
|
+
def self.mutiny(capn, first_mate)
|
347
|
+
carrack = CsvPirate.new(capn)
|
348
|
+
cutthroat = CsvPirate.new(first_mate)
|
349
|
+
|
350
|
+
cutthroat.figurehead
|
351
|
+
|
352
|
+
carrack.scuttle do |cutlass|
|
353
|
+
puts "CUTLASS: #{cutlass.inspect}" if CsvPirate.parlay > 2
|
354
|
+
puts "CARRACK.SWAG: #{carrack.swag.inspect}" if CsvPirate.parlay > 2
|
355
|
+
backstaff = cutlass[carrack.swag] || cutlass["#{carrack.spyglasses}"]
|
356
|
+
puts "BACKSTAFF: #{backstaff}" if CsvPirate.parlay > 2
|
357
|
+
puts "CARRACK.SPYGLASSES: #{carrack.spyglasses.inspect}" if CsvPirate.parlay > 2
|
358
|
+
gully = carrack.grub.send("find_by_#{carrack.spyglasses}".to_sym, backstaff)
|
359
|
+
puts "GULLY: #{gully.inspect}" if CsvPirate.parlay > 2
|
360
|
+
if gully
|
361
|
+
flotsam = cutthroat.grub.is_a?(String) ?
|
362
|
+
gully.send(cutthroat.grub.to_sym) :
|
363
|
+
cutthroat.grub.is_a?(Symbol) ?
|
364
|
+
gully.send(cutthroat.grub) :
|
365
|
+
cutthroat.grub.class == carrack.grub.class ?
|
366
|
+
gully :
|
367
|
+
cutthroat.grub.class == Class ?
|
368
|
+
cutthroat.grub.send("find_by_#{cutthroat.swag}", gully.send(cutthroat.spyglasses)) :
|
369
|
+
nil
|
370
|
+
puts "FLOTSAM: #{flotsam.inspect}" if CsvPirate.parlay > 2
|
371
|
+
if flotsam
|
372
|
+
plunder = cutthroat.prize(flotsam)
|
373
|
+
cutthroat.buried_treasure << plunder
|
374
|
+
cutthroat.scrivener(plunder.map {|bulkhead| "#{bulkhead}"}.join(','))
|
375
|
+
else
|
376
|
+
puts "Unable to locate: #{cutthroat.grub} related to #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'" if CsvPirate.parlay > 1
|
377
|
+
end
|
378
|
+
else
|
379
|
+
puts "Unable to locate: #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'" if CsvPirate.parlay > 1
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
carrack.rhumb_lines.close
|
384
|
+
cutthroat.rhumb_lines.close
|
385
|
+
|
386
|
+
cutthroat.jolly_roger
|
387
|
+
|
388
|
+
# returns the array that is created before exporting it to CSV
|
389
|
+
return cutthroat
|
390
|
+
end
|
391
|
+
|
392
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module NinthBit
|
2
|
+
module PirateShip
|
3
|
+
|
4
|
+
module ActMethods
|
5
|
+
#coding tyle is adopted from attachment_fu
|
6
|
+
def has_csv_pirate_ship(options = {})
|
7
|
+
if options.blank?
|
8
|
+
raise ArgumentError, "must provide required options"
|
9
|
+
end
|
10
|
+
options[:chart] ||= 'log/'
|
11
|
+
options[:aft] ||= '.csv'
|
12
|
+
options[:gibbet] ||= '.export'
|
13
|
+
#Needs to be defined at runtime, doesn't make sense here
|
14
|
+
#options[:chronometer] ||= Date.today
|
15
|
+
options[:waggoner] ||= "#{self}"
|
16
|
+
options[:swag] ||= nil
|
17
|
+
options[:swab] ||= :counter
|
18
|
+
options[:mop] ||= :clean
|
19
|
+
options[:grub] ||= self if options[:swag].nil?
|
20
|
+
options[:spyglasses] ||= [:all]
|
21
|
+
options[:booty] ||= self.column_names
|
22
|
+
options[:bury_treasure] ||= true
|
23
|
+
options[:parlay] ||= 1
|
24
|
+
|
25
|
+
# if they provide both
|
26
|
+
raise ArgumentError, "must provide either specify :swag or :grub" if !options[:swag].blank? && !options[:grub].blank?
|
27
|
+
# if they provide neither
|
28
|
+
raise ArgumentError, "must provide either specify :swag or :grub, not both" if options[:swag].blank? && options[:grub].blank?
|
29
|
+
raise ArgumentError, ":swab is #{options[:swab].inspect}, but must be one of #{CsvPirate::BOOKIE.inspect}" unless CsvPirate::BOOKIE.include?(options[:swab])
|
30
|
+
raise ArgumentError, ":mop is #{options[:mop].inspect}, but must be one of #{CsvPirate::MOP_HEADS.inspect}" unless CsvPirate::MOP_HEADS.include?(options[:mop])
|
31
|
+
raise ArgumentError, ":gibbet is #{options[:gibbet].inspect}, and does not contain a '.' character, which is required for iterative filenames" if options[:gibbet].nil? || !options[:gibbet].include?('.')
|
32
|
+
raise ArgumentError, ":waggoner is #{options[:waggoner].inspect}, and must be a string at least one character long" if options[:waggoner].nil? || options[:waggoner].length < 1
|
33
|
+
raise ArgumentError, ":booty is #{options[:booty].inspect}, and must be an array of methods to call on a class for CSV data" if options[:booty].nil? || options[:booty].empty?
|
34
|
+
|
35
|
+
extend ClassMethods unless (class << self; included_modules; end).include?(ClassMethods)
|
36
|
+
|
37
|
+
self.piratey_options = options
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
|
44
|
+
def self.extended(base)
|
45
|
+
base.class_inheritable_accessor :piratey_options
|
46
|
+
end
|
47
|
+
|
48
|
+
def walk_the_plank(args = {})
|
49
|
+
CsvPirate.parlay = args[:parlay] || self.piratey_options[:parlay]
|
50
|
+
CsvPirate.create({
|
51
|
+
:chart => args[:chart] || self.piratey_options[:chart],
|
52
|
+
:aft => args[:aft] || self.piratey_options[:aft],
|
53
|
+
:gibbet => args[:gibbet] || self.piratey_options[:gibbet],
|
54
|
+
:chronometer => args[:chronometer] || Date.today,
|
55
|
+
:waggoner => args[:waggoner] || self.piratey_options[:waggoner] || "#{self}",
|
56
|
+
:swag => args[:swag] || self.piratey_options[:swag],
|
57
|
+
:swab => args[:swab] || self.piratey_options[:swab],
|
58
|
+
:mop => args[:mop] || self.piratey_options[:mop],
|
59
|
+
:grub => args[:grub] || self.piratey_options[:grub],
|
60
|
+
:spyglasses => args[:spyglasses] || self.piratey_options[:spyglasses],
|
61
|
+
:booty => args[:booty] || self.piratey_options[:booty],
|
62
|
+
:bury_treasure => args[:bury_treasure] || self.piratey_options[:bury_treasure]
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'csv_pirate'
|
2
|
+
require 'ninth_bit/pirate_ship'
|
3
|
+
|
4
|
+
# If you are using this on a vanilla Ruby class (no rails or active record) then extend your class like this:
|
5
|
+
# MyClass.send(:extend, NinthBit::PirateShip::ActMethods) if defined?(MyClass)
|
6
|
+
# Alternatively you can do this inside your class definition:
|
7
|
+
# class MyClass
|
8
|
+
# extend NinthBit::PirateShip::ActMethods
|
9
|
+
# end
|
10
|
+
# If you are using ActiveRecord then it is done for you :)
|
11
|
+
ActiveRecord::Base.send(:extend, NinthBit::PirateShip::ActMethods) if defined?(ActiveRecord)
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv_pirate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Boling
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-01 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: |-
|
17
|
+
CsvPirate is the easy way to create a CSV of essentially anything in Rails, in full pirate regalia.
|
18
|
+
It works better if you are wearing a tricorne!
|
19
|
+
email: peter.boling@gmail.com
|
20
|
+
executables: []
|
21
|
+
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files:
|
25
|
+
- LICENSE
|
26
|
+
- README.rdoc
|
27
|
+
files:
|
28
|
+
- CHANGELOG
|
29
|
+
- LICENSE
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- VERSION.yml
|
33
|
+
- about.yml
|
34
|
+
- csv_pirate.gemspec
|
35
|
+
- init.rb
|
36
|
+
- install.rb
|
37
|
+
- lib/csv_pirate.rb
|
38
|
+
- lib/ninth_bit/pirate_ship.rb
|
39
|
+
- rails/init.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/pboling/csv_pirate
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.3.5
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Easily create CSVs of any data that can be derived from your models (using pirates!).
|
68
|
+
test_files:
|
69
|
+
- spec/csv_pirate_spec.rb
|
70
|
+
- spec/spec_helper.rb
|