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.
@@ -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