csv_pirate 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ Version 2.0.1 2009-10-01
2
+ - Fixed Readme formatting
3
+
4
+ Version 2.0.0 2009-09-30
5
+ - Added a new API a la attachment_fu
6
+ - Fixed bug: now works when specifying grub with no spyglasses
7
+ - Fixed rails/init.rb for use as gem with rails
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.
@@ -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.
@@ -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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 2
3
+ :minor: 0
4
+ :patch: 1
@@ -0,0 +1,7 @@
1
+ author:
2
+ name: Peter H. Boling
3
+ homepage: http://www.peterboling.com
4
+ summary: How does a Pirate create CSVs? With the waggoner, charts, spyglasses, chronometers, swag, and grub.
5
+ version: 2.0.1
6
+ rails_version: 1.0+
7
+ license: MIT
@@ -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
@@ -0,0 +1 @@
1
+ puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
@@ -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
@@ -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)
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "CsvPirate" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+
5
+ require 'csv_pirate'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
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