pboling-csv_pirate 0.0.2

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,4 @@
1
+ ---
2
+ :minor: 0
3
+ :patch: 2
4
+ :major: 0
@@ -0,0 +1,276 @@
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
+ attr_accessor :waggoner #filename
15
+ attr_accessor :chart #directory
16
+ attr_accessor :aft #extension
17
+ attr_accessor :chronometer
18
+
19
+ # Must provide swag or grub (not both)
20
+ attr_accessor :swag # ARrr array of objects
21
+ attr_accessor :grub # ARrr class
22
+ # spyglasses is only used with grub, not swag
23
+ attr_accessor :spyglasses # named_scopes
24
+
25
+ # These are the booty of the CSV
26
+ # Should be methods/columns on the swag
27
+ # also used to create the figurehead (CSV header)
28
+ attr_accessor :booty # methods
29
+
30
+ # The array that gets built as we write the CSV... could be useful?
31
+ attr_accessor :buried_treasure # info array
32
+
33
+ attr_accessor :rhumb_lines # the file object to write the CSV lines to
34
+
35
+ attr_accessor :astrolabe # when true then read only CsvPirate instance for loading of CSVs
36
+
37
+ # CsvPirate only works for commissions of swag OR grub!
38
+ # swag: the ARrr collection of swag to work on (optional)
39
+ # grub: the ARrr class that the spyglasses will be used on (optional)
40
+ # spyglasses: named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses,
41
+ # and order them according to the order of the spyglasses (optional)
42
+ # booty: booty on your model that you want printed in the CSV
43
+ # chart: name of directory where you want to hide your loot
44
+ # wagonner: name of document where you will give detailed descriptions of the loot
45
+ # chronometer: keeps track of when you hunt for treasure
46
+ # See README for examples
47
+
48
+ def initialize(*args)
49
+ return if args.empty?
50
+
51
+ self.chart = args.first[:chart] || 'log/'
52
+ self.aft = args.first[:aft] || '.csv'
53
+ self.chronometer = args.first[:chronometer] || Date.today
54
+ self.waggoner = args.first[:waggoner]
55
+
56
+ self.swag = args.first[:swag]
57
+ self.grub = args.first[:grub]
58
+
59
+ self.spyglasses = (args.first[:spyglasses] || []) if self.grub
60
+
61
+ self.booty = args.first[:booty] || []
62
+
63
+ self.astrolabe = args.first[:astrolabe] || false
64
+
65
+ self.buried_treasure = []
66
+
67
+ # Initialize doesn't write anything to a CSV,
68
+ # but does create the traverse_board and opens the waggoner for writing
69
+ Dir.mkdir(self.traverse_board) if !self.astrolabe && Dir.glob(self.traverse_board).empty?
70
+ self.rhumb_lines = File.open(File.expand_path(self.poop_deck),self.astrolabe ? "r" : "a")
71
+ end
72
+
73
+ ########################################
74
+ ########### INSTANCE METHODS ###########
75
+ ########################################
76
+
77
+ # This is the hardest working method. Get your shovels!
78
+ def dig_for_treasure(&block)
79
+ return false unless block_given?
80
+
81
+ if !grub.nil?
82
+ self.swag = grub
83
+ spyglasses.each {|x| self.swag = self.swag.send(x) }
84
+ end
85
+
86
+ treasure_chest = self.swag.map do |spoils|
87
+ self.prize(spoils)
88
+ end
89
+
90
+ treasure_chest.each do |loot|
91
+ yield loot
92
+ end
93
+ end
94
+
95
+ def prize(spoils)
96
+ gold_doubloons = []
97
+ self.booty.each do |plunder|
98
+ gold_doubloons << spoils.send(plunder.to_sym)
99
+ end
100
+ gold_doubloons
101
+ end
102
+
103
+ def scrivener(msg)
104
+ self.rhumb_lines.puts msg
105
+ end
106
+
107
+ # Sail through your db looking for buried treasure!
108
+ # - restricted to loot that can be seen through spyglasses (if provided)!
109
+ def hoist_mainstay
110
+ # check essential args
111
+ return "Pirate needs a waggoner to write on!" if self.waggoner.blank?
112
+ return "Pirate needs some swag or grub!" if self.swag.blank? && self.grub.blank?
113
+ return "Pirate needs some booty!" if self.booty.blank?
114
+
115
+ self.figurehead
116
+
117
+ self.buried_treasure = self.dig_for_treasure do |treasure|
118
+ self.scrivener(treasure.map {|x| "#{x}"}.to_csv) # |x| marks the spot!
119
+ end
120
+
121
+ self.rhumb_lines.close
122
+
123
+ self.jolly_roger
124
+
125
+ # returns the array that is created before exporting it to CSV
126
+ return self.buried_treasure
127
+ end
128
+
129
+ def jolly_roger
130
+ if self.buried_treasure.is_a?(Array)
131
+ puts "Found #{self.buried_treasure.length} deniers buried here: '#{self.poop_deck}'"
132
+ puts "You must weigh_anchor to review your plunder!"
133
+ else
134
+ puts "Failed to locate treasure"
135
+ end
136
+ end
137
+
138
+ # write the header of the CSV (column names)
139
+ def figurehead
140
+ self.scrivener(self.booty.to_csv)
141
+ end
142
+
143
+ def traverse_board
144
+ "#{RAILS_ROOT}/#{self.chart}"
145
+ end
146
+
147
+ def sand_glass
148
+ "#{self.chronometer.respond_to?(:strftime) ? '_' + self.chronometer.strftime("%Y_%m_%d") : ''}"
149
+ end
150
+
151
+ #complete file path
152
+ def poop_deck
153
+ "#{self.traverse_board}#{self.waggoner}#{self.sand_glass}" + self.aft
154
+ end
155
+
156
+ # Must be done on order to rummage through the loot found by the pirate ship
157
+ def weigh_anchor
158
+ CsvPirate.swab(self.poop_deck)
159
+ end
160
+
161
+ # Sink your own ship! Or run a block of code on each row of the current CSV
162
+ def scuttle(&block)
163
+ return false unless block_given?
164
+ CsvPirate.broadside(self.poop_deck, true) do |careen|
165
+ yield careen
166
+ end
167
+ end
168
+
169
+ ########################################
170
+ ############ CLASS METHODS #############
171
+ ########################################
172
+
173
+ # Used to read any loot found by any pirate
174
+ def self.swab(quarterdeck)
175
+ File.open(File.expand_path(quarterdeck), "r") do |swabbie|
176
+ swabbie.each_line do |swab|
177
+ puts swab
178
+ end
179
+ end
180
+ end
181
+
182
+ # Sink other ships! Or run a block of code on each row of a CSV
183
+ def self.broadside(galley, report_kills = true, &block)
184
+ return false unless block_given?
185
+ count = 1 if report_kills
186
+ FasterCSV.foreach(galley, {:headers => :first_row, :return_headers => false}) do |gun|
187
+ yield gun
188
+ puts "Galleys sunk: #{(count+=1).to_s}" if report_kills
189
+ end
190
+ end
191
+
192
+ # During a mutiny things are a little different!
193
+ # Essentially you are using an existing CSV to drive queries to create a second CSV cased on the first
194
+ # The capn hash is:
195
+ # :grub => is the class on which to make booty [method] calls
196
+ # :swag => column index in CSV (0th, 1st, 2nd, etc. column?)
197
+ # (swag OR spyglasses can be specified, but code defers to swag if provided)
198
+ # :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the first CSV
199
+ # (swag OR spyglasses can be specified, but code defers to swag if provided)
200
+ # :waggoner => where the capn's loot was stashed (filename)
201
+ # :chart => directory where capn's waggoner is located
202
+ # :astrolabe => true (file is opened at top of file in read only mode when true)
203
+ # The first_mate hash is:
204
+ # :grub => is the class on which to make booty [method] calls, or
205
+ # is a method (as a string) we call to get from the object loaded by capn,
206
+ # to the object on which we'll make the first_mate booty [method] calls, or nil, if same object
207
+ # :swag => is the method to call on first CSV row's object to find second CSV row's object (if grub is a class)
208
+ # :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the second CSV (if grub is a class)
209
+ # :booty => is the methods to call on the ARrr object for each row on the second CSV
210
+ # :waggoner => where to stash the first mate's loot (filename)
211
+ # :chart => directory where first mate's waggoner is located
212
+ # :astrolabe => false (false is the default for astrolabe, so we could leave it off the first_mate)
213
+ #
214
+ # Example:
215
+ # capn = {:grub => User,:spyglasses => [:inactive],:booty => ['id','login','status'],:waggoner => 'orig',:chart => 'log/csv/',:astrolabe => false}
216
+ # make_orig = CsvPirate.new(capn)
217
+ # make_orig.hoist_mainstay
218
+ # make_orig.weigh_anchor
219
+ #
220
+ # first_mate = {:grub => 'account',:booty => ["id","number","name","created_at"],:waggoner => 'fake',:chart => 'log/csv/'}
221
+ # OR
222
+ # # for same class, we re-use the object loaded from first CSV and make the booty [method] calls on it
223
+ # first_mate = {:grub => User,:booty => ["id","login","visits_count"],:waggoner => 'fake',:chart => 'log/csv/'}
224
+ # OR
225
+ # first_mate = {:grub => Account,:spyglasses => 'id',:swag=>'user_id',:booty => ["id","name","number"],:waggoner => 'fake',:chart => 'log/csv/'}
226
+ # AND
227
+ # capn = {:grub => User,:spyglasses => 'login',:swag => 1,:waggoner => 'orig',:chart => 'log/csv/',:astrolabe => true}
228
+ # after_mutiny = CsvPirate.mutiny(capn, first_mate)
229
+ #
230
+ def self.mutiny(capn, first_mate)
231
+ carrack = CsvPirate.new(capn)
232
+ cutthroat = CsvPirate.new(first_mate)
233
+
234
+ cutthroat.figurehead
235
+
236
+ carrack.scuttle do |cutlass|
237
+ puts "CUTLASS: #{cutlass.inspect}"
238
+ puts "CARRACK.SWAG: #{carrack.swag.inspect}"
239
+ backstaff = cutlass[carrack.swag] || cutlass["#{carrack.spyglasses}"]
240
+ puts "BACKSTAFF: #{backstaff}"
241
+ puts "CARRACK.SPYGLASSES: #{carrack.spyglasses.inspect}"
242
+ gully = carrack.grub.send("find_by_#{carrack.spyglasses}".to_sym, backstaff)
243
+ puts "GULLY: #{gully.inspect}"
244
+ if gully
245
+ flotsam = cutthroat.grub.is_a?(String) ?
246
+ gully.send(cutthroat.grub.to_sym) :
247
+ cutthroat.grub.is_a?(Symbol) ?
248
+ gully.send(cutthroat.grub) :
249
+ cutthroat.grub.class == carrack.grub.class ?
250
+ gully :
251
+ cutthroat.grub.class == Class ?
252
+ cutthroat.grub.send("find_by_#{cutthroat.swag}", gully.send(cutthroat.spyglasses)) :
253
+ nil
254
+ puts "FLOTSAM: #{flotsam.inspect}"
255
+ if flotsam
256
+ plunder = cutthroat.prize(flotsam)
257
+ cutthroat.buried_treasure << plunder
258
+ cutthroat.scrivener(plunder.map {|bulkhead| "#{bulkhead}"}.join(','))
259
+ else
260
+ puts "Unable to locate: #{cutthroat.grub} related to #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'"
261
+ end
262
+ else
263
+ puts "Unable to locate: #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'"
264
+ end
265
+ end
266
+
267
+ carrack.rhumb_lines.close
268
+ cutthroat.rhumb_lines.close
269
+
270
+ cutthroat.jolly_roger
271
+
272
+ # returns the array that is created before exporting it to CSV
273
+ return cutthroat
274
+ end
275
+
276
+ end
@@ -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,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pboling-csv_pirate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - pboling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-04 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TODO
17
+ email: peter.boling@peterboling.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - VERSION.yml
26
+ - lib/csv_pirate.rb
27
+ - spec/csv_pirate_spec.rb
28
+ - spec/spec_helper.rb
29
+ has_rdoc: true
30
+ homepage: http://github.com/pboling/csv_pirate
31
+ post_install_message:
32
+ rdoc_options:
33
+ - --inline-source
34
+ - --charset=UTF-8
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.2.0
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: TODO
56
+ test_files: []
57
+