lib/csv_pirate.rb in csv_pirate-4.1.4 vs lib/csv_pirate.rb in csv_pirate-5.0.0

- old
+ new

@@ -5,688 +5,20 @@ #License: MIT License #Labels: Ruby, Rails, Gem, Plugin #Version: 1.0 #Project owners: # peter.boling (The Cap'n) +require 'yaml' + if RUBY_VERSION.to_f >= 1.9 require 'csv' else require 'faster_csv' end -class CsvPirate +require 'csv_pirate/version.rb' +require 'csv_pirate/the_capn.rb' +require 'csv_pirate/pirate_ship.rb' - BOOKIE = [:counter, :timestamp, :none] - MOP_HEADS = [:clean, :dirty] - BRIGANTINE = [:first, :last] - CSV_CLASS = (defined?(CSV) ? CSV : FasterCSV) - - attr_accessor :waggoner # First part of filename - attr_accessor :chart # directory, default is (['log','csv']) - attr_accessor :aft # extension, default is ('.csv') - attr_accessor :gibbet # part of the filename after waggoner and date, before swabbie and aft - attr_accessor :chronometer # Date object or false - - # Must provide swag or grub (not both) - attr_accessor :swag # Array of objects - attr_accessor :grub # Class - # spyglasses is only used with grub, not swag - attr_accessor :spyglasses # named_scopes +module CsvPirate - # These are the booty of the CSV - # Should be methods/columns on the swag - # also used to create the figurehead (CSV header) - attr_accessor :booty # methods / columns - - attr_accessor :bury_treasure # should we store the csv data in an array for later inspection, or just write the CSV? - # default is false - # The array that gets built as we write the CSV... could be useful? - attr_accessor :buried_treasure # info array - - attr_accessor :rhumb_lines # the file object to write the CSV lines to - - attr_accessor :astrolabe # when true then read only CsvPirate instance for loading of CSVs - - attr_accessor :shrouds # CSV column separator - - attr_accessor :swab # default is :counter - attr_accessor :mop # default is :clean (only has an effect if :swab is :none) since overwriting is irrelevant for a new file - attr_accessor :swabbie # value of the counter / timestamp - attr_accessor :maroon # text of csv - attr_accessor :nocturnal # basename of the filepath (i.e. filename) - attr_accessor :blackjack # Hash: Specify how you want your CSV header - # {:join => '-'} joins the method names called to get hte data for that column with '_' underscores. - # {:humanize => '-'} first joins as above, then humanizes the string - # {:array => ['col1',col2','col3'] Uses the column names provided in the array. If the array provided is too short defaults to :humanize =>'_' - - attr_accessor :brigantine # the complete file path - attr_accessor :pinnacle # the complete file path - class << self - attr_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 - end - - # CsvPirate only works for commissions of swag OR grub! - # :swag the ARrr collection of swag to work on (optional) - # :grub the ARrr class that the spyglasses will be used on (optional) - # :spyglasses named scopes in your model that will refine the rows in the CSV according to conditions of the spyglasses, - # and order them according to the order of the spyglasses (optional) - # :booty booty (columns/methods) on your model that you want printed in the CSV, also used to create the figurehead (CSV header) - # :chart array of directory names (relative to rails root if using rails) which will be the filepath where you want to hide your loot - # :wagonner name of document where you will give detailed descriptions of the loot - # :aft filename extention ('.csv') - # :shrouds CSV column separator, default is ','. For tsv, tab-delimited, "\t" - # :chronometer keeps track of when you hunt for treasure, can be false if you don't want to keep track. - # :gibbet filename spacer after the date, and before the iterative counter/timestamp. MuST contain a '.' - # :swab can be :counter, :timestamp, or :none - # :counter - default, each successive run will create a new file using a counter - # :timestamp - each successive run will create a new file using a HHMMSS time stamp - # :none - no iterative file naming convention, just use waggoner, aft and gibbet - # :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 - # :clean - do not use :counter or :timestamp, and instead overwrite the file - # :dirty - do not use :counter, or :timestamp, or :overwrite. Just keep adding on. - # :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)? - # :blackjack Specify how you want your CSV header - # {:join => '-'} joins the method names called to get hte data for that column with '_' underscores. - # {:humanize =>'-'} first joins as above, then humanizes the string - # {:array => ['col1',col2','col3'] Uses the column names provided. If the array provided is too short defaults to :humanize =>'_' - # See README for examples - - def initialize(*args) - raise ArgumentError, "must provide required options" if args.nil? - - @swag = args.first[:swag] - @grub = args.first[:grub] - - # if they provide both - raise ArgumentError, "must provide either :swag or :grub, not both" if !self.swag.nil? && !self.grub.nil? - # if they provide neither - raise ArgumentError, "must provide either :swag or :grub" if self.swag.nil? && self.grub.nil? - - @swab = args.first[:swab] || :counter - raise ArgumentError, ":swab is #{self.swab.inspect}, but must be one of #{CsvPirate::BOOKIE.inspect}" unless CsvPirate::BOOKIE.include?(self.swab) - - @mop = args.first[:mop] || :clean - raise ArgumentError, ":mop is #{self.mop.inspect}, but must be one of #{CsvPirate::MOP_HEADS.inspect}" unless CsvPirate::MOP_HEADS.include?(self.mop) - - @gibbet = args.first[:gibbet] || '.export' - raise ArgumentError, ":gibbet is #{self.gibbet.inspect}, and does not contain a '.' character, which is required when using iterative filenames (set :swab => :none to turn off iterative filenames)" if self.swab != :none && (self.gibbet.nil? || !self.gibbet.include?('.')) - - @waggoner = args.first[:waggoner] || "#{self.grub || self.swag}" - raise ArgumentError, ":waggoner is #{self.waggoner.inspect}, and must be a string at least one character long" if self.waggoner.nil? || self.waggoner.length < 1 - - # Not checking if empty here because PirateShip will send [] if the database is unavailable, to allow tasks like rake db:create to run - @booty = args.first[:booty] || [] # would like to use Class#instance_variables for generic classes - raise ArgumentError, ":booty is #{self.booty.inspect}, and must be an array of methods to call on a class for CSV data" if self.booty.nil? || !self.booty.is_a?(Array) - - @chart = args.first[:chart] || ['log','csv'] - raise ArgumentError, ":chart is #{self.chart.inspect}, and must be an array of directory names, which will become the filepath for the csv file" if self.chart.nil? || !self.chart.is_a?(Array) || self.chart.empty? - - @aft = args.first[:aft] || '.csv' - @chronometer = args.first[:chronometer] == false ? false : (args.first[:chronometer] || Date.today) - - @spyglasses = (args.first[:spyglasses] || (self.respond_to?(:all) ? [:all] : nil)) if self.grub - - @shrouds = args.first[:shrouds] || ',' # for tsv, tab-delimited, "\t" - raise ArgumentError, ":shrouds is #{self.shrouds.inspect}, and must be a string (e.g. ',' or '\t'), which will be used as the delimeter for the csv file" if self.shrouds.nil? || !self.shrouds.is_a?(String) - - @astrolabe = args.first[:astrolabe] || false - - @bury_treasure = args.first[:astrolabe] || false - @buried_treasure = [] - - #does not rely on rails humanize! - @blackjack = args.first[:blackjack] || {:humanize => '_'} - - #Make sure the header array is the same length as the booty columns - if self.blackjack.keys.first == :array && (self.blackjack.values.first.length != self.booty.length) - @blackjack = {:join => '_'} - puts "Warning: :blackjack reset to {:join => '_'} because the length of the :booty is different than the length of the array provided to :blackjack" if CsvPirate.parlance(2) - end - - @pinnacle = self.block_and_tackle - - # Initialize doesn't write anything to a CSV, - # but does create the traverse_board and opens the waggoner for reading / writing - self.northwest_passage unless self.astrolabe - - # This will contain the text of the csv from this particular execution - @maroon = "" - - # Once the traverse_board (dir) exists, then check if the rhumb_lines (file) already exists, and set our rhumb_lines counter - @swabbie = self.insult_swabbie - - @brigantine = self.poop_deck(args.first[:brigantine]) - - @nocturnal = File.basename(self.brigantine) - - - # Then open the rhumb_lines - self.rhumb_lines = File.open(File.expand_path(self.brigantine),self.astrolabe ? "r" : "a") - end - - def self.create(*args) - csv_pirate = CsvPirate.new({ - :chart => args.first[:chart], - :aft => args.first[:aft], - :gibbet => args.first[:gibbet], - :chronometer => args.first[:chronometer], - :waggoner => args.first[:waggoner], - :swag => args.first[:swag], - :swab => args.first[:swab], - :shrouds => args.first[:shrouds], - :mop => args.first[:mop], - :grub => args.first[:grub], - :spyglasses => args.first[:spyglasses], - :booty => args.first[:booty], - :astrolabe => args.first[:astrolabe], - :blackjack => args.first[:blackjack], - :bury_treasure => args.first[:bury_treasure] - }) - csv_pirate.hoist_mainstay() - csv_pirate - end - - ######################################## - ########### INSTANCE METHODS ########### - ######################################## - - # This is the hardest working method. Get your shovels! - def dig_for_treasure(&block) - return false unless block_given? - - if !grub.nil? - self.swag = grub - spyglasses.each {|x| self.swag = self.swag.send(x) } - end - - treasure_chest = self.swag.map do |spoils| - self.prize(spoils) - end - - treasure_chest.each do |loot| - yield loot - end - end - - def prize(spoils) - gold_doubloons = [] - self.booty.each do |plunder| - # Check for nestedness - if plunder.is_a?(Hash) - gold_doubloons << CsvPirate.marlinespike(spoils, plunder) - else - gold_doubloons << spoils.send(plunder.to_sym) - end - end - gold_doubloons - end - - def scrivener(msg) - self.rhumb_lines.puts msg - end - - # Sail through your db looking for buried treasure! - # - restricted to loot that can be seen through spyglasses (if provided)! - def hoist_mainstay - - self.swab_poop_deck - - self.dead_mans_chest - - self.rhumb_lines.close - - self.jolly_roger if CsvPirate.parlay && CsvPirate.parlance(1) - - # returns the text of this CSV export - return self.maroon - end - - def dead_mans_chest - self.maroon = CSV_CLASS.generate(:col_sep => self.shrouds) do |csv| - self.sounding(csv) - end - self.scrivener(self.maroon) - self.maroon - end - - def jolly_roger - if self.bury_treasure - if self.buried_treasure.is_a?(Array) - puts "Found #{self.buried_treasure.length} deniers buried here: '#{self.brigantine}'" if CsvPirate.parlay && CsvPirate.parlance(1) - puts "You must weigh_anchor to review your plunder!" if CsvPirate.parlay && CsvPirate.parlance(1) - else - puts "Failed to locate treasure" if CsvPirate.parlay && CsvPirate.parlance(1) - end - end - end - - def sounding(csv) - csv << self.block_and_tackle - # create the data for the csv - self.dig_for_treasure do |treasure| - moidore = treasure.map {|x| "#{x}"} - csv << moidore # |x| marks the spot! - self.buried_treasure << moidore if self.bury_treasure - end - end - - #Takes a potentially nested hash element of a booty array and turns it into a string for a column header - # hash = {:a => {:b => {:c => {:d => {"e" => "fghi"}}}}} - # run_through(hash, '_') - # => "a_b_c_d_e_fghi" - # Ooooh so recursive! - def run_through(hash, join_value) - hash.map do |k,v| - if v.is_a?(Hash) - [k,run_through(v, join_value)].join(join_value) - else #works for Symbols and Strings - [k,v].join(join_value) - end - end.first - end - - #complete file path - def poop_deck(brig) - if BRIGANTINE.include?(brig) - self.old_csv_dump(brig) - elsif brig.is_a?(String) - "#{self.analemma}#{brig}" - else - "#{self.analemma}#{self.swabbie}#{self.aft}" - end - end - - # Swabs the poop_deck (of the brigantine) if the mop is clean. (!) - def swab_poop_deck - self.rhumb_lines.truncate(0) if self.swab == :none && self.mop == :clean && File.size(self.brigantine) > 0 - end - - # Must be done on order to rummage through the loot found by the pirate ship - def weigh_anchor - CsvPirate.rinse(self.brigantine) - end - - # Sink your own ship! Or run a block of code on each row of the current CSV - def scuttle(&block) - return false unless block_given? - CsvPirate.broadside(self.brigantine) do |careen| - yield careen - end - end - - #permanence can be any of: - # {:new => :new} - only calls the initializer with data hash for each row to instantiate objects (useful with any vanilla Ruby Class) - # {:new => :save} - calls the initializer with the data hash for each row and then calls save on each (useful with ActiveRecord) - # {:new => :create} - calls a create method with the data hash for each row (useful with ActiveRecord) - # {:find_or_new => [column names for find_by]} - see below (returns only the new objects - # {:find_or_save => [column names for find_by]} - see below (returns all found or saved objects) - # {:find_or_create => [column names for find_by]} - looks for existing objects using find_by_#{columns.join('_and_')}, (returns all found or created objects) - # and if not found creates a new object. - # The difference between the new, save and create versions are the same as the various :new hashes above. - # {:update_or_new => [column names for find_by]} - see below (returns only the new objects) - # {:update_or_save => [column names for find_by]} - see below (returns all updated or saved objects) - # {:update_or_create => [column names for find_by]} - looks for existing objects using find_by_#{columns.join('_and_')} , (returns all updated or created objects) - # and updates them with the data hash form the csv row, otherwise creates a new object. - #TODO: This is a nasty method. Just a quick hack to GTD. Needs to be rethought and refactored. --pboling - def to_memory(permanence = {:new => :new}, exclude_id = true, exclude_timestamps = true) - return nil unless self.grub - begin - example = self.grub.new - rescue Exception - puts "cannot instantiate instance of #{self.grub} with #{self.grub}.new. CsvPirate#to_memory works most reliably when #{self.grub}.new works with no arguments." if CsvPirate.parlance(1) - example = nil - end - buccaneers = [] - self.scuttle do |row| - data_hash = self.data_hash_from_row(row, exclude_id, exclude_timestamps, example) - case permanence - when {:new => :new} then - buccaneers << self.grub.new(data_hash) - when {:new => :save} then - obj = self.grub.new(data_hash) - buccaneers << obj.save(false) - when {:new => :create} then - buccaneers << self.grub.create(data_hash) - else - if permanence[:find_or_new] - obj = self.send_aye(data_hash, permanence[:find_or_new]) - buccaneers << self.grub.new(data_hash) if obj.nil? - elsif permanence[:find_or_save] - obj = self.send_aye(data_hash, permanence[:find_or_save]) - if obj.nil? - obj = self.grub.new(data_hash) - obj.save(false) if obj.respond_to?(:save) - end - buccaneers << obj - elsif permanence[:find_or_create] - obj = self.send_aye(data_hash, permanence[:find_or_create]) - if obj.nil? - self.grub.create(data_hash) - end - buccaneers << obj - elsif permanence[:update_or_new] - obj = self.send_aye(data_hash, permanence[:update_or_new]) - if obj.nil? - obj = self.grub.new(data_hash) - else - self.save_object(obj, data_hash) - end - buccaneers << obj - elsif permanence[:update_or_save] - obj = self.send_aye(data_hash, permanence[:update_or_save]) - if obj.nil? - obj = self.grub.new(data_hash) - obj.save(false) - else - self.save_object(obj, data_hash) - end - buccaneers << obj - elsif permanence[:update_or_create] - obj = self.send_aye(data_hash, permanence[:update_or_create]) - if obj.nil? - obj = self.grub.create(data_hash) - else - self.save_object(obj, data_hash) - end - buccaneers << obj - end - end - end - buccaneers - end - - def save_object(obj, data_hash) - data_hash.each do |k,v| - obj.send("#{k}=".to_sym, v) - end - unless obj.save(false) - puts "Save Failed: #{obj.inspect}" if CsvPirate.parlance(1) - end - end - - def send_aye(data_hash, columns) - obj = self.grub.send(self.find_aye(columns), self.find_aye_arr(data_hash, columns)) - if obj - puts "#{self.grub}.#{find_aye(columns)}(#{self.find_aye_arr(data_hash, columns).inspect}): found id = #{obj.id}" if CsvPirate.parlance(2) - end - end - - def find_aye(columns) - "find_by_#{columns.join('_and_')}".to_sym - end - - def find_aye_arr(data_hash, columns) - columns.map do |col| - data_hash[col.to_s] - end - end - - def data_hash_from_row(row, exclude_id = true, exclude_timestamps = true, example = nil) - plunder = {} - my_booty = self.booty.reject {|x| x.is_a?(Hash)} - my_booty = exclude_id ? my_booty.reject {|x| a = x.to_sym; [:id, :ID,:dbid, :DBID, :db_id, :DB_ID].include?(a)} : self.booty - my_booty = exclude_timestamps ? my_booty.reject {|x| a = x.to_sym; [:created_at, :updated_at, :created_on, :updated_on].include?(a)} : self.booty - my_booty = my_booty.reject {|x| !example.respond_to?("#{x}=".to_sym)} unless example.nil? - my_booty.each do |method| - plunder = plunder.merge({method => row[self.pinnacle[self.booty.index(method)]]}) - end - plunder - end - - def data_hash_from_row(row, exclude_id = true, exclude_timestamps = true, example = nil) - data_hash = {} - my_booty = self.booty.reject {|x| x.is_a?(Hash)} - my_booty = exclude_id ? my_booty.reject {|x| a = x.to_sym; [:id, :ID,:dbid, :DBID, :db_id, :DB_ID].include?(a)} : self.booty - my_booty = exclude_timestamps ? my_booty.reject {|x| a = x.to_sym; [:created_at, :updated_at, :created_on, :updated_on].include?(a)} : self.booty - my_booty = my_booty.reject {|x| !example.respond_to?("#{x}=".to_sym)} unless example.nil? - my_booty.each do |method| - data_hash = data_hash.merge({method => row[self.pinnacle[self.booty.index(method)]]}) - end - data_hash - end - - # Grab an old CSV dump (first or last) - def old_csv_dump(brig) - file = Dir.entries(self.traverse_board).reject {|x| x.match(/^\./)}.sort.send(brig) - "#{self.traverse_board}#{file}" - end - - protected - - # create the header of the CSV (column/method names) - # returns an array of strings for CSV header based on blackjack - def block_and_tackle - self.blackjack.map do |k,v| - case k - #Joining is only relevant when the booty contains a nested hash of method calls as at least one of the booty array elements - #Use the booty (methods) as the column headers - when :join then self.binnacle(v, false) - #Use the humanized booty (methods) as the column headers - when :humanize then self.binnacle(v, true) - when :array then v - end - end.first - end - - #returns an array of strings for CSV header based on booty - def binnacle(join_value, humanize = true) - self.booty.map do |compass| - string = compass.is_a?(Hash) ? - self.run_through(compass, join_value) : - compass.is_a?(String) ? - compass : - compass.is_a?(Symbol) ? - compass.to_s : - compass.to_s - humanize ? string.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize : string - end - end - - # The directory path to the csv - def traverse_board - #If we have rails environment then we use rails root, otherwise self.chart stands on its own as a relative path - "#{self.north_pole}#{self.chart.join('/')}/" - end - - def sand_glass - "#{self.chronometer.respond_to?(:strftime) ? '.' + self.chronometer.strftime("%Y%m%d") : ''}" - end - - def merchantman - "#{self.waggoner}#{self.sand_glass}#{self.gibbet}" - end - - def analemma - "#{self.traverse_board}#{self.merchantman}" - end - - def north_pole - "#{defined?(Rails) ? "#{Rails.root}/" : defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/" : ''}" - end - - def northwest_passage - self.chart.length.times do |i| - north_star = self.north_pole + self.chart[0..i].join('/') - Dir.mkdir(north_star) if Dir.glob(north_star).empty? - end - end - - def lantern - "#{self.analemma}.*" - end - - def filibuster(flotsam) - base = File.basename(flotsam, self.aft) - index = base.rindex('.') - tail = index.nil? ? nil : base[index+1,base.length] - # Ensure numbers - tail.nil? ? 0 : tail[/\d*/].to_i - end - - # Get all the files in the dir - def axe - Dir.glob(self.lantern) - end - - # File increment for next CSV to dump - def boatswain - return self.swabbie unless self.swabbie.nil? - highval = 0 - self.axe.each do |flotsam| - counter = self.filibuster(flotsam) - highval = ((highval <=> counter) == 1) ? highval : counter - end - ".#{highval + 1}" - end - - def coxswain - return self.swabbie unless self.swabbie.nil? - ".#{Time.now.strftime("%I%M%S")}" - end - - # Sets the swabby file counter - def insult_swabbie - return case self.swab - when :counter - self.boatswain - when :timestamp - self.coxswain - else - "" - end - end - - public - - ######################################## - ############ CLASS METHODS ############# - ######################################## - - # if this is your booty: - # {:booty => [ - # :id, - # {:region => {:country => :name }, :state => :name }, - # :name - # ]} - # so nested_hash = {:region => {:country => :name }, :state => :name } - def self.marlinespike(spoils, navigation) - navigation.map do |east,west| - spoils = spoils.send(east.to_sym) - unless spoils.nil? - if west.is_a?(Hash) - # Recursive madness is here! - spoils = CsvPirate.marlinespike(spoils, west) - else - spoils = spoils.send(west.to_sym) - end - end - spoils - end.compact.join(' - ') - end - - # Used to read any loot found by any pirate - def self.rinse(quarterdeck) - File.open(File.expand_path(quarterdeck), "r") do |bucket_line| - bucket_line.each_line do |bucket| - puts bucket - end - end - end - - # Sink other ships! Or run a block of code on each row of a CSV - def self.broadside(galley, &block) - return false unless block_given? - CSV_CLASS.foreach(galley, {:headers => :first_row, :return_headers => false}) do |gun| - yield gun - end - end - - # During a mutiny things are a little different! - # Essentially you are using an existing CSV to drive queries to create a second CSV cased on the first - # The capn hash is: - # :grub => is the class on which to make booty [method] calls - # :swag => column index in CSV (0th, 1st, 2nd, etc. column?) - # (swag OR spyglasses can be specified, but code defers to swag if provided) - # :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the first CSV - # (swag OR spyglasses can be specified, but code defers to swag if provided) - # :waggoner => where the capn's loot was stashed (filename) - # :chart => array of directory names where capn's waggoner is located - # :astrolabe => true (file is opened at top of file in read only mode when true) - # The first_mate hash is: - # :grub => is the class on which to make booty [method] calls, or - # is a method (as a string) we call to get from the object loaded by capn, - # to the object on which we'll make the first_mate booty [method] calls, or nil, if same object - # :swag => is the method to call on first CSV row's object to find second CSV row's object (if grub is a class) - # :spyglasses => is the column to load ("find_by_#{booty}") the ARrr object for each row on the second CSV (if grub is a class) - # :booty => is the methods to call on the ARrr object for each row on the second CSV - # :waggoner => where to stash the first mate's loot (filename) - # :chart => array of directory names where first mate's waggoner is located - # :astrolabe => false (false is the default for astrolabe, so we could leave it off the first_mate) - # - # Example: -# capn = {:grub => User,:spyglasses => [:inactive],:booty => ['id','login','status'],:waggoner => 'orig',:chart => ['log','csv'],:astrolabe => false} -# make_orig = CsvPirate.new(capn) -# make_orig.hoist_mainstay -# make_orig.weigh_anchor -# -# first_mate = {:grub => 'account',:booty => ["id","number","name","created_at"],:waggoner => 'fake',:chart => ['log','csv']} -# OR -# # for same class, we re-use the object loaded from first CSV and make the booty [method] calls on it -# first_mate = {:grub => User,:booty => ["id","login","visits_count"],:waggoner => 'fake',:chart => ['log','csv']} -# OR -# first_mate = {:grub => Account,:spyglasses => 'id',:swag=>'user_id',:booty => ["id","name","number"],:waggoner => 'fake',:chart => ['log','csv']} -# AND -# capn = {:grub => User,:spyglasses => 'login',:swag => 1,:waggoner => 'orig',:chart => ['log','csv'],:astrolabe => true} -# after_mutiny = CsvPirate.mutiny(capn, first_mate) -# - def self.mutiny(capn, first_mate) - carrack = CsvPirate.new(capn) - cutthroat = CsvPirate.new(first_mate) - - cutthroat.figurehead - - carrack.scuttle do |cutlass| - puts "CUTLASS: #{cutlass.inspect}" if CsvPirate.parlance(2) - puts "CARRACK.SWAG: #{carrack.swag.inspect}" if CsvPirate.parlance(2) - backstaff = cutlass[carrack.swag] || cutlass["#{carrack.spyglasses}"] - puts "BACKSTAFF: #{backstaff}" if CsvPirate.parlance(2) - puts "CARRACK.SPYGLASSES: #{carrack.spyglasses.inspect}" if CsvPirate.parlance(2) - gully = carrack.grub.send("find_by_#{carrack.spyglasses}".to_sym, backstaff) - puts "GULLY: #{gully.inspect}" if CsvPirate.parlance(2) - if gully - flotsam = cutthroat.grub.is_a?(String) ? - gully.send(cutthroat.grub.to_sym) : - cutthroat.grub.is_a?(Symbol) ? - gully.send(cutthroat.grub) : - cutthroat.grub.class == carrack.grub.class ? - gully : - cutthroat.grub.class == Class ? - cutthroat.grub.send("find_by_#{cutthroat.swag}", gully.send(cutthroat.spyglasses)) : - nil - puts "FLOTSAM: #{flotsam.inspect}" if CsvPirate.parlance(2) - if flotsam - plunder = cutthroat.prize(flotsam) - cutthroat.buried_treasure << plunder - cutthroat.scrivener(plunder.map {|bulkhead| "#{bulkhead}"}.join(',')) - else - puts "Unable to locate: #{cutthroat.grub} related to #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'" if CsvPirate.parlance(1) - end - else - puts "Unable to locate: #{carrack.grub}.#{carrack.spyglasses} '#{gully.send(carrack.spyglasses)}'" if CsvPirate.parlance(1) - end - end - - carrack.rhumb_lines.close - cutthroat.rhumb_lines.close - - cutthroat.jolly_roger - - # returns the array that is created before exporting it to CSV - return cutthroat - end - - # verbosity on a scale of 0 - 3 (0=:none, 1=:error, 2=:info, 3=:debug, 0 being no screen output, 1 is default - def self.parlance(level = 1) - self.parlay.is_a?(Numeric) && self.parlay >= level - end - end