csv_pirate 2.4.4 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +30 -20
- data/VERSION.yml +3 -3
- data/csv_pirate.gemspec +1 -1
- data/lib/csv_pirate.rb +43 -30
- data/lib/ninth_bit/pirate_ship.rb +3 -2
- metadata +1 -1
data/CHANGELOG
CHANGED
data/README.rdoc
CHANGED
@@ -5,30 +5,33 @@ Easily create CSVs of any data that can be derived from your models.
|
|
5
5
|
CsvPirate is the easy way to create a CSV of essentially anything in Rails, in full pirate regalia.
|
6
6
|
It works better if you are wearing a tricorne!
|
7
7
|
|
8
|
+
My goal is to have it do EVERYTHING it possibly can for me, since almost every project I do needs CSV exports.
|
9
|
+
|
8
10
|
CsvPirate only works for commissions of swag OR grub!
|
9
11
|
|
10
12
|
Initialize method (a.k.a new()) takes a hash of params:
|
11
13
|
|
12
14
|
# CsvPirate only works for commissions of swag OR grub!
|
13
|
-
# :swag
|
14
|
-
# :grub
|
15
|
-
# :spyglasses
|
16
|
-
#
|
17
|
-
# :booty
|
18
|
-
# :chart
|
19
|
-
# :wagonner
|
20
|
-
# :aft
|
21
|
-
# :shrouds
|
22
|
-
# :chronometer
|
23
|
-
# :gibbet
|
24
|
-
# :swab
|
15
|
+
# :swag the ARrr collection of swag to work on (optional)
|
16
|
+
# :grub the ARrr class that the spyglasses will be used on (optional)
|
17
|
+
# :spyglasses named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses,
|
18
|
+
# and order them according to the order of the spyglasses (optional)
|
19
|
+
# :booty booty (columns/methods) on your model that you want printed in the CSV, also used to create the figurehead (CSV header)
|
20
|
+
# :chart array of directory names (relative to rails root if using rails) which will be the filepath where you want to hide your loot
|
21
|
+
# :wagonner name of document where you will give detailed descriptions of the loot
|
22
|
+
# :aft filename extention ('.csv')
|
23
|
+
# :shrouds CSV column separator, default is ','. For tsv, tab-delimited, "\t"
|
24
|
+
# :chronometer keeps track of when you hunt for treasure
|
25
|
+
# :gibbet filename spacer after the date, and before the iterative counter/timestamp. MuST contain a '.'
|
26
|
+
# :swab can be :counter, :timestamp, or :none
|
25
27
|
# :counter - default, each successive run will create a new file using a counter
|
26
28
|
# :timestamp - each successive run will create a new file using a HHMMSS time stamp
|
27
29
|
# :none - no iterative file naming convention, just use waggoner and aft
|
28
|
-
# :mop
|
30
|
+
# :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
|
29
31
|
# :clear - do not use :counter or :timestamp, and instead overwrite the file
|
30
32
|
# :dirty - do not use :counter, or :timestamp, or :overwrite. Just keep adding on.
|
31
|
-
#
|
33
|
+
# :bury_treasure should we store the csv data as it is collected in an array in Ruby form for later use (true), or just write the CSV (false)?
|
34
|
+
# check the source to see if there anything else hiding in there!
|
32
35
|
|
33
36
|
The create method has the same parameters, and actually creates the CSV.
|
34
37
|
|
@@ -71,6 +74,13 @@ Plugin using Git:
|
|
71
74
|
ruby script/plugin install git://github.com/pboling/csv_pirate.git
|
72
75
|
|
73
76
|
|
77
|
+
== Upgrading
|
78
|
+
|
79
|
+
In older versions :chart was a string which indicated where you wanted to hide the loot (write the csv file)
|
80
|
+
Now it must be an array of directory names. So if you want your loot in "log/csv/pirates/model_name", then chart is:
|
81
|
+
['log','csv','pirates','model_name']
|
82
|
+
CsvPirate ensures that whatever you choose as your chart exists in the filesystem, and creates the directories if need be.
|
83
|
+
|
74
84
|
== Example & Usage
|
75
85
|
|
76
86
|
Assuming a Make (as in manufacturers of automobiles) model like this:
|
@@ -152,7 +162,7 @@ The following two sets of code are identical:
|
|
152
162
|
:spyglasses => [:active,:logged_in],
|
153
163
|
:waggoner => 'active_users_logged_in',
|
154
164
|
:booty => ["id","number","login","created_at"],
|
155
|
-
:chart => 'log
|
165
|
+
:chart => ['log','csv']
|
156
166
|
})
|
157
167
|
csv_pirate.hoist_mainstay() # creates CSV file and writes out the rows
|
158
168
|
|
@@ -161,7 +171,7 @@ The following two sets of code are identical:
|
|
161
171
|
:spyglasses => [:active,:logged_in],
|
162
172
|
:waggoner => 'active_users_logged_in',
|
163
173
|
:booty => ["id","number","login","created_at"],
|
164
|
-
:chart => 'log
|
174
|
+
:chart => ['log','csv']
|
165
175
|
})# creates CSV file and writes out the rows
|
166
176
|
|
167
177
|
Another example using swag instead of grub:
|
@@ -171,7 +181,7 @@ Another example using swag instead of grub:
|
|
171
181
|
:swag => users,
|
172
182
|
:waggoner => 'inactive_users_not_logged_in',
|
173
183
|
:booty => ["id","number","login","created_at"],
|
174
|
-
:chart => 'log
|
184
|
+
:chart => ['log','csv']
|
175
185
|
})
|
176
186
|
csv_pirate.hoist_mainstay()
|
177
187
|
|
@@ -181,11 +191,11 @@ Then if you want to get your hands on the loot immediately:
|
|
181
191
|
|
182
192
|
For those who can't help but copy/paste into console and then edit:
|
183
193
|
|
184
|
-
csv_pirate = CsvPirate.new({:grub => User,:spyglasses => [:active,:logged_in],:waggoner => 'active_users_logged_in',:booty => ["id","number","login","created_at"],:chart => 'log
|
194
|
+
csv_pirate = CsvPirate.new({:grub => User,:spyglasses => [:active,:logged_in],:waggoner => 'active_users_logged_in',:booty => ["id","number","login","created_at"],:chart => ['log','csv']})
|
185
195
|
|
186
196
|
OR
|
187
197
|
|
188
|
-
csv_pirate = CsvPirate.new({:swag => users,:waggoner => 'inactive_users_not_logged_in',:booty => ["id","number","login","created_at"],:chart => 'log
|
198
|
+
csv_pirate = CsvPirate.new({:swag => users,:waggoner => 'inactive_users_not_logged_in',:booty => ["id","number","login","created_at"],:chart => ['log','csv']})
|
189
199
|
|
190
200
|
|
191
201
|
== Downloading the CSV
|
@@ -257,7 +267,7 @@ You have a VehicleModel class and the same Make class as up above:
|
|
257
267
|
|
258
268
|
Then to create the CSV:
|
259
269
|
|
260
|
-
a = VehicleModel.
|
270
|
+
a = VehicleModel.blindfold
|
261
271
|
|
262
272
|
Then check the output from the console:
|
263
273
|
|
data/VERSION.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
---
|
2
|
-
:patch:
|
3
|
-
:major:
|
4
|
-
:minor:
|
2
|
+
:patch: 0
|
3
|
+
:major: 3
|
4
|
+
:minor: 1
|
data/csv_pirate.gemspec
CHANGED
data/lib/csv_pirate.rb
CHANGED
@@ -15,7 +15,7 @@ class CsvPirate
|
|
15
15
|
MOP_HEADS = [:clean, :dirty]
|
16
16
|
|
17
17
|
attr_accessor :waggoner #filename
|
18
|
-
attr_accessor :chart #directory, default is ('log
|
18
|
+
attr_accessor :chart #directory, default is (['log','csv'])
|
19
19
|
attr_accessor :aft #extension, default is ('.csv')
|
20
20
|
attr_accessor :gibbet #part of the filename after waggoner and date, before swabbie and aft
|
21
21
|
attr_accessor :chronometer
|
@@ -56,7 +56,7 @@ class CsvPirate
|
|
56
56
|
# :spyglasses named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses,
|
57
57
|
# and order them according to the order of the spyglasses (optional)
|
58
58
|
# :booty booty (columns/methods) on your model that you want printed in the CSV, also used to create the figurehead (CSV header)
|
59
|
-
# :chart
|
59
|
+
# :chart array of directory names (relative to rails root if using rails) which will be the filepath where you want to hide your loot
|
60
60
|
# :wagonner name of document where you will give detailed descriptions of the loot
|
61
61
|
# :aft filename extention ('.csv')
|
62
62
|
# :shrouds CSV column separator, default is ','. For tsv, tab-delimited, "\t"
|
@@ -92,13 +92,15 @@ class CsvPirate
|
|
92
92
|
@gibbet = args.first[:gibbet] || '.export'
|
93
93
|
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?('.')
|
94
94
|
|
95
|
-
@waggoner = args.first[:waggoner] || "#{self.grub}"
|
95
|
+
@waggoner = args.first[:waggoner] || "#{self.grub || self.swag}"
|
96
96
|
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
|
97
97
|
|
98
98
|
@booty = args.first[:booty] || []
|
99
|
-
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?
|
99
|
+
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].is_a?(Array) || args.first[:booty].empty?
|
100
|
+
|
101
|
+
@chart = args.first[:chart] || ['log','csv']
|
102
|
+
raise ArgumentError, ":chart is #{args.first[:chart].inspect}, and must be an array of directory names, which will become the filepath for the csv file" if args.first[:chart].nil? || !args.first[:chart].is_a?(Array) || args.first[:booty].empty?
|
100
103
|
|
101
|
-
@chart = args.first[:chart] || 'log/'
|
102
104
|
@aft = args.first[:aft] || '.csv'
|
103
105
|
@chronometer = args.first[:chronometer] || Date.today
|
104
106
|
|
@@ -112,7 +114,7 @@ class CsvPirate
|
|
112
114
|
|
113
115
|
# Initialize doesn't write anything to a CSV,
|
114
116
|
# but does create the traverse_board and opens the waggoner for reading / writing
|
115
|
-
|
117
|
+
self.northwest_passage unless self.astrolabe
|
116
118
|
|
117
119
|
# This will contain the text of the csv from this particular execution
|
118
120
|
@maroon = ""
|
@@ -232,28 +234,11 @@ class CsvPirate
|
|
232
234
|
end
|
233
235
|
end
|
234
236
|
|
235
|
-
def traverse_board
|
236
|
-
#If we have rails environment then we use rails root, otherwise self.chart stands on its own as a relative path
|
237
|
-
"#{defined?(Rails) ? Rails.root + '/' : defined?(RAILS_ROOT) ? RAILS_ROOT + '/' : ''}#{self.chart}"
|
238
|
-
end
|
239
|
-
|
240
|
-
def sand_glass
|
241
|
-
"#{self.chronometer.respond_to?(:strftime) ? '.' + self.chronometer.strftime("%Y%m%d") : ''}"
|
242
|
-
end
|
243
|
-
|
244
237
|
#complete file path
|
245
238
|
def poop_deck
|
246
239
|
"#{self.analemma}#{self.swabbie}#{self.aft}"
|
247
240
|
end
|
248
241
|
|
249
|
-
def merchantman
|
250
|
-
"#{self.waggoner}#{self.sand_glass}#{self.gibbet}"
|
251
|
-
end
|
252
|
-
|
253
|
-
def analemma
|
254
|
-
"#{self.traverse_board}#{self.merchantman}"
|
255
|
-
end
|
256
|
-
|
257
242
|
# Swabs the poop_deck if the mop is clean. (!)
|
258
243
|
def swab_poop_deck
|
259
244
|
self.rhumb_lines.truncate(0) if self.swab == :none && self.mop == :clean && File.size(self.poop_deck) > 0
|
@@ -274,6 +259,34 @@ class CsvPirate
|
|
274
259
|
|
275
260
|
protected
|
276
261
|
|
262
|
+
def traverse_board
|
263
|
+
#If we have rails environment then we use rails root, otherwise self.chart stands on its own as a relative path
|
264
|
+
"#{self.north_pole}#{self.chart.join('/')}/"
|
265
|
+
end
|
266
|
+
|
267
|
+
def sand_glass
|
268
|
+
"#{self.chronometer.respond_to?(:strftime) ? '.' + self.chronometer.strftime("%Y%m%d") : ''}"
|
269
|
+
end
|
270
|
+
|
271
|
+
def merchantman
|
272
|
+
"#{self.waggoner}#{self.sand_glass}#{self.gibbet}"
|
273
|
+
end
|
274
|
+
|
275
|
+
def analemma
|
276
|
+
"#{self.traverse_board}#{self.merchantman}"
|
277
|
+
end
|
278
|
+
|
279
|
+
def north_pole
|
280
|
+
"#{defined?(Rails) ? "#{Rails.root}/" : defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/" : ''}"
|
281
|
+
end
|
282
|
+
|
283
|
+
def northwest_passage
|
284
|
+
self.chart.length.times do |i|
|
285
|
+
north_star = self.north_pole + self.chart[0..i].join('/')
|
286
|
+
Dir.mkdir(north_star) if Dir.glob(north_star).empty?
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
277
290
|
def lantern
|
278
291
|
"#{self.analemma}.*"
|
279
292
|
end
|
@@ -372,7 +385,7 @@ class CsvPirate
|
|
372
385
|
# :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the first CSV
|
373
386
|
# (swag OR spyglasses can be specified, but code defers to swag if provided)
|
374
387
|
# :waggoner => where the capn's loot was stashed (filename)
|
375
|
-
# :chart => directory where capn's waggoner is located
|
388
|
+
# :chart => array of directory names where capn's waggoner is located
|
376
389
|
# :astrolabe => true (file is opened at top of file in read only mode when true)
|
377
390
|
# The first_mate hash is:
|
378
391
|
# :grub => is the class on which to make booty [method] calls, or
|
@@ -382,23 +395,23 @@ class CsvPirate
|
|
382
395
|
# :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the second CSV (if grub is a class)
|
383
396
|
# :booty => is the methods to call on the ARrr object for each row on the second CSV
|
384
397
|
# :waggoner => where to stash the first mate's loot (filename)
|
385
|
-
# :chart => directory where first mate's waggoner is located
|
398
|
+
# :chart => array of directory names where first mate's waggoner is located
|
386
399
|
# :astrolabe => false (false is the default for astrolabe, so we could leave it off the first_mate)
|
387
400
|
#
|
388
401
|
# Example:
|
389
|
-
# capn = {:grub => User,:spyglasses => [:inactive],:booty => ['id','login','status'],:waggoner => 'orig',:chart => 'log
|
402
|
+
# capn = {:grub => User,:spyglasses => [:inactive],:booty => ['id','login','status'],:waggoner => 'orig',:chart => ['log','csv'],:astrolabe => false}
|
390
403
|
# make_orig = CsvPirate.new(capn)
|
391
404
|
# make_orig.hoist_mainstay
|
392
405
|
# make_orig.weigh_anchor
|
393
406
|
#
|
394
|
-
# first_mate = {:grub => 'account',:booty => ["id","number","name","created_at"],:waggoner => 'fake',:chart => 'log
|
407
|
+
# first_mate = {:grub => 'account',:booty => ["id","number","name","created_at"],:waggoner => 'fake',:chart => ['log','csv']}
|
395
408
|
# OR
|
396
409
|
# # for same class, we re-use the object loaded from first CSV and make the booty [method] calls on it
|
397
|
-
# first_mate = {:grub => User,:booty => ["id","login","visits_count"],:waggoner => 'fake',:chart => 'log
|
410
|
+
# first_mate = {:grub => User,:booty => ["id","login","visits_count"],:waggoner => 'fake',:chart => ['log','csv']}
|
398
411
|
# OR
|
399
|
-
# first_mate = {:grub => Account,:spyglasses => 'id',:swag=>'user_id',:booty => ["id","name","number"],:waggoner => 'fake',:chart => 'log
|
412
|
+
# first_mate = {:grub => Account,:spyglasses => 'id',:swag=>'user_id',:booty => ["id","name","number"],:waggoner => 'fake',:chart => ['log','csv']}
|
400
413
|
# AND
|
401
|
-
# capn = {:grub => User,:spyglasses => 'login',:swag => 1,:waggoner => 'orig',:chart => 'log
|
414
|
+
# capn = {:grub => User,:spyglasses => 'login',:swag => 1,:waggoner => 'orig',:chart => ['log','csv'],:astrolabe => true}
|
402
415
|
# after_mutiny = CsvPirate.mutiny(capn, first_mate)
|
403
416
|
#
|
404
417
|
def self.mutiny(capn, first_mate)
|
@@ -6,7 +6,7 @@ module NinthBit
|
|
6
6
|
def has_csv_pirate_ship(options = {})
|
7
7
|
raise ArgumentError, "must provide required options" if options.blank?
|
8
8
|
|
9
|
-
options[:chart] ||= 'log
|
9
|
+
options[:chart] ||= ['log','csv']
|
10
10
|
options[:aft] ||= '.csv'
|
11
11
|
options[:gibbet] ||= '.export'
|
12
12
|
#Needs to be defined at runtime, doesn't make sense here
|
@@ -30,7 +30,8 @@ module NinthBit
|
|
30
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
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
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?
|
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].is_a?(Array) || options[:booty].empty?
|
34
|
+
raise ArgumentError, ":chart is #{options[:chart].inspect}, and must be an array of directory names, which will become the filepath for the csv file" if options[:chart].nil? || !options[:chart].is_a?(Array) || options[:chart].empty?
|
34
35
|
|
35
36
|
extend ClassMethods unless (class << self; included_modules; end).include?(ClassMethods)
|
36
37
|
|