csv_pirate 2.0.1
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.
- 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
|